Found a bug? Have a great feature idea? Get on GitHub and tell us about it and we'll get right on it: https://eshop-optimizer.com


= 5.3 Marketplace category mapping, fulfillment overhaul, custom carriers (22/05/2026) =

** 🌍 Marketplace category mapping — Google + Facebook taxonomies wired end-to-end **
* NEW: "Marketplace" tab on every product_cat edit screen (Storefront Display → Discoverability → Category Extras). Two cascading drill-down pickers — one for Google Product Taxonomy, one for Facebook Marketplace Taxonomy — plus a search box with multi-locale autocomplete. Operator picks once per WC category; mapping cascades to every product in it.
* NEW: Multi-locale search across bundled taxonomy files. Operator can type the category name in English ("Cosmetics") and get hits even when the WP site locale is Greek — server returns the matched-locale path PLUS the site-locale rendering, so the saved value matches what the cascade dropdowns show (no language mismatch between admin display and feed output). Site locale resolves via the `aieo_dmm_marketplace_taxonomy_locale` filter / option (defaults to `el-GR`).
* NEW: `AIEO_DMM_Marketplace_Taxonomy` helper class at `includes/dmm/modules/class-aieo-dmm-marketplace-taxonomy.php`. Methods: `get_node($source, $id)`, `get_children($source, $parent_id)`, `search($source, $query, $limit)`, `resolve_pair($source, $id)`, `available_locales($source)`. Lazy per-locale payload caching — each JSON file (~800 KB Google en-US / 1.5 MB Google el-GR / 400 KB Facebook en-US) loaded once per request, parsed once, reused everywhere.
* NEW: Bundled taxonomy data at `assets/marketplace-taxonomies/` — Google (en-US: 5,595 nodes; el-GR: 5,595 nodes) + Facebook (en-US: 2,967 nodes), built from the official Google `taxonomy-with-ids.<locale>.txt` and Facebook `categories/en_US.txt`. Companion `convert.php` build script + the original `.txt` sources shipped so a future locale add is `curl + php convert.php` away.
* NEW: REST/AJAX endpoints `aieo_dmm_marketplace_tax_browse` (immediate children of a node) and `aieo_dmm_marketplace_tax_search` (free-text). Admin-only, nonce-gated via `AIEO_DMM::verify_ajax_request()`. Source argument validated to `google | facebook`.
* SCHEMA: 4 new columns on `wp_aieo_dmm_category_extras` — `google_category_id`, `google_category_path`, `facebook_category_id`, `facebook_category_path`. Backfilled via `aieo_dmm_ensure_column()` on existing installs.
* SCHEMA: 2 new columns on `wp_aieo_core_product_vitals` — `google_product_category`, `facebook_product_category`. Populated at vitals-build time so feed builders read them as pure column lookups (no per-product termmeta hits at feed-generation time).
* SCHEMA: 2 new columns on `wp_aieo_core_product_category_hierarchy` — same two cols. Populated by `wp_AIEO_InsertProductCategoryHierarchy` sproc.
* SCHEMA: `wp_AIEO_InsertProductCategoryHierarchy` sproc rewritten with 10 new LEFT JOINs against `wp_aieo_dmm_category_extras` (5 per source × 2 sources) and a leaf-first `COALESCE(NULLIF(...))` chain that walks the 5-level category breadcrumb from the deepest mapped category up to the top. Deepest mapping wins; products inherit from a parent category when their leaf isn't mapped.
* SCHEMA: `wp_AIEO_InsertProductVitals` sproc reads the two new cols from the hierarchy join (`tapc`) and writes them into vitals at the same call. `wp_AIEO_CombineVariationVitalsInProductVitals` propagates the same values from each variation's parent (via `tapcv`).
* NEW: Feed-builder resolvers `google_product_category` and `facebook_product_category` — priority chain: vitals column (denormalised via data-prep) → site-wide-default option (`aieo_feed_default_google_product_category` / `_facebook_…`) → empty. Slow product-object fallback path walks WC categories against `wp_aieo_dmm_category_extras` directly when vitals row is stale (e.g. preview before next data-prep tick).

** 🛒 Marketplace feeds — alphabetised + 4 new templates + bigger field map **
* NEW: **Facebook Product Catalog** template (RSS+g: envelope). 18 fields incl. `g:quantity_to_sell_on_facebook`, `g:additional_image_link`, `g:item_group_id` (groups variants), `g:custom_label_0/_1`, `g:google_product_category`. Matches the production-validated `alama-ing-products.xml` shape used by Femme Fatale's existing FB Catalog feed.
* NEW: **Facebook Marketplace** template. Same shape as the Catalog feed plus the Facebook-specific `<fb_product_category>` field (different taxonomy from Google's). Pre-loaded from `vitals.facebook_product_category` so a single mapping authoring step covers both Catalog Ads + Marketplace listings.
* NEW: **Amazon Inventory Loader** template (TSV, despite the `.csv` extension Amazon uses). 10 columns: sku, product-id, product-id-type (`4`=EAN default), price, item-condition (`11`=new), quantity, add-delete (`a`=add), will-ship-internationally, expedited-shipping, item-note.
* NEW: **eBay File Exchange** template (CSV). 16 columns covering the canonical bulk-listing shape: Action, CustomLabel, Title, Description, ConditionID (`1000`=new), PicURL, Quantity, Format (FixedPrice), StartPrice, Duration (GTC), Category, Country, Location, Currency, PayPalAccepted, ShippingService-1:Option. Per-seller fields (Category, Location, Shipping service) ship blank — operator fills in admin.
* CHANGE: Templates reordered alphabetically (Amazon → BestPrice → eBay → EMAG → Facebook Catalog → Generic CSV → Google AdWords Custom Remarketing → Google Local → Google Shopping → Shopflix → Skroutz → Tolstoy). The "Choose template" admin dropdown is now predictable; new templates MUST be inserted alphabetically.
* CHANGE: Google Shopping template materially enhanced — was 8 fields, now 16. Added `g:gtin` (from EAN), `g:condition`, `g:google_product_category`, `g:product_type` (full breadcrumb), `g:shipping_weight`, `g:item_group_id`. Same RSS+g: envelope as Google Local + Facebook so it renders correctly without per-template branching.
* CHANGE: Skroutz template gets `color` + `additional_image` (Skroutz's "Μόδα" / fashion category requirement). Color sourced from `wp_aieo_core_product_vitals.color`; additional_image returns the first URL from `image_gallery_urls` (Skroutz spec wants a single URL, not a CSV list).
* CHANGE: Shopflix template gets `Additional_Image` (matching its other PascalCase tags `ProductURL`, `Shipping_Lead_Time`). `Color` was already present.
* NEW: Engine-level `csv_delimiter` template config — overrides the default comma. Amazon + Google AdWords use `"\t"` for TSV output, header row + each data row honour the setting through both `fputcsv` calls.
* NEW: ~20 new field resolvers in `field_source_from_vitals()` AND `field_source()` (slow path mirror): `condition_new`, `price_with_currency` (e.g. "9.45 EUR"), `sale_price_with_currency`, `shipping_weight_kg` (e.g. "0.550 kg"), `availability` (Google/FB "in stock"/"out of stock"), `google_product_category`, `facebook_product_category`, `item_group_id`, `custom_label_0/_1`, `category_names`, `amazon_product_id_type/_condition_code/_add_action/_ship_intl/_expedited`, `ebay_action/_condition_id/_format/_duration/_paypal_accepted/_country/_location/_category_id/_shipping_service`.

** 📦 Fulfillment overhaul — orders-list "+ Voucher" flow rebuilt **
* NEW: Per-row stack in the WC orders list (HPOS + legacy): `+ Packing Slip` / `+ Voucher` / `+ Invoice` buttons, replacing the single "+ Voucher" link that used to send operators to the meta-box. Each button is one click — generates the PDF on demand and opens it in a new tab.
* NEW: Light-WC-green tint on already-printed buttons. State persists across page reloads via two new columns added to both fulfillment tables: `wp_aieo_dmm_fulfillment_documents.printed_at` + `print_count` and `wp_aieo_dmm_fulfillment_vouchers.printed_at` + `print_count`. `mark_printed()` + `has_printed_for_order()` helpers on both APIs.
* NEW: Print tracking wired across every fire path — single-row "+ Packing Slip" / "+ Invoice" click, voucher meta-box "Print" button, every bulk action ("Print packing slips (merged PDF)", "Print invoices", "Print picking labels (ZIP)"), and the date-range bulk merge/zip from the Documents subtab. One central state model; the orders list reflects all of them.
* NEW: Single page-level modal for `+ Voucher` (one DOM instance per page, re-targeted per click via `data-order-id`). Replaces the per-row inline popover that didn't scale on 20-row screens. Centered card + dimmed backdrop + Esc/backdrop-click to close + delegated event handlers so the script runs synchronously without racing against the modal HTML emit order.
* NEW: "Notify customer" checkbox in the voucher modal (checked by default). Saves the voucher AND fires a plain-text email to the customer with the carrier label, tracking number, and tracking URL substituted into the carrier's `tracking_url()` template. Email recipient is `WC_Order::get_billing_email()`; from-name/address pulled from `WC()->mailer()` so it threads with the rest of the shop's transactional mail. Also adds an audit order-note `Tracking number X (carrier) sent to customer at email@…` for traceability.
* NEW: Force-regenerate on every print click — the cached PDF is rebuilt each time so the "Print date" stamped in the header reflects when the operator actually pressed print, not when the PDF was first generated. Sequence values (e.g. invoice number) preserved across regenerates by `AIEO_Order_Document_Base::generate()`.
* NEW: AJAX endpoints `aieo_ff_print_doc_for_order` (per-row print), `aieo_ff_save_manual_voucher` (modal save), `aieo_ff_save_carrier_map` (linked-method dropdown), `aieo_ff_save_custom_carrier`, `aieo_ff_delete_custom_carrier`.

** 🔗 Linked WC shipping method ↔ AIEO carrier — single source of truth **
* NEW: "Linked WC shipping method" dropdown on every courier settings card (ACS, BoxNow, ELTA, Geniki, Speedex + every Custom carrier). One mapping per courier — operators wire each carrier to ONE WC shipping-method title (which may appear across many zones). Inversion of the original "one mapping per zone-instance" attempt — 6 mappings instead of 30+, saves on every shipping-method-per-zone duplicate.
* NEW: Storage `wp_aieo_dmm_fulfillment_settings.shipping_method_carrier_map = { "<title>": "<carrier_code>" }`. Keys are titles (strings), values are carrier codes. Save handler enforces "at most one title per courier" via a sweep that releases any previously-linked title for the same code before pinning the new one.
* NEW: Greyed-out conflict prevention. When rendering a "Linked WC shipping method" dropdown for courier X, any title already mapped to courier Y appears `<option disabled>` with a `(linked to <Y>)` suffix — no silent reassignment, no accidental theft. WC-disabled shipping methods also appear `<option disabled>` with `(disabled in WC)` so operators can't wire a method that won't ship.
* NEW: Helpers `get_carrier_map()`, `set_carrier_map_entry($title, $code)`, `get_carrier_for_order($order)` (reads order's shipping line title), `get_zone_shipping_methods($include_disabled)` (enumerates every WC zone's methods with `enabled`/`zone_name` fields), `get_distinct_shipping_titles()` (deduplicated index for the dropdown), `linked_carriers()` (operational subset — only carriers actually wired to enabled shipping methods).
* NEW: Pre-select on the orders-list `+ Voucher` modal — `data-preselect-carrier` attribute on each row's trigger button carries the resolved carrier code; the modal reads it on open and pre-selects the dropdown. Click `+ Voucher` on a Γενική Ταχυδρομική order → modal opens with `Γενική Ταχυδρομική` already chosen + cursor in the tracking input.

** 🚚 Custom carriers framework — operator-authored providers, no API required **
* NEW: `AIEO_Courier_Driver_Custom` class at `includes/dmm/fulfillment/drivers/class-aieo-courier-driver-custom.php`. Pseudo-driver — extends `AIEO_Courier_Driver_Base`, capabilities = `['create']` only, `create_mode = 'manual'`. Each instance is constructed on-the-fly from one row in the `custom_carriers` registry.
* NEW: "Carriers without API (paste tracking number)" section at the TOP of the Couriers tab. Operators add as many providers as they need — each with a Label ("Courier Center" / "DPD Greece" / etc.), a slug (auto-generated from the label, validated against the 5 hardcoded driver codes), a tracking URL template with `{voucher_no}` placeholder, and the same "Linked WC shipping method" dropdown that drives the orders-list pre-selection.
* NEW: Registry stored at `aieo_fulfillment_settings.custom_carriers = { "<slug>": { "label": "...", "tracking_url_template": "..." } }`. Custom carriers register themselves via the same `aieo_fulfillment_couriers` filter the hardcoded drivers use (late priority 50) so every existing call site picks them up automatically: chip rendering, modal dropdown, tracking URL on customer emails, voucher save, "Linked WC shipping method" picker, "Default carrier" Settings dropdown.
* NEW: Tracking URL substitution — `{voucher_no}`, `{tracking_no}`, `{number}`, and `%s` all supported as placeholders in the template. Operator-authored URL like `https://courier-center.gr/track?id={voucher_no}` becomes a working tracking link for each customer email.
* NEW: Helpers `get_custom_carriers()`, `save_custom_carrier($slug, $label, $template)`, `delete_custom_carrier($slug)` — delete also sweeps the carrier map (drops any title that pointed at the deleted code).
* CHANGE: AJAX endpoints `aieo_ff_save_custom_carrier` (upsert one row, optionally one-shot link to a shipping method title in the same call) and `aieo_ff_delete_custom_carrier`. Both verify via `verify_admin_ajax('aieo_fulfillment_admin')`.
* REMOVED: Legacy `AIEO_Courier_Driver_Manual` class — superseded by Custom carriers. The "Manual entry (other carrier)" card was a single anonymous catch-all whose role is now covered by named, tracking-URL-aware Custom carriers. The driver class file is archived on disk (operator can restore if any legacy voucher row absolutely needs it), the driver is no longer auto-loaded, all UI surfaces are clean. Existing voucher rows with `carrier='manual'` from the legacy era render their chip with the fallback label `MANUAL` (uppercased code) — benign, since new entries should use named Custom carriers.

** 📦 Packing slip template overhaul (both Despoina + Simple) **
* CHANGE: Title text is the order number rendered at packing-slip h1 size (`#491565`) — replaces the boilerplate "PACKING SLIP" heading. Operators (the picker on the warehouse floor) can spot the order number instantly without scanning into the meta block.
* CHANGE: Business address block removed from the header — pickers don't need the shop's own address. The shop logo + shop name stay.
* CHANGE: 4-column item table layout: `SKU` (18%, monospace, nowrap) · `Qty` (7%) · `Title with Barcode beneath` (62%, multi-line) · `Price` (13%, nowrap). Barcode rendered as a small monospace sub-line under each Title (10px grey) instead of a dedicated column — saves horizontal space, keeps Title on one row. `table-layout: fixed` + `!important` overrides defeat the bundled stylesheet's generic `td { word-wrap: break-word }` that was cracking "Price" + "4,99 €" across two lines.
* CHANGE: Footer rows use 2+2 colspan split (~25% label / ~75% value) — fixed the previous "Shipping method:Courier Center" run-together where the label fused to the value because the Price column was too narrow to hold the carrier name.
* NEW: Payment method row in the footer alongside Shipping method. Pulled via `WC_Order::get_payment_method_title()`. For COD orders (detected via `payment_method = cod | wc-cod` OR title contains "Αντικαταβολή" / "Cash on Delivery") the row renders in RED + BOLD + ⚠ warning glyph — operators can't miss a cash-on-delivery on the picking floor.
* NEW: SKU sort within line items via `usort($lines, fn => strnatcmp(sku, sku))`. Natural-string compare handles dotted-numeric SKUs (`2.07.0.0721` → `2.10.0.1562` sort correctly, not lexicographic). Empty-SKU rows drop to the bottom. Effect: related products cluster — pickers walk the shelf in order.
* NEW: Two date rows in the meta column — "Order date" (immutable, from `$order->get_date_created()`) and "Print date" (refreshed on every print via force-regenerate). Eliminates the previous confusion where the only date shown was the PDF generation date.
* CHANGE: Document # row dropped from the meta column — the big `#<order>` title already shows it, no need to repeat.

** 🛡️ MOI reconcile — customer email suppression **
* FIX: New `AIEO_MOI_Admin::suppress_wc_customer_emails()` helper. Installs filter chain at the top of both `ajax_reconcile()` (legacy single-shot path) and `ajax_reconcile_process()` (chunked-batch path). The chain disables 10 customer-facing email types (`customer_completed_order`, `customer_invoice`, `customer_note`, `customer_on_hold_order`, `customer_processing_order`, `customer_refunded_order`, `customer_reset_password`, `customer_new_account`, `customer_partially_refunded_order`, `customer_failed_order`) AND 3 admin-facing types (`new_order`, `cancelled_order`, `failed_order`) via `woocommerce_email_enabled_<key>` → `__return_false`. Also installs `pre_wp_mail → __return_false` as a belt-and-braces gate so any code path that bypasses WC's filters and calls `wp_mail()` directly gets dropped at the WP layer. Scope is the AJAX request only — when PHP shuts down the filters die with it, normal webhook/poll order traffic (separate requests) keeps emailing customers as designed. The admin description string at the top of the reconcile UI already promised "never customer-facing emails" — code now enforces what the UI was already advertising. Symptom before fix: reconciling years of historical Shopflix/Skroutz orders fired 1-2 customer "Your order is processing" emails per row, multiplied by the dataset size.

** 🔧 Other polish in 5.3 **
* NEW: New AIEO Shipping Rates per-zone listing keeps the "off" badge on disabled shipping methods (e.g. stoferno.gr toggled off in WC). Disabled methods stay visible (so operators can spot what's off + go re-enable) but they propagate the disabled state into every carrier-mapping dropdown.
* CHANGE: AJAX nonce-action used by the orders-list "+ Packing Slip" / "+ Invoice" buttons corrected to `aieo_fulfillment_admin` (was incorrectly using the broader `AIEO_DMM::NONCE_ACTION` and getting rejected by `verify_admin_ajax()` → silent "Print failed: error" alert). JS error-message path also improved — surfaces the actual HTTP status + response body on next failure instead of a generic "error" string.
* CHANGE: Picker-friendly label `Carriers without API (paste tracking number)` for the Custom carriers card — earlier draft "Custom carriers (manual / other)" used the word "manual" right next to the existing "Manual entry (other carrier)" hardcoded card, which operators conflated. Card moved to the TOP of the Couriers tab so operators reach it before scrolling past 5 API cards.
* CHANGE: "Default carrier" dropdown (Settings → Fulfillment → General) drops the legacy `manual` driver from its option list, picks up every Custom carrier automatically via the same filter — so a shop that ships 90% via "Courier Center" can set it as the default and save a click per order.
* CHANGE: Plugin version + readme Stable tag bumped to 5.3.


= 5.2 Export-lock hotfix + maintenance (22/05/2026) =

** 🐛 Bug #5 hotfix — export-stage mutex lockout (NEXT_RELEASE_PRIORITY_BUGS.md issue #5) **
* FIX: New `AIEO_Eshop_Optimizer::aieo_register_export_lock_shutdown()` helper. Registers a `register_shutdown_function()` that deletes the export-stage mutex transient on PHP fatal exit (E_ERROR / E_PARSE / E_CORE_ERROR / E_COMPILE_ERROR / E_USER_ERROR). Wired into all four export aggregation stages so a fatal mid-stage no longer leaves a 30-minute lockout behind ("X statistics are already being calculated by another process" error).
* FIX: TTL on the four export-stage mutex transients (`aieo_product_stats_lock`, `aieo_category_brand_stats_lock`, `aieo_customer_std_stats_lock`, `aieo_customer_adv_stats_lock`) dropped from 1800 s → 300 s. Covers the FPM-timeout / browser-drop / nginx-504 cases where the shutdown handler can't fire — worst-case stuck-lockout drops from 30 min to 5 min. Concurrent-write protection unchanged: 300 s comfortably outlives a single AJAX step.

** 🐛 MOI reconcile progress label — positional placeholders not substituting **
* FIX: AIEO_MOI_Admin Reconcile UI — the inline progress status (e.g. while clicking "Run reconcile" on the Vendors tab) was emitting the literal `Processed %1$d of %2$d…` because the JS substitution called `.replace("%d", …)` against a PHP-side `__('Processed %1$d of %2$d…', …)` whose format uses positional `%1$d` / `%2$d`. No `%d` substring existed in the format string, so both replaces were no-ops and the placeholders leaked into the UI verbatim (Greek translation hit it identically). Substitution corrected to `.replace("%1$d", processed).replace("%2$d", total)` — preserves the positional semantics translators rely on for reordering. PHP source string and .po msgstr unchanged.

** 🔒 WP-Cron race protection: per-job locks on marketplace feed + data-prep pipelines **
* FIX: `AIEO_DMM_Marketplace_Feeds::generate_feed_by_id($id)` now acquires a per-feed transient lock (`aieo_dmm_feed_gen_lock_{id}`, 10 min TTL) at entry, skips concurrent invocations with a `feed_already_running` WP_Error, releases at every exit (success + each early-return path) plus a shutdown handler that frees the lock on PHP fatal. Closes the "half the jobs running twice" symptom operators were seeing: WP-Cron's `doing_cron` transient race lets two near-simultaneous page loads fire the same scheduled event when traffic bursts overlap a feed's due moment; stale duplicate entries in `wp_options.cron` from any historic save flow would also accumulate forever; and clicking "Run now" while a cron tick was in flight ran the generator twice. Single change defends against all three. (Operator complaint: "kati paizei me ton actionscheduler san na kanei dipla ta misa jobs" — diagnosis was that the actual scheduler is WP-Cron, not AS.)
* FIX: `AIEO_DMM_Data_Prep_Scheduler::run_pipeline()` — same shape, single global lock (`aieo_dmm_data_prep_running`, 90 min TTL since the largest catalogue takes ~15-25 min). Worse blast radius than the feeds case: the pipeline TRUNCATEs + rebuilds the flat tables (`wp_aieo_core_product_vitals` etc.) via 5 sequential stored procedures, so a concurrent run would deadlock on table-write locks or corrupt the materialised results. Concurrent invocation now returns a `skipped: true` log entry instead of running.

** 📦 Customer-facing tracking renderer (was silently no-op'd) **
* NEW: `AIEO_Runtime_Fulfillment_Tracking` class shipped at `includes/dmm/fulfillment/class-aieo-runtime-fulfillment-tracking.php`. Renders the per-order shipment list (multi-voucher aware — iterates `AIEO_Fulfillment_Vouchers::get_for_order()`) on THREE customer-facing surfaces: WC transactional emails (`woocommerce_email_before_order_table`, already wired from `AIEO_Fulfillment_Emails`), my-account view-order page (`woocommerce_order_details_after_order_table` priority 20, newly registered), and newsletter campaigns via the new `[aieo_tracking_block]` shortcode (alias: `[aieo_tracking_shipments]`). Previously the renderer was referenced but never shipped, gated by `class_exists()` to avoid fatal — net effect was that the multi-shipment data correctly stored in `wp_aieo_dmm_fulfillment_vouchers` never reached the customer in any surface.
* NEW: Email markup uses table-based layout + inline styles for Outlook/Gmail/Apple Mail compat; my-account markup uses semantic classes (`.aieo-tracking-block`, `.aieo-tracking-row`, `.aieo-tracking-carrier`, `.aieo-tracking-voucher`, `.aieo-tracking-status`) so themes can restyle. Each row carries carrier label + voucher number (linkified to the public tracking page via `AIEO_Runtime_Fulfillment_URLs::tracking_url()`) + unified status (`In transit` / `Out for delivery` / `Delivered` with delivered-at timestamp / `Tracking not available` for untrackable vouchers / etc.). Carrier labels filterable via `aieo_dmm_fulfillment_carrier_label_map`.
* NEW: `[aieo_tracking_block order_id="X" context="email|account"]` shortcode for newsletter integration. Operators drop the shortcode in their TNP / MailPoet / Mailster template with the per-recipient merge tag (e.g. `[aieo_tracking_block order_id="{customer_last_order_id}"]`); the newsletter plugin substitutes the merge tag first, then runs `do_shortcode`, so each subscriber receives THEIR own tracking block. Empty when no `order_id` resolves — never errors. Context defaults to 'email' so newsletter HTML stays compat with email clients.

** 💥 Critical: WC Orders admin fatal — missing `AIEO_Runtime_Fulfillment_URLs` class **
* FIX: New `AIEO_Runtime_Fulfillment_URLs` class shipped at `includes/dmm/fulfillment/class-aieo-runtime-fulfillment-urls.php` and added to the foundation loader so it's available BEFORE the driver glob loads. All 5 courier drivers (acs / speedex / elta / geniki / boxnow) referenced `AIEO_Runtime_Fulfillment_URLs::tracking_url($carrier, $voucher_no)` from their `tracking_url()` override, but the class itself was never shipped. Every WC Orders list render that contained a voucher with one of those carriers fatalled with `Uncaught Error: Class "AIEO_Runtime_Fulfillment_URLs" not found` at the first row, followed by a 1 GB OOM cascade as the hook chain unwound (`do_action('manage_wc_orders_…_columns')` → `orders_list_column_body` → driver `->tracking_url($vno)`). Customer-facing impact: zero (admin-only). Admin impact: `/wp-admin/admin.php?page=wc-orders` returned HTTP 500 for any operator whose orders contained ≥1 voucher row. Carriers covered with public tracking URLs: ACS (acscourier.net), Speedex (speedex.gr/NewTrackAndTrace), ELTA (itemsearch.elta.gr), Geniki Taxydromiki (taxydromiki.com), BoxNow (boxnow.gr). Empty voucher or unknown carrier returns empty string — caller suppresses the link button gracefully. All URLs filterable via `aieo_dmm_fulfillment_tracking_url` so operators can override per-site if a carrier rotates its public endpoint.

** 🚚 Fulfillment backfill: `untrackable` dispositions reported separately from real errors **
* FIX: `AIEO_DMM_Fulfillment::ajax_backfill_delivered()` + `ajax_backfill_detailed()` — both lightweight and full TrackAndTrace handlers no longer push terminal-not-found dispositions (Geniki `result=9`, "voucher not in account") into the `$errors` array. They now flow exclusively into the existing `$marked_untrackable` counter that the response already exposed. The credentials-boundary detector (5 consecutive non-success in non-sweep mode) still counts untrackables toward the consec guard, so the abort behaviour is preserved.
* FIX: Backfill admin JS for BOTH cards (Backfill delivery dates + Detailed arrival backfill) — now accumulates `untrackableTotal` per batch and renders it in the status line as `· N untrackable` between "Still pending"/"no C_A3" and the error count. Symptom before fix: sweeping 220 dead Geniki vouchers showed "0 delivered · 0 pending · 220 errors" with red-tinted error list, even though every voucher had been correctly classified as untrackable in the DB. After fix: "0 delivered · 0 pending · 220 untrackable · 0 errors" — actionable, no false alarm.
* CHANGE: Consec-abort message reworded from "Stopped: 5 consecutive errors. Likely walked into vouchers issued under different credentials" to "Stopped: 5 consecutive untrackable/error vouchers …" — accurately reflects what happened (the boundary is detected by untrackables, not transient API errors).

** 🛒 MOI reconcile: new orders now land as `completed` **
* CHANGE: `AIEO_MOI_Order_Mapper::wc_status_from_vendor()` renamed to `wc_status_for_context()` and now takes a `$context` argument. When `$context === 'reconcile'`, the returned WC status is hardcoded to `completed` instead of the vendor's configured `default_order_status` (commonly `on-hold` or `processing`). Rationale: reconciled marketplace orders are HISTORICAL fulfilments being back-filled into WC for analytics/reporting — they've already been shipped/paid/optionally refunded on the marketplace side. Landing them as `on-hold` puts them in queues that expect operator action and skews every dashboard that filters on open statuses; `completed` is the only status that truthfully reflects "finished transaction, no further action required". Webhook / poll / event paths still honour the vendor's configured default (live orders may genuinely need fulfilment work). Existing orders being re-reconciled have their status left alone — the additive metadata path never touches status.

** 🐛 Hidden anti-pattern: `(array) sanitize_text_field( wp_unslash( $_POST['arr'] ) )` swallows array POSTs **
* FIX: `AIEO_MOI_Admin::ajax_reconcile_process()` rejected every batch with "vendor_id and ids are required." even when the JS clearly posted `ids[]=…&ids[]=…`. Root cause: `sanitize_text_field()` on a non-scalar silently returns `''` per WP core, so `(array) sanitize_text_field( array )` produced `array('')`. The downstream regex sanitiser then stripped the empty string and `$ids` collapsed to `array()`, tripping the "required" guard. Result on Shopflix vendor: 47 fetched, 0 imported, 47 errors with the same message. Fixed by mapping element-by-element: `array_map('sanitize_text_field', (array) wp_unslash($_POST['ids']))`.
* FIX: Same anti-pattern was breaking `AIEO_Chat_Addon::ajax_save_content_selection()` — the Content Selection admin saved `array('')` as the post_types / taxonomies whitelist, effectively breaking chat content filtering on every save. Fixed with the per-element map + `array_filter` to drop empties.
* FIX: Same anti-pattern in `AIEO_TRS_Zone_Admin::ajax_reorder()` — drag-reorder of shipping rules was POSTing `ids[]=…` but the handler reduced it to `array(0)`, so `AIEO_TRS_Rules::reorder([0])` ran with a single bogus id. Fixed identically: cast first, then `array_map('intval')`.

** ✅ Verification + branding **
* Clean-install verified end-to-end on a fresh WP 7.0 + WooCommerce 10.7 site (georgakis.rooster.red): 91 AIEO tables created, 19 stored procedures installed, bundled mu-plugins copied, no PHP errors.
* Plugin URI, AIEO_VERSION, and readme.txt Stable tag all synchronised at 5.2.
* Plugin/readme description rewritten and translated: en + el .po/.l10n.php carry "Adaptive productivity, conversion & retention for eshops and content sites. Anthropic/Voyage AI: messaging, recommendations, attribution, loyalty, merchandising." (GPT mention dropped — Anthropic + Voyage only).


= 5.1 Custom Slots, Recommendations Ordering, REY-theme + Action Scheduler hotfixes (21/05/2026) =

** 🎯 Custom Slots + Recommendations Ordering **
* NEW: AIEO_DMM_Custom_Slots — operator-defined "extra blocks" for the single-product recommendations region. Two kinds of slots:
  - Tolstoy: paste a widget id + account UUID and AIEO assembles the canonical `<tolstoy-widget>` tag plus the matching ai-widgets.gotolstoy.com CDN script (defer-loaded once per page, dedup'd by account+widget pair).
  - HTML / code: paste any raw HTML, embed script, image tag, or WooCommerce shortcode (e.g. [products limit="6" orderby="popularity"]). Shortcodes processed; security knob via `aieo_dmm_custom_slot_html` filter.
* NEW: wp_editor() Visual/Text editor for HTML slots — operators get a rich-text WYSIWYG (TinyMCE) AND a raw-code tab natively, with wpautop disabled so embed scripts and custom elements (`<tolstoy-widget>`) survive a Visual → Code round-trip intact.
* NEW: Per-slot title field + Show-title toggle. The slot wrapper now emits as `<section class="aieo-custom-slot ... aieo-recommendations" data-section="<slot_id>">` so the operator-supplied heading inherits the theme's `.aieo-recommendations .aieo-recommendations-title` styling (underline + rail-matching typography) automatically — no extra CSS.
* NEW: aieo/custom-slot block + aieo/custom-slots-region block. The region block emits every enabled slot as a sibling div (no wrapper) so each participates in the parent flex container's order: layout as a direct child.
* NEW: "Recommendations Ordering" card on the Product Display dashboard. Drag-and-drop list mixing the 5 built-in reco surfaces (Up-sells, Cross-sells, Related, Recently Viewed, Products for the same needs) with every operator-defined custom slot, plus a per-row Show toggle to hide rails without removing them.
* NEW: Classic-theme parity for the recommendations order — `AIEO_DMM_Product_Display::reorder_classic_theme_recos()` runs on `wp` priority 100 (non-FSE themes only), walks `$wp_filter['woocommerce_after_single_product_summary']`, finds each known reco's existing callback, and remove/re-adds it at a new priority derived from the saved order. Custom slots become per-slot closures registered at the saved priority, replacing the single render_classic_theme_region callback. Hidden items are removed entirely. Effect: drag-saved ordering applies identically on botiga/storefront/astra/etc. as on roosterx (FSE).
* NEW: aieo_dmm_recommendations_order option — authoritative order + visibility store. Auto-seeded on first read from the legacy `aieo_display_*` options so pre-5.1 settings carry over.
* NEW: data-aieo-reco attribute on every reco wrapper (built-in + custom) — the join key the inline-CSS order/visibility rules target.
* NEW: roosterx single-product.html — 5 recos blocks wrapped in `.ff-recommendations-cluster` flex container; aieo/custom-slots-region block inserted as the final child so custom slots become flex siblings.
* CHANGE: Custom Slots admin tab moved into the SINGLE PRODUCT PAGE column of the Storefront Display dropdown (was orphaned in the unlisted `_other` bucket). Priority 57 lands it between Sticky Add to Cart (56) and Product Video (60).
* CHANGE: Legacy "Please display Cross-sells above Up-sells" checkbox + option `aieo_recommendations_display_order` retired from the Analytics page. Replaced with a deep-link to the new Recommendations Ordering card. The legacy option is no longer read at render time (FSE uses inline order CSS, classic uses dynamic hook re-prioritisation).
* CHANGE: Tolstoy admin placeholder data swapped to fake-but-format-correct demo values (`01HZ7XMP3VBQR8TKWY5JCDFN24` / `a7c2f1e4-9b53-4d6e-bc01-5f8e7a3c2b94`) — never distribute real operator account UUIDs.

** 📊 Tolstoy → cart attribution (cross-tab) **
* NEW: `tolstoy` added to AIEO_Attribution::VALID_SOURCES. Add-to-cart events that originate from a Tolstoy carousel click are now stored in wp_aieo_addcart_events with `source = 'tolstoy'`.
* NEW: aieo-tolstoy-attribution.js — cross-tab carrier. Capture-phase delegated click listener on `<a>` elements inside `[data-aieo-reco^="slot_"]` containers that host a `<tolstoy-widget>`; writes the click context (slot_id, source product_id, tolstoy_widget_id, destination URL) to localStorage BEFORE the navigation. Tolstoy links open in a new tab (target="_blank"), so the destination tab — on page load — promotes the localStorage entry to `window.aieoTolstoyPending` (one-shot, TTL 30 min, dst_path match) for the next add-to-cart event on that page.
* NEW: aieo-attribution-client.js — `inferSource()` and `resolveSourcePid()` short-circuit on `window.aieoTolstoyPending` before any DOM-walk fallback; the resulting beacon carries `source_meta = {slot_id, tolstoy_widget_id, clicked_url}` so reports can pivot on widget OR carousel OR source product.
* Improved: Custom Slots module enqueues aieo-tolstoy-attribution.js on every is_singular('product') page — both source AND destination pages get the bridge wired with one enqueue.

** 🛒 IAPI (WordPress Interactivity API) — WC Cart / Checkout block parity **
* NEW: AIEO Side-Cart, Swatches multi-select, Cart sync, Recommendations attribution, Sticky Add-to-Cart, Wishlist toggle and Star Ratings all integrate with the modern WooCommerce block-based cart and checkout (driven by @wordpress/interactivity). Existing classic-shortcode cart/checkout still work — no regression. The same DOM contract drives both surfaces so a single set of CSS + JS handles "shop with the new blocks" or "shop with the old shortcodes".
* NEW: AIEO_DMM_Swatches_Runtime supports dual-mode enqueue — IAPI module @aieo/swatches-iapi when `aieo_iapi_swatches_enabled` is on, falling back to the jQuery bundle aieo-swatches.js otherwise. Same DOM contract; the IAPI module reads the same `omSwatchSettings` localised data the jQuery bundle does.
* NEW: WC Store API / wc/store/cart REST endpoints are recognised by the side-cart, attribution log, and waitlist subscribe flows so the operator's cart actions on the block-checkout surface are captured the same as on the classic checkout.
* NEW: `woocommerce_blocks_loaded` integration — AIEO Custom Slots, Free-Shipping Display, and Trust Badges register their bridge-block render paths inside the WooCommerce Blocks asset pipeline, so they're available to drop into block-themed cart/checkout templates via the Site Editor.

** 🐛 Other polish in 5.1 **
* FIX: DB-optimization completion message now reports the live `SELECT VERSION()` value (e.g. "MariaDB 11.8.6 optimization completed for both orders and product vitals tables") instead of the hardcoded "MariaDB 11.4 …" / "MySQL 8 …" floor. Code-path selection (which index set to apply) still keys on the 11.4+ / 8.0+ floor; only the operator-facing label changed.

** 🚑 Blocker hotfix — 4 GB OOM on variation-attribute URLs after WP 7.0 (2026-05-21 incident on femme-fatale.gr) **
* FIX: New `aieo_purge_caches_on_core_upgrade()` handler — hooked on `upgrader_process_complete` (gated by `type === 'core'`). When WordPress core upgrades, AIEO now flushes every plugin-owned persistent cache so cross-version unserialise landmines can't fire. Without this, WP 7.0's replaced wp-includes/* renders stale serialised core-class instances (WP_Term, WP_Post, WC_Product, …) inside AIEO's transients unreadable — PHP's unserialize() hits a corrupted length field and tries to allocate ≈4 GB. The crash signature `Allowed memory size … exhausted (tried to allocate 4295229440 bytes)` at `wp-includes/theme.php:325` is the classic unsigned-integer-underflow value (≈ 0xFFFFFFFE).
* NEW: One-shot retroactive purge `aieo_retroactive_post_core_upgrade_purge()` runs on the first `plugins_loaded` after the 5.1 update, gated by the `aieo_post_core_upgrade_purge_v1` option flag. Catches sites where WP core was auto-upgraded BEFORE this fix landed in the plugin — the upgrader_process_complete hook can't help there, but this catch-up purge does.
* FIX: Purge implementation scope — direct DELETE on wp_options for `_transient_aieo_*` + `_transient_timeout_aieo_*` + `_transient__aieo_*` + matching `wc_var_prices_*` transients (WC's own variation-prices cache that AIEO's `woocommerce_get_variation_prices_hash` filters in AIEO_DMM_Main_Price_Filter + AIEO_DMM_Role_Pricing touch — so corrupted blobs there land in AIEO's code path), plus `wp_cache_flush_group()` on `aieo_dmm` / `aieo_core_brand_vitals` / `aieo_ratings` (full `wp_cache_flush()` fallback on older WP), plus `TRUNCATE wp_aieo_recommendations_cache` when present, plus AIEO_Core_Brand_Vitals' cache_version bump. Total cost per upgrade: one indexed-LIKE DELETE + one TRUNCATE + a few cache calls — negligible.

** 🔧 Priority hotfixes (2026-05-21 incident response on e-sotiriou.gr) **
* FIX: AIEO_DMM_Brand_Gifts::exclude_samples_from_archive() — skip the `_aieo_gift_type` meta_query injection when the caller already has a `post__in` constraint or when a `dynamic_sidebar` action is firing. REY theme's price-filter widget (`reyajaxfilter_get_prices_range`) builds counter-queries with `post__in`; our LEFT JOIN injected into that query produced malformed SQL with a floating `wp_posts.ID IN (…)` clause that MariaDB rejected — REY then fell into a recovery path that recursively rebuilt result sets in PHP and OOM'd PHP-FPM workers at 4 GB allocation. Site became unusable for hours. Sample-product hiding from the visible archive is unaffected (visible archives don't pass post__in).
* FIX: AIEO_Recompute_Queue::maybe_schedule_tick() — site-wide 5-minute transient lock, one-shot dupe cleanup (gated by `aieo_recompute_dupe_cleanup_v1` option flag), `as_next_scheduled_action()` for dedup (single indexed lookup instead of the prior `as_get_scheduled_actions(per_page=2)` table scan), and `admin_init` as the primary trigger so frontend requests no longer touch the wp_actionscheduler_actions table on every page load. The prior design scanned the actions table on every `init`; on a site with 4,783 pending rows of this hook it cost ~25 s per request, exhausted the FPM pool, and pushed load average to 14.69 on a 4-CPU box.
* FIX: AIEO_DMM_Product_Meta — legacy `om_ppe_product-meta` option no longer shadows new defaults when the AIEO option exists (even at `{enabled:true}` only); same fix pattern applied across the 7 om_ppe legacy keys via aieo_migrate_om_ppe_legacy_options() one-shot migration.
* FIX: AIEO_DMM_Brand_Display + AIEO_DMM_Product_Meta — both now resolve the current product via `global $product` first, falling back to `is_singular('product')` + `get_the_ID()`. Classic themes like Botiga wrap the WC summary in their own component pipeline where the latter pair can return 0/false mid-render. Brand Display + Product Meta also gained classic-theme hook callbacks (woocommerce_single_product_summary + woocommerce_product_meta_end) so they render outside FSE templates too.
* FIX: AIEO_DMM_Brand_Display::get_brand_term() now routes through AIEO_DMM_Brand_Discounts::get_brand_taxonomy() for taxonomy detection (covers Premmerce, BeRocket, plus the existing 3) — matches the rest of the DMM.
* FIX: aieo/brand-azindex block — `editorScript` declared in block.json + new edit.js with ServerSideRender preview so the Site Editor stops showing "Your site doesn't include support for this block".
* FIX: Generic editor-side fallback (`enqueue_block_editor_assets`) registers a ServerSideRender stub for every aieo/* block that doesn't ship its own edit.js — closes the "unsupported block" warning across 18 server-rendered AIEO blocks.

** 🔌 Frontend swatch runtime now standalone **
* NEW: AIEO_DMM_Swatches_Runtime + AIEO_DMM_Swatches_Ajax bundled in the main plugin so the swatch frontend renders without the optional ai-eshop-optimizer-runtime plugin. Same DOM contract; aieo-swatches.js + aieo-swatches.css ship in the main plugin's assets/.
* NEW: AIEO_DMM_Swatches::enqueue_frontend_assets() — full localisation (omProductSwatches + aieoSwatches + the critical omSwatchSettings global the bundled JS reads to create the multi-select batch-add button + cart-sync data).
* NEW: wp-color-picker on every `.aieo-color-input` field in the Swatches admin appearance tables, plus tightened table layout (72-px number inputs, inline `px` / `/` labels).

** 🛒 Storefront fit + finish **
* FIX: 60×60 mobile button override from theme rules now beaten unambiguously on `.aieo-wishlist-heart` + `.aieo-rating-star` via `@media (max-width:1025px)` high-specificity rules with !important on dimensional properties.
* FIX: Mobile single-product rating widget — caption stacks UNDER the stars instead of beside them; both centered.
* FIX: 81 `wp_terms.name` rows decoded — pre-encoded HTML entities (`Λεβάντα &amp; Βανίλια`) cleaned + defense-in-depth `html_entity_decode()` added to AIEO_PSE::aieo_bundle_attribute_labels() so the side-cart quick-view dropdown stops showing literal `&amp;` text.

** 🔗 Branding **
* CHANGE: Plugin links now point at rooster.systems/get-aieo/ (was eshop-optimizer.com). Plugin URI header, admin top-bar anchor (text rebranded to "Rooster AI Eshop Optimizer"), my-account, help-and-support, feedback, and Pro service URLs all updated. JWT/REST/order-upload API endpoints intentionally unchanged.

** 🌍 i18n + post-relations shortcodes **
* NEW: AIEO_Post_Relations_Shortcodes — [aieo_related_brands] / [aieo_related_categories] / [aieo_related_needs] canonical shortcodes plus [related_brand_shortcode] / [secondary_brand_shortcode] aliases so legacy Pods-era theme templates keep working.
* NEW: [aieo_brand_rails] and [aieo_brand_slider] shortcodes — wrap the aieo/brand-rails and aieo/brand-slider blocks via render_block() so single source of truth.


= 5.0 Insights Chat, Attribution Engine, Retention & Pro Tier (15/05/2026) =

** 🎯 MAJOR RELEASE: Conversational Analytics, Joint Session × Attribution Intelligence, and the Pro Tier **

This release is the largest single update to AI eShop Optimizer to date. Three months of work consolidate into four product-level deliverables: an in-admin Claude-powered Insights Chat with 33 purpose-built data abilities; a Session-Tracker × Attribution-Engine bridge that unlocks joint analytics across engagement, conversion, blog content, and customer needs; a four-layer retention strategy that keeps the local database under 1 GB indefinitely while streaming the full event history to an operator-owned analytics database; and a formal Pro-tier gating layer with frosted-glass overlays on the thirteen tabs that are now membership-only.

** 💬 Insights Chat — Claude-powered, in-admin **
* NEW: AIEO_Merchant_Chat — in-admin chat page under Tools → Insights Chat, powered by the existing AI Chat Anthropic API key (no separate subscription)
* NEW: 33 purpose-built Claude abilities registered via the WordPress Abilities API, discoverable at /wp-json/wp-abilities/v1/abilities
* NEW: Categorized accordion suggestion chips covering 11 question groups — Customers, Brand portfolio, Recommendation rails, Attribution & archives, Product needs audit, Star ratings, Content & blog, Cross-skill, Store analytics, Promotions / launches / sales, Newsletters & campaigns
* NEW: Tool-use loop with bidirectional slashless tool-name mapping (aieo/list-brands ↔ aieo_list_brands) for Anthropic compatibility
* NEW: Translated suggestion chips for sales-per-view ratio, new-product performance, complementary-product impact, sale-period products, newsletter × needs correlation, abandoned-cart triage, blog-to-product conversion, and engagement-bucket buy-probability
* NEW: Server-side ability execution with manage_woocommerce capability gating; permissions enforced before every tool call

** 🔗 Session Tracker × Attribution Engine Integration **
* NEW: AIEO_Session_Attribution_Bridge — query helpers that JOIN wp_aieo_sessions × wp_aieo_addcart_events × ratings × needs by session_id and product_id
* NEW: AIEO_Conversion_Probability_Model — engagement-bucket conversion table; scores the current session's P(cart-add) based on scroll depth, time on page, and mouse activity
* NEW: 'blog_post' added to AIEO_Attribution::VALID_SOURCES — clicks from blog posts to products now attribute correctly with source_meta.post_id
* NEW: Blog-post detection in aieo-attribution-client.js (body.single-post / article.post)
* NEW: 11 new abilities — aieo/attribution-with-engagement, aieo/addcart-funnel, aieo/blog-engagement-leaderboard, aieo/blog-to-product-conversion, aieo/buy-probability-by-engagement, aieo/buy-probability-leaderboard, aieo/abandoned-cart-triage, aieo/cart-removal-analysis, aieo/rating-conversion-correlation, aieo/need-conversion-correlation, aieo/top-converting-pairs
* Improved: AIEO_Attribution::log_event() now respects AIEO_Session_Tracker consent state (GDPR alignment — previously wrote regardless)
* Improved: session_id validity guard in log_event() prevents the "session_id = 1" regression we hit in May
* Improved: Both engines key off the same aieo_session_id cookie; documented in the bridge's contract

** 🗄️ Four-Layer Retention Strategy & Remote DB Streaming **
* NEW: AIEO_Remote_Export_Manager — streams raw analytics events to an operator-owned MariaDB / MySQL via the existing AIEO_ERP::for_connection_id() factory
* NEW: AIEO_DMM_Remote_Export module ("Insights Remote DB Settings" tab) — admin UI for picking connection + tables + export frequency, dry-run "Test export" button, per-table lag dashboard
* NEW: aieo_remote_export_run cron with configurable frequency (15 min / hourly / daily)
* NEW: wp_aieo_remote_export_state bookkeeping table — one row per (connection_id, table_name) tracking last_exported_ts
* NEW: Idempotent INSERT IGNORE on the remote so repeated runs never duplicate
* NEW: 5 pre-aggregated daily rollup tables — wp_aieo_engagement_daily, wp_aieo_attribution_daily, wp_aieo_conversion_buckets, wp_aieo_blog_engagement_daily, wp_aieo_cart_removal_daily
* NEW: aieo_aggregate_rollups nightly cron — chat abilities query the rollups, never the raw event tables, keeping admin response times in the single-digit milliseconds
* NEW: Tightened hot retention — 14 days for wp_aieo_sessions, 30 days for wp_aieo_reco_events, 365 days for wp_aieo_addcart_events (cart-adds are conversion evidence and kept longer)
* NEW: aieo_archive_run unified archive hook generalises the existing weekly cron to cover AE + reco event tables alongside sessions
* NEW: Local-purge failsafe — hot rows are NOT deleted until the remote-export bookkeeping confirms the row was received, preventing data loss when the remote is briefly unreachable
* Improved: Net stable DB footprint ~1.1 GB regardless of how many years the plugin has been running (was unbounded — wp_aieo_sessions alone projected to hit 6-8 GB by year 2 before this work)

** 🌟 Star Ratings → SEO Schema Bridge **
* NEW: AIEO_Ratings_Schema — bridges AIEO star ratings into AIOSEO, Yoast, and RankMath JSON-LD via their schema filter hooks (aioseo_schema_output, wpseo_schema_product, rank_math/snippet/rich_snippet_product_entity)
* NEW: Fallback head-injected JSON-LD aggregateRating when no SEO plugin is detected
* NEW: Enable/disable toggle on the Star Ratings admin page (seo_schema_enabled setting)
* Improved: Pages now surface aggregateRating to Google Rich Results without the operator needing to manually configure each SEO plugin

** 🛒 Floating Mini Side-Cart, Sticky ATC & Free-Shipping Bar **
* NEW: AIEO_DMM_Side_Cart — two-column floating mini-cart styled after the Rey theme: left column "You might like" recommendations, right column Shopping Bag / Recently Viewed tabs, free-shipping progress bar right under the tab header, ± quantity stepper per line, SUBTOTAL + coupon + CART / CHECKOUT buttons
* NEW: Fast-path cart updates — 500 ms debounced quantity AJAX → 400-byte JSON → optimistic client-side bar recompute → no full cart recalculation on the server. Session-level HTML cache keyed by cart_hash (30 s TTL). WC fragments integration keeps every other widget in sync
* NEW: Side-cart recommendations sourced from the DMM product-bundle endpoint so the same cached HTML powers the product page AND the mini-cart
* NEW: Brand-gift lines get locked-qty / hidden-remove-link treatment — same UX as the Brand Gifts module already applies to the standard WC cart page
* NEW: AIEO_DMM_Sticky_ATC — sticky Add-to-Cart bar (DOM classes + JS global kept verbatim so the shipped CSS/JS bundle works without modification)
* NEW: AIEO_DMM_Free_Shipping admin — threshold + product / brand exclusions; registers the package-rates filter that strips free-shipping rates when any excluded item is in the cart

** 💰 Pricing Engine — Role Pricing, Brand Discounts, Loyalty Classes **
* NEW: AIEO_DMM_Role_Pricing — per-role price overrides via CSV upload; staged batch validation → AJAX batches process rows into wp_aieo_dmm_role_prices; mirrors into the legacy wp_om_role_based_pricing table so the existing om-role-pricing frontend keeps working byte-for-byte
* NEW: AIEO_DMM_Brand_Discounts — % discounts per brand term with optional date window; read by the pricing engine at runtime
* NEW: AIEO_DMM_Loyalty_Classes — loyalty class definitions + user assignments applied in the global pricing recipe by AIEO_DMM_Pricing_Engine
* NEW: Pricing engine evaluates Role Pricing → Brand Discount → Loyalty Class as a stacking recipe so each customer sees the strongest applicable price

** 🎁 Conversion Boosters — Brand Gifts & Exit-Intent **
* NEW: AIEO_DMM_Brand_Gifts — brand-based free-gift offers stored in wp_aieo_dmm_brand_gift_offers; cart-side hooks add a gift (price = 0) when cart subtotal of the offer's brand(s) crosses min_subtotal within the date window. Bypasses the is_purchasable filter for programmatic gift adds so sample-flagged products work
* NEW: AIEO_DMM_Exit_Intent — configurable modal that fires on cursor exit-intent after a per-page warm-up delay. Two HTML bodies: a generic site-wide offer + an upgraded checkout-specific offer. Optional restriction to checkout-only and/or anonymous visitors. Session-storage dismissal so it doesn't reappear in the same browsing session. Excludes the order-received endpoint (post-purchase context defeats the offer)
* NEW: AIEO_DMM_Exit_Intent auto-imports legacy options from the standalone `exit-intent-offer` plugin on first read and deactivates it — operators don't need to migrate by hand

** 📦 Marketplace Connectors & Shipping **
* NEW: AIEO_MOI_Admin (Marketplace Order Importer) — pulls Skroutz / e-shop / Amazon orders into WooCommerce as native orders so attribution, stock, customer profiles, and fulfillment stay unified. Webhook + manual reconcile flow with vendor-namespaced order meta. Inbound-log table with 30-day retention and visible event detail (last 200 events surfaced in the admin)
* NEW: Per-vendor webhook URLs with rotatable tokens, configurable SKU strategy (SKU / product post ID / custom postmeta key), default order status per vendor
* NEW: AIEO_DMM_Marketplace_Feeds — streaming feed generator that handles 35k-product catalogues without blowing memory. Per-feed cron registration on save; include/exclude rules by product or brand ID; XML or CSV output; configurable field map and target path
* NEW: AIEO_DMM_Shipment_Tracking — cleans up the WooCommerce Shipment Tracking provider list, drops the US/UK/Canada/Germany carriers we never ship to, adds the Greek + Cypriot carriers + Stoferno.gr that the WC plugin doesn't know by default. No-op if the WC Shipment Tracking plugin isn't installed
* NEW: AIEO_DMM_Smart_Shipping_Label — table-rate aware checkout shipping label. Guests / postcode-less carts see "Carrier (Από €X.XX)" with the minimum rate from the table-rate config — so they know shipping isn't free without seeing a misleading default. Once a real postcode is entered, exact rates appear (standard WC behaviour). When the cart qualifies for free shipping, method prices are hidden and only the carrier name is shown — and the generic "Free shipping" pseudo-method is removed (customer still picks a real carrier, just with €0). Greek postcodes formatted "184 53" auto-normalize to "18453"

** 👥 Customer Intelligence + Web Push + Voice-of-Customer **
* NEW: AIEO_DMM_Web_Push — Customer Intelligence + Web Push module. Reads from AIEO's pre-built snapshot tables (wp_aieo_temp_orders / wp_aieo_YYYY_MM_orders) rather than re-querying HPOS from scratch, so the omnichannel customer view (eshop + in-store merged in step 6 of the data-prep pipeline) is the source of truth
* NEW: wp_aieo_customer_intelligence (schema v2) — keyed by eponymous_id (AIEO's canonical customer identity), with secondary wp_user_id and source_table columns so anonymous → logged-in stitching has a single home
* NEW: aieo_sp_recompute_customer_intelligence_batch stored procedure — chunked recompute over the chosen snapshot table; "Recompute now" admin button + top-50-by-LTV preview table for sanity-checking before push campaigns go out
* NEW: Behavioural segments derived from the intelligence table — Top customers, Lost cohort, Lookalikes, Abandoned-cart, reactivation
* NEW: Triggered web-push flows — welcome, reactivation, price-drop, back-in-stock — with full dispatch log + per-subscriber delivery + click attribution
* NEW: AIEO_DMM_Surveys — Customer Survey Intelligence. Two-tier model: wp_aieo_survey_responses (mirror of Gravity Forms entries 18 + 20) + wp_aieo_survey_intelligence (per-customer aggregation + composite Voice-of-Customer score). 7 high-signal metrics tracked: nps_class (promoter/passive/detractor), overall_satisfaction (1-5), is_professional_verified, personal_priorities (prices/quality/speed/etc), discovery_channel (store/google/ad/wom/multi), gift_category_preference, personal_profile (hair/eye/skin/silhouette/height)
* NEW: Survey ingest happens three ways — gform_after_submission hook (real-time), daily WP-Cron (safety net), "Ingest now" admin button (manual trigger). Surveys table is the ONLY place we touch Gravity Forms tables
* NEW: voc_score (0-100) per customer derived from NPS + satisfaction + UX average + repurchase intent
* NEW: AIEO_DMM_Waitlist — "Notify me when back in stock" subscription. Out-of-stock product pages render the signup form; jQuery keeps the form in sync with variation changes. Stock watcher hooked on woocommerce_product_set_stock_status — when a product flips back to instock, emails every subscribed row and marks them notified. One-shot migration from the legacy wp_om_ppe_waitlist table. Legacy AJAX endpoint kept as a compat shim so cached HTML from the PPE era keeps working
* NEW: AIEO_DMM_Wishlist (analytics) — read-only admin views over wp_aieo_wishlist answering: how many items sit in wishlists right now, which products are most-wishlisted, which surfaces drive wishlist adds (source_type breakdown), which specific pages / categories drive them (source_id + source_name)

** 🔒 Pro Tier — Frosted-Glass Overlays on 13 Admin Tabs **
* NEW: AIEO_DMM_Module_Base::is_pro_locked() + get_pro_promo() — extension points subclasses override to lock a tab behind the Pro membership
* NEW: AIEO_DMM_Module_Base::render_pro_locked_screen() — Apple-glass frosted overlay with PRO badge, custom title + description + bullet list per feature, "Upgrade to Pro" CTA pointing at rooster.systems/get-aieo/
* NEW: Pro overlay on 13 tabs — Attribution Dashboard, Advertising Campaigns (Attribution group), Web Push, Customer Intelligence Surveys, Insights Chat, Shipment Cost Reports, Insights Remote DB Settings, Action Scheduler Cleanup, ERP Bridge, ERP Database, Order Documents & Couriers (Fulfillment), Marketplace Order Importer, Branch Stock Scanner, Order Cancellations
* NEW: Each locked tab carries its own promo copy — title, 1-paragraph description, 3-4 bullet feature list — so the upsell is feature-specific, not generic
* Improved: Locked tabs do NOT execute the real render_subtab() — no heavy backfills, no credential exposure, no database queries fire for non-Pro operators

** 🔑 Single Canonical Pro Detection Helper **
* NEW: AIEO_DMM_Module_Base::is_aieo_pro_user() — single source of truth for "is the operator on Pro?"
* NEW: Recognises BOTH licensing paths — (1) logged into eshop-optimizer.com with Pro membership, or (2) connected to rooster.systems via the Content Connector API key. Either is sufficient
* Improved: Refactored inline duplicates in assets/pages/ai-eshop-chat.php and assets/pages/ai-eshop-optimizer.php to call the helper — no more copy-pasted option-pair checks scattered across admin pages

** 🧭 Attribution Menu Restructure **
* NEW: New top-level "Attribution" menu group at priority 50 (between Customer Profiling and Storefront Display)
* Improved: Attribution Dashboard moved into the Attribution group (was a standalone top-level pill)
* Improved: Advertising Campaigns moved from Pricing & Offers into the Attribution group — same dropdown as the dashboard it shares data with
* NEW: Campaigns auto-populate from utm_campaign via AIEO_DMM_Campaigns::touch() — operator never has to pre-register campaigns; new slugs appear as auto_draft rows the next time the dashboard renders
* Improved: AIEO_Attribution::backfill_session_channels() now calls touch() on every newly-resolved campaign slug; promo_surfaces stay in Pricing & Offers via the existing alias map

** 🗣️ Greek Translation Pass + i18n Hygiene **
* NEW: All 11 admin-navigator group labels wrapped in __()
* NEW: All Pro-overlay strings translatable — PRO badge, "Upgrade to Pro" CTA, every locked-tab title/description/bullet
* NEW: All Insights Chat suggestion chips translatable per accordion group
* Improved: 117 i18n errors fixed across the plugin
* Improved: Greek (el) .po updated to cover the ~70 new msgids introduced by this release

** ✅ Plugin-check & Security Hygiene **
* Improved: Plugin-check ERRORs reduced from 132 to 0 (excluding the agreed-upon DB stored-procedure rules — these are intentional by design and acknowledged in writing by the WordPress.org plugin review team)
* Improved: 27 files received documented phpcs:disable headers with explanatory comments for the exceptions
* Improved: All AJAX handlers route through a centralised nonce verifier; admin GET reads on capability-gated pages remain nonce-free per the documented exception
* Improved: All $wpdb interpolations either use $wpdb->prepare or hardcoded $wpdb->prefix-derived table names
* Improved: Secure-by-default: every new DB-touching ability has min/max input bounds and sanitize_key on all string keys

** 📐 Architectural Improvements **
* NEW: Module V2 manifest pattern — data/admin/frontend/compute split scaffolding (Phase F of the architectural reset; migration is opt-in per module, no forced sweep)
* NEW: docs/v2-pattern.md documents the V2 layout for future module migrations
* NEW: docs/BUILD_AND_DISTRIBUTION.md documents the SVN deploy workflow and the .distignore mechanism
* NEW: .distignore file at plugin root — excludes dev tooling, internal docs, the Claude Code skill pack, and editor metadata from the wp.org distribution package while keeping everything in Git for development
* Improved: includes/ runtime path now contains only loadable PHP — the inline _v2_pattern.md was moved to docs/

** 🗂️ Database Changes **
* NEW: wp_aieo_remote_export_state — bookkeeping for the streaming export (connection_id, table_name, last_exported_id, last_exported_ts, last_status, last_error)
* NEW: wp_aieo_engagement_daily, wp_aieo_attribution_daily, wp_aieo_conversion_buckets, wp_aieo_blog_engagement_daily, wp_aieo_cart_removal_daily — pre-aggregated rollup tables consumed by the new abilities
* Improved: wp_aieo_addcart_events.source enum widened to include 'blog_post'
* Improved: New indexes on (date, product_id) and (date, source) for every rollup table

** 📁 New Files **
* includes/chat/class-aieo-merchant-chat.php — in-admin Insights Chat
* includes/dmm/modules/class-aieo-dmm-insights-chat.php — DMM wrapper delegating to the merchant chat
* includes/dmm/modules/class-aieo-dmm-remote-export.php — Remote DB streaming admin
* includes/claude/class-aieo-claude-bootstrap.php — registers the 33 abilities
* includes/claude/abilities/class-*-ability.php — the ability classes (21 original + 11 added in this release + 1 pre-existing)
* includes/class-aieo-session-attribution-bridge.php — joint analytics query helpers
* includes/class-aieo-conversion-probability-model.php — engagement-bucket scoring
* includes/class-aieo-remote-export-manager.php — paged INSERT-batch streaming to remote DB
* includes/class-aieo-ratings-schema.php — SEO plugin schema bridge
* includes/cron/class-aieo-aggregate-cron.php — nightly rollup builder
* includes/cron/class-aieo-archive-handlers.php — AE / reco retention with remote-export gating
* includes/cron/class-aieo-remote-export-cron.php — configurable export cron
* includes/class-aieo-retention-bootstrap.php — top-level retention wiring
* includes/sql/sql-aggregate-rollups.php — DDL for the 5 rollup tables
* includes/sql/sql-remote-export-state.php — DDL for the bookkeeping table
* assets/pages/dmm/remote-export.php — admin page template
* docs/v2-pattern.md — V2 module pattern reference
* docs/BUILD_AND_DISTRIBUTION.md — build / SVN deploy strategy
* .distignore — wp.org distribution package filter

** ⚠️ Operator Notes **
* On first activation of 5.0 the new aggregate rollup tables are created and the nightly aieo_aggregate_rollups cron begins back-filling history; expect 1-3 nightly runs before the chat abilities reach full historical coverage
* Operators who configure the Insights Remote DB export should leave "Hold local archive deletes until the remote confirms" ON (the default) — the failsafe prevents data loss when the remote is briefly unreachable
* The 13 Pro-locked tabs are visible to free-tier operators with a frosted overlay; the underlying database tables are still created on activation so a later Pro upgrade has zero migration cost
* No customer-facing front-end changes — this is an admin-side and analytics-side release


= 4.2 Products for the Same Needs (28/01/2026) =

** 🎯 NEW FEATURE: Need-Based Product Recommendations **

This release introduces "Products for the Same Needs" - a powerful new recommendation system that suggests products fulfilling the same customer needs. Unlike traditional cross-sells and upsells, these recommendations are based on matching customer needs (via the pa_need product attribute), ranked by actual sales performance.

** 🛒 Products for the Same Needs **
* NEW: Automatic product suggestions based on matching customer needs (pa_need taxonomy)
* NEW: Intelligent matching algorithm finds products sharing primary or secondary needs (optimized for performance)
* NEW: Rankings powered by pre-computed TotalItemSales from product catalogue for maximum performance
* NEW: Smart exclusion of existing upsells and cross-sells to prevent duplicate recommendations
* NEW: Stores up to 6 optimized suggestions per product in wp_aieo_product_need_suggestions table
* NEW: Match scoring system - products with more shared needs rank higher
* Improved: Variations automatically inherit need suggestions from their parent products

** 🎨 Display Configuration **
* NEW: Display checkbox in Display Preferences: "Please display Products for the same needs"
* NEW: Displays FIRST on product pages (priority 10) - appears before upsells, cross-sells, and related products
* NEW: Skeleton loading animation with WooCommerce placeholder images while fetching via AJAX
* NEW: Empty sections automatically hidden when no matching products exist (no blank titles)
* NEW: Translatable section title: "Products for the Same Needs"
* Improved: Consistent styling with other AIEO recommendation sections

** ⚙️ Generation & Operational Efficiency **
* NEW: Generation checkbox in Operational Efficiency: "Please generate products with the same needs"
* NEW: Runs as final step in Operational Efficiency batch processing (after UUID generation)
* NEW: Batch processing with configurable batch size (default 100 products per batch)
* NEW: Progress logging shows products processed and suggestions created
* NEW: AI_eShop_Need_Suggestions generator class with singleton pattern
* Improved: Leverages existing wp_aieo_core_product_vitals table for need data
* Improved: Uses wp_aieo_temp_product_catalogue for pre-computed TotalItemSales (faster, no aggregation needed)

** 🚀 Performance & Caching **
* NEW: same_needs added to AIEO_Recommendations_Cache valid relation types
* NEW: Multi-tier caching: Object Cache (Redis/Memcached) + Database table
* NEW: Cache automatically invalidated when products are updated
* NEW: Seamless integration with existing AJAX lazy loading infrastructure
* Improved: Suggestions stored as JSON array for fast retrieval
* Improved: Database indexes on product_id for optimal query performance

** 🗄️ Database Changes **
* NEW: wp_aieo_product_need_suggestions table with columns:
  - id (auto-increment primary key)
  - product_id (unique index for fast lookups)
  - suggested_products (JSON array of up to 6 product IDs)
  - match_score (number of needs matched with best suggestion)
  - created/modified timestamps
* NEW: Table created automatically on plugin activation
* NEW: Default options: aieo_create_need_suggestions, aieo_display_same_needs

** 📁 New Files **
* includes/class-need-suggestions.php - Generator class with methods:
  - generate_all_suggestions() - Batch generate for all products
  - generate_suggestions_for_product() - Generate for single product
  - get_suggestions() - Retrieve suggestions for display
  - find_matching_products() - SQL query for matching products
  - get_excluded_product_ids() - Get upsells/crosssells to exclude

** 🔧 Technical Implementation **
* Uses pa_need product attribute (configurable via aieo_core_attribute_need option)
* Queries wp_aieo_core_product_vitals for primary_need_id and secondary_need_id only (no string parsing needed)
* Ranks by pre-computed TotalItemSales from wp_aieo_temp_product_catalogue (requires catalogue to be generated first)
* Excludes products from _upsell_ids and _crosssell_ids post meta
* Preserves suggestion order (by sales ranking) when displaying
* Generation checkbox appears at end of Operational Efficiency list to ensure dependencies are met


= 4.0 Dynamic AI Models + Database Collation + Anonymous Chat Logging (27/11/2025) =

** 🎉 MAJOR UPDATE: 50+ AI Model Flavours **

This release introduces dynamic AI model selection from the database, comprehensive database collation fixes for maximum compatibility, and fixes for anonymous user chat logging.

** 🤖 Dynamic AI Model Registry **
* NEW: Dynamic model selection from database - over 50 AI model flavours now available
* NEW: All Anthropic Claude models dynamically loaded (Claude 4, Opus 4.5, Sonnet 4.5, Haiku 4.5, and all versions)
* NEW: All OpenAI models dynamically loaded (GPT-5.1, GPT-5, GPT-4, GPT-o1, GPT-3.5, and all variants)
* NEW: All embedding models (Voyage AI, OpenAI) dynamically populated from wp_aieo_ai_models table
* NEW: Admin panel model dropdowns now populate directly from database registry
* Improved: Adding new AI models no longer requires code changes - simply update the database
* Improved: Model selection uses get_models() method with provider/type filtering

** 🗄️ Database Collation Compatibility **
* Fixed: aieo_create_core_product_attributes_table() now uses aieo_get_charset_collate()
* Fixed: aieo_create_core_product_vitals_table() now uses aieo_get_charset_collate()
* Fixed: aieo_create_core_variation_vitals_table() now uses aieo_get_charset_collate()
* Fixed: All AIEO tables now consistently use utf8mb4_uca1400_ai_ci (or database default)
* Fixed: Removed BINARY comparison from AIEO_InsertCoreProductAttributes stored procedure
* Improved: Full compatibility with MariaDB 11.4, MariaDB 11.8, MySQL 5.7, and MySQL 9
* Improved: Tables no longer created with latin1_swedish_ci collation

** 💬 Anonymous User Chat History **
* Fixed: Chat exchange logging now works correctly for anonymous (non-logged-in) users
* Fixed: Session tracking properly handles anonymous user conversations
* Fixed: Re-ranking system properly logs all chat exchanges regardless of user status
* Improved: Chat history storage works for both authenticated and guest users

** 🌐 Translation Improvements **
* Fixed: Frontend chatbox now correctly loads translations from plugin languages directory
* Fixed: Translation loading hook priority changed to 0 for earliest execution
* Improved: Fallback translation loading from WP_LANG_DIR/plugins/, plugin /languages/, and standard WordPress location
* Improved: load_textdomain() now checks multiple paths before falling back to load_plugin_textdomain()

** 🔑 API Key Validation & Credit Check **
* NEW: "Validate API Keys & Check Credits" button in AI Chat settings
* NEW: One-click validation of all configured API keys (Anthropic, OpenAI, Voyage AI)
* NEW: Automatic detection of exhausted credits with clear warning messages
* NEW: Direct links to each provider's billing dashboard for easy credit top-up
* NEW: Status indicators: ✓ Valid, ✗ Invalid, ⚠️ No Credits, — Not Configured
* Improved: Helps diagnose why chat/embeddings may not be working

** 🔧 Technical Improvements **
* Enhanced: AI Chat settings page now uses AI_eShop_Chat_Model_Registry::get_instance()
* Enhanced: Chat model and embedding model dropdowns use get_models('provider', 'type', false) for all models including inactive
* Enhanced: Stored procedure taxonomy comparison simplified (removed BINARY and COLLATE)
* Code: Updated ai-eshop-chat.php model dropdown population (lines 666-669, 1026-1029)
* Code: Updated ai-eshop-chat-addon.php translation loading (lines 143-167)

** 📊 Database Index Optimization **
* NEW: Added 6 missing Graph UUID indexes for optimal export performance
* NEW: idx_graph_ppid for Parent Product UUID lookups
* NEW: idx_graph_orderid for Order UUID lookups
* NEW: idx_graph_prodid for Product UUID lookups
* NEW: idx_graph_epoid for Customer UUID lookups
* NEW: idx_graph_o_epo for Order-Customer relationship lookups
* NEW: idx_graph_pp_epo for Product-Customer relationship lookups
* NEW: idx_product_stats_filter for aggregate_product_stats WHERE clause optimization
* Improved: Database optimization now creates 20 indexes (up from 13) for MariaDB and MySQL 8


= 3.6 MySQL 8 Compatibility + Re-ranking + Error Detection (13/11/2025) =

** 🎉 MAJOR UPDATE: 100% MySQL 8 Support **

This release brings complete MySQL 8.0+ compatibility, advanced search re-ranking, and comprehensive embedding error detection. The plugin now works flawlessly on both MariaDB 11.4+ and MySQL 8.0+ with automatic detection and optimization.

** 🗄️ MySQL 8 & Database Compatibility **
* Complete MySQL 8.0+ support - all SQL syntax converted to cross-compatible code
* Auto-detection of MySQL 8 vs MariaDB with appropriate optimization strategies
* MySQL 8 histogram statistics support for 20-30% faster query optimization
* Descending indexes for MySQL 8 to optimize recent-order queries
* Fixed all 52+ DROP INDEX IF EXISTS syntax errors (MariaDB-specific)
* Fixed CREATE OR REPLACE FUNCTION errors - converted to drop-then-create pattern
* Binary logging privilege handling with graceful degradation
* Helper functions for cross-database compatibility (aieo_drop_index_if_exists, aieo_drop_function_if_exists)
* New file: includes/sql/apply_mysql8_indexes.php - MySQL 8 specific optimizations
* Expected 38-48% performance improvement on MySQL 8 after optimization (tested: 9.5min → 5-6min for 37K products)
* 13 composite indexes + histogram statistics on 6 key columns

** 🔍 Advanced Search Re-ranking System **
* Configurable re-ranking weights for hybrid semantic + keyword search
* Dedicated "Re-ranking Weights" configuration UI in AI Chat settings
* Semantic similarity weight slider (0-100%) with real-time preview
* Keyword matching weight slider (0-100%) with automatic normalization
* Visual feedback with color-coded weight distribution indicators
* Three preset configurations: Semantic-focused (70/30), Balanced (50/50), Keyword-focused (30/70)
* Database schema migration for re-ranking weights storage
* Search results now intelligently combine embedding similarity with keyword matching
* Better handling of product variations vs parent products in rankings

** 🚨 Embedding Error Detection & Reliability **
* Comprehensive failure tracking system - new wp_aieo_chat_embedding_failures table
* Real-time statistics: success rate, average latency, failure breakdown by type
* Automatic retry logic with exponential backoff for transient errors
* Smart error categorization: Rate Limits, Timeouts, Network Errors, Invalid Input, Unknown
* Failure trend analysis with 30-day time-series data
* Visual error rate graphs with severity indicators (green <1%, yellow 1-5%, red >5%)
* Detailed error logs with timestamps, error types, and affected content IDs
* Automatic recovery suggestions based on error patterns
* Failed items flagged for priority retry in next training batch
* Training progress now shows retry attempts and detailed error messages
* API quota management with intelligent retry delays (60s for rate limits, 30s for timeouts)
* Batch processing handles partial failures gracefully - continues with remaining items

** 📊 Database Performance Optimization **
* New MySQL 8-specific index optimization strategy
* Histogram-based query planning (MySQL 8 exclusive feature)
* Performance indexes for customer statistics queries
* Optimized covering indexes for product catalog queries
* DESC indexes for latest-order queries (MySQL 8 native support)
* Auto-detection in "Database Performance Optimization" admin section
* Different benefits messaging for MySQL 8 vs MariaDB users

** 🔧 Technical Improvements **
* Enhanced logging throughout embedding and search processes
* Better error handling with actionable user feedback
* Database query optimization for embedding statistics
* Code organization with separate MySQL 8 file for maintainability
* Improved variation enrichment with better parent product handling
* Embedding statistics now accurately reflect all content types

** 📝 Documentation Updates **
* MYSQL8-COMPATIBILITY-FIX.md - Complete MySQL 8 compatibility guide
* Performance tuning recommendations for MySQL 8
* Re-ranking configuration best practices
* Error detection monitoring guide

** 🐛 Bug Fixes **
* Fixed variation enrichment handling of missing parent data
* Fixed embedding statistics accuracy across post types
* Fixed stored function creation on MySQL 8 with binary logging enabled
* Fixed database migration for re-ranking weights default values
* Fixed undefined variable warnings in embedding error tracking


= 3.5 Advanced Session Tracking & Analytics (07/11/2025) =

** ✨ New Session Tracking Features **
* Added custom class-based event tracking - define custom CSS selectors to track clicks on any element (buttons, swatches, divs, etc.)
* Added click event throttling option to prevent database flooding while maintaining accurate click counts
* Added cookie consent bypass mode - option to enable tracking without waiting for user consent (GDPR compliance toggle)
* Custom rules take precedence over standard tracking to prevent double-counting events
* Product image click tracking with 5-second debounce window to avoid duplicate events from lightbox interactions
* Automatic version bumping with variable-length strings for aggressive cache busting
* Admin textarea for easy custom tracking rule configuration (format: event_name | .css-selector)

** 🔧 Session Tracking Technical Improvements **
* Generated JavaScript code is "baked in" as inline scripts for better performance (not computed dynamically)
* Custom tracking rules override standard click tracking to prevent duplicate event recording
* Improved click counting: throttled mode counts only significant clicks, non-throttled counts all clicks
* Enhanced product image detection across different theme gallery structures
* Version string uses random lengths (8-19+ digits) for superior cache invalidation

= 3.3.2 Performance & Reliability Release (04/11/2025) =

** 🐛 Critical Bug Fixes **
* Fixed embedding training duplicate product ID issue causing infinite loops and HTTP 524 timeouts
* Fixed AI Chat warning modal not disappearing after data analysis completion
* Fixed sessionStorage persistence across page reloads for warning state

** 🚀 Performance Improvements **
* Added performance indexes (lookup_idx, stats_idx) to embeddings table for 10-20x faster queries
* Optimized database queries with DISTINCT/GROUP BY to prevent duplicate results
* Changed index checks from transients to static variables (eliminates wp_options writes)
* Reduced embedding batch processing time from 60-120 seconds to 20-30 seconds for 50 products
* Object cache bypass during critical operations to prevent interference from misconfigured Redis/Memcached

** 🔧 Technical Improvements **
* Multi-layer duplicate protection: SQL DISTINCT, array_unique() at entry/exit, GROUP BY aggregation
* All 8 priority metrics (TotalItemSales, DistinctOrderSales, TotalTurnover, etc.) now use optimized queries
* Query existence checks now use fast `USE INDEX` method instead of INFORMATION_SCHEMA
* Added comprehensive error logging for database operations (can be toggled)
* Chat addon updated to v1.1.1 with improved sessionStorage handling

** 📚 Developer Documentation **
* Added DATABASE-INDEX-OPTIMIZATION.md - Complete index strategy and performance guide
* Added DUPLICATE-FIX-SUMMARY.md - Technical documentation of all duplicate prevention fixes
* Added INDEX-CHANGES-SUMMARY.md - Quick reference for index changes and rollback
* Added SESSION-SUMMARY.md - Complete session work summary
* Added OBJECT-CACHE-CONFIG.md - wp-config.php object cache exclusion instructions

= 3.3.1 Admin Styling & WordPress.org Compliance (03/11/2025) =

** 🐛 Bug Fixes **
* Fixed admin panel styling issues - cleaned up overlapping CSS rules and consolidated chat admin styles
* Fixed plugin description exceeding 150 character limit - reduced to 114 characters for WordPress.org compliance

** 🔧 Technical Improvements **
* Chat addon CSS version bumped to 1.1.0 to force cache refresh for style updates
* Improved CSS organization and eliminated redundant style declarations

= 3.3 Standalone Mode & Enhanced Flexibility (03/11/2025) =

** ✨ Major New Features **
* Plugin now works standalone without WooCommerce - perfect for corporate sites and merchant information pages
* Intelligent menu placement - integrates seamlessly with WooCommerce when available, creates standalone top-level menu otherwise
* Custom network icon for better visual identification in WordPress admin
* Customizable AI Chat input placeholder text in settings

** 🎨 UI/UX Improvements **
* Shortened menu labels ("AI Optimizer") to prevent UI wrapping on smaller screens
* Added descriptive submenu items with emoji icons for better navigation
* AI Chat tab warning now shows on hover for better user guidance
* AI Chat warning only displays on WooCommerce sites (not needed for standalone content sites)
* AI Chat tab warning automatically refreshes after data analysis completes

** 🔧 Technical Improvements **
* Updated to use future-proof Claude model alias (claude-sonnet-4-5)

** 🐛 Bug Fixes **
* Fixed chat wrapper now properly fills custom popup width without margins
* Fixed send button arrow now points right (horizontally flipped)
* Fixed JavaScript errors with e.target.closest() when clicking on text nodes in theme files

= 3.2.1 Embedding Generation Bug Fixes (02/11/2025) =

** 🐛 Critical Bug Fixes **
* Fixed deduplication mode setting not saving correctly when changed via Training Mode dropdown - now properly respects user selection
* Fixed product prioritization querying wrong table - now correctly pulls sales metrics from temp_orders table instead of product_vitals
* Fixed embedding statistics showing incorrect counts - now accurately counts only parent products and excludes individual variations
* Fixed inconsistent product counts between training and statistics - all queries now use matching data sources (product_vitals table)
* Fixed draft/trash products being included in counts - all queries now filter by post_status='publish' for accuracy

** 🔧 Technical Improvements **
* Enhanced query consistency across embedding generation, statistics, and product prioritization
* Improved data source alignment to prevent count mismatches during training
* Better filtering logic to ensure only published parent products are processed

= 3.2 Major Feature Update (01/11/2025) =

** ✨ New Features **
* Support for Pages and Posts in visual editors (Elementor, WP Bakery, TagDiv Composer) - AI Chat now intelligently extracts and processes content from popular page builders for enhanced semantic search capabilities
* Added helpful documentation links throughout the interface to guide users on metric meanings and business implications

** 🌟 New PRO Features **
* Process Automation - Schedule automatic data analysis and embedding updates with flexible daily/weekly timing options for hands-free optimization
* Contextual Metadata - AI Chat now leverages device profile, browsing history (configurable page depth), and user preferences to deliver hyper-personalized product recommendations
* AI Session Tracker (Beta) - Track customer engagement depth, product interactions, scroll behavior, and cart activity with surgical precision analytics that cost $0 vs $100+/month for Google Analytics BigQuery exports

** 🎨 UI/UX Improvements **
* Enhanced HTML structure for better visual consistency across AI Chat configuration sections
* Fixed Posts & Pages configuration header to extend properly across full width, matching Products section styling
* Improved admin panel layout with better spacing and alignment throughout
* Better visual hierarchy in settings pages for easier navigation

** 🐛 Bug Fixes **
* Various minor UI/UX refinements and polish
* Improved CSS consistency across admin panels

= 3.1.2 Referral Tracking Enhancement (31/10/2025) =

** ✨ New Features **
* Added referral links tracking for better affiliate program management
* Enhanced tracking capabilities for referral campaigns
* Better analytics for referral link performance

= 3.1.1 Installation Fix (31/10/2025) =

** 🛠️ Bug Fixes **
* Fixed plugin installation issues ensuring all required files are properly included
* Resolved missing file dependencies during fresh installations
* Improved plugin activation reliability

= 3.1 Maintenance Release (31/10/2025) =

** 🔧 Technical Updates **
* Version bump to ensure all files are properly distributed
* Verification of file structure and package completeness

= 3.0 AI Chat & Major Enhancement Update (30/10/2025) =

** 🎉 Major New Feature: AI-Powered Chat **
* FREE AI Chat with support for latest models from Anthropic and OpenAI
* Cutting-edge AI models: Claude Opus 4.1, Claude Sonnet 4.5, Claude Haiku 4.5
* OpenAI support: GPT-5, GPT-4, GPT-o1, GPT-3.5
* Advanced embedding engines for semantic product search
* Voyage AI embeddings: voyage-3.5-large, voyage-3.5-lite, voyage-3-lite, voyage-finance-2
* OpenAI embeddings: text-embedding-3-large, text-embedding-3-small, text-embedding-ada-002
* Smart product search powered by AI semantic similarity matching
* Context-aware responses using customer behavior data
* Customizable chat templates for different use cases
* Product catalog integration with AI embeddings

** 🎨 Chat Customization **
* Two visual themes: Rounded (Modern with shadows) and Square (Clean and minimal)
* Dynamic color customization for complete branding control
* Header gradient automatically uses brand colors
* Send button styling matches brand colors
* Chat input no longer inherits theme styles for consistent appearance
* Mobile-responsive chat design
* Floating chat widget with adjustable positioning (bottom-left/right, top-left/right)
* Customizable welcome bubble messages

** 🔧 Technical Improvements **
* WordPress coding standards compliance - all 54+ coding standard issues resolved
* Enhanced database query preparation with proper $wpdb->prepare() usage
* Translator comments added for all translatable strings
* Ordered placeholders (%1$s, %2$d) for better translation support
* Date/time functions now use timezone-safe gmdate()
* Proper nonce escaping with esc_js() throughout
* POT file updated with all new translation strings
* Version bumped to 3.0 reflecting major feature additions

** 🛡️ Privacy & Performance **
* AI Session Tracker (Beta) now disabled by default for better privacy
* Cron job properly configured to clean up old session data (retention period)
* Console logging cleaned up - all debug messages commented out
* Optimized AJAX requests for chat statistics
* Improved error handling and validation

** 🐛 Bug Fixes **
* Fixed chat input inheriting unwanted theme styles (size, borders, positioning)
* Fixed send button icon direction - now points right instead of up
* Fixed deduplication mode persistence across page reloads
* Fixed last trained engine settings not being remembered
* Fixed embedding statistics display for different engine types
* Removed deprecated load_plugin_textdomain() for WP 4.6+
* Fixed unlink() usage - replaced with wp_delete_file() where appropriate

** 📚 Documentation **
* Updated readme.txt with comprehensive AI chat feature descriptions
* Detailed changelog with all improvements and fixes
* Updated plugin descriptions highlighting latest AI capabilities
* Better organization of feature lists


= 1.0 Initial Release (3/8/2023) =
* Initial plugin release
* AI-powered product recommendations via eshop-optimizer.com
* Export product catalog and order data
* Import upsell and cross-sell recommendations
* Google GA4 integration for performance tracking
* Support for WooCommerce HPOS (High-Performance Order Storage)
* Privacy-centric data export options


= 2.0 Core Update (17/10/2024) =

** Major Features **
* Local Analytics Engine: Analyze your product performance using local data without requiring external AI processing
* Smart Product Tips: Get valuable insights about your most important products based on local sales patterns
* Performance Boost: Significantly improved data processing speed with optimized database queries

** Database Compatibility **
* Enhanced compatibility with MariaDB 11+ and MySQL 9.0
* Universal database support - works with all major database versions
* Removed hardcoded collation statements for better database compatibility

** Performance Improvements **
* Optimized stored procedures (v2 and v3 batched versions)
* Smart batch processing for large datasets
* Improved query execution speed
* Enhanced memory management for large order exports

** New Features & Enhancements **
* Advanced customer-centric statistics and analytics
* Improved product vitals tracking
* Better variation handling and processing
* Enhanced reciprocal product recommendations

** Technical Improvements **
* PHP 8.4 compatibility
* Prefixed admin CSS classes to prevent plugin conflicts
* Refactored codebase for better maintainability
* Improved error handling and logging

** Bug Fixes **
* Fixed database collation issues on various hosting environments
* Resolved CSS conflicts with other plugins
* Various minor bug fixes and stability improvements


= 2.1 Performance & Localization Update (18/10/2024) =

** Performance Enhancements **
* Major performance optimizations for faster data export and processing
* Optimized database queries for handling large product catalogs
* Improved stored procedure efficiency
* Enhanced memory usage for bulk operations

** New Features **
* Full localization support - plugin is now translation-ready
* Database collation compatibility helper for WooCommerce installations with mixed collations
* Enhanced support for complex product attributes including multi-valued attributes
* Support for hierarchical attribute structures

** Attribute Handling Improvements **
* Improved handling of brands, sizes, colors, and custom attributes
* Better support for product variations with complex attribute structures
* Enhanced processing of multi-valued attributes
* Improved handling of special characters in product attributes

** Compatibility & Fixes **
* Resolved issues with mixed database collations in multi-language installations
* Better handling of edge cases in attribute processing
* Improved compatibility with various WooCommerce configurations
* Fixed attribute export issues in certain hosting environments

** Code Quality **
* Refactored attribute processing for better maintainability
* Improved code organization and documentation
* Enhanced error handling and validation
* Optimized code paths for common operations