- Additional services in widget are now derived from rental_objects (not a separate list)
- Settings page shows rental objects with enable/disable toggle; link to rental module
- Saves only {id, enabled}[] to widget_services — rental objects manage name/price/icon
- Standalone widget also derives additionalServices from config.rentalObjects
- Widget config API includes services[] in widgetSettings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add upsertGuestFromBooking() service — creates or updates guest record
when a booking is made (matches by email/phone, creates if not found)
- Integrated into bookings.ts (manual PMS bookings) and publicWidget.ts
(online widget bookings)
- Name parsed as Russian format: "Фамилия Имя [Отчество]"
- Non-throwing: booking creation never fails due to guest upsert errors
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add rental_objects to /api/widget/:slug/config response
- Add POST /api/widget/:slug/rental-bookings endpoint with availability check
- Widget rental tab now shows real rental objects (not additionalServices)
- Date picker, hourly or full-day toggle, start/end hour selectors
- Booking submit creates actual rental_booking record in DB
- Success screen shows rental object, date and time slot
- WidgetRentalObject + WidgetRentalBookingPayload types added to api.ts
- BookingWidgetStandalonePage passes rentalObjects to WidgetPreview
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add YooKassa webhook handler (POST /api/yookassa/webhook) that updates
booking to confirmed+paid on payment.succeeded, cancelled on payment.canceled
- Add public status endpoint GET /api/online-bookings/:id for return URL polling
- Add 'reserved' booking status (violet) shown in calendar while awaiting payment
- Add 'website' to BookingSource type
- Payment timeout job (every 2 min) auto-cancels expired unpaid bookings
- Widget setting: "Время ожидания оплаты" (5–60 min, default 15)
- BookingConfirmPage at /booking-confirm/:id — polls status, countdown timer,
shows success/pending/cancelled state
- Widget bookings now use status='reserved' instead of 'inquiry' when YooKassa
is configured; set to 'confirmed' by webhook on payment.succeeded
- Migration 070: add 'reserved' to bookings status constraint + payment_expires_at
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- localDate() uses local timezone instead of UTC (prevents showing yesterday
after midnight in UTC+3)
- Guest lookup: fallback to bookings table by guest_email/guest_phone when
no guest profile found in CRM (guests table)
- Fix REGEXP_REPLACE pattern from '\D' (PCRE) to '[^0-9]' (PostgreSQL POSIX)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add is_blacklisted column to guests (migration 068)
- POST /widget/bookings: block blacklisted guests (403, generic message)
- GET /widget/:slug/guests/lookup: find existing guest by email/phone for auto-fill
- Widget form: debounce guest lookup on email/phone input, show "Заполнить" suggestion
- Widget payment screen: replace fake card form with proper YooKassa redirect screen or bank transfer details page
- Add bank transfer details field to widget settings (saved as widget_bank_details)
- GuestsPage: blacklist toggle button in edit modal + 🚫 ЧС badge in table
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>