05/06/2026 Initial commit

This commit is contained in:
2026-05-06 14:19:07 -04:00
parent 9da1dffd9a
commit dde18a2cd2
116 changed files with 4276 additions and 7 deletions
+5 -5
View File
@@ -82,7 +82,7 @@ The **Customer Check-In Kiosk** (`mydomain.com/checkin/{tenant_slug}`) is a sepa
- No access to other staffs data, full customer PII, reports, inventory, or any settings
- **Customers** — tenant-wide profiles; visit history (location-aware); notes; loyalty points; birthday and preferred-staff tracking; customer search and merge
- **Services & products** — tenant-wide service menu (name, category, duration, price); product catalogue (SKU, category, sale price); enable/disable per location; promotions management (percentage-off discount on any service or product, with defined start/end dates; multiple active promotions supported simultaneously)
- **Customer Check-In Kiosk** — public iPad page at `mydomain.com/checkin/{tenant_slug}`; no login required; customer enters their name and phone number and optionally selects the service they are here for; system looks up the customer profile by phone (new profile created automatically if not found); a walk-in entry is queued and a real-time alert is pushed to the receptionists dashboard; the iPad screen shows a confirmation message and resets automatically after 10 seconds for the next customer; rate-limited and CSRF-exempt (no session); tenant slug validated against allowlist to prevent enumeration
- **Customer Check-In Kiosk** — public iPad page at `mydomain.com/checkin/{tenant_slug}`; no login required; customer enters their name and phone number and optionally selects the service they are here for; system looks up the customer profile by phone (new profile created automatically if not found); a walk-in entry is queued and an alert is surfaced on the receptionists dashboard via **5-second polling** (`GET /api/v1/checkin/queue?status=waiting`); the iPad screen shows a confirmation message and resets automatically after 10 seconds for the next customer; rate-limited and CSRF-exempt (no session); tenant slug validated against allowlist to prevent enumeration
- **Bookings / Appointments** — calendar view per location; walk-in and advance bookings; status workflow (pending → confirmed → in-progress → completed → cancelled); cancellation reason capture; appointment notes
- **Online Customer Booking** — public booking page at `mydomain.com/book/{tenant_slug}` (no login required); customer selects service → preferred staff (optional) → available time slot → submits name + phone; appointment lands in calendar as `pending`; owner can configure auto-confirm; confirmation email sent to customer; available on Growth and Pro plans
- **Waitlist** — when a time slot is full, customers can join a waitlist for that slot or staff member; on cancellation the next waitlist entry is notified by email automatically; waitlist queue visible in the appointment calendar; SMS notification deferred to future phase
@@ -671,7 +671,7 @@ WantedBy=multi-user.target
- [ ] Promotions management (create/edit/deactivate promotions; percentage-off; date range; target specific or all services/products)
- [ ] Promotion engine: `get_active_promotion(item_id, item_type)` helper — called at checkout to resolve applicable promotion for each line item automatically
- [ ] Appointments (calendar, walk-in flag, status workflow, cancellation + no-show capture; no-show count incremented on customer record)
- [ ] Customer check-in kiosk (`/checkin/{slug}`; no auth; phone lookup or new profile; walk-in queued to `checkin_queue`; real-time dashboard alert to receptionist; auto-reset timer; rate limiting; `service_requested` selector)
- [ ] Customer check-in kiosk (`/checkin/{slug}`; no auth; phone lookup or new profile; walk-in queued to `checkin_queue`; receptionist alert via 5-second polling (`GET /api/v1/checkin/queue?status=waiting`); auto-reset timer; rate limiting; `service_requested` selector)
- [ ] Online customer booking (`/book/{slug}`; no auth; availability check; confirmation email; auto-confirm toggle)
- [ ] Waitlist (join when slot full; auto-notify on cancellation; queue management in calendar)
- [ ] Staff Portal — `/staff/portal` (personal schedule, upcoming appointments, clock-in/clock-out, commission summary, payment history, read-only profile)
@@ -764,7 +764,7 @@ WantedBy=multi-user.target
| Location isolation | `g.location` validated against `staff_locations` for `tenant_staff` role; 403 on violation |
| Feature gating | `@tenant_feature_required('flag')` decorator; flags from `plans.features_json` |
| Staff portal isolation | `tenant_staff` sessions routed exclusively to `/staff/portal/*`; `@require_role` blocks all owner/manager routes; staff cannot access other staff members' data |
| Kiosk endpoint security | `/checkin/{slug}` is CSRF-exempt (no session); tenant slug validated against known slugs (not guessable); rate-limited at 20 req/min per IP (Flask-Limiter); only `name`, `phone`, and `service_requested` accepted — all other fields ignored; phone number sanitised and validated before profile lookup |
| Kiosk endpoint security | `/checkin/{slug}` is CSRF-exempt (no session); tenant slug validated against known slugs (not guessable); rate-limited at 20 req/min per IP (Flask-Limiter); only `name`, `phone`, and `service_requested` accepted — all other fields ignored; phone number sanitised and validated before profile lookup; receptionist dashboard alert delivered via 5-second JS polling (`GET /api/v1/checkin/queue?status=waiting`) — no WebSockets or SSE required |
| Demo read-only | `@demo_readonly` decorator returns 403 on any write operation for demo tenant |
| Superadmin IP gate | Nginx `allow`/`deny` + Flask checks `ADMIN_IP_ALLOWLIST` for double enforcement |
@@ -883,5 +883,5 @@ MySQL backup credentials stored in `/etc/mysql/backup.cnf` (mode 600, owned by `
| 15 | End-of-day reconciliation | Close Day action; cash + app + tip totals vs actual cash counted; variance flagged; stored in `daily_reconciliations`. |
| 16 | Soft delete | All tenant models carry `deleted_at`. Queries filter `WHERE deleted_at IS NULL`. No hard deletes from application code. Restore available to admin and tenant_admin. |
| 17 | Tenant health dashboard | Superadmin portal shows per-tenant signals: last login, appointment volume trend, days to subscription expiry, billing issues. |
| 18 | Customer check-in kiosk | Dedicated iPad page at `/checkin/{slug}` (no auth). Customer enters name + phone, optionally selects service. System looks up or creates the customer profile, queues a walk-in entry in `checkin_queue`, and pushes a real-time alert to the receptionist dashboard. Page auto-resets after 10 seconds. Rate-limited; slug allowlist prevents enumeration. |
| 19 | Next-visit scheduling at checkout | Optional rebook prompt after payment is confirmed. Receptionist picks date, time, and staff for the next appointment from the checkout screen. New appointment created as `pending` with `rebook_source = checkout`. Next visit date printed on receipt and confirmation email. 24-hour reminder auto-scheduled. Customer can decline — step is skipped gracefully. |
| 18 | Customer check-in kiosk | Dedicated iPad page at `/checkin/{slug}` (no auth). Customer enters name + phone, optionally selects service. System looks up or creates the customer profile, queues a walk-in entry in `checkin_queue`, and surfaces an alert on the receptionist dashboard via **5-second polling** (`GET /api/v1/checkin/queue?status=waiting`) — chosen over SSE/WebSockets to minimise resource usage on a constrained VPS. Page auto-resets after 10 seconds. Rate-limited; slug allowlist prevents enumeration. |
| 19 | Next-visit scheduling at checkout | Optional rebook prompt after payment is confirmed. Receptionist picks date, time, and staff for the next appointment from the checkout screen. New appointment created as `pending` with `rebook_source = checkout`. Next visit date printed on receipt and confirmation email. 24-hour reminder auto-scheduled. Customer can decline — step is skipped gracefully. |