=== Vachr Short-Term Rental Reservations ===
Contributors: chuckumi
Tags: booking, rental, calendar, ical, reservation
Requires at least: 5.0
Tested up to: 7.0
Requires PHP: 7.2
Stable tag: 2.1.8
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

A professional short-term rental booking engine for WordPress with two-way iCal synchronization.

== Description ==

**Vachr Short-Term Rental Reservations** turns WordPress into a booking and management system for apartments, vacation homes, and rooms. Manage availability, pricing, reservations, guests, services, payments, and external calendars from one place.

[youtube https://www.youtube.com/watch?v=ss2JD-JexpM]
Video tutorial: https://www.youtube.com/watch?v=ss2JD-JexpM
The plugin is free and supports unlimited apartments. Donations help fund continued development.

Need help with setup? I offer one-time WordPress setup for short-term rentals, including connection to Booking.com, Airbnb, and more for 100 EUR, plus optional yearly light support for 20 EUR.

= Key Advantages =
* **Pricing and availability:** Configure seasonal and weekend rates, discounts, exact closures, and recurring rental seasons.
* **Two-way iCal:** Synchronize availability with Airbnb, Booking.com, VRBO, and other compatible services.
* **Payments and invoices:** Accept Stripe, Mollie, bank transfer, or cash and track transactions, invoices, and payment status.
* **Czech QR payments:** Generate SPD QR codes for CZK transfers when the supplier uses a Czech IBAN.
* **Guest registration:** Create custom fields, link registrations to bookings, and correct unmatched records in the administration.
* **Services and amenities:** Price optional services and assign a reusable, translatable amenities catalog to each apartment.
* **Multilingual content:** Reuse translated descriptions and arrival instructions with apartment-specific overrides.
* **Automation:** Email arrival instructions and assign cleaning, handover, management, or repair tasks with confirmation and escalation.
* **Staff calendar:** Show each staff member a color-coded duty calendar with task details and takeover links.
* **Custom appearance:** Preview calendar and form styling on desktop or mobile before publishing.
* **Google Vacation Rental SEO (Pro):** The optional add-on generates editable `VacationRental` JSON-LD from apartment data.

== Screenshots ==

1. Frontend availability calendar.
2. Booking form - guest contact information.
3. Booking form - services and price overview.
4. Admin - Unit management and settings.
5. Admin - Rates and pricing engine configuration.
6. Admin - Apartment-specific services configuration.
7. Admin - Bookings overview from website and external channels (Booking.com, Airbnb, etc.).
8. Admin - Customizing the visual appearance of the calendar and forms.
9. Admin - Guest registration management for tourist tax records.
10. Admin - Advanced discounts and seasonal pricing rules.
11. Admin - Two-way iCal synchronization settings for external OTAs.
12. Admin - Global services and amenities management.
13. Public guest registration form for collecting tourist tax data.
14. Admin - Payment methods configuration.
15. Admin - Invoice management.
16. Booking form - payment options.
17. Hosted payment page via Stripe or Mollie.
18. Admin - Multilingual apartment content templates.
19. Admin - Apartment-specific arrival instructions and content overrides.

== Installation ==

[youtube https://www.youtube.com/watch?v=ss2JD-JexpM]
Video tutorial: https://www.youtube.com/watch?v=ss2JD-JexpM
*Watch this video to see how to manage and use the system.*

1. Upload the plugin files to the `/wp-content/plugins/vachr-short-term-rental-reservations` directory, or install the plugin through the WordPress plugins screen directly.
2. Activate the plugin through the 'Plugins' screen in WordPress.
3. Go to the 'Apartments' menu to create your first rental unit.
4. Use the provided shortcodes to display the booking form or calendar on your pages.

== Frequently Asked Questions ==

= How do I display the booking form? =
Use the shortcode `[vstrb_booking_form unit="YOUR_ID"]`.

Optional parameters:

* `months="N"` - number of calendar months to display (default: `1`).
* `grid="N"` - number of month columns in the calendar grid (default: `1`).
* `currency="CODE"` - currency code used in the price breakdown (default: `EUR`).
* `language="CODE"` - two-letter language code (e.g. `cs`, `de`, `en`) used for services, contact field labels, and the price quote. If omitted, the language of the current page (reflected in the `<html lang>` attribute, set automatically by multilingual plugins) is used; the visitor's browser language serves as a last resort fallback.
* `enable_stripe="1"` - show card payment via Stripe. Configure Stripe keys and webhook secret in **Bookings -> Settings -> Payments**.
* `enable_mollie="1"` - show card payment via Mollie. Configure the Mollie API key and use the displayed Mollie webhook URL in **Bookings -> Settings -> Payments**.
* `enable_bank_transfer="1"` - show bank transfer payment. Configure default bank instructions in **Bookings -> Settings -> Payments**, or translated per-apartment instructions in **Bookings -> Apartment Content**.
* `enable_cash="1"` - show cash payment.

Billing details in the booking form are optional for all payment methods. Guests can enable them with **Add Billing Details** when they need an invoice. Card and bank-transfer payments without billing details create only a payment record; bank-transfer instructions and Czech QR payments use the booking ID as the payment reference and variable symbol.
* `show_gender="0"` - hide the gender field (shown by default).
* `show_country="1"` - show an optional ISO 3166-1 country selector (hidden by default).
* `show_children="0"` - hide the children field and use one combined guest count instead. Adults and children are shown separately by default; capacity and prices use their combined total.
* `country="CZ"` - preselect an ISO 3166-1 country. If omitted, the form detects it from the shortcode or page language.

Example: `[vstrb_booking_form unit="YOUR_ID" language="de" show_gender="0" show_country="1" country="DE"]`

Payment example with Mollie and bank transfer:
`[vstrb_booking_form unit="YOUR_ID" enable_mollie="1" enable_bank_transfer="1" enable_cash="1"]`

Payment example with all payment methods:
`[vstrb_booking_form unit="YOUR_ID" enable_stripe="1" enable_mollie="1" enable_bank_transfer="1" enable_cash="1"]`

= How do I enable Mollie payments? =
1. Go to **Bookings -> Settings -> Payments**.
2. Enter your Mollie API key (`test_...` or `live_...`).
3. Copy the displayed **Mollie Webhook URL** into your Mollie dashboard webhook settings.
4. Add `enable_mollie="1"` to your booking form shortcode.

Example: `[vstrb_booking_form unit="YOUR_ID" enable_mollie="1"]`

= How do I display the availability calendar? =
Use the shortcode `[vstrb_availability_calendar unit="YOUR_ID" months="3"]`.

= How do I collect guest registration data (e.g. from Booking.com guests)? =
Use the shortcode `[vstrb_guest_registration unit="YOUR_ID"]` on any page. Share the link with your guest - they fill in their personal details without needing to register. Submitted data is saved to the database and sent by email to both the admin and the guest. You can review all registrations under **Bookings -> Guest Registrations** in the admin panel.

If the registration link contains a specific apartment (`unit`), that apartment is kept as the primary assignment. The plugin then tries to link the registration to a matching booking for the same apartment and stay dates. When the apartment is not known and there is exactly one matching booking for the submitted stay dates, the registration can be linked automatically to that booking. If the match is not clear, you can manually pair or unpair the registration group in the admin panel.

The Guest Registrations admin screen includes filters for apartment, booking source, stay date range, guest/email/reference search, booking ID, and linked or unlinked registration groups. You can also change the apartment for a whole registration group when a guest submitted the form under the wrong apartment.

Optional parameters:

* `title="TEXT"` - custom heading displayed above the form. If omitted, the apartment name is used as the heading.
* `language="LOCALE"` - locale code (e.g. `cs_CZ`, `de_DE`, `en_US`) to render the form labels in a specific language. If omitted, the current site language is used.
* `param="accommodation"` - resolve the apartment from a URL parameter instead of a fixed `unit` ID. This is useful for Booking.com links with `[PROPERTY_NAME]`.
* `match="contains"` - allow one unique partial match when Booking.com sends a longer or shorter property name.
* `fallback="TEXT"` - text shown when no unique apartment is matched.

Example: `[vstrb_guest_registration unit="YOUR_ID" title="Apartment Modry Dum" language="cs_CZ"]`

Booking.com example page shortcode:
`[vstrb_guest_registration param="accommodation" language="en_US" fallback="Accommodation not found. Please contact us."]`

Booking.com message URL:
`https://example.com/accommodation-registration-tourist-tax/?accommodation=[PROPERTY_NAME]&source=booking_com`

Add the exact Booking.com property name to **External accommodation names / aliases** in the matching apartment editor. One alias per line is supported. Matching ignores case, accents, and extra punctuation.

= How do I manage multilingual apartment descriptions, amenities, arrival instructions, and bank transfer instructions? =
Create reusable content templates under **Bookings -> Apartment Content**. Each template can contain translated apartment descriptions, amenities text, arrival instructions, and bank transfer payment instructions for the languages configured in **Bookings -> Settings**.

Then open an apartment and use the **Guest-facing Apartment Content** section in the apartment details. You can assign a template and optionally override any language directly for that apartment.

Template texts support placeholders in double braces. Built-in placeholders include:

* `{{apartment_name}}`
* `{{apartment_id}}`
* `{{site_name}}`
* `{{checkin_time}}`
* `{{checkout_time}}`
* `{{capacity}}`
* `{{booking_id}}` - available in arrival instruction emails.
* `{{guest_name}}` - available in arrival instruction emails.
* `{{date_from}}` and `{{date_to}}` - available in arrival instruction emails.

Bank transfer templates additionally support:

* `{{iban}}` - the supplier account in international IBAN format.
* `{{account_number}}` and `{{bank_account}}` - the domestic account number. A Czech IBAN is converted to formats such as `19-2000145399/0800`.
* `{{account_country}}` - the two-letter IBAN country code, for example `CZ`.
* `{{invoice_number}}` - the generated invoice number.
* `{{variable_symbol}}` - digits extracted from the invoice number, limited to the last 10 digits.
* `{{payment_reference}}` - the full invoice number used as the payment reference.
* `{{payment_amount}}` and `{{payment_amount_due}}` - the amount to pay.
* `{{payment_currency}}` - the payment currency, for example `CZK`.
* `{{amount}}` and `{{currency}}` - short aliases for the payment amount and currency.

Example bank transfer template:

`
Amount: {{payment_amount}} {{payment_currency}}
IBAN: {{iban}}
Variable symbol: {{variable_symbol}}
Reference: {{payment_reference}}
`

Bank transfer callbacks use the generic **Bank Notify URL** from settings. The endpoint accepts JSON, form-encoded payloads, and simple XML bodies. It recognizes common wrapper keys such as `transaction`, `payment`, `data`, `entry`, or `accountStatement`, and common aliases:

* token: `token`, `webhook_token`, `secret`, `shared_secret`, `api_key`, or the `X-VSTRB-Token`, `X-Webhook-Token`, or `Authorization: Bearer ...` header.
* booking: `booking_id`, `bookingId`, `reservation_id`, or `order_id`.
* invoice: `invoice_number`, `invoiceNumber`, `invoice_no`, `invoice`, or `bill_number`.
* reference / variable symbol: `reference`, `payment_reference`, `variable_symbol`, `variableSymbol`, `vs`, `transaction_id`, `bank_reference`, `endToEndId`, `remittanceInformation`, `message`, `note`, or `userIdentification`.
* amount and currency: `amount`, `value`, `transaction_amount`, `castka`, `sum`, `currency`, `ccy`, or `mena`.

The plugin matches payments by booking ID first, then invoice number, then exact reference/variable-symbol match against stored payment references or invoice numbers. It does not connect directly to a specific bank API by itself; a bank export, external automation, or middleware can call the endpoint in one of the supported shapes.

You can also define custom placeholders in the apartment editor, for example `door_pin`, `wifi_password`, or `parking_note`. Custom placeholders can have one global value or language-specific values. Use them as `{{door_pin}}` or with a fallback like `{{door_pin|provided before arrival}}`.

Example: door PIN and Wi-Fi password

1. Open **Apartments -> Edit Apartment**.
2. In **Guest-facing Apartment Content -> Template Placeholders**, add:
   * Key: `door_pin`
   * Global value: `123456`
3. Add another placeholder:
   * Key: `wifi_password`
   * Global value: `MySecureWifiPassword`
4. Use these placeholders in an arrival instruction template:

`
Your apartment is {{apartment_name}}.
Check-in is possible from {{checkin_time}}.
The entrance door PIN is {{door_pin}}.
The Wi-Fi password is {{wifi_password}}.
`

Example: translated custom placeholder

If a placeholder contains text that should be translated, fill the language-specific values:

* Key: `parking_note`
* Global value: `Parking is available behind the house.`
* CS value: `Parkovani je mozne za domem.`
* DE value: `Parken ist hinter dem Haus moeglich.`

Then use it in a template:

`
{{parking_note}}
`

When the content is displayed or emailed in Czech, the CS value is used. In German, the DE value is used. If no matching language value exists, the global value is used.

Example: fallback text when a value is missing

`
Door PIN: {{door_pin|provided before arrival}}
`

If `door_pin` is empty, the guest will see `provided before arrival` instead of a blank value.

You can display the saved content with these shortcodes:

* `[vstrb_unit_description unit="YOUR_ID" language="cs"]`
* `[vstrb_unit_amenity_list unit="YOUR_ID" language="cs"]`
* `[vstrb_unit_arrival_instructions unit="YOUR_ID" language="cs"]`
* `[vstrb_unit_arrival_instructions_by_code param="unit_code" language="cs"]`
* `[vstrb_unit_arrival_instructions_by_name param="accommodation" language="cs"]`
* `[vstrb_unit_arrival_instructions_by_reservation]`

If the `language` attribute is omitted, the site language is used. Apartment-specific text has priority over the selected template.

To show arrival instructions without exposing an internal apartment ID, prefer `[vstrb_unit_arrival_instructions_by_code]`. Set a **Public apartment code** in the apartment editor and put that code into the external channel link. This is more reliable than matching a name sent by Booking.com, Airbnb, or another channel.

Example page shortcode:
`[vstrb_unit_arrival_instructions_by_code param="unit_code" language="cs" fallback="Accommodation not found. Please contact us."]`

Example guest URL:
`https://example.com/arrival/?unit_code=blue-house`

If an external channel can only send the accommodation name, use `[vstrb_unit_arrival_instructions_by_name]` and add the exact channel-specific names to **External accommodation names / aliases** in the apartment editor. The shortcode matches the URL value against the apartment title and these aliases. Matching ignores case, accents, and extra punctuation.

Optional parameters:

* `param="NAME"` - GET parameter containing the public code or accommodation name. Defaults: `unit_code` for the code shortcode, `unit_name` for the name shortcode.
* `code="TEXT"` - use a fixed public apartment code instead of a GET parameter, useful for testing.
* `name="TEXT"` - use a fixed accommodation name or alias instead of a GET parameter, useful for testing.
* `match="exact"` - default exact normalized title match.
* `match="contains"` - name shortcode only: allow one unique partial match when an external channel sends a longer or shorter name.
* `show_title="1"` - show the matched apartment title above the instructions.
* `fallback="TEXT"` - text shown when no unique apartment is matched.

= How do automated arrival instruction emails work? =
In the apartment editor, enable **Email arrival instructions automatically before check-in** and set **Send days before arrival**.

The daily WordPress cron checks confirmed bookings with a customer email address. If arrival instructions exist for the apartment, the guest receives an email with the apartment description, amenities, and arrival instructions in the saved booking language when available. Each booking is marked after sending so the instructions are not sent repeatedly.

To display arrival instructions by matching a guest stay, use:
`[vstrb_unit_arrival_instructions_by_reservation]`

The shortcode reads arrival date, departure date, first name, and last name from URL parameters. It accepts ISO dates and common local numeric date formats such as `2026-06-01`, `1.6.2026`, `01/06/2026`, and `06/01/2026`; ambiguous formats are tried against stored reservations without converting through UTC. It first tries to find one apartment by stay dates only. If multiple apartments match the same dates, it narrows the result by both guest names, then by last name only, then by first name only. Name matching ignores accents and letter case. If no unique match is found, it displays the first apartment with available instructions in alphabetical order.

Example page shortcode:
`[vstrb_unit_arrival_instructions_by_reservation date_from_param="checkin" date_to_param="checkout" first_name_param="guest_first_name" last_name_param="guest_last_name" language="cs" available_days_before="1" unavailable_text="Arrival instructions are not available yet." fallback="Arrival instructions are not available. Please contact us."]`

Example guest URL, password: `testtest`:
`https://vachr.cz/apartment-1-demo-pin/?checkin=2026-06-23&checkout=25.6.2026&guest_first_name=Jan&guest_last_name=Novak`

Optional parameters:

* `unit="ID"` - limit matching to one apartment, useful when date combinations may repeat across apartments.
* `date_from_param="date_from"` - URL parameter containing arrival.
* `date_to_param="date_to"` - URL parameter containing departure.
* `first_name_param="first_name"` - URL parameter containing first name.
* `last_name_param="last_name"` - URL parameter containing last name.
* `date_from="YYYY-MM-DD"` - fixed arrival date instead of a URL parameter.
* `date_to="YYYY-MM-DD"` - fixed departure date instead of a URL parameter.
* `first_name="TEXT"` - fixed first name instead of a URL parameter.
* `last_name="TEXT"` - fixed last name instead of a URL parameter.
* `language="cs"` - language used for apartment instructions.
* `show_title="1"` - show the matched or fallback apartment title above the instructions.
* `fallback="TEXT"` - text shown when no apartment has arrival instructions.
* `available_days_before="1"` - how many days before arrival the instructions may be shown. Default: `1`. Use `0` to show them only on the arrival day.
* `unavailable_text="TEXT"` - text shown when the guest opens the page too early. If omitted, a translated default message is shown.

= Can I pre-fill the guest registration form from a URL? =
Yes. The `[vstrb_guest_registration]` shortcode reads the following GET parameters and pre-fills the corresponding form fields:

* `date_from` - check-in date (format: `YYYY-MM-DD`)
* `date_to` - check-out date (format: `YYYY-MM-DD`)
* `first_name` - guest's first name (pre-fills the first guest block)
* `last_name` - guest's last name (pre-fills the first guest block)
* `source` - booking source; accepted values: `booking_com`, `airbnb`, `vrbo`, `expedia`, `direct`, `other`
* `booking_ref` - booking reference number
* `accommodation` - external accommodation name used to resolve the apartment when `[vstrb_guest_registration param="accommodation"]` is used

Example URL:
`https://bayernrelax.eu/en/accommodation-instructions/?accommodation=[PROPERTY_NAME]&date_from=2026-07-01&date_to=2026-07-07&first_name=John&last_name=Doe&source=booking_com&booking_ref=1234567890`

This is useful when sending a registration link directly from a booking confirmation email.

= How do admin price offers work for imported reservations? =

Open **Bookings**, click **Offer** on any reservation, and use the **Price offer** panel. The plugin calculates the price from the current reservation unit, dates, guest count, services, seasonal rates, discounts, surcharges, and refundable deposit settings.

You can add a one-time manual discount as a fixed amount or percentage. The generated result is saved back to the booking total and into the booking price breakdown, so notification emails and admin details show the same final amount.

For imported iCal reservations from e-chalupy.cz, Booking.com, Airbnb, and similar channels, the guest email is often not stored as the booking customer email. In that case you can enter any recipient in **Send offer to email**. If the iCal description contains an email address, the field is prefilled automatically.

Each offer also has a private public link in **Public price offer link**. Copy that URL and send it manually through any channel. The link uses a random token and displays only the price offer page for that booking.

The email sent from the price offer panel includes the offer summary and the same public price offer link.


= Where are plugin settings managed? =
Open **Bookings -> Settings**. The page is split into tabs so operational configuration is kept in one place:

* **General** - languages, currencies, conversion rates, and shared defaults.
* **Notifications** - staff work types, recipients, confirmation, escalation, and staff calendar shortcode.
* **Payments** - Stripe, Mollie, bank transfer, payment callbacks, and payment behaviour.
* **Invoicing** - invoice numbering, language, supplier, tax, and bank details.
* **Calendar** - the global booking-calendar price display mode.
* **Appearance** - booking form and calendar styling, including the live desktop/mobile preview.
* **Services**, **Amenities**, and **Contact Fields** - reusable catalogs and fields used by apartments and booking forms.

**iCal Imports** remains a separate item under **Bookings**, because it is an operational synchronization screen with per-apartment imports, export URLs, and an immediate import action.

= How do I automate cleaning, handover, manager, or repair notifications? =
Open **Bookings -> Settings -> Notifications**. Create a notification type for each duty, for example Cleaning, Manager, Repair, or a custom duty. For every type choose:

* The trigger: new booking, guest registration, arrival, departure/cleaning, or iCal cancellation.
* A calendar color.
* The visible booking data for each recipient.
* Recipient language; for the first recipient, the number of days before arrival or departure when the task begins.
* Recipient order and the escalation interval in hours.

For arrival and departure types, enable **Require confirmation and escalate** to turn the notification into a task. The first recipient receives an email with a personal confirmation link. If they do not confirm within their configured interval, the next recipient receives an escalation email. When any notified recipient confirms, the task is assigned to them and earlier recipients receive a takeover notice. The hourly WordPress task runner processes pending escalations; for precise delivery on low-traffic sites, configure a real server cron to call WordPress cron.

The confirmation link is signed for the specific booking, task type, and recipient. It can be used without a WordPress login, which makes it practical for cleaners and contractors; place staff calendar pages behind a WordPress password or another access control method.

Existing installations do not need to re-enter their settings. Legacy rules in the old `email|fields|events|language|days_before` format are migrated automatically during the upgrade and shown in the structured form.

Available fields:

* `unit` - apartment name.
* `dates` - stay dates.
* `guests` - guest count.
* `status` - booking status.
* `source` - booking source.
* `customer` - customer name.
* `contact` - customer email and phone.
* `price` - booking total and currency.
* `note` - booking note.
* `booking_reference` - external booking reference on guest registrations.
* `guest_names` - names submitted in guest registrations.
Available triggers:

* `new_booking` - email when a new booking is created.
* `registration` - email when guest registration details are submitted.
* `arrival` - email before check-in; it can be turned into a confirmed handover task.
* `departure` - email before checkout; it can be turned into a confirmed cleaning task.
* `ical_cancelled` - email when a future imported iCal reservation disappears from its source feed and is treated as cancelled.
For each recipient, select only the visible fields they need. For example, a cleaner may receive apartment, dates, and guest count without price or contact information.

For a traditional staff booking list, create a WordPress page or post, optionally protect it with a WordPress password, and add:

`
[vstrb_staff_bookings email="cleaner@example.com" days="30"]
`

For the color-coded duty calendar with daily details, assignment state, and confirmation buttons, use:

`
[vstrb_staff_tasks email="cleaner@example.com" days="90"]
`

`[vstrb_staff_bookings]` optional shortcode parameters:

* `email="EMAIL"` - staff rule to use.
* `days="N"` - number of upcoming days to display (default: `30`).
* `unit="ID"` - limit to one apartment.
* `limit="N"` - maximum number of bookings (default: `50`).
* `include_registrations="0"` - hide guest registration groups.
* `status="all"` - include cancelled and blocked bookings too.

`[vstrb_staff_tasks]` optional shortcode parameters:

* `email="EMAIL"` - required email address of a configured recipient; it limits the calendar to that person's duties.
* `days="N"` - number of upcoming days to display (default: `90`, maximum: `365`).
* `unit="ID"` - limit the calendar to one apartment.

= How can AI posts be generated automatically? =
Open **Bookings -> AI Post Generator** and use **Scheduled AI generation profiles**. Each profile can have its own source URLs, pasted source text, or an AI research prompt built from location, radius, date range, event types, source-link rules, and extra instructions. Profiles also include languages, category, draft/publish status, notification email, and daily, every-2-days, every-3-days, weekly, or monthly schedule with a selected time.

WordPress cron runs enabled profiles automatically. After each run, the configured notification email receives a summary telling whether new drafts are ready, posts were published, or errors occurred. The processed-source history is reused, so the same source is not generated repeatedly.

= Can I add Google Vacation Rental structured data? =
Yes, with the optional **Vachr Short-Term Rental Reservations Pro** add-on. Open **Bookings -> Google SEO Data**, select an apartment, and generate editable `VacationRental` JSON-LD from its existing settings.

The generator reuses the apartment name, description or content template, capacity, check-in and check-out times, base price, currency, featured image, and amenities. You can then complete the address, coordinates, room details, floor size, languages, ratings, reviews, and at least eight accommodation photos.

Place the generated structured data on the public apartment detail page with:

`[vstrb_vacation_rental_schema unit="YOUR_ID"]`

Structured data helps search engines understand the accommodation, but Google does not guarantee a rich result. Displaying live prices and availability in Google Vacation Rentals requires a separate Google Hotel Center integration.

== Changelog ==

= 2.1.8 =
* Improved responsive calendar pricing: prices are shown inline when they fit, reveal on hover when space is limited, and wrap safely for selected compact calendar days.
* Added localized currency symbols for supported currencies, including €, $, £, ¥, Kč, and Ft.
* Prevented available-day labels from taking space needed by calendar prices.
* Added check-in and check-out date inputs below the booking calendar, synchronized with calendar selection and the price quote.
* Added availability validation for manually entered dates in the quote and booking endpoints.

= 2.1.6 =
* fixed missing file

= 2.1.4 =
* Added an apartment-page wizard that creates editable Gutenberg pages with a chosen layout, language, currency, booking form, calendar, amenities, and image placeholder.
* Added direct access to the apartment-page wizard from the Apartments menu and apartment row actions.

= 2.1.3 =
* Enhanced the apartment setup wizard with clearer basic details, a simplified monthly availability selection, separate amenities, and automatic public-code generation.
* Streamlined base pricing to show only the selected price input, added basic long-stay discounts, and linked to advanced pricing policies.
* Added the apartment-wizard strings to all bundled language catalogs.

= 2.1.2 =
* Added a guided apartment setup wizard with inline catalog additions and validation.
* Simplified apartment availability configuration with monthly booking selection and advanced options.
* Added a basic long-stay discount and direct access to advanced pricing policies in the apartment wizard.

= 2.1.1 =
* Documented staff task automation, signed confirmation links, escalation timing, the staff calendar, and the structured notification migration.
* Reorganized Settings into tabs for general options, notifications, payments, invoicing, calendar, appearance, services, amenities, and contact fields.
* Kept iCal Imports as a dedicated Bookings menu item and updated administration links accordingly.

= 2.1.0 =
* Replaced pipe-separated staff notification editing with a structured notification type and recipient form.
* Added optional per-task confirmation, ordered email escalation, and takeover notices.
* Added the color-coded `[vstrb_staff_tasks]` staff calendar with daily task details.
* Existing staff rules are automatically presented in the new structured editor and retained on save.
* Consolidated general, notification, payment, invoicing, calendar, appearance, services, amenities, and contact-field configuration into tabs on the Settings page while keeping iCal imports in the main plugin menu.

= 2.0.18 =
* Fixed WordPress coding-standard issues in iCal export and cancelled-booking cleanup queries.

= 2.0.17 =
* Fixed iCal import safety so HTTP errors, invalid calendar responses, or empty parsed feeds do not remove existing imported reservations.
* Changed iCal cancellation cleanup to mark missing imported reservations as cancelled first, then permanently delete only old unlinked cancelled records after a retention period.

= 2.0.16 =
* Added a separate iCal export URL for direct booking-system reservations only, excluding imported external iCal blocks.

= 2.0.15 =
* Added an admin extension hook for booking row actions so Pro modules can add booking-specific invoice actions.
* Added a public admin helper for issuing an invoice for an existing booking through the invoicing workflow.

= 2.0.14 =
* Fixed an incomplete 2.0.12 package: bundled the previously missing payment QR and country-list files (`includes/payment-qr.php`, `includes/countries.php`, `assets/payment-qr.js`) and the `assets/vendor` libraries (tom-select, qrcodejs) that the plugin requires.
* Added translations

= 2.0.12 =
* Added an isolated live preview to the Styling administration page.
* Styling changes are now previewed immediately without affecting the published booking calendar or form until they are saved.
* Added desktop and mobile preview widths.
* The preview includes representative available, occupied, selected, check-in, check-out, discount, and surcharge calendar states.
* Added live previews for calendar colors, typography, headings, price badges, form background, maximum width, and booking button styling.
* Added preview support for an external custom CSS URL when Custom CSS mode is enabled.


