Passa al contenuto principale

Schema del database

Agon usa SQLite (modalità WAL) come database locale. Tutti i valori datetime sono conservati in UTC (senza fuso orario).

Diagramma entità-relazione

erDiagram
users {
int id PK
string email UK
string password_hash
string full_name
string role
bool is_active
datetime created_at
datetime updated_at
}

clients {
int id PK
int location_id
string email UK
string password_hash
string full_name
string phone
date dob
string notes
string push_token
bool is_active
datetime created_at
datetime updated_at
}

instructors {
int id PK
int user_id FK
int location_id
string bio
string specialties
datetime created_at
datetime updated_at
}

class_templates {
int id PK
int location_id
string name
string description
int duration_minutes
int default_capacity
string color
int default_instructor_id FK
bool is_active
datetime created_at
datetime updated_at
}

scheduled_classes {
int id PK
int template_id FK
int instructor_id FK
int location_id
datetime starts_at
datetime ends_at
int capacity
string status
string meeting_url
datetime created_at
datetime updated_at
}

bookings {
int id PK
int client_id FK
int scheduled_class_id FK
int location_id
string status
datetime created_at
datetime updated_at
}

checkins {
int id PK
int booking_id FK
int client_id FK
int scheduled_class_id FK
int checked_in_by FK
string method
datetime checked_in_at
}

waitlist {
int id PK
int client_id FK
int scheduled_class_id FK
int position
string status
datetime expires_at
datetime created_at
datetime updated_at
}

membership_types {
int id PK
int location_id
string name
string type
float price
string currency
int credits
int duration_days
bool is_active
datetime created_at
datetime updated_at
}

memberships {
int id PK
int client_id FK
int membership_type_id FK
int location_id
string status
date starts_at
date expires_at
int credits_remaining
int credits_used
string stripe_subscription_id
datetime created_at
datetime updated_at
}

payments {
int id PK
int client_id FK
int membership_id FK
float amount
string currency
string status
string provider
string provider_payment_id
datetime paid_at
datetime created_at
datetime updated_at
}

locations {
int id PK
string name
string address
string phone
bool is_active
datetime created_at
datetime updated_at
}

studio_settings {
int id PK
string studio_name
string timezone
string currency
string stripe_public_key
string stripe_secret_key
string cloudflare_tunnel_token
int booking_cancellation_hours
bool waitlist_enabled
datetime created_at
datetime updated_at
}

consent_log {
int id PK
int client_id FK
string type
bool granted
string ip_address
datetime created_at
}

email_templates {
int id PK
string name
string subject
string body_html
string variables
bool is_active
datetime created_at
datetime updated_at
}

email_event_assignments {
int id PK
string event_type
int template_id FK
}

smart_lists {
int id PK
string name
string filters_json
datetime created_at
datetime updated_at
}

users ||--o{ instructors : "has profile"
clients ||--o{ bookings : "makes"
clients ||--o{ memberships : "holds"
clients ||--o{ waitlist : "joins"
clients ||--o{ payments : "makes"
clients ||--o{ consent_log : "logs"
scheduled_classes ||--o{ bookings : "receives"
scheduled_classes ||--o{ waitlist : "has"
scheduled_classes ||--o{ checkins : "records"
class_templates ||--o{ scheduled_classes : "spawns"
instructors ||--o{ scheduled_classes : "teaches"
instructors |o--o{ class_templates : "default for"
bookings ||--o| checkins : "has"
memberships ||--o{ payments : "paid via"
membership_types ||--o{ memberships : "defines"
email_templates |o--o{ email_event_assignments : "assigned to"

Tabelle principali

TabellaScopo
usersAccount dello studio manager e degli istruttori (credenziali di accesso)
clientsClienti della palestra che prenotano lezioni tramite l'app mobile
instructorsProfilo uno-a-uno collegato a una riga users
class_templatesDefinizione riutilizzabile di lezione (Yoga, HIIT, Pilates…)
scheduled_classesUn'occorrenza specifica di un tipo di lezione in una data/ora
bookingsLa prenotazione di un cliente per una lezione pianificata
checkinsRegistro di presenza confermata per una prenotazione
waitlistVoce in coda quando una lezione è al completo
membershipsL'abbonamento o pacchetto crediti attivo di un cliente
membership_typesModello per piani ricorrenti o pacchetti crediti
paymentsRegistri di pagamento (Stripe o manuale)
locationsSede fisica dello studio (V1: sempre id=1)
studio_settingsRiga singleton con la configurazione dello studio
consent_logEventi di consenso GDPR
email_templatesCorpi delle email transazionali personalizzati
email_event_assignmentsMappa gli eventi del ciclo di vita ai template personalizzati
smart_listsQuery di filtro clienti salvate per messaggistica mirata

Note di progettazione

Datetime UTC senza fuso orario

Tutte le colonne DateTime conservano valori UTC senza informazioni di fuso orario. Il livello applicativo confronta sempre con utcnow() da app.utils. Non inserire mai datetime con fuso orario nel database.

Convenzione location_id

Ogni entità di business porta un campo location_id (predefinito 1). La sede 1 (Main Studio) viene creata al momento della migration. Questa colonna è il gancio per il supporto multi-sede della V2 — nessuna modifica allo schema necessaria.

Eliminazione soft vs hard

  • Eliminazione soft (status/is_active): clienti, istruttori, tipi di lezione, tipi di abbonamento, prenotazioni, abbonamenti, lezioni pianificate.
  • Eliminazione hard: solo su richiesta esplicita di cancellazione GDPR. I dati personali vengono anonimizzati sul posto; le righe di cronologia prenotazioni/pagamenti vengono conservate per finalità di audit.

Vedi ARCHITECTURE.md nel codice sorgente per la strategia completa di eliminazione e le regole di cascata.

Indici composti

I pattern di query critici per le prestazioni sono coperti da indici composti:

IndiceColonnePattern di query
idx_booking_class_statusscheduled_class_id, statusTutte le prenotazioni confermate per una lezione
idx_booking_client_statusclient_id, statusCronologia prenotazioni del cliente
idx_membership_client_statusclient_id, statusRicerca abbonamento attivo del cliente
idx_class_starts_at_location_statusstarts_at, location_id, statusQuery di calendario per intervallo di date