BR-BOOK — Regole di business del dominio Booking / Prenotazioni
Estrazione delle regole di business del modulo BookCenter (creazione e gestione prenotazioni di entità spaziali), ancorata a
docs/07-business-flows/FLOW-BOOK-001.mde alla relativa evidenza. Backend OracleINFOCAD_TEST38(procedure top-levelBOOK_*, nessun ORM), frontend ASP.NET Web FormsBookCenterWeb(C#), BL/DAL nel servizio WindowsBookManagervia .NET Remoting; path programmatico equivalente WCFBookingReaderWCF.svc.Il create-booking runtime passa dal modulo Book* (
BookController/BookDAL), non dal modulo omonimoBooking*(amministrazione/config). Le regole sotto coprono: validazioni di inserimento, anti-sovrapposizione, ricorrenza, workflow DEM, permessi, transizioni di stato, vincoli DB, e le varianti customer-specific (soglie comfort/consumi energetici).
Legenda enforcement: UI = solo lato interfaccia (code-behind) · BL = business layer (controller/DAL/helper) · DB = vincolo/PL-SQL sul database · mixed = più punti. Legenda confidence: VERIFIED = letto su codice e/o sorgente DB · SUPPORTED = per analogia documentata · INFERRED = dedotto dalla struttura, non testato a runtime.
Registro regole
| ID | Regola | Tipo | Enforcement | Conf. |
|---|---|---|---|---|
| BR-BOOK-001 | Entità spaziale obbligatoria per creare la prenotazione | validation | UI | VERIFIED |
| BR-BOOK-002 | Tipo orario obbligatorio per creare la prenotazione | validation | UI | VERIFIED |
| BR-BOOK-003 | Anti-sovrapposizione occorrenze sulla stessa entità (stati Booked/Approved/Confirmed) | validation | UI ⚠️ | VERIFIED |
| BR-BOOK-004 | Data inizio non successiva alla data fine ricorrenza | date-logic | UI | VERIFIED |
| BR-BOOK-005 | La ricorrenza è espansa in N occorrenze, ciascuna con proprio elemento DEM | calculation | UI/BL | VERIFIED |
| BR-BOOK-006 | Ogni occorrenza genera un elemento di workflow DEM di tipo Create | state-transition | BL | SUPPORTED |
| BR-BOOK-007 | Ramo di scrittura Current vs Stored selezionato da BookTable/PTABLE |
config-flag | mixed | VERIFIED |
| BR-BOOK-008 | BOOK_DEMAND esige ID_USER e ID_CDP NOT NULL, non popolati nel path interattivo |
validation | DB ⚠️ | VERIFIED |
| BR-BOOK-009 | BOOK_SCHEDULE esige START/END_DATETIME NOT NULL + FK verso demand/time-type |
validation | DB | VERIFIED |
| BR-BOOK-010 | Nessun vincolo DB anti-doppia-prenotazione (no trigger, no unique su entità+intervallo) | validation | DB (assenza) ⚠️ | VERIFIED |
| BR-BOOK-011 | InsertDemand e InsertScheduleWithElement non sono in una transazione unica → domanda orfana possibile |
state-transition | BL/DB ⚠️ | INFERRED |
| BR-BOOK-012 | Creazione consentita solo con CanAll oppure (CanBook e tabella Current) |
permission | UI | VERIFIED |
| BR-BOOK-013 | Utente senza CanAll può editare solo appuntamenti in stato Booked |
permission | UI | VERIFIED |
| BR-BOOK-014 | Non è modificabile una serie con occorrenze Approved/Confirmed | validation | UI | VERIFIED |
| BR-BOOK-015 | DeleteDemand annulla (Activity.Cancel) solo gli elementi in stato Booked |
state-transition | BL/UI | VERIFIED |
| BR-BOOK-016 | Update di serie ricorrente = cancellazione schedule Booked + re-insert occorrenze | state-transition | UI/BL | VERIFIED |
| BR-BOOK-017 | Bottoni di cambio stato visibili solo se attore ∈ CanGroup e stato-successivo valido |
permission | UI | VERIFIED |
| BR-BOOK-018 | Notifica mail di approvazione "consumi" agli attori Confirm | customer-specific | BL | VERIFIED |
| BR-BOOK-019 | Alert soglia comfort (ore min/max + percentuali) attivato da IfConfortClassExists() |
customer-specific | BL | VERIFIED |
| BR-BOOK-020 | Id di stato/attività e GUID workflow hard-coded (magic numbers) | config-flag | UI/BL ⚠️ | VERIFIED |
| BR-BOOK-021 | Path WCF: idActivity deve essere di tipo Create; salta l'anti-sovrapposizione e forza valori fissi |
validation | BL ⚠️ | VERIFIED |
| BR-BOOK-022 | Finestra calendario di default: da SelectedDate-8gg a SelectedDate+38gg |
date-logic | UI | VERIFIED |
⚠️ = regola a rischio (solo-UI, solo-DB, gap di vincolo, magic number, o bypass).
Dettaglio regole
BR-BOOK-001 — Entità spaziale obbligatoria
Tipo: validation · Enforcement: UI · Conf.: VERIFIED · customer_specific: no
Se e.Appointment.Resources.GetResourceByType(GroupEntity) è null l'inserimento viene annullato (e.Cancel = true) con radalert('Scegliere una entità spaziale!'). Nessuna scrittura DB.
Evidenza: BookCenterWeb/UserControl/Schedule.ascx.cs:580-590.
BR-BOOK-002 — Tipo orario obbligatorio
Tipo: validation · Enforcement: UI · Conf.: VERIFIED · customer_specific: no
Se manca la risorsa GroupTime → e.Cancel = true + radalert('Scegliere un tipo di orario!').
Evidenza: Schedule.ascx.cs:591-601.
BR-BOOK-003 — Anti-sovrapposizione occorrenze ⚠️ SOLO-UI
Tipo: validation · Enforcement: UI (rischio) · Conf.: VERIFIED · customer_specific: no
Prima dell'insert si legge GetScheduleByEntity(...), si filtra sugli stati Booked (21) / Approved / Confirmed e, per ogni occorrenza, ExistsOccurenceInRangeDates(...) verifica 5 casi di overlap (uguaglianza, sovrapposizione sinistra/destra, contenimento reciproco). In conflitto → e.Cancel = true.
Rischio: il controllo è esclusivamente lato UI. Non esiste alcun vincolo/trigger DB (cfr. BR-BOOK-010) né controllo nel path WCF (cfr. BR-BOOK-021): due sessioni concorrenti o l'app mobile possono creare prenotazioni sovrapposte. Inoltre l'enum Status in codice espone solo Booked=21/Canceled=22 (Approved/Confirmed commentati) → gli stati Approved/Confirmed usati nel filtro dipendono da id caricati altrove.
Evidenza: Schedule.ascx.cs:602-651,1248-1286; Resources/Workflow.cs:12-23.
BR-BOOK-004 — Coerenza data inizio / fine ricorrenza
Tipo: date-logic · Enforcement: UI · Conf.: VERIFIED · customer_specific: no
Con ricorrenza attiva: se e.Appointment.Start > parsedRule.Range.RecursUntil → radalert('La data inizio è maggiore della data fine ricorrenza') e e.Cancel = true.
Evidenza: Schedule.ascx.cs:607-621.
BR-BOOK-005 — Espansione ricorrenza in N occorrenze
Tipo: calculation / date-logic · Enforcement: UI/BL · Conf.: VERIFIED · customer_specific: no
La RecurrenceRule Telerik è espansa lato UI (parsedRule.Occurrences); per ogni occorrenza EndDateTime = occurrence + Duration (giorni/ore/minuti/secondi dell'appuntamento) e viene chiamata InsertScheduleWithElement. Risultato: N righe BOOK_SCHEDULE + N elementi DEM + N righe BOOK_SCHEDULE_WF_INTERFACE, tutte legate all'unica BOOK_DEMAND.
Evidenza: Schedule.ascx.cs:677-698.
BR-BOOK-006 — Creazione elemento di workflow DEM (tipo Create)
Tipo: state-transition · Enforcement: BL · Conf.: SUPPORTED · customer_specific: no
GetActivityToAddBook seleziona l'attività DEM il cui ActivityType.GUID == DEM.Constants.ActivityTypes.Create; GetElementAddedToBook esegue DEM.WorkflowCreatingActivity.Run(idActor) → crea l'elemento e ne restituisce l'id, assegnato a schedule.IdElement. Lo stato iniziale è governato dal workflow DEM (cross-schema DEM_TEST38, per analogia con FLOW-REQ-001).
Evidenza: Schedule.ascx.cs:675,686,695; Resources/Workflow.cs:150-182.
BR-BOOK-007 — Ramo Current vs Stored (PTABLE)
Tipo: config-flag · Enforcement: mixed · Conf.: VERIFIED · customer_specific: no
BookTable.Current=1 (dato operativo) / Stored=2 (storico) viaggia come parametro PTABLE. Nel path interattivo è sempre Current=1 (INSERT su BOOK_DEMAND/BOOK_SCHEDULE); con PTABLE=2 (import Excel/archiviazione) le procedure scrivono su BOOK_DEMAND_HISTORY/BOOK_SCHEDULE_HISTORY.
Evidenza: Resources/Enumeration.cs:10-14; BookDAL.cs:851,1671; USER_SOURCE BOOK_INSERTDEMAND/BOOK_INSERTSCHEDULEWITHELEMENT (rami IF PTABLE=1/2).
BR-BOOK-008 — ID_USER/ID_CDP NOT NULL non popolati nel path interattivo ⚠️ SOLO-DB + gap dati
Tipo: validation · Enforcement: DB · Conf.: VERIFIED · customer_specific: no
Vincoli DB confermati: BOOK_DEMAND.ID_USER NOT NULL (SYS_C001715692) e BOOK_DEMAND.ID_CDP NOT NULL (SYS_C001715693). La procedura BOOK_INSERTDEMAND inserisce i valori passati dal DAL (PID_USER, PID_CDP).
Rischio: nel path interattivo schedBook_AppointmentInsert il DemandDTO valorizza Username/Subject/Description/Entity/IdEntity/IdTimeType/RecurrenceRule/BookingTable ma non IdUser/IdCdp → il DAL invia il default int = 0. Il vincolo NOT NULL è soddisfatto formalmente (0 ≠ null) ma le domande interattive risultano con ID_USER=0/ID_CDP=0, dato di scarsa qualità/non tracciabile. Nel path WCF entrambi sono valorizzati esplicitamente (GetIdUserByUserId, newBooking.idCdp).
Evidenza: USER_CONSTRAINTS BOOK_DEMAND (C: "ID_USER" IS NOT NULL, "ID_CDP" IS NOT NULL); Schedule.ascx.cs:654-666 (assenza set IdUser/IdCdp); BookingReaderWCF.svc.vb:272,274; BookDAL.cs:1678-1679.
BR-BOOK-009 — Vincoli DB su BOOK_SCHEDULE
Tipo: validation · Enforcement: DB · Conf.: VERIFIED · customer_specific: no
START_DATETIME NOT NULL (SYS_C001713529), END_DATETIME NOT NULL (SYS_C001713530), PK ID_SCHEDULE, FK FK_BOOK_SCHEDULE_BOOK_DEMAND (verso BOOK_DEMAND), FK FK_BOOK_SCHEDULE_BOOK_TIME_TYP, FK FK_BOOK_SCHEDULE_ID_ASSOCIATION. Un'occorrenza non può quindi esistere senza domanda padre e senza date. Analogamente BOOK_DEMAND ha FK FK_BOOK_DEMAND_BOOK_TIME_TYPE → IdTimeType deve esistere in BOOK_TIME_TYPE (nota: il path WCF forza IdTimeType=0, cfr. BR-BOOK-021).
Evidenza: USER_CONSTRAINTS BOOK_SCHEDULE / BOOK_DEMAND.
BR-BOOK-010 — Nessun vincolo DB anti-doppia-prenotazione ⚠️ GAP
Tipo: validation · Enforcement: DB (assenza) · Conf.: VERIFIED · customer_specific: no
Lookup su USER_TRIGGERS per BOOK_DEMAND/BOOK_SCHEDULE/BOOK_SCHEDULE_WF_INTERFACE/relative history: nessun trigger. USER_CONSTRAINTS: nessun UNIQUE su (entità, intervallo). L'unica difesa contro le sovrapposizioni è la UI (BR-BOOK-003), bypassabile da concorrenza o dal path WCF.
Evidenza: USER_TRIGGERS (NONE); USER_CONSTRAINTS BOOK_SCHEDULE (solo PK + FK, nessun UNIQUE temporale).
BR-BOOK-011 — Assenza di transazione applicativa unica ⚠️
Tipo: state-transition · Enforcement: BL/DB · Conf.: INFERRED · customer_specific: no
InsertDemand e InsertScheduleWithElement sono chiamate Remoting/procedure autonome distinte. BOOK_INSERTDEMAND non ha protezione transazionale; solo BOOK_INSERTSCHEDULEWITHELEMENT ha SAVEPOINT start_tran + EXCEPTION WHEN OTHERS THEN ROLLBACK TO start_tran; RAISE (rollback della singola occorrenza). Un errore in fase di occorrenza lascia persistita la BOOK_DEMAND già inserita → domanda senza occorrenze. Dedotto dalla struttura, non testato a runtime.
Evidenza: Schedule.ascx.cs:666,688,697; USER_SOURCE BOOK_INSERTSCHEDULEWITHELEMENT (SAVEPOINT/ROLLBACK TO start_tran); USER_SOURCE BOOK_INSERTDEMAND (INSERT diretto, no savepoint).
BR-BOOK-012 — Permesso di creazione prenotazione
Tipo: permission · Enforcement: UI · Conf.: VERIFIED · customer_specific: no
Il form di inserimento è consentito solo se CanAll || (CanBook && IsCurrentTable); altrimenti schedBook_FormCreating annulla (e.Cancel = true). Le voci di toolbar/menu ("Prenotazione", import Excel, "Amministrazione") seguono la stessa logica (Amministrazione richiede CanAll). IsCurrentTable = tabella Current.
Evidenza: Schedule.ascx.cs:388-392,1046-1054; IsCurrentTable def. :58.
BR-BOOK-013 — Edit limitato agli appuntamenti in stato Booked
Tipo: permission · Enforcement: UI · Conf.: VERIFIED · customer_specific: no
Se l'utente non ha CanAll, non sta aggiungendo occorrenze, e lo stato dell'appuntamento ≠ Booked (21) → il form è annullato.
Evidenza: Schedule.ascx.cs:393-397.
BR-BOOK-014 — Serie con occorrenze approvate/confermate non modificabile
Tipo: validation · Enforcement: UI · Conf.: VERIFIED · customer_specific: no
In AdvancedEdit di una serie (IsUpdateSeriesToBooking), se esistono occorrenze in stato Approved/Confirmed → radalert('Non è possibile modificare una serie con occorrenze approvate o confermate!') e e.Cancel = true.
Evidenza: Schedule.ascx.cs:398-414.
BR-BOOK-015 — Cancellazione domanda: annulla solo elementi Booked
Tipo: state-transition · Enforcement: BL/UI · Conf.: VERIFIED · customer_specific: no
DeleteDemand scorre gli elementi della domanda ed esegue ChangeActivityState(..., Activity.Cancel (22)) solo per quelli in stato Booked (21); gli elementi in altri stati non vengono annullati.
Evidenza: Schedule.ascx.cs:857-872; Resources/Workflow.cs:103-114 (ChangeActivityState).
BR-BOOK-016 — Update di serie ricorrente = delete + re-insert
Tipo: state-transition · Enforcement: UI/BL · Conf.: VERIFIED · customer_specific: no
Nell'update, se non si sta aggiungendo un'occorrenza (!IsAddOccurenceToBooking) e c'è ricorrenza / serie: DeleteScheduleByIdDemand(..., Status.Booked) elimina le occorrenze Booked e poi le occorrenze vengono re-inserite con nuovo elemento DEM. Le occorrenze non-Booked (approvate/confermate) sopravvivono.
Evidenza: Schedule.ascx.cs:801-848.
BR-BOOK-017 — Visibilità dei bottoni di cambio stato
Tipo: permission · Enforcement: UI · Conf.: VERIFIED · customer_specific: no
In schedBook_AppointmentCreated, i bottoni di transizione sono generati solo se l'utente ha almeno uno tra CanAll/CanConfirm/CanApprove/CanBook (in tabella Current) e, per ciascuna attività, solo se l'attore è presente in CanGroup, lo stato-successivo ≠ "0" e diverso dallo stato corrente.
Evidenza: Schedule.ascx.cs:888-917.
BR-BOOK-018 — Notifica mail approvazione "consumi" (customer-specific)
Tipo: customer-specific · Enforcement: BL · Conf.: VERIFIED · customer_specific: sì
All'attività Approve, NotifyApproved invia mail agli indirizzi degli attori Actor.Confirm con corpo "...sono stati approvati i seguenti consumi...". La terminologia ("edificio", "consumi", "ore approvate") indica un adattamento del modulo prenotazioni a un contesto di gestione consumi/energia specifico del cliente, non generico "sala/postazione".
Evidenza: Schedule.ascx.cs:212-250 (NotifyApproved, body "consumi").
BR-BOOK-019 — Alert soglia comfort (customer-specific, config-flag)
Tipo: customer-specific / config-flag · Enforcement: BL · Conf.: VERIFIED · customer_specific: sì
NotifyAlert è eseguito solo se IfConfortClassExists() è vero (feature-flag di configurazione). Calcola totHours = TotHoursByEntity(..., Status.Approved) per l'entità, recupera MinHours/MaxHours dalla ConfortClass e le percentuali GetConfortAlert() ([minPerc, maxPerc]); invia alert se totHours >= (maxPerc/100)*maxHours (soglia massima) oppure totHours <= minHours + (minPerc/100)*minHours (soglia minima). Logica di capacità/soglia specifica del dominio comfort/energia.
Evidenza: Schedule.ascx.cs:215-216,252-309; BookControllers/BookController.cs:102-104,126-128 (GetConfortAlert, IfConfortClassExists).
BR-BOOK-020 — Id di stato/attività e GUID workflow hard-coded ⚠️ MAGIC NUMBERS
Tipo: config-flag · Enforcement: UI/BL · Conf.: VERIFIED · customer_specific: no
Id hard-coded in Resources.Workflow: Status.Booked=21, Canceled=22; Activity.Book=21, Cancel=22, Update=26, View=29, Overwrite=32, Add=35, Delete=38. GUID di fallback del workflow hard-coded e1db8d10-7319-44f2-a994-11f01ee9e2bb usato quando BookWorkFlows.Count != 1. Enum Actor (Book=22221 … Confirm=22223) e i riferimenti Actor.Confirm sono in parte commentati/deprecati (GetActorListByCan/SetPermissionToWorkflow risultano richiamati ma con definizione commentata nel sorgente ispezionato → possibile disallineamento del codice). Questi id devono corrispondere ai record del catalogo DEM/BOOK sul DB: cambiando ambiente/cliente il rischio è di mismatch silenzioso.
Evidenza: Resources/Workflow.cs:12-39,51-58,42-48; riferimenti Schedule.ascx.cs:224,671; BookingReaderWCF.svc.vb:248-254 (stesso set di id attività).
BR-BOOK-021 — Path WCF: attività Create obbligatoria, anti-overlap bypassato ⚠️
Tipo: validation / permission · Enforcement: BL · Conf.: VERIFIED · customer_specific: no
CreateNewBooking (mobile/integrazione) accetta la creazione solo se activity.ActivityType.GUID == ActivityTypes.Create, altrimenti "IDACTIVITY specificato non corrisponde ad una azione di Creazione". Condivide lo stesso BL (InsertDemand + InsertScheduleWithElement) ma non esegue né la validazione entità/orario né l'anti-sovrapposizione (BR-BOOK-001/002/003) e forza valori fissi: IdTimeType=0, RecurrenceRule="", idServicelist={-1}. Quindi le regole di sovrapposizione e di tipo orario valgono solo per il path UI.
Evidenza: BookingReaderWCF.svc.vb:258-313 (guardia Create :266, valori fissi :279-281,290).
BR-BOOK-022 — Finestra calendario di default
Tipo: date-logic · Enforcement: UI · Conf.: VERIFIED · customer_specific: no
Il range di caricamento del calendario è impostato a FromDate = SelectedDate.AddDays(-8) e ToDate = SelectedDate.AddDays(38) (magic numbers: 8 giorni indietro, 38 avanti dal primo del mese selezionato). Influenza quali prenotazioni sono visibili e quindi il set su cui opera l'anti-sovrapposizione UI.
Evidenza: Schedule.ascx.cs:190-192.
Sintesi rischi (flag)
- Solo-UI (bypassabili): BR-BOOK-001, 002, 003 (anti-overlap), 004, 012, 013, 014, 017.
- Solo-DB (invisibili alla UI): BR-BOOK-008 (NOT NULL ID_USER/ID_CDP), BR-BOOK-009 (NOT NULL date + FK).
- Gap di vincolo DB: BR-BOOK-010 (nessun trigger/unique anti-doppia-prenotazione).
- Transazionalità: BR-BOOK-011 (domanda orfana possibile).
- Magic numbers / hard-coded id: BR-BOOK-020 (stati/attività/GUID), BR-BOOK-022 (finestra ±8/38 gg), BR-BOOK-021 (
IdTimeType=0, service-1). - Date-sensitive: BR-BOOK-004, 005, 022.
- Customer-specific: BR-BOOK-018 (notifica consumi), BR-BOOK-019 (soglie comfort/energia).
- Bypass di path: BR-BOOK-021 (WCF salta validazioni e anti-overlap del path UI).
Open questions
- Da dove provengono a runtime gli id di stato
Approved/Confirmedusati nel filtro anti-sovrapposizione, dato che l'enumStatusinWorkflow.csli ha commentati (soloBooked=21/Canceled=22)? Possibile disallineamento tra sorgente ispezionato e binario in esercizio. GetActorListByCan/SetPermissionToWorkflowsono richiamati (Schedule.ascx.cs:671,1030) ma definiti solo in forma commentata nelWorkflow.csispezionato: verificare l'esistenza di una definizione attiva (partial/altro file) o confermare che si tratta di uno snapshot non compilabile.- Comportamento reale di cleanup della
BOOK_DEMANDin caso di fallimento diInsertScheduleWithElement(BR-BOOK-011), da verificare a runtime. - Le tabelle DEM esatte toccate da
WorkflowCreatingActivity.Runin questo flusso sono assunte per analogia con FLOW-REQ-001 (BR-BOOK-006).