Circolino SPAP · v1.0.41← Indietro
# Circolino SPAP Prenotazioni v1.0.41
Applicazione web mobile-first per la gestione interna delle prenotazioni tavoli e degli ordini da asporto del Circolino SPAP.
## Installazione nuova
1. Crea un database MySQL vuoto.
2. Scompatta il contenuto dello ZIP direttamente nella document root del sito.
3. Rendi scrivibili dal web server le directory `config/`, `uploads/` e `storage/`.
4. Apri il dominio nel browser e completa l'installer.
## Aggiornamento
Puoi sovrascrivere direttamente i file della versione precedente. La directory `config/` dello ZIP non contiene `config.php`, quindi le credenziali MySQL restano intatte.
Le migrazioni database necessarie vengono eseguite automaticamente al primo accesso.
Dalla v1.0.26 le migrazioni sono versionate e vengono eseguite una sola volta; le normali richieste non effettuano più controlli DDL ripetitivi. In caso di problemi di aggiornamento consultare `storage/runtime-schema.log`.
## Regola di versionamento del progetto
- Il file `VERSION` contiene la versione corrente dell'applicazione.
- Il footer mostra sempre la versione, un collegamento a **README / Novità** (viewer UTF-8 di `README.md`) e quindi la dicitura `powered by IT Easy Informatica`.
- A ogni nuova release questo `README.md` deve essere aggiornato mantenendo lo storico delle versioni precedenti.
# Changelog
## v1.0.41
- Aggiunto alle prenotazioni il dato **Prenotazione presa da**, così Staff e Admin possono vedere subito chi ha registrato originariamente la prenotazione senza consultare il Log attività.
- Il nominativo dell’operatore viene salvato come dato storico direttamente nella prenotazione, quindi resta disponibile anche se in futuro l’account Staff viene cancellato.
- Il dato viene mostrato nella **Dashboard**, nell’**Agenda prenotazioni** e nella schermata **Modifica prenotazione**.
- Per le prenotazioni già esistenti l’aggiornamento prova a ricostruire automaticamente il nominativo dall’utente creatore e, quando necessario, dal Log attività disponibile.
- Aggiornati `VERSION`, footer e changelog.
## v1.0.40
- Il menu principale evidenzia ora con un colore dedicato la sezione attualmente aperta, sia su smartphone sia su desktop, rendendo immediatamente riconoscibile la posizione nell'app.
- L'evidenziazione segue anche le pagine collegate: modifica prenotazione resta sotto **Agenda**, nuovo/modifica asporto sotto **Asporti**, modifica contatto sotto **Rubrica** e tutte le pagine amministrative sotto **Admin**.
- Nei calendari di **Nuova prenotazione** e **Nuovo asporto** le date già trascorse non sono più cliccabili e vengono indicate come `Passato`.
- Aggiunto un controllo lato server che impedisce di salvare prenotazioni tavolo o asporti con data antecedente a quella corrente, anche in caso di URL o richiesta modificata manualmente.
- Anche l'API di verifica disponibilità rifiuta le date già trascorse.
- Aggiornati `VERSION`, footer e changelog.
## v1.0.39
- Il campo **Bambini** nelle nuove prenotazioni non viene più precompilato con `0`; se lasciato vuoto viene interpretato come zero. Anche in modifica, un valore zero viene mostrato vuoto.
- Aggiunta in **Amministrazione → Impostazioni → Regole prenotazioni** la voce **Cancella automaticamente prenotazioni, asporti e log più vecchi di (giorni)**.
- Impostando `0` la cancellazione automatica resta disattivata; impostando ad esempio `7`, vengono eliminate le prenotazioni tavolo, gli asporti e le voci del Log attività più vecchie di 7 giorni.
- La pulizia automatica viene eseguita al massimo una volta al giorno ed è protetta da lock MySQL per evitare esecuzioni concorrenti.
- Quando il numero di giorni viene modificato, la nuova regola viene applicata dalla richiesta successiva senza attendere il giorno seguente.
- La cancellazione riguarda lo storico operativo di prenotazioni, asporti e Log attività; la **Rubrica resta sempre invariata**.
- Al termine della pulizia viene inserita nel Log attività una nuova voce di manutenzione con il numero di prenotazioni, asporti e log eliminati.
## v1.0.38
- Il campo **Seggioloni** non viene più precompilato con `0`: resta vuoto nelle nuove prenotazioni e, se lasciato vuoto, viene interpretato come `0`.
- Anche in modifica prenotazione un valore pari a zero viene mostrato come campo vuoto, mantenendo comunque il calcolo corretto dei coperti.
- Rinominata la voce **Dolce** in **Dolce loro cura**, per indicare chiaramente che il dolce viene portato dal cliente.
- Aggiornata la stessa dicitura anche nella Dashboard e nei PDF dello staff.
- Aggiornati `VERSION`, footer e changelog.
## v1.0.37
- Aggiunta una **barra Salva sempre raggiungibile** durante la compilazione dei principali moduli, pensata soprattutto per l’utilizzo da smartphone.
- Il comando di salvataggio resta visibile sopra la barra di navigazione inferiore mentre il relativo form è a schermo, evitando di dover scorrere fino in fondo alla pagina.
- La barra scompare automaticamente quando si esce dal form, per esempio entrando nella sezione separata di cancellazione, così da non creare ambiguità tra `Salva` e azioni distruttive.
- Applicato il comportamento a prenotazioni tavoli, asporti, preparazione tavoli, impostazioni amministrative, servizi/eccezioni, struttura/inventario tavoli e modifica Rubrica.
- Aggiunto spazio di sicurezza in fondo ai form per impedire che la barra fissa copra gli ultimi campi; supportati tema chiaro/scuro e safe-area degli smartphone.
- Aggiornati `VERSION`, footer e changelog.
## v1.0.36
- Il limite coperti non è più unico per l’intera giornata: ogni **fascia di apertura / servizio** possiede ora il proprio massimo coperti.
- In **Amministrazione → Impostazioni → Giorni e orari** ogni fascia dispone del nuovo campo `Max coperti`, così pranzo e cena possono avere capacità differenti anche nello stesso giorno.
- Esempio: domenica `12:00–14:30` con massimo 100 coperti e `19:00–00:00` con massimo 180 coperti.
- Durante una prenotazione l’app individua automaticamente il servizio in base all’orario di arrivo e conteggia i coperti esclusivamente nel relativo massimale; la durata continua invece a essere usata per la disponibilità fisica dei tavoli.
- L’Admin mantiene la possibilità di **forzare i limiti**: la forzatura agisce ora sul limite del singolo servizio interessato.
- La Dashboard mostra una card distinta per ciascun servizio con `coperti utilizzati / massimo`, numero di prenotazioni e, separatamente, il totale informativo della giornata.
- La pagina amministrativa `Serate` è stata rinominata **Servizi** e permette di creare, per una singola data, una o più fasce straordinarie con massimale coperti dedicato. Se non vengono inserite fasce straordinarie, vengono ereditati orari e limiti settimanali.
- Aggiunta la tabella `service_override_periods` per le eccezioni multi-servizio e la colonna `max_covers` a `opening_periods`; la migrazione automatica assegna alle fasce esistenti il precedente limite predefinito, senza perdere configurazioni.
- I PDF tavoli riportano ora nell’intestazione i limiti dei singoli servizi anziché un unico “limite serata”.
- Aggiornati `VERSION`, footer, schema runtime e changelog.
## v1.0.35
- Aggiunta la gestione di **più fasce orarie nello stesso giorno della settimana** dalle impostazioni amministrative.
- Ogni giornata può ora avere un numero libero di fasce, ad esempio la domenica `12:00–14:30` e `19:00–00:00`, mantenendo non prenotabile l'intervallo pomeridiano tra i due servizi.
- Nel pannello **Amministrazione → Impostazioni → Giorni e orari** ogni giorno dispone dei comandi `Aggiungi fascia` e `Rimuovi fascia`; impostando il giorno su `Chiuso` non viene accettata alcuna prenotazione.
- Aggiunta la nuova tabella MySQL `opening_periods`; la migrazione automatica importa senza perdita le vecchie fasce singole presenti in `opening_hours`. La tabella storica viene mantenuta e sincronizzata per compatibilità.
- Aggiornati i calendari di prenotazioni tavoli e asporti: i giorni con più servizi mostrano gli orari di inizio delle varie fasce e rimangono cliccabili come normali giorni di apertura.
- Aggiornata la validazione lato server: una prenotazione o un asporto è accettato solo se l'orario rientra in **una delle fasce attive**; gli orari nel vuoto tra pranzo e cena vengono rifiutati.
- Aggiornati i selettori Ora/Minuti mobile-first per lavorare con più fasce nello stesso giorno e mantenere gli intervalli da 15 minuti.
- Le eccezioni della singola data continuano a funzionare: lasciando vuoti gli orari ereditano tutte le fasce settimanali; compilando gli orari sostituiscono le fasce di quel giorno con una singola fascia straordinaria.
- Aggiornati i PDF tavoli e asporti: vengono create fasce da 15 minuti **solo nei periodi realmente aperti**, evitando righe inutili durante le ore di chiusura tra pranzo e cena.
- Aggiornati `VERSION`, footer e changelog.
## v1.0.34
- Aggiunta nella **Rubrica interna** la voce **Cancella** accanto a `Modifica`, disponibile sia agli utenti Staff sia agli Amministratori.
- La cancellazione richiede una conferma esplicita per evitare eliminazioni accidentali.
- La cancellazione rimuove esclusivamente il contatto dalla Rubrica interna: **prenotazioni tavoli e ordini da asporto storici restano invariati**.
- La cancellazione del contatto viene registrata nel **Log attività** con nome, numero di cellulare, utente e data/ora dell'operazione.
- Se in futuro lo stesso nominativo/numero viene nuovamente utilizzato in una prenotazione o in un asporto, il normale salvataggio automatico della Rubrica potrà ricreare il contatto.
- Nessuna modifica allo schema database.
- Aggiornati `VERSION`, footer e changelog.
## v1.0.33
- Aggiunta una **Rubrica interna condivisa** disponibile sia agli utenti Staff sia agli Amministratori tramite la nuova voce `Rubrica` nella navigazione principale.
- I clienti vengono salvati automaticamente nella rubrica quando una prenotazione tavolo o un ordine da asporto contiene sia **nome** sia **numero di cellulare**. Le prenotazioni senza telefono restano valide e non generano contatti vuoti.
- La rubrica usa il numero di cellulare normalizzato come identificativo per evitare duplicati dello stesso numero e conserva la data dell'ultimo utilizzo.
- Al primo aggiornamento alla v1.0.33 vengono importati in modo conservativo nella rubrica anche i clienti già presenti nelle prenotazioni tavoli e negli asporti recenti.
- Aggiunta la pagina **Rubrica** con ricerca per nome o cellulare, elenco mobile-first, collegamento telefonico rapido e possibilità per Staff/Admin di creare e modificare nome, cellulare e note del contatto.
- Il vecchio pulsante `Rubrica` nei form è stato rinominato **Importa da Rubrica Telefono**, chiarendo che usa la rubrica nativa dello smartphone tramite Contact Picker API quando supportata dal browser.
- Aggiunto il nuovo pulsante **Importa da Prenotazioni Recenti** nelle schermate di creazione e modifica di prenotazioni tavoli e asporti. Apre un selettore mobile con ricerca e richiama in un tocco nome e cellulare dei clienti usati più di recente.
- La modifica manuale di un contatto nella Rubrica interna non viene sovrascritta automaticamente da prenotazioni successive con lo stesso numero: l'app aggiorna soltanto l'ultimo utilizzo, preservando le correzioni fatte dallo staff.
- Le creazioni automatiche e le modifiche manuali dei contatti vengono registrate nel **Log attività**.
- Aggiunta la tabella MySQL `contacts` con migrazione automatica protetta dal sistema di schema versionato introdotto nella v1.0.26.
- Aggiornati `VERSION`, footer e changelog.
## v1.0.32
- Fix completo della codifica caratteri **UTF-8** per nomi, prenotazioni, note, asporti e interfaccia.
- Le risposte HTML dell’app inviano ora esplicitamente l’header HTTP `Content-Type: text/html; charset=UTF-8`, evitando che configurazioni Apache/Plesk legacy interpretino il contenuto come ISO-8859-1.
- Tutti i form dichiarano esplicitamente `accept-charset="UTF-8"`, così anche l’invio di caratteri accentati (`à è é ì ò ù`) resta coerente.
- La connessione MySQL/MariaDB forza esplicitamente `SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci`; al primo accesso vengono convertite a `utf8mb4` soltanto eventuali tabelle applicative ancora su charset legacy.
- Aggiunta una correzione conservativa in visualizzazione per il mojibake più comune (es. `Novità ` → `Novità`) senza alterare automaticamente i dati originali nel database.
- Corretto il motore PDF: i font Helvetica/Helvetica Bold dichiarano ora `/WinAnsiEncoding`, coerente con la conversione Windows-1252 usata dal generatore PDF, così i caratteri accentati italiani vengono stampati correttamente.
- Reso il wrapping/troncamento del PDF **UTF-8 safe anche senza estensione PHP mbstring**: le stringhe non vengono più tagliate a metà di un carattere multibyte, causa concreta di accenti corrotti nelle note più lunghe.
- Aggiunto `readme.php`, viewer UTF-8 del file `README.md`; il footer e l’installer puntano ora a questo viewer per evitare problemi di charset del server sui file `.md` statici.
- `README.md` viene salvato con firma UTF-8 BOM per migliorare anche la visualizzazione diretta del file su server/browser che non dichiarano correttamente il charset dei Markdown.
- Aggiornati `VERSION`, footer e changelog.
## v1.0.31
- Aggiunto nel footer un collegamento sempre visibile a **README / Novità**, disponibile per tutti gli utenti dell’applicazione.
- Il collegamento apre `README.md` in una nuova scheda, così staff e amministratori possono consultare rapidamente lo storico delle modifiche e le funzionalità introdotte nelle varie versioni.
- Aggiornato anche il footer dell’installer iniziale con lo stesso collegamento.
- Aggiunto uno stile dedicato al link del footer per mantenerlo leggibile sia nel tema diurno sia in quello notturno.
- Nessuna modifica allo schema database.
- Aggiornati `VERSION`, footer e changelog.
## v1.0.30
- Corretto il vincolo introdotto nella v1.0.29: **Bambini** e **Seggioloni** sono ora nuovamente conteggi indipendenti.
- Rimosso il controllo `Seggioloni ≤ Bambini` sia dall’interfaccia sia dal backend.
- Rimosso il limite dinamico del campo Seggioloni basato sul numero di bambini.
- Rimane invariato il controllo obbligatorio di **almeno 1 adulto** per ogni prenotazione.
- Il totale coperti continua a essere calcolato come `Adulti + Bambini + Seggioloni`.
- Nessuna modifica allo schema database.
- Aggiornati `VERSION`, footer e changelog.
## v1.0.29
- Velocizzato l'inserimento di una nuova prenotazione: il campo **Adulti** non è più precompilato con `2` e parte vuoto.
- Aggiunto il vincolo obbligatorio di **almeno 1 adulto** per ogni prenotazione, verificato sia nell'interfaccia sia lato server.
- Aggiunto il vincolo **Seggioloni ≤ Bambini**: il numero di seggioloni non può superare il numero di bambini indicati nella prenotazione.
- Il limite massimo del campo Seggioloni viene aggiornato dinamicamente in base al numero di bambini e il browser segnala immediatamente eventuali valori non validi.
- Gli stessi controlli vengono applicati anche durante la modifica di una prenotazione esistente.
- Nessuna modifica allo schema database.
- Aggiornati `VERSION`, footer e changelog.
## v1.0.28
- Aggiunta nella pagina **Agenda prenotazioni** una ricerca testuale rapida, ottimizzata per smartphone.
- La ricerca filtra istantaneamente le prenotazioni del periodo visualizzato per **nome cliente** oppure **numero di cellulare**, senza ricaricare la pagina.
- La ricerca del telefono ignora la formattazione: è possibile digitare anche solo una parte delle cifre del numero.
- I giorni che non contengono risultati vengono nascosti automaticamente durante la ricerca e ricompaiono appena il filtro viene cancellato.
- Aggiunto un contatore dinamico delle prenotazioni trovate e un pulsante rapido per azzerare la ricerca.
- Il cellulare, quando presente, viene mostrato anche nella scheda Agenda per identificare più velocemente prenotazioni con nomi simili.
- Nessuna modifica allo schema database.
- Aggiornati `VERSION`, footer e changelog.
## v1.0.27
- Secondo intervento prestazionale dopo il persistere di timeout Apache/PHP-FPM `AH01075` anche con le migrazioni runtime rese leggere nella v1.0.26.
- Ottimizzato il motore di **disponibilità tavoli**, che nelle serate affollate poteva esplorare un numero molto elevato di combinazioni e assegnazioni tra prenotazioni sovrapposte.
- Le combinazioni di tavoli vengono ora memorizzate per richiesta, deduplicate e ridotte eliminando le soluzioni dominate che consumano più risorse senza offrire vantaggi.
- Il backtracking che assegna virtualmente i tavoli usa ora memoizzazione degli stati già falliti e sceglie prima le prenotazioni con meno alternative disponibili, riducendo drasticamente i rami esplorati.
- Aggiunto un **limite di sicurezza temporale** al calcolo disponibilità: invece di lasciare un worker PHP-FPM occupato fino al timeout Apache, l'app interrompe il solver e restituisce un messaggio gestibile invitando a selezionare una zona specifica o, per gli admin, usare la forzatura.
- Aggiunti piccoli cache per-request per servizio effettivo, stagione/zone attive e inventario tavoli, riducendo query duplicate durante la stessa pagina.
- Impostati, quando supportati dal motore DB, timeout server-side anche sulle query (`max_statement_time` MariaDB / `MAX_EXECUTION_TIME` MySQL), oltre ai timeout di connessione e lock già presenti.
- Aggiunto il log diagnostico `storage/performance.log`: vengono registrate automaticamente solo richieste lente (>= 2 secondi), errori HTTP 5xx e fatal error, con URI, durata, IP e memoria di picco.
- Nessuna modifica allo schema database: la versione runtime dello schema resta `1.0.26`, quindi l'aggiornamento alla v1.0.27 non forza una nuova migrazione DDL.
- Aggiornati `VERSION`, footer e changelog.
## v1.0.26
- Corretto il meccanismo di migrazione runtime che nelle versioni precedenti eseguiva controlli `SHOW COLUMNS`, `CREATE TABLE IF NOT EXISTS` e verifiche su `information_schema` ad ogni singola richiesta HTTP.
- Introdotta la chiave database `runtime_schema_version`: dopo il primo aggiornamento, le richieste normali eseguono soltanto una lettura leggera della versione schema e non rilanciano più DDL o controlli strutturali.
- Le migrazioni vengono eseguite **una sola volta** quando necessario e sono protette da un advisory lock MySQL (`GET_LOCK`) per impedire migrazioni concorrenti da più worker PHP-FPM.
- Ridotte le interrogazioni di migrazione a poche query aggregate su `information_schema`; `ALTER TABLE` e `CREATE TABLE` vengono eseguiti solo quando una struttura risulta realmente mancante.
- Impostati timeout DB prudenti (`PDO` connection timeout e lock wait MySQL) per evitare che un worker PHP-FPM rimanga occupato per minuti in presenza di lock.
- In caso di migrazione temporaneamente bloccata l'app risponde rapidamente con **HTTP 503 + Retry-After**, invece di restare appesa fino al timeout Apache `AH01075`.
- Aggiunto il log diagnostico `storage/runtime-schema.log`, utile per verificare l'esecuzione o eventuali errori delle migrazioni.
- Le nuove installazioni vengono marcate direttamente con schema `1.0.26`, quindi non eseguono migrazioni ridondanti al primo accesso.
- Nessuna modifica funzionale a prenotazioni, asporti, PDF o WhatsApp rispetto alla v1.0.25.
- Aggiornati `VERSION`, footer e changelog.
## v1.0.25
- Rinominato il pulsante principale `+ ASPORTI` in **`+ Asporti`** nella Dashboard e nell’Agenda asporti.
- Ridisegnato il **PDF Asporti**: ogni ordine occupa ora **una sola riga tabellare**, anche quando contiene più piatti.
- I piatti dello stesso ordine vengono accorpati nella cella **Piatto** con quantità e descrizione su più righe interne alla stessa cella.
- La colonna **QTÀ** mostra la quantità complessiva dei pezzi/piatti dell’ordine.
- Le note specifiche dei singoli piatti, le note generali dell’ordine e l’eventuale cellulare vengono accorpati nella cella **Note** della stessa riga.
- L’altezza della riga dell’ordine cresce automaticamente in base al numero di piatti/note, evitando che uno stesso ordine possa essere interpretato come più ordini distinti.
- Restano invariate le fasce da 15 minuti, le righe vuote del planner e la regola che impedisce di spezzare una fascia tra due pagine.
- Aggiornati `VERSION`, footer e changelog.
## v1.0.24
- Aggiunto il numero di cellulare nella colonna **Note** di tutti i PDF quando il telefono è presente.
- Nel PDF prenotazioni tavoli ogni prenotazione mostra `Cell: numero` insieme alle altre note operative.
- Nel PDF asporti il cellulare viene riportato nelle note della prima riga dell'ordine, oltre alla colonna Telefono già esistente, evitando ripetizioni su ogni piatto dello stesso ordine.
- Le prenotazioni e gli asporti senza numero di telefono non mostrano alcuna dicitura aggiuntiva.
- Aggiornati `VERSION`, footer e changelog.
## v1.0.23
- Aggiunto il nuovo modulo **Asporti**, separato dalle prenotazioni tavoli e senza occupazione di tavoli o zone.
- Aggiunto il pulsante **+ ASPORTI** nella Dashboard e una voce dedicata nella navigazione mobile.
- Aggiunta una **Agenda asporti** con filtro per intervallo di date, orario di ritiro, cliente, stato e riepilogo dei piatti ordinati.
- La creazione di un asporto usa lo stesso calendario del locale: giorni di chiusura e ferie sono rossi/non selezionabili e gli orari di ritiro seguono la fascia di servizio configurata, compresi i servizi oltre mezzanotte.
- Ogni asporto contiene nome cliente, telefono facoltativo, data/ora di ritiro, note generali e una lista dinamica di piatti.
- Ogni riga piatto dispone di **quantità, nome/tipologia del piatto e note specifiche**, con pulsanti per aggiungere o rimuovere righe direttamente da smartphone.
- Aggiunti gli stati asporto: **Prenotato, Confermato, In preparazione, Pronto, Ritirato, Annullato dal cliente**.
- Aggiunta la cancellazione per errore di immissione, separata dall'annullamento del cliente e tracciata nel **Log attività**.
- Il Log attività registra creazione, modifica, annullamento e cancellazione degli asporti con utente, data/ora e descrizione.
- La Dashboard mostra ora il numero di ordini da asporto e il totale dei piatti della serata, oltre a una lista rapida degli ordini.
- Aggiunto un **PDF Asporti** separato dal PDF tavoli.
- Il PDF asporti usa lo stesso planner staff: A4 verticale, fasce da 15 minuti, almeno 5 righe per fascia, crescita automatica se i piatti sono più numerosi e nessuna fascia viene spezzata tra due pagine.
- Nel PDF asporti ogni piatto occupa una riga con colonne **Orario, Cliente, Telefono, Qtà, Piatto, Note**; le righe residue restano vuote per aggiunte a penna.
- Il parametro **Righe minime per fascia** nelle impostazioni PDF viene applicato sia al planner tavoli sia al planner asporti.
- Aggiunte le tabelle MySQL `takeaway_orders` e `takeaway_items`, create automaticamente al primo accesso in fase di aggiornamento.
- Aggiornati `VERSION`, footer e changelog.
## v1.0.22
- Il planner PDF non è più limitato a 2 pagine: genera automaticamente tutti i fogli A4 verticali necessari.
- Ogni fascia di 15 minuti contiene sempre **almeno 5 righe complessive**, comprese le prenotazioni già registrate.
- Se una fascia contiene più di 5 prenotazioni, cresce automaticamente per stamparle tutte senza perdere dati.
- Mantenuta l’altezza delle righe della v1.0.21 per preservare la leggibilità del planner.
- Le fasce orarie sono **indivisibili**: se una fascia non entra nello spazio residuo del foglio, viene spostata interamente alla pagina successiva.
- Il numero totale di pagine viene calcolato automaticamente e riportato nell’intestazione (`Pagina X / Y`).
- In **Amministrazione → Impostazioni → PDF / Planner serata** la preferenza diventa **Righe minime per fascia di 15 minuti**, con minimo 5 e valore predefinito 5.
- Aggiornati `VERSION` e changelog.
## v1.0.21
- Aggiunta nuovamente la colonna **Orario** nelle righe del PDF, mantenendo anche l'intestazione orizzontale `FASCIA ORARIA HH:MM`.
- Ridotte drasticamente le righe vuote: ogni fascia di 15 minuti usa normalmente una sola riga libera oltre alle prenotazioni già presenti, fino a un massimo configurabile di **5 righe complessive per fascia**.
- Se una fascia contiene già 5 prenotazioni non vengono aggiunte righe vuote; se eccezionalmente contiene più di 5 prenotazioni, vengono comunque stampate tutte senza perdere dati.
- Le fasce più affollate usano **carattere più grande e righe più alte**, invece di rimpicciolire progressivamente il testo.
- La divisione tra le 2 pagine viene ora calcolata in base all'altezza reale delle fasce per distribuire meglio lo spazio disponibile.
- Il PDF resta composto da **esattamente 2 fogli A4 verticali**.
- In **Amministrazione → Impostazioni → PDF / Planner serata** è disponibile la nuova preferenza **Righe massime per fascia di 15 minuti**, predefinita a 5.
- Aggiornati `VERSION` e changelog.
## v1.0.20
- Ridisegnato il planner PDF per migliorare la leggibilità mantenendo sempre **2 pagine A4 verticali**.
- Le fasce orarie restano ogni **15 minuti**, ma l'orario non è più una cella verticale stretta: ogni fascia viene ora mostrata come una **riga orizzontale a tutta larghezza**, come nel planner precedente.
- Rimossa la colonna `Ora` dalle righe operative: lo spazio recuperato viene distribuito a Nome tavolo, Zona, Note e Numero tavolo.
- Ogni fascia riceve lo stesso numero di righe operative; valore predefinito **2 righe per fascia**, molto più alte e leggibili rispetto alla v1.0.19.
- Se una fascia contiene più prenotazioni del minimo configurato, tutte le fasce vengono ampliate automaticamente allo stesso numero di righe, preservando la proporzione tra gli orari.
- In **Amministrazione → Impostazioni → PDF / Planner serata** la precedente densità 40–60 è stata sostituita dalla preferenza **Righe minime per fascia di 15 minuti** (1–4, predefinito 2).
- Il secondo foglio resta sempre disponibile anche per servizi molto brevi.
- Nessuna modifica distruttiva al database; la nuova preferenza viene aggiunta automaticamente alla tabella impostazioni.
## v1.0.19
- Ripristinata nel PDF staff la suddivisione del planner in **fasce orarie ogni 15 minuti**.
- Le fasce vengono generate automaticamente dal primo all'ultimo orario prenotabile configurato per la serata, comprese le fasce che attraversano la mezzanotte (es. 19:00–00:00).
- Tutte le fasce ricevono **esattamente lo stesso numero di righe**, così lo spazio disponibile è distribuito in maniera proporzionale e uniforme tra tutti gli orari.
- Le prenotazioni vengono inserite nella propria fascia di 15 minuti e le righe residue restano vuote per la compilazione a penna dello staff.
- La colonna **Ora** è visualizzata come una cella unica verticale per tutta la fascia, rendendo immediata la lettura del planner.
- Il PDF resta composto **sempre e soltanto da 2 pagine A4 verticali**.
- La preferenza in **Amministrazione → Impostazioni → PDF / Planner serata** viene ora interpretata come densità indicativa di righe per foglio (40–60): l'app calcola automaticamente le righe per fascia mantenendole uguali per ogni orario.
- Se una singola fascia contiene più prenotazioni delle righe previste, il sistema aumenta automaticamente il numero di righe di **tutte** le fasce, preservando la proporzione e mantenendo sempre 2 pagine.
- Le prenotazioni restano ordinate per orario e la zona continua a essere mostrata nell'apposita colonna, senza raggruppamenti per zona.
- Nessuna modifica allo schema MySQL.
## v1.0.18
- Reso il **numero di telefono facoltativo** nella creazione e modifica delle prenotazioni.
- Una prenotazione senza telefono resta pienamente valida e partecipa normalmente a disponibilità tavoli, coperti, preparazione serata e PDF.
- Se il telefono è vuoto, il **Reminder WhatsApp** viene automaticamente disabilitato e non viene salvato come attivo.
- Il cron dei reminder ignora esplicitamente le prenotazioni senza numero di telefono, evitando tentativi di invio inutili.
- In caso di **Annullata dal cliente**, se la prenotazione non ha un numero di telefono l'app non tenta l'invio WhatsApp di annullamento e registra nel Log attività che il messaggio non era previsto.
- Aggiunta nell'interfaccia una nota chiara accanto al telefono e al reminder per indicare che il numero è opzionale.
- Nessuna modifica allo schema MySQL.
## v1.0.17
- Aumentato il planner PDF da almeno **20 a 40 righe per foglio**.
- Il PDF resta composto da **esattamente 2 pagine A4 verticali**, quindi mette a disposizione almeno **80 righe complessive**, comprese le prenotazioni già registrate.
- Le righe non occupate restano vuote e compilabili a penna dallo staff.
- Se le prenotazioni superano le 80 complessive, il sistema aumenta automaticamente e in modo uniforme le righe per pagina mantenendo comunque soltanto 2 fogli.
- In **Amministrazione → Impostazioni → PDF / Planner serata** il valore è ora configurabile da **40 a 60 righe per foglio**, con valore predefinito 40.
- Le installazioni aggiornate da versioni precedenti con un valore inferiore a 40 vengono portate automaticamente a 40 al primo accesso.
## v1.0.16
- Semplificato il PDF staff in un **planner completamente libero**, senza suddivisione per zone e senza blocchi/fasce orarie.
- Le prenotazioni presenti vengono stampate semplicemente in **ordine cronologico**; la zona resta disponibile nell'apposita colonna.
- Il planner viene generato **sempre e soltanto su 2 pagine A4 verticali**.
- Ogni foglio contiene almeno **20 righe totali**, comprese le prenotazioni già registrate; le righe non occupate restano completamente vuote e compilabili a penna.
- Se le prenotazioni superano le 40 complessive, il sistema aumenta automaticamente e in modo uniforme il numero di righe per pagina per mantenere tutte le prenotazioni nei 2 fogli.
- In **Amministrazione → Impostazioni → PDF / Planner serata** il numero minimo di righe per foglio è configurabile da 20 a 30, con valore predefinito 20.
- Rimosso dal planner il precedente concetto di “righe libere per fascia di 30 minuti”.
- Ridimensionate colonne e caratteri per il nuovo orientamento verticale mantenendo: Ora, Nome tavolo, Zona, Adulti, Bambini, Seggioloni, Note e Numero tavolo.
- Nessuna modifica allo schema MySQL: la nuova preferenza utilizza la tabella impostazioni esistente.
## v1.0.15
- Rifatto il PDF staff in modalità **planner cronologico**: le prenotazioni non sono più separate per zona e vengono ordinate esclusivamente per orario.
- Aggiunta la colonna **Zona** direttamente nella tabella del PDF.
- Il planner è suddiviso in **fasce di 30 minuti** dalla prima all’ultima ora prenotabile della serata, comprese le fasce che attraversano la mezzanotte.
- In ogni fascia vengono stampate le prenotazioni già presenti con il loro orario esatto e vengono aggiunte righe vuote compilabili a penna dallo staff.
- Il numero di righe libere per ogni fascia di 30 minuti è configurabile da **Amministrazione → Impostazioni → PDF / Planner serata**; valore predefinito: **6**.
- Le righe libere mantengono tutte le colonne operative: Ora, Nome tavolo, Zona, Adulti, Bambini, Seggioloni, Note e Numero tavolo.
- Migliorata la gestione multipagina del PDF: intestazione tabella e fascia oraria vengono ripetute quando il planner prosegue nella pagina successiva.
- La stampa A4 orizzontale viene generata su **almeno 2 fogli**; se il planner dovesse occupare una sola pagina, il secondo foglio viene predisposto con una griglia vuota per prenotazioni aggiuntive scritte a mano.
- Nessuna modifica allo schema MySQL: la nuova preferenza viene salvata nella tabella impostazioni esistente.
## v1.0.14
- Aggiunta in **Amministrazione → Utenti** la possibilità per gli amministratori di **cancellare definitivamente gli utenti Staff**.
- Il pulsante **Cancella utente** è disponibile esclusivamente sugli account con ruolo `staff`; gli account amministratore non possono essere eliminati da questa funzione.
- Aggiunta protezione contro la cancellazione/disattivazione del proprio account.
- Le prenotazioni create da uno Staff eliminato vengono conservate nello storico: il riferimento `created_by` viene reso nullable con vincolo `ON DELETE SET NULL` tramite migrazione automatica.
- I precedenti eventi del **Log attività** restano consultabili anche dopo la cancellazione dell'utente, perché nome, username e ruolo sono memorizzati nello storico.
- Il filtro utenti del Log include ora anche gli utenti eliminati, evidenziati con la dicitura **(eliminato)**.
- La cancellazione di uno Staff viene registrata nel Log attività con descrizione e numero di prenotazioni storiche preservate.
## v1.0.13
- Aggiunta in **Amministrazione → Impostazioni → Test invio WhatsApp** una funzione di verifica manuale della WhatsApp Cloud API.
- Aggiunto il campo **Numero WhatsApp destinatario** con normalizzazione automatica dei numeri italiani (`333...`, `+39...`, `0039...`).
- Il pulsante **Invia messaggio di test** utilizza il template Meta standard `hello_world` con lingua `en_US`, ideale per verificare il numero di prova fornito da Meta.
- Il test usa Graph API version, Phone number ID e Access token già salvati nelle impostazioni e può essere eseguito anche con l'automazione WhatsApp dell'app disabilitata.
- In caso di successo viene mostrato anche l'ID messaggio restituito da Meta; in caso di errore viene mostrata la descrizione restituita dalla Cloud API quando disponibile.
- Il destinatario usato per l'ultimo test resta precompilato per agevolare prove successive.
- I tentativi di test, riusciti o falliti, vengono registrati nel **Log attività** con utente, destinatario ed esito.
- Migliorata la gestione interna delle risposte della WhatsApp Cloud API e degli errori cURL senza esporre l'Access token.
- `server-check.php` verifica ora anche la disponibilità dell'estensione PHP **cURL**, necessaria per l'invio WhatsApp.
## v1.0.12
- Le prenotazioni distinguono ora i coperti in **Adulti**, **Bambini** e **Seggioloni**.
- Il totale della prenotazione viene calcolato automaticamente come somma delle tre categorie e continua a essere usato per disponibilità tavoli e limite coperti della serata.
- Aggiunto il totale coperti aggiornato in tempo reale durante inserimento e modifica della prenotazione.
- Dashboard, Agenda e Preparazione tavoli mostrano anche la composizione della prenotazione.
- Il PDF staff è stato ridisegnato in **A4 orizzontale** con griglia tabellare ispirata al foglio operativo fornito:
- Ora;
- Nome tavolo;
- Numero persone suddiviso in Adulti / Bambini / Seggioloni;
- Note;
- Numero tavolo.
- Le prenotazioni nel PDF sono delimitate da celle e linee ben visibili; le zone sono separate da intestazioni dedicate.
- Il PDF riporta in testata anche i totali della serata per categoria.
- Aggiunta migrazione automatica delle installazioni precedenti: il totale storico viene preservato attribuendo inizialmente a `Adulti` i coperti non associati ai seggioloni e lasciando `Bambini` a 0 fino a eventuale modifica manuale.
- Aggiornati log attività e descrizioni operative con il dettaglio Adulti / Bambini / Seggioloni.
## v1.0.11
- Distinta definitivamente la semantica tra **Annullata dal cliente** e **Cancella prenotazione**.
- Lo stato `cancelled` viene ora mostrato come **Annullata dal cliente** e rappresenta una disdetta ricevuta dal cliente.
- Aggiunto nella modifica prenotazione il pulsante **Cancella prenotazione**, destinato esclusivamente agli errori di inserimento di staff/admin.
- La cancellazione è **logica**: la prenotazione non compare più in agenda operativa, disponibilità tavoli, preparazione serata, conteggi o PDF, ma resta nel database per garantire la tracciabilità.
- La cancellazione per errore richiede un motivo e **non invia mai WhatsApp**.
- Aggiunta la possibilità, configurabile in **Amministrazione → Impostazioni → WhatsApp**, di inviare un template WhatsApp quando una prenotazione passa allo stato **Annullata dal cliente**.
- Aggiunto template separato configurabile per l’annullamento (`reservation_cancelled` come default), con le stesse quattro variabili del reminder: cliente, data, ora, persone.
- Migliorata la normalizzazione dei numeri italiani per WhatsApp.
- Introdotto **Log attività** visibile esclusivamente agli amministratori.
- Il log registra data/ora, utente, ruolo, azione, descrizione, oggetto interessato e indirizzo IP.
- Tracciate le principali azioni: accessi/uscite, creazione/modifica/annullamento/cancellazione prenotazioni, preparazione tavoli, impostazioni, ferie, utenti, serate, assetti, zone, tipi tavolo e inventario.
- Aggiunti filtri del log per periodo, utente e ricerca testuale.
- Aggiunte migrazioni automatiche per cancellazione logica e tabella `audit_logs`.
## v1.0.10
- Migliorata la leggibilità del PDF: ogni prenotazione è separata dalla successiva tramite una linea orizzontale.
- Aggiunto in **Preparazione tavoli** il campo **Numero tavolo** per ogni prenotazione.
- Il numero tavolo viene salvato nell’allestimento e riportato anche nel PDF della serata.
- Il campo numero tavolo accetta anche valori composti, ad esempio `12-13`, utili quando più tavoli vengono accorpati.
- Rinominata in tutta l’interfaccia la voce **Dolce portato da fuori / Dolce esterno** in **Dolce**.
- Aggiunta migrazione automatica del database per il nuovo campo `table_number` nella tabella `setups`.
## v1.0.9
- Completato il `README.md` con lo storico delle modifiche dalla v1.0.0 in avanti.
- Versione dell'applicazione mostrata sempre nel footer nel formato `vX.Y.Z · powered by IT Easy Informatica`.
- Versione mostrata anche nel footer dell'installer iniziale.
- Il CSS dell'installer utilizza ora anch'esso il numero versione per evitare cache obsolete.
## v1.0.8
- Pulsante **Rubrica** in nuova/modifica prenotazione.
- Quando il browser supporta la Contact Picker API, vengono richiesti insieme **nome del contatto e numeri di telefono**.
- Il nome viene compilato automaticamente nel campo cliente.
- I numeri duplicati dello stesso contatto vengono accorpati anche quando sono salvati con formati diversi (`+39...`, `0039...`, numero nazionale).
- Se dopo la deduplicazione rimane un solo numero, viene compilato automaticamente.
- Se il contatto ha più numeri realmente diversi, l'app mostra un selettore mobile e permette di scegliere **un solo numero**.
- Se il browser non supporta l'accesso diretto alla rubrica, resta disponibile l'inserimento manuale/autocomplete standard.
- Nessuna modifica allo schema MySQL rispetto alla v1.0.7.
## v1.0.7
- Nella schermata **Nuova prenotazione** il campo data è stato sostituito da un calendario mobile-first.
- I giorni in cui il locale è chiuso sono mostrati in **rosso e non sono cliccabili**.
- Il calendario considera:
- giorni settimanali di chiusura;
- eccezioni della singola serata;
- periodi di ferie / chiusure programmate.
- I periodi di ferie sono gestibili da **Amministrazione → Impostazioni → Chiusure per ferie**.
- Ogni periodo ha data iniziale, data finale e descrizione, ad esempio `Ferie estive`.
- Le chiusure possono essere attivate/disattivate oppure eliminate definitivamente.
- Impedita la creazione accidentale di periodi di chiusura attivi sovrapposti.
- Una eccezione della singola serata impostata esplicitamente su **Aperto** può riaprire una data compresa in un periodo di ferie.
- Versionamento automatico di CSS/JS per ridurre i problemi di cache sugli smartphone dopo gli aggiornamenti.
## v1.0.6
- Eliminata la perdita posti globale fissa per le unioni.
- Aggiunta per ogni tipologia di tavolo la configurazione:
- **posti persi alla prima unione**;
- **posti persi alla seconda/unione aggiuntiva**.
- L'algoritmo calcola la capienza delle combinazioni usando le perdite specifiche dei singoli tipi di tavolo.
- Le tipologie esistenti vengono migrate mantenendo il comportamento precedente con valori iniziali compatibili.
- Supportati casi in cui un tavolo non perde posti alla prima unione.
## v1.0.5
- Aggiunta la proprietà **Unibile ad altri tavoli** per ogni tipologia.
- Un tavolo marcato come non unibile può essere utilizzato solo singolarmente per una prenotazione.
- Un tavolo non unibile non può essere combinato né con tavoli dello stesso tipo né con tipologie differenti.
- Più tavoli non unibili possono comunque essere utilizzati contemporaneamente per prenotazioni diverse.
- La preparazione della serata propone `Singolo` quando la soluzione usa un solo tavolo e disposizioni multiple solo quando necessario.
## v1.0.4
- Aggiunta la cancellazione definitiva di asset amministrativi oltre alla semplice disattivazione.
- Eliminazione disponibile per:
- assetti/stagioni;
- zone;
- tipologie di tavolo.
- Introdotte protezioni contro la cancellazione di elementi già usati nello storico prenotazioni o ancora collegati ad altri dati.
- Conferma obbligatoria prima della cancellazione definitiva.
## v1.0.3
- Corretta la gestione degli orari che attraversano la mezzanotte.
- Una fascia `19:00–00:00` viene interpretata come servizio dalle 19:00 fino alla mezzanotte conclusiva della stessa serata.
- Supportate anche fasce come `19:00–01:00`.
- Corretto il calcolo delle sovrapposizioni dei tavoli quando la prenotazione termina dopo mezzanotte.
- Migliorati i messaggi di errore per gli orari fuori fascia mostrando la fascia realmente applicata.
- Orari amministrativi impostabili a intervalli di 15 minuti.
- Rifatta in chiave mobile la sezione **Inventario tavoli per zona**, eliminando la tabella orizzontale poco leggibile su smartphone.
- Migliorata la navigazione delle sezioni amministrative su schermi stretti.
## v1.0.2
- Orario di arrivo della prenotazione separato in selezione **Ora** e **Minuti**.
- Ore selezionabili una per una.
- Minuti disponibili: `00`, `15`, `30`, `45`.
- Rimosso il campo e-mail dalla prenotazione cliente.
- Rimossa la voce specifica `Compleanno`; ricorrenze ed esigenze vengono gestite nelle note.
- Mantenuta la compatibilità con le colonne tecniche eventualmente già presenti nel database.
## v1.0.1
- Rimossi i file `.htaccess` dalla distribuzione per evitare errori HTTP 500 su hosting/Plesk che non consentono alcune direttive Apache a livello directory.
- Aggiunta la pagina `server-check.php` per verificare rapidamente requisiti PHP e permessi.
- Mantenuta l'applicazione indipendente da configurazioni `.htaccess` obbligatorie.
## v1.0.0
Prima versione installabile dell'applicazione.
Funzioni principali introdotte:
- Stack **Apache + PHP + MySQL**.
- Installer guidato con:
- host, porta, database, utente e password MySQL;
- creazione amministratore;
- e-mail recupero password;
- titolo iniziale applicazione.
- Ruoli **Amministratore** e **Staff**.
- Interfaccia **mobile-first** pensata principalmente per smartphone.
- Logo configurabile.
- Titolo applicazione configurabile da pannello admin.
- Tema **Diurno / Notturno / Automatico** e cambio tema dall'interfaccia.
- Footer `powered by IT Easy Informatica`.
- Gestione prenotazioni interne con:
- cliente;
- telefono;
- data e ora;
- adulti;
- bambini;
- seggioloni;
- totale coperti calcolato automaticamente;
- durata prevista;
- zona;
- dolce;
- note ed esigenze particolari;
- stato prenotazione.
- Stati prenotazione: prenotata, confermata, arrivata, terminata, annullata, no-show.
- Assetti/stagioni del locale, inizialmente **Estivo / Esterno** e **Invernale / Interno**.
- Zone iniziali estive: Tendone, Aperto, Altalene, Giochi, Privée, Fresco.
- Giorni e orari di apertura configurabili da amministrazione.
- Valori iniziali Giovedì–Domenica, fascia serale.
- Eccezioni configurabili per singola serata.
- Massimo coperti configurabile per ciascuna fascia di servizio (es. pranzo/cena), con possibilità di forzatura amministratore.
- Tipologie tavolo iniziali:
- Quadrato, 4 posti;
- Rettangolare, 6 posti.
- Quantità dei tipi di tavolo configurabile per ogni zona.
- Calcolo della disponibilità fisica dei tavoli per fascia oraria.
- Gestione di più turni nella stessa serata.
- Durata standard prenotazione e tempo di riassetto configurabili.
- Prenotazione separata dall'allestimento fisico dei tavoli.
- Schermata **Preparazione tavoli** con suggerimenti di disposizione.
- PDF A4 orizzontale multipagina per lo staff con griglia operativa, orari, nome tavolo, adulti, bambini, seggioloni, note, numero tavolo e separazione per zone.
- Predisposizione per reminder tramite **WhatsApp Cloud API** e cron giornaliero.
- Recupero password via e-mail.
# Regole funzionali attuali
## Prenotazione e allestimento
Durante la prenotazione non viene fissata immediatamente la disposizione fisica. L'app verifica che esista una combinazione di tavoli sufficiente nella zona e nella fascia richiesta. La disposizione viene definita successivamente nella schermata di preparazione della serata.
## Tavoli unibili
Ogni tipologia può essere marcata come unibile o non unibile. Per i tipi unibili, la perdita di capienza viene calcolata usando i valori specifici della tipologia per prima e seconda unione.
## Limite coperti per servizio
Ogni fascia di apertura possiede il proprio massimo coperti (per esempio pranzo e cena con limiti diversi). La prenotazione viene conteggiata nel servizio corrispondente al proprio orario di arrivo; l’amministratore può forzare il relativo limite quando necessario. Il totale giornaliero resta disponibile come dato informativo.
## Chiusure e ferie
La disponibilità di una data deriva da giorni settimanali, eccezioni per singola serata e periodi di ferie. Una eccezione esplicita `Aperto` può prevalere su una chiusura per ferie.
## Orari oltre mezzanotte
Una fascia `19:00–00:00` indica il servizio dalle 19:00 fino alla mezzanotte conclusiva della stessa serata. Sono supportate anche fasce che terminano dopo mezzanotte, ad esempio `19:00–01:00`.
## Compatibilità database
Per compatibilità con installazioni precedenti, alcune colonne tecniche non più usate dall'interfaccia, come `email` e `birthday` nella tabella `bookings`, possono rimanere nel database.
## Asporti
Gli ordini da asporto sono separati dalle prenotazioni tavoli: rispettano giorni, ferie e orari del servizio, ma non impegnano zone o tavoli e non concorrono al calcolo della disponibilità fisica della sala. Ogni ordine può contenere più righe piatto, ciascuna con quantità e note specifiche. Il PDF asporti è un planner dedicato alla cucina/staff: ogni ordine occupa una sola riga tabellare e i suoi piatti vengono accorpati nella stessa cella, così un ordine con più pietanze non può essere scambiato per più ordini distinti. Il planner non modifica il PDF delle prenotazioni tavoli.