246 lines
7.6 KiB
Plaintext
246 lines
7.6 KiB
Plaintext
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")
|
|
}
|