Modèle de données
Aperçu des principales entités stockées dans Supabase. Utile pour comprendre la structure interne de Batlr et pour les intégrations avancées (export, BI, sync externe).
Vue d'ensemble
restaurants
↓ owns
├── tables ──── zones
├── floor_plans
├── services (opening hours)
├── customers
│ ↓ may have
│ reservations
│ ↓ may have
│ ├── reservation_events (audit log)
│ └── communication_events
├── waitlist_entries
└── team_members
restaurants
| Colonne | Type | Description |
|---|
id | UUID | PK |
name | text | Nom commercial |
address, phone, email | text | Coordonnées |
settings | jsonb | Tous les réglages app (durée, modes paiement, etc.) |
is_pro | bool | Restaurant inclus dans abonnement Pro |
subscription_status | jsonb | Détails palier, trial, restaurants_assignés |
pro_source | text | apple|stripe|admin |
created_at, updated_at | timestamptz | Métadonnées |
reservations
| Colonne | Type | Description |
|---|
id | UUID | PK |
restaurant_id | UUID | FK → restaurants |
customer_id | UUID | FK → customers (peut être null) |
service_id | UUID | FK → services (peut être null) |
table_id | UUID | FK → tables |
table_number | text | Numéro affiché ("7" ou "7+8") |
guest_name | text | Nom du client (denormalisé) |
guest_phone, guest_email | text | Contact denormalisé |
party_size | int | 1-99 (CHECK constraint) |
date | date | YYYY-MM-DD |
time | time | HH:MM:SS |
duration | int | Minutes (nullable, défaut = restaurant settings) |
status | text | Confirmed|Pending|Arrived|Seated|Completed|Cancelled|No-show |
tags | text[] | Noms de tags appliqués |
notes | text | Notes internes |
source | text | Site web|Téléphone|Walk-in|App|TheFork|Google|Liste d'attente |
created_by | UUID | FK → team_members |
seated_at | timestamptz | Quand le client s'est assis |
arrived_count | int | Nb de personnes arrivées (partial arrival) |
skip_deposit | bool | Exempté de paiement |
payment_status | text | pending|card_saved|charged|refunded|... |
stripe_payment_method_id | text | ID Stripe |
stripe_checkout_session_id | text | ID Stripe Checkout |
reconfirmation_token | text | Token unique pour reconfirmation |
reconfirmation_sent_at | timestamptz | Date d'envoi |
reconfirmation_status | text | pending|confirmed|declined |
reminders_sent | jsonb[] | Historique rappels envoyés |
reminder_overrides | jsonb | Surcharges par réservation |
created_at, updated_at | timestamptz | Métadonnées |
Trigger d'overlap
Sur INSERT/UPDATE, un trigger SQL vérifie qu'il n'y a pas de conflit temporel avec une autre réservation sur la même table (en tenant compte des durées).
customers
| Colonne | Type | Description |
|---|
id | UUID | PK |
restaurant_id | UUID | FK → restaurants (ou null si CRM partagé) |
name, phone, email | text | Coordonnées |
notes | text | Notes internes |
tags | text[] | VIP|Habitué|Allergie|... |
classification | text | VIP|Regular|New (calculé) |
visit_count | int | Visites complétées (calculé) |
last_visit | timestamptz | Dernière visite |
no_show_count | int | Calculé |
cancellation_count | int | Calculé |
total_spent | int | Cumul (cents) |
created_at, updated_at | timestamptz | Métadonnées |
tables
| Colonne | Type | Description |
|---|
id | UUID | PK |
restaurant_id | UUID | FK → restaurants |
floor_plan_id | UUID | FK → floor_plans |
zone_id | UUID | FK → zones |
name | text | Numéro affiché |
capacity_min, capacity_max | int | Capacité |
position_x, position_y | float | Coordonnées canvas |
width, height | float | Dimensions custom (nullable) |
rotation | float | Degrés |
shape | text | round|square|rectangular |
chair_sides | int | Bitmask (1=top, 2=right, 4=bottom, 8=left) |
capacity_overrides | jsonb | Capacités par combo (ex. { "7+8": 6 }) |
fill_group | int | Priorité d'assignation auto |
updated_at | timestamptz | — |
zones
| Colonne | Type | Description |
|---|
id | UUID | PK |
restaurant_id | UUID | FK → restaurants |
floor_plan_id | UUID | FK → floor_plans |
name | text | Nom |
color | text | Hex |
position_x, position_y, width, height | float | Rectangle |
sort_order | int | Ordre |
floor_plans
| Colonne | Type | Description |
|---|
id | UUID | PK |
restaurant_id | UUID | FK |
name | text | "Salle principale", "Terrasse"… |
sort_order | int | Ordre |
Tables associées
daily_table_overrides — positions/hidden par date
daily_extra_tables — tables temporaires par date
daily_floor_notes — notes par date
services
| Colonne | Type | Description |
|---|
id | UUID | PK |
restaurant_id | UUID | FK |
name | text | "Midi", "Soir", "Brunch dominical" |
start_time, end_time | time | HH:MM |
days_of_week | int[] | [1..7] (Mon-Sun) |
is_special | bool | Service ponctuel ? |
specific_date | date | Si spécial : date concernée |
label, message | text | Affichage client |
waitlist_entries
| Colonne | Type | Description |
|---|
id | UUID | PK |
restaurant_id | UUID | FK |
date | date | Date souhaitée |
time | time | Heure préférée (nullable) |
party_size | int | — |
service_id, preferred_floor_plan_id | UUID | Préférences |
guest_name, guest_email, guest_phone | text | Contact |
notes | text | — |
status | text | waiting|notified|called|converted|expired|cancelled |
notified_at, called_at | timestamptz | Action history |
notification_token | text | Token pour page de suivi |
position_in_queue | int | Calculé |
created_at, updated_at | timestamptz | — |
team_members
| Colonne | Type | Description |
|---|
id | UUID | PK |
user_id | UUID | FK → auth.users (null pour serveurs PIN-only) |
restaurant_id | UUID | FK |
role | text | Admin|Manager|Server |
display_name | text | Nom affiché |
email | text | Si rôle Manager/Admin |
pin | text | Hash PBKDF2 |
pin_salt | text | Sel du hash |
allowed_tabs | text[] | Surcharge des permissions par défaut |
is_active | bool | Désactivable sans suppression |
created_at | timestamptz | — |
reservation_events
Audit log immuable des changements de statut.
| Colonne | Type | Description |
|---|
id | UUID | PK |
reservation_id | UUID | FK |
customer_id | UUID | FK |
from_status, to_status | text | Transition |
changed_by | UUID | FK → team_members |
metadata | jsonb | Contexte (raison annulation, arrived_count, …) |
created_at | timestamptz | — |
communication_events
Tracking des envois email/SMS.
| Colonne | Type | Description |
|---|
id | UUID | PK |
restaurant_id | UUID | FK |
reservation_id, customer_id, waitlist_entry_id | UUID | FK (nullable) |
type | text | reminder|confirmation|review_request|... |
channel | text | email|sms |
recipient | text | Email ou téléphone |
status | text | sent|delivered|bounced|complained |
sent_at, delivered_at, bounced_at | timestamptz | Timeline |
bounce_reason | text | — |
sms_usage
Quota SMS mensuel.
| Colonne | Type | Description |
|---|
restaurant_id | UUID | FK |
month | text | YYYY-MM |
sms_sent | int | Compteur |
sms_credits | int | Crédits achetés en plus |
RPC get_sms_quota(restaurant_id, included) retourne remaining = (included + sms_credits - sms_sent).
RLS
Toutes les tables sont protégées par Row-Level Security. Le pattern par défaut :
- Authenticated (staff) — accès aux données de leur restaurant via
get_user_restaurant_ids()
- Anon (widget) — accès limité aux colonnes nécessaires (pas de payment_*, pas de notes internes)
À lire ensuite