Fascicoli+Tassonomia+permessi

This commit is contained in:
2026-06-17 21:47:46 +02:00
parent e31676d22e
commit 3fd3c72f06
42 changed files with 4554 additions and 99 deletions
@@ -0,0 +1,78 @@
"""
Migrazione 0017: Tassonomia di Classificazione Multi-livello (Feature N2).
Estende la tabella `labels` con:
- parent_id: FK self-referenziale (nullable) per struttura ad albero
- description: testo descrittivo opzionale
Struttura tassonomica:
Livello 0 (radice) = Ambito (Area Aziendale) — parent_id IS NULL
Livello 1 = Processo — parent_id = ID Ambito
Livello 2 (foglia) = Classificazione — parent_id = ID Processo
I vincoli di unicità vengono sostituiti con indici parziali per supportare
nomi identici a diversi livelli dell'albero.
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
revision = "0017"
down_revision = "0016"
branch_labels = None
depends_on = None
def upgrade() -> None:
# ── Aggiunge parent_id (FK self-referenziale nullable) ─────────────────────
op.add_column(
"labels",
sa.Column(
"parent_id",
postgresql.UUID(as_uuid=True),
nullable=True,
),
)
op.create_foreign_key(
"fk_labels_parent",
"labels",
"labels",
["parent_id"],
["id"],
ondelete="CASCADE",
)
# ── Aggiunge description ────────────────────────────────────────────────────
op.add_column(
"labels",
sa.Column("description", sa.Text(), nullable=True),
)
# ── Indice su parent_id per query gerarchiche ───────────────────────────────
op.create_index("idx_labels_parent", "labels", ["parent_id"])
# ── Sostituisce il vincolo di unicità con indici parziali ───────────────────
# Il vecchio vincolo (tenant_id, name) non supporta nomi uguali a livelli diversi
op.drop_constraint("uq_label_name_tenant", "labels")
# Nodi radice: nome unico per tenant (parent_id IS NULL)
op.execute(
"CREATE UNIQUE INDEX uq_label_name_root "
"ON labels (tenant_id, name) WHERE parent_id IS NULL"
)
# Nodi non-radice: nome unico per (tenant, parent) — stesso nome ammesso sotto parent diversi
op.execute(
"CREATE UNIQUE INDEX uq_label_name_parent "
"ON labels (tenant_id, name, parent_id) WHERE parent_id IS NOT NULL"
)
def downgrade() -> None:
op.execute("DROP INDEX IF EXISTS uq_label_name_parent")
op.execute("DROP INDEX IF EXISTS uq_label_name_root")
op.create_unique_constraint("uq_label_name_tenant", "labels", ["tenant_id", "name"])
op.drop_index("idx_labels_parent", table_name="labels")
op.drop_constraint("fk_labels_parent", "labels", type_="foreignkey")
op.drop_column("labels", "description")
op.drop_column("labels", "parent_id")