generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } enum StatoMateriale { proposto approvato rifiutato @@map("stato_materiale") } enum StatoMagazzinoVoce { buono da_riparare mancante @@map("stato_magazzino_voce") } enum StatoLista { bozza privato gruppo pubblico @@map("stato_lista") } enum StatoModerazioneLista { proposto approvato rifiutato @@map("stato_moderazione_lista") } enum StatoCategoria { confermata da_approvare @@map("stato_categoria") } enum TipoNotifica { MATERIALE_PROPOSTO CATEGORIA_PROPOSTA TIPO_EVENTO_PROPOSTO LISTA_PROPOSTA @@map("tipo_notifica") } // Coda di notifiche di moderazione (nuove proposte di materiale/categoria/tipo // evento/lista in attesa di revisione): visibile solo a chi ha ruolo 'moderatore', // non ci sono ancora notifiche personali nel dominio magazzino. model Notifica { id Int @id @default(autoincrement()) tipo TipoNotifica messaggio String link String? letta Boolean @default(false) dataCreazione DateTime @default(now()) @map("data_creazione") @@map("notifica") } // Tassonomia delle categorie di materiale, gestita dal moderatore (CRUD diretto, // sempre creata "confermata") con un flusso di proposta aperto a qualsiasi org // autenticata (crea in "da_approvare", poi approvata/rifiutata dal moderatore). // Materiale.categoria resta per ora un campo stringa libero, non collegato a questa // tabella: introdurla come FK è un passo successivo, fuori scope qui. model Categoria { id String @id @default(uuid()) nome String stato StatoCategoria @default(confermata) creatoDaOrgId String? @map("creato_da_org_id") creatoIl DateTime @default(now()) @map("creato_il") @@map("categoria") } model Materiale { id String @id @default(uuid()) nome String categoria String unitaMisura String @map("unita_misura") stato StatoMateriale @default(proposto) propostoDaOrgId String @map("proposto_da_org_id") creatoIl DateTime @default(now()) @map("creato_il") listaVoci ListaVoce[] magazzinoVoci MagazzinoVoce[] eventoCheck EventoCheck[] @@map("materiale") } model TipoEvento { id String @id @default(uuid()) nome String // Stessa semantica/enum di Categoria.stato: un tipo evento creato dal moderatore // nasce 'confermata', uno proposto da un'organizzazione nasce 'da_approvare'. stato StatoCategoria @default(confermata) creatoDaOrgId String? @map("creato_da_org_id") creatoIl DateTime @default(now()) @map("creato_il") liste Lista[] @@map("tipo_evento") } model Lista { id String @id @default(uuid()) nome String // Valorizzato solo per le liste in stato 'gruppo' (condivise con l'organizzazione): // le liste 'bozza'/'privato'/'pubblico' sono personali e non hanno alcuna org, anche // per un utente che ha un'organizzazione attiva sul token al momento della creazione. orgId String? @map("org_id") // Nullable perché le liste create prima di questo campo non hanno un autore noto; // da qui in poi è sempre valorizzato da req.auth.userId alla creazione. creataDaUserId String? @map("creata_da_user_id") stato StatoLista @default(bozza) creataIl DateTime @default(now()) @map("creata_il") // Moderazione, ortogonale a `stato`: nullo per bozza/privato/gruppo, valorizzato // solo quando stato = pubblico (proposto/approvato/rifiutato, vedi liste.service.ts). statoModerazione StatoModerazioneLista? @map("stato_moderazione") // Ex campo obbligatorio di ListaModello, ora opzionale su tutte le liste: serve solo // a filtrare il catalogo pubblico per tipo evento. tipoEventoId String? @map("tipo_evento_id") tipoEvento TipoEvento? @relation(fields: [tipoEventoId], references: [id]) // Lineage self-referenziato: da quale lista pubblica approvata è stata forkata questa // (fork = "usa come base"). Best-effort/storico: cancellare l'origine non deve // bloccare né trascinare i figli, quindi SetNull. parentId String? @map("parent_id") parent Lista? @relation("ListaParent", fields: [parentId], references: [id], onDelete: SetNull) figlie Lista[] @relation("ListaParent") voci ListaVoce[] eventi Evento[] // Sotto-liste agganciate a questa lista personale: un solo livello di nesting // (una sotto-lista non può avere a sua volta sotto-liste), validato in liste.service.ts. sottoListe ListaSottoLista[] @relation("SottoListePadre") usataComeSottoListaIn ListaSottoLista[] @relation("SottoListaFiglia") @@index([stato, statoModerazione]) @@index([tipoEventoId]) @@index([parentId]) @@map("lista") } model ListaVoce { listaId String @map("lista_id") materialeId String @map("materiale_id") quantita Int lista Lista @relation(fields: [listaId], references: [id]) materiale Materiale @relation(fields: [materialeId], references: [id]) @@id([listaId, materialeId]) @@map("lista_voce") } model ListaSottoLista { listaId String @map("lista_id") sottoListaId String @map("sotto_lista_id") lista Lista @relation("SottoListePadre", fields: [listaId], references: [id]) sottoLista Lista @relation("SottoListaFiglia", fields: [sottoListaId], references: [id]) @@id([listaId, sottoListaId]) @@map("lista_sotto_lista") } model GruppoMagazzino { id String @id @default(uuid()) orgId String @map("org_id") nome String // Un solo livello di nesting (uno scaffale contiene kit, un kit non contiene // a sua volta altri gruppi), validato in gruppiMagazzino.service.ts. parentId String? @map("parent_id") creatoIl DateTime @default(now()) @map("creato_il") parent GruppoMagazzino? @relation("GruppoMagazzinoParent", fields: [parentId], references: [id], onDelete: SetNull) figli GruppoMagazzino[] @relation("GruppoMagazzinoParent") voci MagazzinoVoce[] @@index([orgId]) @@index([parentId]) @@map("gruppo_magazzino") } model MagazzinoVoce { id String @id @default(uuid()) orgId String @map("org_id") materialeId String @map("materiale_id") quantitaPosseduta Int @map("quantita_posseduta") stato StatoMagazzinoVoce posizione String? note String? // Nullo finché la giacenza non viene organizzata in un gruppo (scaffale/kit): // eliminare il gruppo non elimina la voce, la riporta solo tra le "non organizzate". gruppoId String? @map("gruppo_id") materiale Materiale @relation(fields: [materialeId], references: [id]) gruppo GruppoMagazzino? @relation(fields: [gruppoId], references: [id], onDelete: SetNull) @@index([gruppoId]) @@map("magazzino_voce") } model Evento { id String @id @default(uuid()) orgId String @map("org_id") nome String listaId String @map("lista_id") data DateTime lista Lista @relation(fields: [listaId], references: [id]) check EventoCheck[] @@map("evento") } model EventoCheck { eventoId String @map("evento_id") materialeId String @map("materiale_id") portato Boolean @default(false) note String? evento Evento @relation(fields: [eventoId], references: [id]) materiale Materiale @relation(fields: [materialeId], references: [id]) @@id([eventoId, materialeId]) @@map("evento_check") }