""" 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")