Skip to content

4.13.0 dev - #313

Merged
developeregrem merged 102 commits into
masterfrom
4.13.0-dev
Oct 17, 2026
Merged

developeregrem merged 102 commits into
masterfrom
4.13.0-dev

Conversation

@developeregrem

Copy link
Copy Markdown
Owner

No description provided.

developeregrem and others added 30 commits September 21, 2026 07:20
- Hide payment QR, PDF header/footer snippets and image upload for email templates
- Keep snippet sidebar in view while scrolling; let the editor shrink in narrow windows
- Mark root-level dates in the schema, guard nullable dates with data-if on insert
- Describe portal fee snippets, clarify the embedded template tooltip
- Fix paste/drop in the visual editor and loop autocomplete swallowing text
AI assistants (Claude Code, Cursor, n8n, ...) can connect to /mcp to
evaluate data, check availability and prices and create reservations.

- symfony/mcp-bundle with stateless and session-based MCP revisions;
  JSON responses only, subscriptions/listen refused and no listChanged
  capability, so no stream pins a PHP-FPM worker
- Off by default: operator switch MCP_ENABLED, admin switches under
  Settings > AI assistants (allowed host names, reservation writes)
- Tokens with the use "AI assistant" (scope mcp:access) only work at
  /mcp and must expire; the REST API rejects them. New scopes
  guests:read and reservations:write, always bound to the owner's roles
- Every tool declares #[McpRequiresScope], enforced centrally by
  ScopedReferenceHandler (fail closed) and written to mcp_tool_call_log;
  entity changes record channel and token prefix
- Guest names, contact data and remarks only with guests:read; free
  text is marked as untrusted_text
- 14 read and booking tools; reservations are booked via preview token
  (idempotent), strict availability check under a row lock, optional
  extras, and notify staff through the new assistant_booking.created
  workflow trigger (system workflow seeded when MCP is switched on)
- Session-free ReservationBookingService; REST query logic moved into
  shared services (reservations, invoices, stay parameters, statistics,
  tourist tax) used by REST and MCP alike
- Keep MCP debug logs with tool arguments out of the prod error buffer
- Move InvalidInvoiceNumberPatternException to App\Exception
Hoteliers who want to know something like the planned arrival time
had no way to ask for it without adding another field to the form,
and every extra field is one more hurdle for guests. A placeholder
in the existing comment field asks the question without making the
form any longer.

The text is set under Settings > Online booking > Checkout texts,
stored as plain text (max. 255 characters) and rendered as the
textarea's placeholder attribute, escaped by Twig. Empty means no
placeholder, so nothing changes for existing installations.

The classic theme gets the attribute as well. Its templates are
frozen for custom CSS, but an attribute leaves structure and classes
untouched, and without it the setting would silently do nothing there.
- Settings: paginate the MCP tool call log like the workflow log;
  paginator accepts an optional CSS class
- get_operations_report: room plan per day with housekeeping status and
  booked extras; new AI scope operations:read (ROLE_OPERATIONS);
  prompt daily_operations_briefing
- Read-only pricing tools: get_rate_calendar (nights merged into
  periods), get_price_rules, get_occupancy_forecast
  (AvailabilityService::getRoomNightsPerDay), get_booking_pace
  (previous-year comparison); prompt price_review
- Special prices: preview_special_price / create_special_price via
  session-free SpecialPriceService; new AI scope prices:write
  (administrators only); conflicts are only overwritten with consent,
  uninvoiced reservations in the period are listed as a warning
- Price periods get an optional description (price form, overview,
  REST API, OpenAPI)
…prepare them

- Drafts move from the session to bank_import_drafts: private per user,
  deleted after 2 days without changes (on access and by app:purge-logs),
  edits lock the draft row, excluded from the change log
- Line editing rules extracted to BankImportLineEditor, shared by the
  bank import pages and MCP
- MCP: list_bank_import_drafts, get_bank_import_lines,
  get_bank_import_context, update_bank_import_lines; new AI scope
  bank-import:write (ROLE_CASHJOURNAL), purposes as untrusted_text,
  IBANs shortened
The due date so far always followed the payment period in the invoice
settings. An invoice can now carry a date of its own, set when it is
created or later in the edit modal: a number of days from the invoice
date, the last departure of its room positions, or a fixed date.

Only the resulting date is stored (invoices.payment_due_date); days and
departure are ways of entering it. An issued invoice therefore keeps its
date when its date or positions change later. Invoices without one keep
following the settings, so nothing changes for existing data.

Invoice::resolvePaymentDueDate() is the single place that decides, and
the invoice view, the template variable paymentDueDate, the ZUGFeRD
payment terms and the EN 16931 check all read it.
"X days after invoice date" offers each invoice on one fixed day, so a
payment reminder built on it goes out too early once an invoice has a
later due date of its own. A condition cannot fix that: it can refuse
the day, not postpone it.

The new trigger invoice.days_after_due_date matches on the due date the
invoice prints (Invoice::resolvePaymentDueDate()). Days may be 0 (on the
due date) or negative (before it). The query narrows candidates by the
configured payment periods and resolves each one, since a due date
without an own date depends on the issuer that applies to the invoice.
Follows the review on #303.

The due date is now always stored: a new invoice gets the invoice date
plus its issuer's period when it is created (prefilled in the form, and
set by a prePersist listener for every other way an invoice is made),
and the migration fixes the date existing invoices have been printing.
A later change of the period in the settings no longer rewrites
invoices already sent. A rollback is refused while an invoice has a
date other than its settings give.

The dialog has a single date field. A number of days and the last
departure are helpers next to it that fill in the date in the browser;
they carry no name, so they are never submitted and cannot block saving.
The mode choice and its help text are gone; the label reads
"Fälligkeitsdatum".

With every invoice carrying its date, the "X days after the due date"
trigger selects ids by payment_due_date and limits in the query instead
of loading and resolving candidates. InvoiceService no longer needs the
settings resolver.
Bookings keep the price they were booked at (room nights, guest
adjustments, extras); later price list changes only apply to new
bookings. Existing invoices are untouched.

- price promise as JSON on reservations, written on every create/edit path
- open bookings without invoice are promised today's price before the
  first price change
- price tab shows when today's prices differ and lets you apply them
- prices still used by an open booking are deactivated instead of deleted
- fix child discount positions for stays across different price rows
Rules change room prices by weekday, period, lead time or occupancy,
for one, several or all subsidiaries and room categories. Matching
rules are added up and held within configurable limits and rounding.

- new "Price rules" tab with templates, live preview and limits
- rules apply to quotes, online booking, new bookings and the rate
  calendar; existing bookings keep their promised price
- occupancy per subsidiary, optionally counted across subsidiaries
- reservation shows which rules are part of the room price
- online and MCP bookings only at the price shown (create_reservation
  now takes expectedTotal)
- hint to price rules in the period section of the price form
Follows the second review on #303.

The due date now sits with number and invoice date: in the creation
step between them (saved on the fly like both), in the number and date
dialog when editing, and in the invoice header as a label-value line
below them. The payment method and remark form is back to its original
shape.

Next to the date field, a small menu sets it to the issuer's standard
period, an own number of days typed into the menu, or the last
departure. The departure is only offered while it does not lie before
the invoice date, and a moved invoice date takes the due date along at
the same distance. The menu carries no names, so only the date is ever
submitted.

The invoice header shows number, invoice date and due date as three
label-value lines, so it is no taller than the address beside it.
The price calendar shows per branch, room category and occupancy what
a night costs today and why. Day prices set a fixed price for single
nights or ranges and take the place of the price rules there.

- day price stated for an occupancy; other occupancies and origins
  follow in proportion
- manual day prices are exact; assistant/API ones stay within limits
- phone list and desktop month grid, night details in an offcanvas
- reservation and rate calendar show day prices
… canceled invoices when fixing a booking's promised price
Agreed on #303. Next to the last departure, the due date menu now
offers the earliest arrival of the room positions. Both follow the same
rule: offered only while they do not lie before the invoice date, and
the divider above them goes when neither is.
MeisterAdebar and others added 22 commits October 9, 2026 17:43
In the normal view on touch screens the first swipe up hides the navbar, the second the filter row and the third opens the week view. Closing the week view brings the normal view back with navbar and filter row.
…hown

The line sat on the first header row (the month row), so it went missing when the table settings hid that row; with navbar and filter row swiped away the gap showed at the top of the screen. The scroll container now draws it in every view, as the week view already did.
The floating button now appears on all touch devices, tablets included, instead of below a width; with a mouse the button in the filter row stays. It is drawn as translucent glass in the manner of iOS 26: the content behind it blurred, a fine light edge and a soft shadow, with a dark variant for the dark theme.
…ce-template-payment-means

# Conflicts:
#	translations/Templates/messages.de.xlf
…ayment-means

feat: pick the invoice template by payment means
- Navbar, filter row and the normal view's header rows are slimmer on touch screens (navbar 82 -> 58px, filter row and table header together about 80px less on a tablet); desktop keeps its sizes.
- The filter row is vertically centred and fits one line from 576px, so also on phones held sideways; tighter spacing on touch screens.
- The notification badge stays inside the slimmer bar, and the menu button is white like the rest of the bar.
The opaque holiday tint only applied to holidays on a weekend; on a weekday the header cell kept Bootstrap's translucent bg-info bg-opacity-25, and reservation bars showed through it when scrolling.
…uch screens

- Going back with the browser left the reservation overview without its listeners (no swiping, paging, week view): the setup flag was an attribute that Turbo's page cache kept. It is a property now, and the controller clears it and its modal observer on disconnect.
- The side menu is cleaned up synchronously before Turbo caches the page; Offcanvas.hide() released the body's scroll lock only after the animation.
- The side menu is for touch screens below lg (phones, tablets upright); with a mouse the bar is always expanded and wraps in a narrow window. Below xl the user menu shows its icon only.
- Menu button without frame and without the focus ring after a tap.
- Following the new AGENTS.md rules: theme variables instead of fixed colours for the glass button and the menu button, text-body-secondary instead of text-secondary for the filter row's icons.
…lash

- In the touch week view a swipe down returns to the normal view as it was before the third swipe up, with navbar and filter row still hidden; a swipe up still restores the complete normal view.
- Switching between normal and week view hides the table until the new one has loaded, so the old table no longer flashes up in the new layout.
…ens only

As before, desktop gets the side menu below xl. Touch screens switch to lg on connect, so a tablet held sideways keeps the full bar. The collapsed-state rules are tied to the expand class in use; the always-expanded desktop bar is gone.
#308 feat(reservations): week view for the reservation table
…lease notes; fix: keep the due date test independent of the last invoice number
@developeregrem developeregrem added this to the 4.13.0 milestone Oct 16, 2026
Comment thread docs/release-notes/4.13.0.de.md Outdated
@developeregrem
developeregrem merged commit 7a8ddcf into master Oct 17, 2026
11 checks passed
@developeregrem
developeregrem deleted the 4.13.0-dev branch October 17, 2026 17:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants