"""Finance saved explorer views. Adds ``finance_saved_views`` — a finance user's persisted spend-explorer configuration (group_by + filters + range), optionally shared with all finance viewers. No FK on ``owner_person_id`` (plain String(36)), same TiDB CHAR/VARCHAR FK avoidance as the rest of the finance domain. Idempotent: guarded on table-absence. """ from __future__ import annotations import sqlalchemy as sa from alembic import op revision = "295" down_revision = "294" branch_labels = None depends_on = None def _table_exists(bind, table: str) -> bool: row = bind.execute( sa.text( "SELECT 1 FROM information_schema.TABLES " "WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = :t" ), {"t": table}, ).fetchone() return row is not None def upgrade() -> None: bind = op.get_bind() if _table_exists(bind, "finance_saved_views"): return op.create_table( "finance_saved_views", sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False), sa.Column("owner_person_id", sa.String(length=36), nullable=False), sa.Column("name", sa.String(length=128), nullable=False), sa.Column("config", sa.JSON(), nullable=False), sa.Column("is_shared", sa.Boolean(), nullable=False, server_default="0"), sa.Column( "created_at", sa.DateTime(timezone=True), server_default=sa.text("CURRENT_TIMESTAMP"), nullable=False, ), sa.Column( "updated_at", sa.DateTime(timezone=True), server_default=sa.text("CURRENT_TIMESTAMP"), nullable=False, ), sa.PrimaryKeyConstraint("id"), ) op.create_index( "ix_fin_saved_views_owner", "finance_saved_views", ["owner_person_id"], ) def downgrade() -> None: bind = op.get_bind() if _table_exists(bind, "finance_saved_views"): op.drop_table("finance_saved_views")