EU Withdrawal Compliance — Full changelog

For each release, see the entries below. The latest version is always at the top.
The current release notes also live in readme.txt under "== Changelog ==".

= 2.1.2 =
* Improved: Compatibility with WooCommerce 11.0 verified.
* Fix: The settings that hold customer-facing copy stopped following the language of each visitor. Their editors pre-filled the field with the bundled default text, so the first time the settings page was saved that text was stored verbatim and printed as is from then on, leaving the form intro, the consumer self-declaration, the checkout consent texts, the excluded-product notices and the status-change email bodies stuck in the language of whoever saved. The fields now show the default as a placeholder and discard a text identical to it on save, and updating clears the defaults stored by an earlier save, so the bundled translations apply again. Texts written by the trader are left untouched.
* Fix: The status-change email bodies were missing from `wpml-config.xml`, so on WPML and Polylang there was no way to translate them once customised. They are now exposed under String Translation with the rest of the editable texts.

= 2.1.1 =
* Improved: settings that hold customer-facing copy are now translatable on multilingual sites. The plugin ships a `wpml-config.xml` file, read by both WPML and Polylang, that exposes the excluded-product notice titles and bodies, the form intro text, the consent texts, the trader postal address and the email sender name under String Translation, and copies the withdrawal status to translated products and categories. Texts bundled with the plugin keep coming from the language pack of each locale, as before.
* Improved: clearer message when a request is no longer awaiting confirmation. The old wording mentioned a "confirmation link", which sounded like a link sent by email instead of the review screen of step 2; it now explains that the screen stays available for 15 minutes and can only be confirmed once. A new FAQ documents the usual cause when it happens right after submitting: an object cache that does not keep transients.
* Fix: on WPML and Polylang sites, products and categories in a translated language ignored their withdrawal status, which is stored on the original-language product. The excluded-product notice did not appear (and its stylesheet was not loaded), the Art. 16(m) and Art. 14(4)(a) consents were not requested at checkout when buying in a translated language, and excluded items were not recorded on the order. The status now resolves back to the original product or category, and an explicit status set on a translation still takes precedence, so per-language exceptions remain possible. The product editor of a translation also states where its effective status comes from, instead of looking unflagged. Single-language sites are unaffected.
* Fix: the form now returns the customer to the configured withdrawal page when the referer is not available, instead of to the site home page, so they land back on the form and see the step that follows. Security plugins that restrict access to /wp-admin/ can intercept the submission before it is dispatched, which is when this happened.

= 2.1.0 =
* New: "Accept unmatched requests" opt-in setting (Withdrawals → Settings → Eligible order statuses). When the order number and email do not match any WooCommerce order the request can now be registered anyway, flagged as "Unverified" for manual review, instead of being rejected upfront. The consumer sees a notice on the confirmation screen inviting them to double-check the reference (and can still confirm); the request arrives highlighted in the notification email (with the matched-order hint when only the billing email differs), in the requests list, in the request detail and in a new "Unverified" column of the CSV export. Unverified requests are never linked to an order and the acknowledgement of receipt is still sent. Off by default: nothing changes unless you enable it.
* Fix: Uninstall now removes every plugin option and the custom role capabilities the plugin had granted; it previously deleted only three options.

= 2.0.1 =
* Improved: On Polylang and WPML sites, the links to the withdrawal page now resolve to the page translation in the visitor's language: the printable Annex I.B form, the button in WooCommerce emails, the excluded-product "See the full withdrawal policy" link and the [ayudawp_withdrawal_link] shortcode. Single-language sites are unaffected.
* Fix: The mandatory digital-content consent checkbox (Art. 16(m)) is now validated before WooCommerce PayPal Payments (and other express-checkout smart buttons) open their payment window, so the "you must accept the consent" message appears up front instead of only after returning from PayPal. The payment was never completed without the checkbox; this only corrects the confusing timing.

= 2.0.0 =
* New: Configurable email sender. Two new fields in Settings ("From name" and "From address") set the sender for the plugin's own emails (acknowledgement, admin notification and status updates), which until now went out with the WordPress default (wordpress@yourdomain). The override is applied around each message only, the same way WooCommerce sets the sender of its own emails, so it never changes the site's global sender. Left empty, the behaviour is unchanged.
* New: Editable status-change email texts. The body of the accepted, rejected and completed emails can now be edited from Settings, so the wording matches how your shop really works (for example an instant Bizum refund instead of "a few business days"). Left empty, each falls back to the bundled default; the per-request comment field still works for one-off notes.
* New: Editable (and hideable) intro text on the public form. The introductory paragraph above the form can be edited from Settings, or hidden entirely when the page already explains the withdrawal above the shortcode. The fixed legal note below it stays unchanged, since it must read identically across the form, the confirmation screen and the acknowledgement email.
* New: Optional strict deadline mode. A setting under Withdrawals → Settings → Withdrawal deadline switches from the default "advisory" behaviour (never blocks, only flags late requests for review) to "strict", which hides the My Account button and rejects new requests once the deadline plus grace days has passed. Advisory stays the default and the safe choice for goods with unpredictable delivery; strict is exact for services, digital content and shop pickup, where the period runs from a date the shop knows. A customer contesting a rejected request is never blocked by it: strict mode stops opening new requests, not handling one already open.
* New: Optional "bought as a consumer" self-declaration on the form, for shops that also sell to businesses (B2B), where the right of withdrawal does not apply. Off by default; when enabled, the form shows a required checkbox declaring the purchase was made as a consumer (a natural person acting outside their trade or profession), stored with the request as durable proof. A shortcode attribute (consumer_check="yes") and the ayudawp_euw_show_consumer_check filter allow per-page or per-order control.
* New: Permissions section to choose which user roles, besides the administrator, can view and manage withdrawal requests. Until now any Editor (and, on WooCommerce sites, the Shop manager) could see requests, which hold personal data; the request log now uses its own capability, granted only to the administrator and to the roles you tick. On update, the roles that could already see requests keep their access so nothing breaks, and you tighten it from there; new installs start administrator-only.
* New: Dedicated trader contact email for the Annex I.B model form, set in Settings (Model withdrawal form section), separate from the notification address, so the public model form can show a clean contact mailbox while internal notices go elsewhere. Left empty it falls back to the notification address and then the site admin email.
* New: [ayudawp_withdrawal_excluded_notice] shortcode to place the excluded-product notice with page builders (Divi, Elementor, Bricks, ShopLentor, etc.) that render their own product template and skip the standard WooCommerce hook where the notice normally appears. Drop it into the product layout and it prints the notice for the current product when that product is excluded.
* New: Developer hooks. ayudawp_euw_form_before_submit (action) to render a captcha/anti-spam widget or an extra field inside the form, rejected through the ayudawp_euw_validation_result filter; ayudawp_euw_admin_email_lines to customise the admin notification; and ayudawp_euw_order_number_meta_keys to add custom order-number meta keys. The form shortcode also accepts more pre-fill attributes (name, date, scope, details).
* Improved: The notification email field now accepts several recipients separated by commas, so the notice of each new request can reach more than one mailbox (shop manager and accounting, for example). Invalid addresses are dropped; if none are valid it falls back to the site admin email.
* Improved: When the public form is submitted with something missing, each missing or invalid field is now highlighted in red with a short note next to it, instead of a single generic message with no visual cue, so the customer sees exactly what to fix. Accessible (aria-invalid, role="alert") and still without JavaScript.
* Improved: After each step (confirmation, success message, validation errors) the form now scrolls into view instead of jumping to the top of the page, so the relevant content is where the customer is looking.
* Fix: Order-number compatibility with Tyche "Custom Order Numbers for WooCommerce". The resolver matched the order only against the _order_number meta, which Tyche does not use, so its customers got "we could not match this email with the order number provided" even with the right details. It now also checks Tyche's _alg_wc_full_custom_order_number and _alg_wc_custom_order_number, with the new ayudawp_euw_order_number_meta_keys filter to add other numbering plugins.
* Fix: The category "Withdrawal status" dropdown no longer offers a redundant "Standard" option that appeared not to save (in a category it meant the same as the empty option, and the save handler cleared it the same way). The per-product dropdown keeps it, where it is a real override of the category inheritance.
* Fix: The Annex I.B model form no longer prints "Phone: [Phone number, if any]" when no trader phone is configured. The phone line is optional and is now omitted when empty, and the example shows as a placeholder in the settings field instead.

= 1.9.0 =
* New: Trader postal address field in Settings — the address shown in the Annex I.B model withdrawal form, where the consumer sends the withdrawal. Annex I.B requires the trader's full address, but until now it was only read from the WooCommerce store address, so sites without WooCommerce (for example, issuing contracts from a CRM) were left with an empty address placeholder. The field closes that gap and also lets WooCommerce shops override the store address with a dedicated returns address; left empty, it falls back to the WooCommerce store address as before.
* New: Dedicated Article 16(l) exclusion status for dated services — accommodation other than residential, transport of goods, car rental, catering or leisure activities with a specific date or performance period. Previously covered textually under the generic "other Article 16 exception", they now have their own dropdown option for products and categories and their own editable product-page notice citing Art. 16(l), so a hotel night or a dated tour is described separately from a perishable or custom-made item.

= 1.8.1 =
* Fix: the "View in admin" link in the new-request notification email reached the shop manager empty. The email is composed during the customer's public submission, so WordPress checked the edit permission against the customer (who has none) instead of the recipient and returned no URL. The link is now built directly from the request ID, so it always opens the request in wp-admin; visitors without permission are still stopped by WordPress when they open it.

= 1.8.0 =
* Improved: **Withdrawal requests are no longer auto-rejected by the deadline.** The 14-day period legally runs from delivery (Art. 9(2)(b) of Directive 2011/83/EU), a date the shop cannot detect automatically, so blocking a request on the order or completion date risked turning away consumers still within their legal window. Eligibility is now governed by the order status, and requests that look past the approximate window are flagged in the admin notification email for manual review instead of being rejected — the same "never auto-reject, a human decides" approach already used for Article 16 excluded items. The My Account button and the transactional-email notice follow the same rule, so an order in an eligible status always offers the withdrawal entry points. The deadline settings (basis, grace days) now tune that advisory flag, and the `ayudawp_euw_skip_deadline_check` filter is retained for back-compat but no longer has any effect.
* Improved: the public form, the confirmation screen, the success message and the acknowledgement email now state clearly that submitting a request does not by itself mean the withdrawal is accepted: it remains subject to the statutory deadlines and conditions (the 14-day period counted from delivery, or from the start of the download for digital content, and the Article 16 exceptions). Keeps expectations honest now that the deadline is no longer a hard gate.
* Improved: the default "Other Article 16 exception" excluded-product notice, the per-product/category status label and the auto-generated withdrawal page now mention sealed audio, video or software media unsealed after delivery (Art. 16(i) of Directive 2011/83/EU), so shops selling CDs, vinyl, films or boxed software recognise the exception without rewriting the text. Existing notices edited from Settings keep their stored value; only fresh installs and untouched defaults see the new wording.
* Fix: on the classic checkout, the digital-content consent checkbox (Art. 16(m)) could be shown twice — once in the customer-details column and once in the order-review panel that WooCommerce refreshes via AJAX, which a per-request guard could not deduplicate across the two render passes. It now renders a single time, in the customer-details column, a spot the AJAX refresh leaves untouched, so the required checkbox also stops losing its checked state when the customer changes address, shipping or payment method.

= 1.7.0 =
* New: **Two-step confirmation flow (Article 11a(3) of Directive 2011/83/EU)**. The public form now leads to a confirmation screen showing a read-only summary of the declaration and a dedicated button labelled only "Confirm withdrawal"; the request is registered only when that button is pressed. This prevents the unintended exercise of the right and is the EU-wide baseline of the online withdrawal function, not a national add-on. Between the two steps the declaration is held server-side in a single-use, 15-minute token, so what gets registered is exactly what was reviewed. Works without JavaScript.
* New: **Complete acknowledgement of receipt (Article 11a(4))**. The confirmation email now reproduces the full content of the declaration (name, order, order date, scope, affected products) and the exact date and time of submission, in addition to the verifiable SHA-256 hash — the durable-medium acknowledgement the directive requires, not just a verification code.
* New: **CSV export of the request log**. "Export to CSV" bulk action on the Withdrawals listing, plus a filtered export (by status and date range) under **Withdrawals → Export withdrawals**. Columns include submission and resolution timestamps, scope, status, acknowledgement-delivery flag, receipt hash and excluded items. Useful for accounting and consumer-protection audits; cells are escaped against CSV/formula injection.
* New: **Resolution timestamp** recorded on every status change and shown in the request detail and the CSV export, so the handling timeline is auditable from the request itself, not only from the WooCommerce order notes.
* New: **Acknowledgement-delivery tracking** — whether the receipt email was accepted for delivery, with its timestamp, recorded on the request as burden-of-proof evidence.
* Improved: access links now read "Withdraw from contract here" (transactional emails) and "Withdraw from contract" (My Account orders), matching the wording of Article 11a(1). The public form's email field clarifies that the acknowledgement is sent to that address.

= 1.6.1 =
* Improved: the default text of the "Other Article 16 exception" excluded-product notice now lists dated accommodation and rentals among the examples, alongside perishable, custom-made and hygiene-sealed items. The previous default only cited goods-side exceptions (custom-made, perishable, hygiene-sealed), which read incorrectly for hotels, vacation rentals, car-rental shops and any other Art. 16(l) merchant whose service has a specific date or period of performance. The new wording lets those merchants leave the notice at its default without having to rewrite it from Settings. Existing notices that were edited from Settings keep their stored value — only fresh installs and installs that never touched the default text see the new wording.
* Improved: the sample template of the auto-generated "Right of withdrawal" page now ships with separate sections for each exception type the plugin covers — digital content (Art. 16(m)), services started early (Art. 14(4)(a)), custom-made / perishable / hygiene-sealed goods (Art. 16(a)(c)(d)(e)) and dated accommodation, rentals, catering or leisure (Art. 16(l)) — plus the existing 14-day intro and the form. The previous template was a single generic paragraph that lumped all exceptions together, which led many shops to publish it unedited even when most of those exceptions did not apply to their catalogue. The new template makes it obvious which sections need to stay and which can be deleted: a non-technical merchant can simply remove the H3 blocks that don't match what they sell and keep legally-correct copy for the rest. Only fresh activations see the new template — sites that already had a "Right of withdrawal" page from a previous activation are not modified, so any local edits are preserved.
* Improved: the public withdrawal form no longer forces horizontal centring of its wrapper. Until now `.ayudawp-euw-wrapper` shipped with `margin: 2rem auto`, which centred the form in the content column even when the surrounding text was left-aligned, creating a visual jump between the page copy and the form. The wrapper now uses `margin: 2rem 0`, so the form flows with the rest of the page content (left-aligned in LTR locales, right-aligned in RTL ones once the RTL stylesheet lands). The `max-width: 640px` cap is preserved for readability on wide containers.

= 1.6.0 =
* Improved: **Consistent admin menu regardless of WooCommerce**. The plugin now always registers a top-level **Withdrawals** menu with the request log as the main entry and a **Settings** submenu underneath, whether WooCommerce is active or not. Previously the menu was nested under WooCommerce when WC was active and only fell back to its own top-level menu in standalone mode, which made the plugin harder to find for new admins and inconsistent across installs. The page slugs are unchanged (`edit.php?post_type=ayudawp_withdrawal` and `?page=ayudawp-euw-settings`), so any bookmarks or external links that already pointed at the standalone URLs keep working. Bookmarks pointing at the old `admin.php?page=ayudawp-euw-settings` (the WooCommerce-nested URL) need to be updated.
* Fix: the two text editors of the "Excluded products notice" section in **Withdrawals → Settings** (one for Article 16(m) digital content and one for the other Article 16 exceptions) now show a clear "WooCommerce is not active, so there are no product pages where the notice could be rendered." message when WC is not installed, matching the rest of the WooCommerce-dependent fields on the same page. Before, both editors rendered empty under their labels.
* Internal: `ayudawp_euw_get_settings_url()` and the CPT/settings registration code dropped the `class_exists( 'WooCommerce' )` branch that switched between menu locations, simplifying the loader and removing a small amount of redundant code.

= 1.5.0 =
* New: **Checkout consent for digital content (Art. 16(m))**. Mandatory checkbox injected at the WooCommerce checkout when the cart contains a product flagged as digital content. Without acceptance the place-order step blocks with a clear notice. The exact text shown is editable from settings and persisted on the order together with timestamp, IP and user agent — durable proof that the trader recorded the customer's express consent to lose the 14-day withdrawal right upon starting access.
* New: **Checkout consent for services started early (Art. 14(4)(a))**. Optional checkbox at the WooCommerce checkout when the cart contains a service flagged as eligible for early start. With it accepted and recorded the trader may charge a pro-rated amount if the customer withdraws within the 14-day window; without it, an early withdrawal forces a full refund regardless of the work already delivered. Same persistence model as the digital-content consent.
* New: **"Withdrawal status" dropdown per product and per category** (Products → Categories edit screen, and the General tab of the product editor). One single field with four explicit choices — Standard, Digital content (Art. 16(m)), Service started early (Art. 14(4)(a)), Other Article 16 exception — controls both the Article 16 exclusion flag and the matching checkout consent in one place. Categories propagate the status down to all descendant products; per-product values override the inherited default. Sites running 1.4.x are migrated automatically on first admin request: every category that was listed in the old `ayudawp_euw_excluded_categories` option is moved to the new per-category term meta with status *Other Article 16 exception*, and the option is dropped — no manual reconfiguration needed.
* New: **Annex I.B model withdrawal form** (Directive 2011/83/EU). Generated dynamically from the shop name, address (pulled from WooCommerce when available), notification email and an optional trader phone configurable from settings. Rendered as a collapsible block right below the public withdrawal form, with a printable view available from the same page. Meets the pre-contractual information obligation of Art. 6(1)(h) of Directive 2011/83/EU, which is preserved on top of the new online function introduced by Directive 2023/2673.
* New: **Public excluded-product notice**. When a product carries an excluded status (directly or by category inheritance), a configurable notice is rendered between price and add-to-cart on the single product page. Two separate title+body pairs are editable from settings — one for Article 16(m) digital content, one for the other Article 16 exceptions — so a digital course and a perishable item can be described in their own terms. The body supports basic HTML and the `{withdrawal_page_link}` placeholder, which expands to a link to the configured withdrawal policy page.
* New: `[ayudawp_withdrawal_link]` shortcode that renders a permanent link to the configured withdrawal page. Use it in widget areas, classic-theme footers, the "Shortcode" block in block themes or anywhere a shortcode is accepted. Helps meet the "clearly identifiable and accessible throughout the withdrawal period" requirement of Article 11a introduced by Directive 2023/2673 without forcing a specific footer layout. Supports `text` and `class` attributes; returns empty when no withdrawal page is configured so it never breaks layouts.
* New: **Withdrawal link visibility** documentation section in the settings page explaining the three ways to surface the link (classic theme menu, block theme template part editor, shortcode) without auto-injecting anything.
* New: **Checkout consents** sub-section in the request detail metabox. Shows for each consent type the exact text shown to the customer, accepted/declined state, timestamp, IP and user agent — useful as durable proof if the customer contests the request later. Skips silently when the linked order has no consent meta.
* New: **Legal disclaimer block** rendered once on the settings page right before the "Save changes" button, clarifying that the plugin provides optional technical tools and does not guarantee legal compliance. Discreet grey card with a neutral info glyph.
* New: action hook `ayudawp_euw_after_form` fired inside the public form wrapper right after the `</form>` tag. Lets third-party modules inject content below the form without coupling — used by the Annex I.B module to render the collapsible block.
* New: trader phone settings field (optional) used by the Annex I.B model when populating the trader address block.
* New: HPOS-safe persistence of the new checkout consent meta via `WC_Order::update_meta_data()` and `WC_Order::save()`.
* New: every setting description starts with a uniform <strong>Mandatory</strong> / <strong>Recommended</strong> / <strong>Optional</strong> tag so the merchant can scan the form quickly.
* New: settings page detects when the WooCommerce Checkout block is in use and shows an in-place note recommending the classic `[woocommerce_checkout]` shortcode, which is what the consent hooks attach to in this release. Block-based checkout integration is on the roadmap.
* New: every public-facing link rendered by the plugin (privacy policy in the form, Annex I.B reference, printable view, excluded-product notice link, `[ayudawp_withdrawal_link]` shortcode, withdrawal link in transactional emails) carries `rel="noopener nofollow"` to keep the site's link equity contained.
* Internal: new module files `includes/functions-checkout-consent.php`, `includes/functions-annex-b.php` and `includes/functions-footer-link.php`, plus `assets/css/excluded-notice.css`. Conditional asset loading: the excluded-product notice CSS and Dashicons are enqueued only on single-product pages where the notice will actually render.

= 1.4.1 =
* Fix: removed `Requires Plugins: woocommerce` from the plugin header. The plugin has always been designed to run as a standalone tool (its own top-level **Withdrawals** menu and **Settings** submenu activate automatically when WooCommerce is not present), but the header was blocking activation on sites without WooCommerce. Stores that don't use WooCommerce can now use the public form, shortcode, request log, email notifications, SHA-256 receipt hash and native GDPR integration as a standalone solution. WooCommerce-specific features (order validation, My Account endpoint, transactional email injection, order notes, Article 16 exclusions) still light up automatically when WooCommerce is active.
* Tweak: reworded the plugin description and FAQ to make the standalone mode explicit.

= 1.4.0 =
* New: native integration with the WordPress GDPR tools. The plugin contributes a suggested Privacy Policy snippet (visible in **Settings → Privacy → Policy Guide**) and registers a personal data exporter and eraser, so admins can answer access and erasure requests for withdrawal data from **Tools → Export Personal Data** and **Tools → Erase Personal Data** without leaving WordPress. The exporter and eraser match by customer email and cover every meta field stored alongside the request (name, order reference, IP, user-agent, scope, submission timestamp, receipt hash and free-text details).
* New: declares `Requires Plugins: woocommerce` in the plugin header so WordPress can prompt users to install or activate WooCommerce before activating the plugin.
* Internal: WPCS pass over the codebase (double arrow alignment, multi-item arrays expanded over multiple lines, trailing newlines on every PHP file, escaped output where PHPCS could not infer the type was already safe). PHPCS is back to blocking the GitHub Actions workflow.

= 1.3.0 =
* New: configurable list of order statuses for which the withdrawal button and email notice are offered. All WooCommerce-registered statuses appear in the new **WooCommerce → EU Withdrawal → Eligible order statuses** section (custom statuses from shipping or fulfilment plugins included). Default: Processing and Completed.
* New: the notice is now also injected in the **Customer invoice / Order details** email (the manually triggered one). It only renders when the order status matches the configured list, so the manual invoice can still be sent on any status without leaking the notice.
* Change: the notice is no longer shipped in the **On-hold** email by default. On-hold typically means payment is still pending, so offering the right of withdrawal at that point would be misleading. Sites that want the previous behaviour can opt back in via the `ayudawp_euw_email_ids` filter.
* New filter `ayudawp_euw_allowed_statuses` to override the eligible statuses programmatically (receives the array and the current `WC_Order` when available).
* Internal: shared `ayudawp_euw_should_show_withdrawal( $order )` helper that combines deadline + status check; reused by the My Account action and the email notice injector.
* Dev tooling: PHP_CodeSniffer ruleset (`phpcs.xml.dist`), `composer.json` with WPCS and PHPCompatibilityWP as dev dependencies, and a GitHub Actions workflow to run PHPCS on push and pull requests. Thanks to @webdados for the suggestions.

= 1.2.2 =
* Fix: the form now resolves the order by its displayed number, not just by the internal post ID. Compatible with WooCommerce Sequential Order Numbers, Sequential Order Numbers Pro, Custom Order Numbers for WooCommerce (WPFactory) and any plugin that stores the customer-facing number in the standard `_order_number` post meta.
* Improvement: the "Withdraw" button on the WooCommerce **My Account → Orders** screen and the link injected into WooCommerce transactional emails now pre-fill the form with the same order number the customer sees in their receipt.
* New filter `ayudawp_euw_pre_resolve_wc_order` to short-circuit the resolver for plugins with non-meta numbering schemes (e.g. YITH Sequential Order Number) and `ayudawp_euw_resolve_wc_order` for late override or auditing.

= 1.2.1 =
* Fix: validate that the WooCommerce order exists when WC is active. The previous fallback used to accept submissions whose order number could not be matched against a real WC order — intended as an escape hatch for non-WC purchases — which let users submit withdrawals with completely invented order numbers. Sites that genuinely accept non-WC purchases can opt back into the lenient behaviour with the new `ayudawp_euw_allow_unverified_order` filter.
* Fix: translate the Scope value (Full/Partial) in the withdrawal detail metabox. It used to render the raw stored value in English even on translated sites.

= 1.2.0 =
* New: Article 16 exclusions. Mark individual products or whole WooCommerce categories as excluded from the right of withdrawal. Subcategories inherit the exclusion from the parent automatically. Withdrawal requests on orders containing excluded items are flagged for manual review (never auto-rejected) so a partial withdrawal over the rest of the order can still be valid.
* New: instant-search picker for excluded categories in the settings page, with removable chips and instant auto-save.
* New: inherited exclusion is reflected in the product editor — the per-product checkbox renders ticked and disabled with a note pointing to the category responsible for the inheritance.
* New: verifiable SHA-256 receipt hash. Every submission generates a hash sent to the customer in the confirmation email and stored on the request. Acts as tamper-evident proof on a durable medium and can be recomputed later from the stored fields.
* New: configurable withdrawal deadline. Choose whether the 14-day window starts from the order date or from the WooCommerce completion date, and add extra grace days directly from the settings page. The `ayudawp_euw_grace_days` filter still works on top of the stored value.
* New: submission timestamp (UTC) stored alongside each request and surfaced in the request detail metabox.
* Tweak: polished CPT labels ("Edit withdrawal", "New withdrawal", etc.).
* Tweak: split `functions-admin.php` into `admin/columns.php`, `admin/metaboxes.php` and `admin/bulk-actions.php` for easier maintenance. No behavioural change.
* i18n: updated Spanish (es_ES) translation with every new string.

= 1.1.0 =
* New: customer email notifications on every status change (accepted, rejected, completed).
* New: optional admin comment forwarded to the customer on status change. Required for rejections, optional for completed requests.
* New: WooCommerce order notes on every status change so the order timeline reflects the full withdrawal lifecycle.
* New: bulk actions in the withdrawals listing to mark several requests as accepted, rejected or completed at once.
* New: "Withdrawal" column in the WooCommerce orders screen (legacy and HPOS) showing the status of any linked request, toggleable from "Screen Options".
* Tweak: trimmed inline styles in the WooCommerce email notice so it inherits the email template styles instead of forcing a coloured callout box.

= 1.0.0 =
* Initial release.
