2026-08-21 13:55:11 +02:00
|
|
|
"""Reports: folder_id column for folder-based sorting
|
|
|
|
|
|
|
|
|
|
Revision ID: 0139
|
|
|
|
|
Revises: 0138
|
|
|
|
|
Create Date: 2026-08-21
|
|
|
|
|
|
|
|
|
|
Adds folder_id to report_templates for folder-based organization.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from alembic import op
|
|
|
|
|
import sqlalchemy as sa
|
|
|
|
|
from sqlalchemy.dialects.postgresql import UUID as PGUUID
|
|
|
|
|
|
|
|
|
|
# revision identifiers, used by Alembic.
|
|
|
|
|
revision = "0139"
|
|
|
|
|
down_revision = "0138"
|
|
|
|
|
branch_labels = None
|
|
|
|
|
depends_on = None
|
|
|
|
|
|
|
|
|
|
|
2026-08-23 21:36:56 +02:00
|
|
|
def _table_exists(conn, table_name: str) -> bool:
|
|
|
|
|
"""True when the table exists (dual-path convergence, Gate B).
|
|
|
|
|
|
|
|
|
|
On a fresh install the report_generator plugin SQL migration has not
|
|
|
|
|
run yet when Alembic reaches this revision — skip instead of failing.
|
|
|
|
|
The plugin-side convergence migration adds the same column.
|
|
|
|
|
"""
|
|
|
|
|
row = conn.execute(
|
|
|
|
|
sa.text("SELECT to_regclass(:tname) IS NOT NULL"),
|
|
|
|
|
{"tname": f"public.{table_name}"},
|
|
|
|
|
).scalar()
|
|
|
|
|
return bool(row)
|
|
|
|
|
|
|
|
|
|
|
2026-08-21 13:55:11 +02:00
|
|
|
def upgrade() -> None:
|
2026-08-23 21:36:56 +02:00
|
|
|
conn = op.get_bind()
|
|
|
|
|
if not _table_exists(conn, "report_templates"):
|
|
|
|
|
return
|
2026-08-21 13:55:11 +02:00
|
|
|
op.add_column("report_templates", sa.Column("folder_id", PGUUID(as_uuid=True), nullable=True))
|
|
|
|
|
op.create_index("ix_report_templates_folder", "report_templates", ["folder_id"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def downgrade() -> None:
|
2026-08-23 21:36:56 +02:00
|
|
|
conn = op.get_bind()
|
|
|
|
|
if not _table_exists(conn, "report_templates"):
|
|
|
|
|
return
|
2026-08-21 13:55:11 +02:00
|
|
|
op.drop_index("ix_report_templates_folder", table_name="report_templates")
|
|
|
|
|
op.drop_column("report_templates", "folder_id")
|