*** WooCommerce Appointments Changelog *** 2026.08.08 - version 5.4.8 * Feature - Appointable product: set Padding Before and After separately on one row. Existing padding stays Before; After defaults to 0. * Improvement - Availability / slots: the gap between appointments uses After from the earlier booking plus Before from the later one (timed, day, and shared staff). With only Before set (the default after upgrade), identical products keep the same handoff gap. * Improvement - Admin panel: create/edit recurring uses the same outlined card as billing and add-ons; schedule fields use compact floating labels in one row. * Improvement - Admin calendar: on narrow screens the period title opens a month or date picker (prev/next hide); week and list show a month label; tapping a month day opens Day view and Back returns to that month. * Dev - Padding: `padding_duration` remains buffer before; added `get_padding_after_duration()`, `get_padding_after_in_minutes()`, `set_padding_after_duration()`, and `get_padding_before_in_minutes()` (alias of `get_padding_duration_in_minutes()`). Overlap builders and `per_day_booked_totals` reserve both sides; day/month `get_padding_days_for_fully_booked_dates()` expands `(before + after)` days on each side. Meta `_wc_appointment_padding_after_duration` via REST (`padding_after_duration`), product CSV (`Padding after duration`), and WPML / Polylang. Interim cross-product `max()` bridge removed. * Dev - Tests: PaddingBeforeAfterLiveTest (timed/day, shared-staff gaps, cancelled, empty-window, dynamic vs indexed); ProductConfigLiveTest and SlotsScenarioTest lock 24h hourly listing and after-only / before+after index parity; ProductConfigCheckLiveTest skips notices on simple products; `wca-when-deps-ready.test.ts` for `wcaShared.whenDepsReady`. * Dev - Tests: opt-in `npm run test:php:stress` and `npm run test:php:smoke`; default `npm run test:php` stays full unit+integration. Clean test-blog setup in `tests/phpunit-env.local.example`. * Dev - Integrations: `WC_Appointments_Integration_Page_Optimizers` opts form JS out of LiteSpeed / Autoptimize / WP Rocket Delay-Defer on form pages. Appointable product General number/unit widths moved into `admin.scss`. * Tweak - Admin panel: create opens upper-biased on narrow screens; tighter floating-label insets; availability dims while checking; status chips match border to label on create/edit; customer search drops the clear control; overnight +Nd sits beside the End date floating label. * Tweak - Admin calendar: Screen Options/Help tuck with a peek and hide the toolbar when open; toolbar stays fixed under the admin bar; borderless period/view controls; wider padding on large screens. * Tweak - Storefront: Customizer Selection color skips inline CSS when it matches the default. * Fix - Storefront: multi-day day-duration products again highlight the booking span after picking a start day; timezone abbr shows one tip; the form still initializes when page optimizers delay jQuery or script params. * Fix - Availability / slots: 24h duration with an hourly grid again lists each hour when the next day is also appointable. * Fix - Admin panel: Select2 aligns on create from Add New; focused fields scroll above the mobile keyboard; mini calendar fits narrow panels; edit keeps Update in the footer (disabled until dirty). * Fix - Admin panel create: Product Add-Ons date picker no longer closes the panel; footer cost refreshes when add-on prices change without duration. * Fix - Admin panel: product dropdown lists appointable products again on non-calendar screens; customer search and Customer label translate on standalone create/edit. * Fix - Admin panel: Pick from calendar keeps the selected day for staff timezones; day/all-day edit availability uses civil dates; mini calendar refreshes when product, staff, or quantity changes; switching product on edit updates date/time for the new type without jumping the panel. * Fix - Admin panel: staff on unstaffed products is optional for managers only and ignored in availability checks. * Fix - Admin calendar: Order Total after panel create is correct; narrow layouts keep the filter panel, month cells, and timed events in bounds; day headers stay flush under the fixed toolbar with admin notices below. * Fix - Admin calendar: midnight appointments show when staff timezone differs from the site; opening a view scrolls to now or 08:00 in week/day, and to the top in month/list. * Fix - Admin calendar: Screen Options and Help are usable again for admins and staff (WooCommerce sticky header no longer covers them). * Fix - Admin: legacy Create as a Customer again books for the selected customer; configuration notices no longer appear on non-appointable products. * Fix - Appointable product: Padding Before/After keeps equal gap when the unit wraps and no longer sits flush against the field above. 2026.08.03 - version 5.4.7 * Improvement - Admin panel: create/edit fields use outlined floating labels (Checkout-style); status uses compact filter chips; attendance is hidden on create, editable on edit, and muted text on view; Pick from a calendar stays a compact trigger until opened. * Improvement - Admin calendar: sticky toolbar and day headers under admin chrome with one page scrollbar; the toolbar docks under the admin bar while you scroll. * Performance - Admin: calendar/panel product list loads with a light id/title/color query instead of hydrating up to 200 full product objects on every admin load. * Performance - Admin panel browse: day availability evaluates each rule once; all-day RRULEs reuse the in-request path instead of rebuilding minutes every civil day. * Dev - Tests: AdminCalendarAvailabilityStripeTimezoneLiveTest (PHP) and admin-calendar-availability-rule-timezone Vitest lock civil vs timed stripe timezone policy for staff UTC, Australia/Perth, and America/Los_Angeles. * Dev - Tests: SeriesCartCheckoutLiveTest covers series creation on on-hold, processing, completed, and partial-payment (pending is not hooked). * Dev - Tests: RuleMinutesRruleCivilDayTest, RruleCivilDayAvailabilityLiveTest, RuleMinutesOvernightApplyEvaluateTest, and OvernightTimeAvailabilityLiveTest cover overnight/all-day RRULE civil-day parity and time:N overnight vs end-at-midnight. * Dev - Tests: `SlotsTimezoneLiveTest` and `AdminPanelTimezoneLiveTest` cover store/staff timezone boundaries for slots and panel availability (Fake UTC chip fields). * Dev - Tests: IcsExporterStaleEntryRegressionLiveTest covers offset-based store timezones (`gmt_offset` / `+01:00`) so VTIMEZONE generation does not fatal when `getTransitions()` returns false. * Dev - Build: `npm run build` runs `test:js` (calendar timezone Vitest) alongside gcal and PHP suites. * Tweak - Admin panel: primary form controls use 14px body text; series list shows date and time ranges on separate lines. * Tweak - Admin panel create: browse no longer auto-opens when a slot is unavailable; Add New without calendar prefill leaves date/time empty (date-only calendar picks still auto-select the first time). * Tweak - Admin panel: product select uses preloaded products (instant); clear (x) removed on create/edit; duplicate titles append SKU or #id; duration sits on the When row; line Total shows only when it differs from order total. * Tweak - Admin calendar: compact one-row toolbar; Add New is a leading filled button (label on wide screens, icon-only on narrow); filter button shows an active-count badge only (selections stay in the filter panel). * Tweak - Admin calendar: hovered and selected events use a white ring plus lift; selected events rise above overlaps. * Fix - Admin calendar (staff): appointments you create for products without assigned staff stay on the calendar immediately (creator matches the locked staff filter, same as after reload). * Fix - Admin panel create: real midnight slots (00:00), including admin-TZ midnight, keep a timed calendar preview and availability checkmark (slot timestamp distinguishes them from date-only placeholders). * Fix - Admin panel: narrow view fills the viewport; series uses panel scroll; customer timezone and cost lines stack; series rows are date | status | open. * Fix - Admin calendar: switching the appointment panel from view to edit no longer flickers the calendar event (preview ghost only appears after the draft schedule changes). * Fix - Admin calendar: full-day unavailable stripes stay on the civil day (not shifted by admin timezone); stripe tips on hover/tap instead of a modal; tapping a stripe no longer opens create; empty cells open create again. * Fix - Admin calendar: sticky WooCommerce header chrome scoped to the calendar page only; staff Screen Options/Help no longer inflate Firefox scroll; empty preload ranges still seed so first paint skips REST; availability rules load again when indexed cache is enabled. * Fix - Admin calendar SSE: after a recurring series materializes, the view panel gets the full occurrence list and package pricing; one series create snackbar with customer and pattern. * Fix - Admin calendar SSE: new appointments stay after month navigation (cache insert); payloads build only when queued and flush once per request; burst creates/bookings collapse to one snackbar count. * Fix - Admin calendar/panel (staff): product preload respects Create Appointments for Any Product; create auto-selects the logged-in staff when assigned; toolbar Add New uses the same path as slot-pick (no full reload). * Fix - Admin panel create: required date/time errors show under those fields and clear when valid; hourly auto-select no longer rolls the form date forward when staff timezone differs from the site. * Fix - Admin panel: Staff field shows or hides from product preload (same as add-ons); create availability responds faster; day-duration checks use the correct civil day when staff timezone differs; timed availability keeps slot timestamps when site and admin browse days differ. * Fix - Admin panel browse: skeleton cells under a kept header; day click and view toggles reuse in-memory data; SSE invalidates browse caches while open; product switch reloads the open mini-cal. * Fix - Availability / slots: overnight RRULE blocks (e.g. weekly Fri 22:00-02:00) again split across civil days (evening on the start day, morning on the next). * Fix - Storefront recurring: create the series when the order reaches on-hold (cheque/BACS), not only processing/completed, so offline payment checkouts still materialize appointments. * Fix - ICS export: offset-based store timezones (e.g. `+01:00` from `gmt_offset`) no longer fatal VTIMEZONE generation when `DateTimeZone::getTransitions()` returns false. 2026.07.30 - version 5.4.6 * Dev - Tests: global-only availability with indexing enabled (plain and staff products), index readiness ignoring malformed scoped cache rows, and product config probe when global rules start after the initial 14-day window. * Fix - Admin panel: product configuration probe scans the booking horizon in capped chunks (14/30/60 days) instead of only the next two weeks, so global rules or RRULEs with a later start do not trigger false "no bookable times" notices. * Fix - Indexed availability: index readiness and multi-staff slot generation align with fetch guards (malformed scope rows no longer mark the index ready; empty indexed batch falls back to dynamic/global rules); fixes global-only rules not applying and false product config notices after 5.4.5. 2026.07.30 - version 5.4.5 * Improvement - Admin panel: Product edit shows configuration notices at the top when saved settings may prevent bookable slots; notices distinguish misconfiguration from fully booked inventory (slots engine diagnostics) and link to the matching Appointments tab. * Improvement - Admin panel: Clearer help tips on duration, interval, availability check, and rule quantity fields. * Dev - Tests: ProductConfigCheckLiveTest, MalformedAvailabilityIndexLiveTest, and StorefrontSlotPickerMarkupLiveTest cover product config probe, malformed indexed rules, storefront slot notice markup, and `getInstanceContext()` form-element resolution. * Fix - Indexed availability: product- or staff-scoped rules with an empty kind_id no longer leak as global rules (read-side scope guard, write-side skip on index); manual re-index reports skipped malformed rules; `get_title()` returns empty string for NULL legacy titles (avoids PHP 8 TypeError on re-index). * Fix - Admin panel: Configuration check and probe use the slots provider only (indexed availability, staff merge, index fallback): no false duration-vs-window warnings on full-day or weekday rules; priority-based engine diagnostics; Availability check mismatches when slots exist but none qualify as bookable starts. * Fix - Admin calendar: month view highlights today with the same badge as week and day views; legacy calendar falls back to week view when the saved view is List (React-only). * Fix - Storefront time picker: `getInstanceContext()` works when passed the form element (legacy theme markup); empty-day and AJAX error notices use consistent `slot_row` / `ul` / `li.slot` markup (full width, no bullet). * Fix - Storefront date picker: multi-staff autoselect uses staff-aware calendar `day_data` (async month load); autoselect products skip full-range default-date scan; non-autoselect uses chunked first-slot for the pre-selected provider. 2026.07.27 - version 5.4.4 * Dev - PHPStan raised to level 5 — type narrowing, expanded plugin and WordPress stubs, and dead-code cleanup across admin, data stores, API, GCal, and integrations (clean baseline). * Fix - Product Add-ons integration: decode jQuery `.serialize()` form payloads with `wp_unslash()` before `parse_str()` in legacy cost filter. * Fix - Admin calendar: guest customer labels on HPOS stores read billing from `wc_order_addresses` (not legacy `_billing_*` order meta), restoring "Name (Guest)" on calendar preload and SSE updates. 2026.07.26 - version 5.4.3 * Performance - Admin calendar preload: eligible-ID query plus batched postmeta hydration; batched order meta (no per-order WC_Order hydration). Recurring series package summary on bulk fetch (`get_panel_preload`, one load per series id); full appointment panel still loads on open. * Dev - PHPCS: WPCS security ruleset (`phpcs.security.ruleset.xml`, `npm run lint:php:security`), shared path rulesets and request helpers; `lint:php` / build gate runs security scan + PHPStan; optional `lint:php:standards` / `lint:php:quality`; PHP-CS-Fixer via `format:php`. * Dev - PHPStan raised to level 4 — dead-code cleanup across admin, slots, API, GCal, and core (clean baseline). * Dev - Rate limiting: filterable `enabled`, `guest_only`, and `strict_max_requests`; named guest-read defaults on `WC_Appointments_Rate_Limiter`. * Dev - Tests: `PublicAjaxSecurityLiveTest`, `PhpstanLevel4RegressionLiveTest`, `IcsExporterStaleEntryRegressionLiveTest`, `MyAccountRescheduleContractLiveTest`, `CalendarPreloadParityLiveTest` (calendar preload vs independent eligibility SQL; non-UTC and all-day; attribute term labels), `RequestBoundaryRegressionLiveTest`, `SanitizerBeforeParseGuardTest`, and `SseTopicContractVerificationTest` (request transport boundaries, PHP/JS SSE topic contract, FC week/day header zone). * Deprecated - `WC_Product_Appointment_Rule_Manager::get_minutes_from_rules()` remains as a compatibility shim; use `WC_Appointment_Slots_Rule_Resolver::build_minute_mask()` instead. * Breaking change - Availability: `decode_rules_from_post()` requires the unslashed POST array argument (no default); third-party save handlers must pass `wp_unslash( $_POST )`. * Tweak - Admin panel: remove Cancel from create/edit footer (close via X / backdrop). * Tweak - Admin calendar: Google-style grid (horizontal week lines only in month, stacked week/day headers, centered month dates); neutral toolbar chrome (admin body text/borders), segmented view switcher, primary + Add New beside Filters (icon-only; label on hover/aria). * Tweak - ICS export: log a warning when unloadable entries are skipped (WooCommerce → Status → Logs). * Fix - Admin calendar: toolbar spans full wrap width below Screen Options/Help; staff without WooCommerce header get Add New in the toolbar; staff/creator filter uses EXISTS (not INNER JOINs); Fake UTC civil preload range and `YmdHis` row parsing (not `strtotime`); shared excluded-status list (cancelled visible; holds/trash/drafts hidden) on SQL query, legacy API, and frontend; orphan/deleted `post_parent` order IDs pre-filtered before batch order load; availability rule background stripes reload when switching months. * Fix - REST API: appointments `get_item_schema()` extends the parent schema instead of starting from null. * Fix - Security: sanitize/unslash POST/GET at origin across storefront AJAX, data stores, cart, SSE, cron, widget, admin (AJAX, settings, calendar, list table, exporters, meta boxes, product edit), WPML, Google Calendar, and integrations; prepared SQL for lookups and migrations; escape output in admin views; guest-only rate limit preserved for cached pages with optional nonces. * Fix - Storefront/admin AJAX: parse jQuery `.serialize()` form payloads before per-field sanitization (not `sanitize_text_field()` on the encoded string — destroys `%XX` sequences for timezones, times, and add-on text). * Fix - SSE: preserve dotted event names (`appointment.created`) in the stream; `sanitize_key()` stripped dots and broke admin calendar live updates. * Fix - Availability rules JSON: decode unslashed payloads first (callers pass `wp_unslash( $_POST )`); double-unslash broke rule titles containing quotes. * Fix - Admin calendar: week/day column headers use browser-local date (not UTC parts); legacy week/day `data-time` and `calendar_day=` links use site timezone (not `gmdate`); batched order line item meta resolves global attribute term names (not raw slugs) in the appointment panel. * Fix - Add Appointment and My Account series actions: read `REQUEST_METHOD` from `$_SERVER` (not `filter_input( INPUT_SERVER )`, unreliable under some SAPIs). * Fix - Legacy install migration: delete product availability post meta by key after table migration (not a single rule value). * Fix - ICS export: skip unloadable appointment entries so stale IDs do not fatal calendar generation. 2026.07.18 - version 5.4.2 * Dev - PHPStan raised to level 3 (phpVersion 7.4). * Dev - Tests: regression coverage for range_type RRULE routing parity and orphan `rrule` field cleanup. * Dev - Tests: lock GCal failure-path contracts (`make_gcal_request()` → `WP_Error`, `sync_event_resource()` → `false`) and rule-minutes payload shape across all `compute_rule_minutes()` branches. * Fix - Indexed availability: RRULE expansion follows `range_type` (same as dynamic); orphan `rrule` on non-`rrule` rules uses the non-RRULE path and purges legacy expand rows on background passes. * Fix - Google Calendar: inbound sync clears `rrule` when an event is stored as non-recurring (`custom` / `custom:daterange`). 2026.07.16 - version 5.4.1 * Improvement - PHPStan: level 2 with a clean baseline — expanded plugin and integration stubs plus PHPDoc/type fixes across core. * Performance - Indexed availability: occupancy reads from appointment cache when healthy (live fallback during backfill; in-cart recurring holds merged); backfill batches gap-only missing appointments. * Tweak - Storefront calendar: `data-engine` reports rules/occupancy source (1 indexed, 2 dynamic, 3 indexed + appointment cache). * Dev - Orders: `get_appointment_ids_from_order_and_item_id()` deprecated — use `get_appointment_ids_from_order_item_id( $order_item_id, true, $order_id )`. * Dev - Orders: `get_appointment_ids_from_order_item_id()` triggers `wc_doing_it_wrong` when `$order_id` is omitted (except opt-in order-line `_appointment_id` cache). * Fix - Indexed availability: weekday `time:N` rules count only matching days; re-enable no longer wipes inline indexing; backfill pending flag clears/re-queues stale jobs and stops on zero-gap batches. * Fix - Google Calendar: scheduled sync is forward-only unless admin full re-sync; inbound sync skips stale events (event ID / `site_url` mismatch). * Fix - Availability / slots: overlap day keys use Fake UTC civil midnight so 00:00 wall slots stay on the correct day on negative-offset stores. * Fix - Orders: order-scoped line-item lookup (`$order_id` on `get_appointment_ids_from_order_item_id`) — orphaned `_appointment_order_item_id` no longer shows another customer’s appointment; line delete clears the link before ID reuse. * Fix - Cart: session load removes holds tied to terminal orders or past start dates and releases `in-cart` appointments to `was-in-cart`. 2026.07.10 - version 5.4.0 * Improvement - Settings → Indexing: combined section intro with scannable structure (benefit, when to use, warning, maintenance). * Improvement - Pricing: display-only quotes can skip `is_appointable()` in `calculate_appointment_cost()` (filter/post flag); add-to-cart still validates. * Improvement - Recurring package footer: single cost summary, session count under hero price, deposits excluded from preview; removed deprecated `checkout_footer_html`. * Performance - Removed ~1 MB of unused Moment.js from storefront scripts. * Dev - PHPStan: third-party and WP-CLI stubs, admin include `@var` docblocks, Sprint 5–6 cleanup — baseline cleared (0 suppressed errors). * Fix - REST API: allowlist availability `filter` column keys before SQL (closes unauthenticated injection on public `/availabilities`). * Fix - Storefront timezone **Default**: persists via `appointments_use_site_timezone`; custom zones still use `appointments_time_zone`. * Fix - Storefront: unavailable `?time=HHMM` shows selected time with "(Selected time is unavailable)" instead of auto-picking first slot. * Fix - Availability / slots: past-date guard and legacy timed storage use Fake UTC civil day; day/month add-to-cart omits `_time` after midnight. * Fix - Availability rules: fix rule id assignment and `$rule_val` init; priority input fills column on global and staff tables. * Fix - Pricing: multi-day day-unit totals sum each civil day (matches slot preview; PAO duration flats skipped when billed per day). * Fix - Per-day inventory: in-cart recurring `session_starts` occupancy included when marking civil days sold out. * Fix - Admin add appointment: reject missing posted date before parsing year/month/day. * Fix - Admin panel: create panel anchors to calendar slot when **Add New** is opened first, then a time is picked. * Fix - Admin product staff panel: fix assign-staff AJAX scope and staff view `self::` call. * Fix - Admin calendar: preload spans prev/current/next month grids so view switches use cached appointments. * Fix - Google Calendar: sync errors log exception messages; HTTP backoff returns explicit error when retry loop exits without a response. * Fix - Partial refunds: each reduced-qty appointment keeps the correct per-line-item `$remaining_qty`. * Fix - Series My Account: invalid action redirect no longer leaves `$result` / `$notice` undefined. * Fix - Helpers: remove undefined `$gmt_offset` / `$cart_item` reads in timezone and addon duration helpers. * Fix - Privacy: exporter registration returns exporters array from `register_exporters()`. * Fix - CRM integration: initialize note strings before appending comment HTML. * Fix - Webtomizer deposits: check `$appointment_ids` array before updating partial-payment status. * Fix - Product Vendors integration: declare `global $wp_roles` before role cap setup. * Fix - Follow-Up Emails: pass `null` appointment to variable-replacement filter when queue item has no appointment context. 2026.06.28 - version 5.4.0-beta6 * Improvement - Minimum WordPress raised to 5.9 (Requires at least) so `str_starts_with()` is polyfilled by core on PHP 7.4; aligns the declared requirement with the existing `Requires PHP: 7.4` matrix. * Improvement - Recurring: add-to-cart preview cache and request helpers on `WC_Appointment_Series_Request` (cart trait and integrations call it directly). * Improvement - Recurring storefront: per-occurrence reschedule suggests nearest available slots across a frequency-aware window (one RRULE step before and after the target day) instead of a fixed same-day-only 3-day span, so weekly/monthly series now offer adjacent same-weekday/week slots; the reschedule action is a labeled pill (not an icon-only glyph) and the alternatives list is headed "Nearest available slots" for discoverability. Shared by the admin create panel. * Dev - Recurring: appointments enabled by default (`wc_appointments_recurring_enabled`); disable site-wide with `add_filter( 'wc_appointments_recurring_enabled', '__return_false' );` when needed. * Dev - Recurring pay-first: balance order auto-create and invoice filters live on `WC_Appointment_Series::auto_create_balance_order_if_due()` (removed unused `wc_appointment_series_pay_first_created` action and Payment pass-through helpers). * Dev - PHPUnit: guard against static calls on trait names (PHP 8.1+ deprecation while Requires PHP is 7.4). * Dev - PHPUnit: `DaysPricingTimezoneLiveTest` — day-of-week pricing on Fake UTC day slots vs non-UTC store timezone. * Dev - PHPUnit: `TimezoneAxisLiveTest` — Fake UTC vs site-TZ axis regressions with America/Chicago probe (Rule_Manager, padding `date()` keys, legacy hourly pricing, day pricing); store-native parity tests retained. * Dev - Recurring: `WC_Appointment_Series_RRule::interval_days()` is the single freq/interval parser (replaces duplicated parsing in the expansion-range estimator and the suggestion window). * Fix - Checkout validation: **per-day inventory** no longer counts the customer’s own in-cart hold when re-checking availability (same overlap-exclude scope as slot occupancy), fixing false “slot no longer available” at checkout on qty-per-day products. * Fix - Storefront: `[appointment+_form]` shortcode and Appointment Form block post add-to-cart to the embedding page (same as WooCommerce `[product_page]`) so product permalink redirects no longer drop booking data. * Fix - Recurring storefront: frequency and payment chips stack in a single left-aligned column below 36em (same breakpoint as Every/For). * Fix - Membership credits: recurring packages no longer emit PHP 8.1+ deprecation notices on add-to-cart (subscriptions integration no longer calls static trait methods). * Fix - Recurring pay-first: balance orders are created when the pay-first series is confirmed at checkout (next sequential order id); opt out with `wc_appointments_recurring_auto_create_balance_order`. * Fix - Recurring pay-first: balance orders get custom order numbers on create (same hook as other programmatic WooCommerce orders; default pending status from first save). * Fix - Recurring pay-first: balance pay links read the stored payable balance order only (`get_balance_pay_url()` no longer creates orders on read; staff/customer actions use `ensure_payable_balance_order()`). * Fix - Recurring pay-first: balance order staff actions on the WooCommerce order edit screen use nonced admin links (no nested forms or JavaScript); HPOS-safe order edit redirect after balance actions. * Fix - Pricing: day/month bookings on the Fake UTC axis set `_slot_token_timestamp` so range-of-days rules use `gmdate` weekday (not `wp_date`), fixing a one-day shift on non-UTC stores (e.g. America/Chicago). 2026.06.21 - version 5.4.0-beta5 * Improvement - i18n: merge generic admin confirms, errors, and required-field messages to shared strings; fix post-status `_x()` context bug; caching horizon months use `_n()`; align AutomateWoo status labels; normalize All Staff / Export Appointments / Edit product casing. * Improvement - Membership credits: View Subscription **Credited appointments** reuses `myaccount/appointments.php` (pagination, newest first); credits show as *X of Y remaining*. * Improvement - My Account templates: consolidate series list into `appointments.php`; rename `series-detail.php` → `view-series.php` (matches endpoint). * Fix - Membership credits: shift stored `_wca_credit_balance` on linked subscriptions when a plan product's appointments-per-period changes (product save). * Fix - Order lines: `get_appointment_ids_from_order_item_id()` uses appointment `_appointment_order_item_id` only by default; order-line `_appointment_id` cache is opt-in via `woocommerce_appointments_use_order_item_appointment_cache`. * Fix - Membership credits: consume on payment or when a paid order links an appointment; skip lines with no linked appointment. * Fix - Storefront: Product Add-ons show again after a time slot is selected — revert 5.4.0 show/hide to `wc-appointments-appointment-hook-after` (matches CSS and theme overrides); remove unused `wc-appointments-appointment-extra` class. * Fix - Storefront: recurring **Repeat this appointment** checkbox shows again after slot selection — toggle `.wc-appointments-recurring-hook` with the same cost-calculation show/hide as add-ons (without restoring `wc-appointments-appointment-extra`). * Fix - Storefront: recurring series price quote updates when WooCommerce Deposits pay-deposit / pay-in-full (or plan) options change. * Fix - Admin panel: Add New opens the creation panel again — only defer visibility for calendar-anchored create; restore preview fallback dropped in 5.4.0-beta4. * Fix - Google Calendar: disconnect clears sync tokens and cached access tokens; connected UI follows refresh token; upgrade removes legacy pre-5.0 gCal transients; global inbound sync no longer skipped when staff sync for the same calendar failed; 404 on calendar fetch clears sync token and surfaces re-select calendar. * Fix - Recurring pay-first: balance orders are payable only when pending/failed (no read-time line sync or status reset); series balance settles on `partial-payment` as well as processing/completed; due occurrences marked paid when the balance order is paid online. * Fix - Recurring storefront: order-received and My Account use the compact balance block again — email balance table stays on emails only. * Fix - Recurring pay-first: balance order emails and order views label only the first session as paid on the original order; later sessions show balance-due/confirmed labels; My Account series detail lists original and balance orders; pay-balance link on the order line series display (processing email uses the same order instance as series creation); balance payment links due occurrences to the balance order; series confirmation email no longer embeds the parent order table; staff balance actions stay on the order edit screen only (not in admin order emails). 2026.06.16 - version 5.4.0-beta4 * Feature - Recurring packages: Product Add-ons per add-on **Use once per recurring series** (default: price and duration on every session); series preview, cart, pay-first, admin panel create, and Add-ons product tab. * Improvement - AutomateWoo: verified compatibility with 6.4.0; appointment time variables use AutomateWoo core `AbstractTime` (removed duplicate). * Improvement - Recurring: scoped reschedule (Apply to: this appointment, following, or all) in admin panel and My Account; fixes series datetime edits that bypassed EXDATE/RDATE. My Account skip/cancel routes through the same scope layer as admin status changes. Batch reschedule conflict errors name the blocking occurrence. * Improvement - Recurring pay-first: balance payment request email reuses the shared balance-summary block and package-focused intro (paid-on-original-order and package total above the order table) instead of generic “order created” copy. * Improvement - Admin panel: pay-first **Package balance** section reuses pricing **Cost/Tax** row styling; bordered action chips and icon-only feedback. * Improvement - Membership credits: booking-form picker applies credit at add-to-cart (single `get_booking_context()` resolver); checkout is confirm-only with compact fallback opt-in. * Improvement - Membership credits: cart shows strikethrough covered value with $0 due; membership credit row shows plan, covered amount, and balance (orders too); credit lines stay $0 after other cart pricing hooks. * Improvement - Membership credits: checkout opt-in uses the same markup and styles as the booking form footer. * Improvement - Membership: subscribe thank-you **Book appointment** CTA; `_wca_first_booking_product_id` stored at grant time. * Improvement - My Account → Appointments: compact membership summary card (replaces full credits table); empty-state **Book** links to stored first-booking URL when available. * Dev - Settings → Appointments: removed **Recurring packages** section; auto balance order and customer invoice timing ship as recommended defaults (auto-create on, manual invoice), overridable via `wc_appointments_recurring_auto_create_balance_order` and `wc_appointments_recurring_balance_invoice_timing` filters. * Fix - Recurring storefront: package preview via wc-ajax (shared WooCommerce session) — no stale REST nonce / “Cookie check failed”; preview stays public like slots AJAX. * Fix - Recurring pay-first: repair incomplete pay-first meta when series totals are missing on the series row. * Fix - Admin panel: recurring series orders use the preview package total — set unit price before `add_product()` so the order line is correct at creation. * Fix - My Account: cancelling a recurring appointment from the appointments list now updates the series (EXDATE) instead of orphaning the occurrence. * Fix - Membership credits: recurring packages can use membership credits — one credit per billable appointment in the series (cart $0, validate balance, consume/restore on order); cart and booking UI show remaining credits after cart reservations. 2026.06.09 - version 5.4.0-beta3 * Improvement - Recurring pay-first: balance orders as appointable line items with in-item series summary (legacy fee orders upgraded on repair); mirror parent balance block and occurrence labels. * Improvement - Admin panel: preset last-used appointment status on create. * Fix - Recurring storefront: qty/add-on/payment refresh pricing only; balanced reschedule suggestions; Fake UTC `session_starts` for TZ-aligned cart/checkout; guest preview no login; no double-charge when inventory > 1. * Fix - Recurring pay-first: Mark paid offline completes balance order; balance admin mirrors parent block; parent qty/tax/shipping before totals; incl-tax catalog as ex-tax nets; materialization no longer overwrites line subtotals. * Fix - Recurring / emails & My Account: **View series** 404 — register `view-series` query var; permalink flush. * Fix - Recurring series: occurrences inherit checkout inventory/qty at create. * Fix - Recurring / product admin: Repeat frequency checkboxes no longer overlap (plugin admin CSS). * Fix - Admin panel: view/edit qty from calendar inventory; create form browse-on-focus, debounce, day-slots spinner, auto-pick success. * Fix - AutomateWoo: appointable type on `product_type_selector` outside wp-admin for Action Scheduler product queries. * Fix - Admin calendar: live SSE storefront updates via `admin-ajax` (EventSource cannot auth REST cookies). 2026.06.04 - version 5.4.0-beta2 * Tweak - Recurring & membership: shorter customer-facing copy; package/series for bundles, appointment (not session) for counted slots; email intros consolidated. * Performance - Availability rules: DB 5.4.4 indexes on `event_id`, `parent_event_id`, `(kind, kind_id)`; base-table column filters for Google Calendar and product/staff rule loads. * Improvement - Recurring storefront: preview/cost loading bars, dimmed list/price, Book now disabled while preview runs or after errors. * Dev - Recurring: `format_session_time` and admin customer TZ line on `WC_Appointment_Series_Email`. * Tweak - Block emails: missing ##WOO_CONTENT## falls back to classic PHP template from the sending `WC_Email`. * Fix - Recurring / emails & orders: one **Appointment Series** `created` email per order (all series in one table); line `_wca_recurring` / `_wca_series_id` only on mixed checkout; skip non-series occurrence ids; session TZ short name when booking TZ ≠ site. * Fix - Recurring storefront: booking-axis RRULE expansion and Fake UTC preview labels; store-axis conflict checks; orders/emails use `format_session_time()`. * Fix - Recurring: REST idempotency cleanup; pay-first totals backfill on create; parent `_wca_series_id` only when empty; materializer optimistic-lock retry; Fake-UTC store-wall "future" scope cutoff. * Fix - My Account: cancel/reschedule on `wp_loaded`; recurring in-cart occupancy after `wp_loaded`. * Fix - WooCommerce Subscriptions: renewal credits once on `woocommerce_subscription_renewal_payment_complete` (no double grant); restore clamped to period allotment when rollover is off. 2026.06.02 - version 5.4.0-beta * Feature - Recurring prepaid packages: series checkout (RRULE, REST v2 `/series`, storefront, cart, My Account) with min/max sessions, frequencies, and partial checkout when some sessions are unavailable. * Feature - Recurring pay-first: first session at checkout, balance order for remainder (product options, Settings → Recurring packages, **Pay remaining balance** on storefront and My Account). * Feature - Recurring package pricing: **Sessions in package** rule on series subtotal (per-session adjust; ×/÷/= on package total). * Feature - WooCommerce Subscriptions membership plans: credits per billing period, eligible appointable products, $0 credit checkout. * Improvement - Settings → Appointments: **Recurring packages** (auto balance order, customer invoice timing). * Improvement - Recurring / product admin: recurring flags, frequencies, payment model (`wc_appointments_recurring_default_payment_model`), min/max sessions, billable minimum. * Improvement - Admin panel: recurring badge, sessions panel, scoped **Apply to**, pay-first pricing, balance actions, list filter, calendar ↻ preload; subscription **Appointments** tab and **Eligible products**. * Improvement - Storefront: **Repeat** picker with live preview; prepaid/pay-first totals; hides Deposits during recurring checkout. * Improvement - Cart & checkout: recurring lines, pay-first first charge, virtual occupancy, one membership credit per order at checkout. * Improvement - My Account: series list/detail (cancel, skip, reschedule, pay balance), standard tables, **Series** column, `series_details`; membership credit balance. * Improvement - Emails / recurring: **Appointment Series** (prepaid created, bulk scoped); balance and schedule on orders, thank-you, and invoice. * Performance - Recurring: cached preview, batched recurrence, single-query admin counts, lightweight REST preview, one-SQL panel `/occurrences`. * Dev - Recurring foundation: series tables, `WC_Data` store, manager/preview/materializer, REST v2, cart trait, storefront JS, My Account, WCS module, CLI, tests; DB 5.4.1–5.4.2. * Dev - Recurring layout: meta on `WC_Product_Appointment`; consolidated WCS membership; series block emails via `woocommerce/email-content`. * Tweak - Recurring series object cache off by default (`wc_appointments_series_cache_enabled`). * Fix - Recurring / pay-first balance: `wp_wc_appointment_seriesmeta`, HPOS staff links, `calculate_balance_due_display()`, prepaid option match, first occurrence on parent until paid. * Fix - Recurring / storefront & REST: RRULE COUNT/UNTIL, Fake UTC labels and `session_starts`, cart pricing on `init`, `/occurrences` meta and scoped **following**, PHP 8 REST visibility. * Fix - Admin calendar: ↻ prefix in `eventDataTransform`; `series_id` from `_wca_series_id` on preload. * Fix - Recurring / emails & orders: block `appointment_series` emails; consolidated occurrence suppression; no duplicate mail after materialize or bulk scope. * Fix - WooCommerce Subscriptions: no `woocommerce_is_subscription` recursion via appointable `get_price()`. * Fix - My Account view-series: cancel/reschedule per product on completed prepaid series; POST permission checks; occurrence reschedule via standard form (`reschedule_one`). * Fix - Emails & My Account: block ##WOO_CONTENT## classic fallback; reschedule notes admin-only; skip-one suppresses order sync (prepaid order cancels when all appointments cancelled). 2026.06.09 - version 5.3.11 * Fix - Twilio SMS integration: restore hook registration removed in 5.1.0 (Appointments SMS settings, scheduling, and notifications with WooCommerce Twilio SMS Notifications 1.12+); `{appointment_date}` / time tokens use `get_start_date()` / `get_end_date()` so all-day civil dates match emails and order summary on non-UTC stores. * Fix - All-day (day/month duration) appointments after checkout: order summary, emails, and My Account show the same civil date as cart/checkout — `get_start_date()` / `get_end_date()` and email formatters use Fake UTC civil digits (`gmdate` + `date_i18n`) instead of `wp_date()` on stored midnight tokens, which shifted the calendar day on non-UTC site timezones. * Fix - All-day bookings: `_local_timezone` is written only for timed products in `wc_appointments_get_posted_data()`; `WC_Appointment::set_all_day()` / `set_local_timezone()` and the data store drop customer TZ meta on all-day saves — civil dates stay timezone-agnostic without per-caller guards. 2026.06.02 - version 5.3.10 * Fix - Storefront appointment cost quote when catalog prices are entered inclusive of tax: footer HTML uses the same display amount as AJAX `raw_price` (not tax-stripped `line_net`), so logged-out customers see the correct gross total. * Fix - Availability / add-to-cart: day- and month-unit checks use Fake UTC civil tokens (`gmdate` / shared slot-token helpers) for past-date guards, span end calculation, and day-slot capacity keys — not `wp_date()` / site-TZ `DateTime` on the posted timestamp — so same-day bookings and slot lookup stay aligned on non-UTC stores (e.g. America/Vancouver). * Fix - Storefront calendar (no staff preference, day/month + staff): multi-staff indexed slot generation no longer runs the minute-mask loop on day/month products — use per-staff civil-day slots and union merge instead; no-preference day summary uses per-staff OR (max), not summed slot counts across merged fragments. * Fix - Admin panel create: day/month availability check and save use Fake UTC civil midnights for date/month ranges (matches storefront slots and `get_day_civil_span_capacity` keys); admin month calendar bounds for day/month products use the same civil UTC axis. * Fix - `is_appointable()` past-date guard for day/month products uses `wc_appointments_is_past_request_date()` (Fake UTC civil axis) instead of `date()` on `_date`, matching availability checks. * Fix - Admin panel create success message formats saved day/month ranges from appointment timestamps via `wp_date()` (not `strtotime()` on date strings). 2026.05.25 - version 5.3.9 * Improvement - Improved admin panel styling with bigger input selection. * Improvement - Admin panel create/edit: datetime row shows a loading state while product details, first-available auto-pick, day slots, or availability AJAX run; success/error only after the check completes. * Performance - Admin panel create: first-available auto-pick scans the bookable horizon in 14-day chunks and stops at the first slot instead of loading the full range up front. * Fix - Admin panel create: availability no longer flashes loading twice or clears to blank before first-available prefill; date-only calendar prefill waits for day slots before checking; browse does not re-open after the user closes it during the same unavailable episode. * Fix - Admin panel: datetime loading no longer sticks when day-slot AJAX fails or returns empty, when a superseded availability request aborts, or while bootstrap waits only on an in-flight slots request (not missing cached data). * Fix - Slots / day & month products: timed global RRULE blocks (e.g. weekly Tue 4–6pm) block the whole civil day again — date-level checks use RRULE occurrence expansion, not midnight-only overlap, so higher-priority global rules override product day rules (`RuleMinutesRruleCivilDayTest`). * Fix - Admin panel browse: slot chips show remaining capacity (e.g. **1 available**) on multi-capacity products when only one place is left, not only when two or more remain. * Fix - Slots: dynamic availability (indexing off) paints full half-open unappointable ranges on the minute mask — priority blocks no longer leave appointable boundary minutes (e.g. 10:00–11:00 block showing 10:59 slots); matches indexed mask painting. * Fix - Slots / calendars: minute/hour bookings that start at site midnight (including long durations such as 12 hours) block capacity again — timed overlap day-keys use Fake UTC site civil buckets for both occupancy indexing and slot lookup instead of mixing wp_date() site midnight with the civil UTC-midnight fast path. * Fix - Slots / calendars: multi-day day-unit occupancy for admin-created (site-local midnight) bookings — civil overlap keys use the site calendar date (Fake UTC) so every booked day blocks correctly (e.g. June 4–5 for a 2-day booking starting June 4); `wc_appointments_get_posted_data()` stores matching Fake UTC start/end for new day/month appointments. * Fix - Slots / calendars: multi-day day-unit products block every booked civil day again — occupancy uses live appointments; overlap day-keys use timezone-agnostic civil dates (gmdate on stored tokens), matching slot tokens, not wp_date() site-local midnight. * Fix - Admin panel **create**: first available date/time via one AJAX call (`WC_Appointment_Slots_Provider::get_first_available_slot`, same as storefront), including when the first slot is outside the current month; calendar day click/drag date-only prefill auto-picks the first time or opens browse once with `noSlotsOnDateSelect` when that day has no slots; browse auto-opens only on unavailable validation (not during silent auto-pick) and stays dismissible (no immediate re-open after close until date or product changes); live preview placeholder while slots load; panel expands when the product form is ready. * Fix - Slots / calendars: minute/hour day availability (storefront `day_data`, admin mini-calendar, time-slot lists) counts only bookable slot starts via shared `WC_Appointment_Slots_Collection::slots_for_day_summary()` on the same `get_collection()` engine — fully booked days no longer stay selectable when only sub-duration engine segments remain. * Fix - Slots / time picker: **Availability check → The starting slot only** no longer lists grid starts in appointable fragments shorter than the booking duration (phantom evening slots such as 19:00 on 12h/12h products when the index leaves a tail after a gap); full-day ranges still allow starts when duration exceeds the window (e.g. 25h in 24h). * Fix - Checkout validation (`WC_Appointment_Cart_Manager::validate_appointment_order`): re-validates cart slots via `WC_Appointments_Controller::get_indexed_total_available_for_range()` (same `slot_matches_requested_range` tolerance as add-to-cart) instead of strict `start_ts` equality; **all staff together** uses staff id `0` fan-out like `is_appointable()`; in-cart holds are excluded from overlap during the check. * Fix - **Staff all together:** cart booking no longer collapses multi-staff `_staff_ids` to a single `_staff_id` during `WC_Appointment_Booking_Handler::create_appointment()` availability merge. * Fix - Admin panel: **WooCommerce Deposits** on create — `calculate_costs` / `create_appointment` honour deposit choice and tax display (incl./excl. catalog); deposit footer quote and order item meta (`_deposit_full_amount`, etc.) match storefront; pay-deposit hides footer price edit (product deposit rules); pay-in-full edits full service price via `raw_full_price`; mistaken deposit-only overrides corrected on save. 2026.05.20 - version 5.3.9-beta * Breaking change - Removed `WC_Appointments_Controller::find_padding_day_slots()`, `get_padding_day_slots_for_scheduled_days()`, and `find_scheduled_month_slots()`. Use `WC_Appointment_Slots_Provider::get_padding_days_for_product()`, `get_padding_days_for_fully_booked_dates()`, and `get_scheduled_month_slots()`. * Breaking change - Removed `WC_Appointments_Controller::find_scheduled_day_slots()`, `WC_Appointment_Slots_Provider::get_scheduled_day_slots()`, and `WC_Appointment_Slots_Collection::legacy_scheduled_arrays_from_day_data()`. Use `get_day_data()` / `to_day_data()`; padding anchors from fully booked `day_data` (`get_padding_days_for_fully_booked_dates()`, `Y-n-j` keys); months via `get_day_data(..., 'Y-n')`. `woocommerce_appointments_scheduled_day_slots` runs inside `get_day_data()` with the `to_day_data()` payload (not legacy four-array shapes). * Breaking change - Calendar AJAX `wc_appointments_find_scheduled_day_slots` no longer returns legacy keys (`partially_scheduled_days`, `remaining_scheduled_days`, `fully_scheduled_days`, `unavailable_days`, `padding_days`, `restricted_days`) or invokes that filter at the AJAX layer. Read `day_data` / `meta`, filter the JSON response, or use `get_day_data()` in PHP where the filter still applies. * Improvement - Unified `day_data`: storefront calendar is `day_data`-only — per-day `padding` / `restricted`, full per-staff occupancy fields, redundant `display_day_summaries` removed; `to_day_data()` optional fold-in; availability widget/block use `get_day_data()` with `selectable`; `resolve_schedule_bounds()` maps Unix `0` to the booking horizon. * Improvement - Padding and fully-booked month aggregation call `get_padding_days_for_product()`, `get_padding_days_for_fully_booked_dates()`, and `get_scheduled_month_slots()` (`get_day_data()`-based). * Improvement - Storefront `date-picker.js`: removed legacy four-array branch and `is_slot_appointable`; removed unused `getTzOffsetFromString` / `getCustomerTzOffsetMinutes` (offset still server-supplied on AJAX). * Improvement - `get_rest_records()` date-range mapping uses `wp_date()` / `wp_timezone()` instead of `date()` / `strtotime()`. * Improvement - **WPML / WCML:** admin appointment panel **create** — independent multicurrency selector (`integrationCurrency` on `wc_appointments_admin_panel_config`); appointment-currency cookie + `wcml_client_currency` on panel cost, add-on, and set-currency AJAX; WCML loads in admin for those actions; rate conversion via pricing-rule filters, `wc_price_args`, and WCML `get_product_addons_fields` (add-ons refetch on currency change); footer total from server `calculate_costs` HTML; `_order_currency` on `woocommerce_new_appointment_order`; legacy add-appointment page unchanged. * Improvement - **WooCommerce Payments Multi-Currency:** admin appointment panel **create** — independent multicurrency selector (same `integrationCurrency` pattern as WCML); appointment-currency cookie + `wcpay_multi_currency_override_selected_currency` filter for exchange rate conversion on panel cost AJAX; addon prices converted via `woocommerce_product_addons_*_price_raw` (WC Payments skips admin); `_order_currency` persisted on `woocommerce_new_appointment_order`. WCML takes priority when both plugins are active. * Improvement - Admin appointment panel **create**: **WooCommerce Deposits** — deposit / pay-in-full / plan UI (storefront POST keys), line totals and meta on `wc_appointments_create_appointment`, `_wc_deposits_version` when applicable; product AJAX exposes `wc_deposits`; compact amounts in `wca-slots-header` / `wca-slots-count`. * Improvement - Admin panel browse: time slot chips use a four-column equal-width grid (`.wca-slots-grid` / `.wca-slot-chip` in `appointment-admin-panel.scss`). * Improvement - Google Calendar OAuth (settings + staff profile): [Google Identity branding](https://developers.google.com/identity/branding-guidelines) — **Continue with Google**, `assets/images/google-g-mark.svg`, shared markup via `WC_Appointments_GCal::get_google_oauth_connect_button_html()`. * Improvement - Admin panel: `woocommerce_appointments_add_appointment_page_redirect` gains a 5th string `$context` (`legacy_post`, `admin_ajax_standalone`, `admin_ajax_calendar`); full admin calendar never returns `post_create_redirect` (callbacks may still read context). * Improvement - Update translations (+7 new strings, -2 strings removed). * Improvement - Settings → Indexing: during manual re-index, an admin notice explains the storefront uses dynamic availability checks until rebuild finishes (availability stays correct; performance may be slower during rebuild). * Fix - Storefront auto-select with `woocommerce_appointments_include_sold_out`: slots AJAX picks the first bookable time from the availability collection (marks it `selected` in HTML); the time picker applies that server selection instead of the first listed slot when sold-out rows are shown. * Fix - Auto-select slot highlighting: match engine `start_ts` directly and compare unified `display_time` with `gmdate()` on Fake UTC timestamps so `wp_date()` does not shift the selected row (e.g. 10:00 marked as 12:00). * Fix - Storefront time picker: rapid date changes with auto-select no longer apply a stale slots AJAX response (wrong time/timestamp for the selected day); cost calculation ignores superseded responses before enabling add to cart. * Fix - Storefront time picker: multiple appointment forms on one page (shortcodes/blocks) scope slots, autoselect, and `date-selected` per `.wc-appointments-appointment-form` widget again. * Fix - Storefront cost calculation: multiple appointment forms on one page no longer share one AJAX slot via `form.index()` (earlier forms were left loading while only the last form showed a price). * Fix - CSV appointment export **Cost**: linked order lines use `WC_Order::get_line_total( $item, true )` (after discounts, incl. tax) instead of stored `_appointment_cost`; appointments without a line still use stored cost. * Fix - Appointment export **Products** filter: `woocommerce_json_search_appointable_products` returns plain-text labels (fixes raw HTML in Select2); admin product lists keep HTML labels. * Fix - All-day civil dates: `WC_Appointment::get_start_date()` / `get_end_date()` and all-day email branches use `wp_date()` so local-midnight UTC storage does not show the wrong calendar day vs other `wp_date()`-based flows. * Fix - All-day **reschedule** (`WC_Appointment_Form_Handler`): persists posted `_start_date` / `_end_date`, sets `all_day`, clears `local_timezone`, skips form timezone metadata. **New bookings** (`WC_Appointment_Booking_Handler`): all-day day/month products skip the legacy `strtotime()` pass so instants match the posted-data pipeline (night bookings unchanged). * Fix - **Settings → Appointments** global availability: synced Google Calendar rows (`#availability_rows_synced`) merge into submit JSON with site rules so reorder/`ordering` persist; `rrule` rows expose `data-wca-rrule` and include `rrule` for `is_rule_valid` on save. * Fix - Admin appointment panel **create**: manual price override when **prices are entered inclusive of tax** no longer double-applies tax-inclusive→net conversion; saved order line matches the edited gross price. * Fix - Admin day slots: with `woocommerce_appointments_slots_display_pricing`, `price.display` matches storefront `serialize_slots()` (staff-aware default; ``/`` only when `WC_Appointment_Slots_Pricing::is_slot_price_discounted()`). * Fix - **Restrict start days:** `WC_Product_Appointment::is_appointable()` weekday uses the civil posted `_date` with **UTC noon + `gmdate( 'w' )`** (Gregorian, TZ-agnostic; matches day slot / Fake UTC digits); falls back to `gmdate( 'w', _start_date )` if `_date` is missing or unparsable. Avoids `date( 'w', … )` under PHP default UTC wrongly rejecting allowed starts. * Fix - Completed appointments now persist customer status as `arrived`, so `_appointment_customer_status` and CSV appointment exports no longer remain `expected` after automatic completion. * Fix - Admin panel **create** billing: restored **Country / Region** and **State / County** (`wcaPanel.billingCountries` / `billingStates`); country, state, and payment `