"""add recurring engagement period support Revision ID: 20260805_periodic_engagements Revises: 20260805_performing_partner """ from alembic import op import sqlalchemy as sa from sqlalchemy import inspect revision = "20260805_periodic_engagements" down_revision = "20260805_performing_partner" branch_labels = None depends_on = None TABLE = "client_service_subscriptions" OLD_UQ = "uq_client_service_subscription_tenant_client_service_year" NEW_UQ = "uq_css_tenant_client_service_fy_period" INDEX = "ix_client_service_subscriptions_period_label" def _inspector(): return inspect(op.get_bind()) def _columns() -> set[str]: return {row["name"] for row in _inspector().get_columns(TABLE)} def _unique_constraints() -> set[str]: return {row.get("name") for row in _inspector().get_unique_constraints(TABLE) if row.get("name")} def _indexes() -> set[str]: return {row.get("name") for row in _inspector().get_indexes(TABLE) if row.get("name")} def upgrade(): if "period_label" not in _columns(): op.add_column(TABLE, sa.Column("period_label", sa.String(length=30), nullable=False, server_default="")) op.execute("UPDATE client_service_subscriptions SET period_label = '' WHERE period_label IS NULL") uniques = _unique_constraints() dialect = op.get_bind().dialect.name if dialect == "sqlite": with op.batch_alter_table(TABLE, recreate="always") as batch: if OLD_UQ in uniques: batch.drop_constraint(OLD_UQ, type_="unique") if NEW_UQ not in uniques: batch.create_unique_constraint( NEW_UQ, ["tenant_id", "client_id", "service_catalogue_id", "financial_year", "period_label"], ) else: if OLD_UQ in uniques: op.drop_constraint(OLD_UQ, TABLE, type_="unique") if NEW_UQ not in _unique_constraints(): op.create_unique_constraint( NEW_UQ, TABLE, ["tenant_id", "client_id", "service_catalogue_id", "financial_year", "period_label"], ) if INDEX not in _indexes(): op.create_index(INDEX, TABLE, ["period_label"], unique=False) def downgrade(): if INDEX in _indexes(): op.drop_index(INDEX, table_name=TABLE) uniques = _unique_constraints() dialect = op.get_bind().dialect.name if dialect == "sqlite": with op.batch_alter_table(TABLE, recreate="always") as batch: if NEW_UQ in uniques: batch.drop_constraint(NEW_UQ, type_="unique") if OLD_UQ not in uniques: batch.create_unique_constraint( OLD_UQ, ["tenant_id", "client_id", "service_catalogue_id", "financial_year"], ) if "period_label" in _columns(): batch.drop_column("period_label") else: if NEW_UQ in uniques: op.drop_constraint(NEW_UQ, TABLE, type_="unique") if OLD_UQ not in _unique_constraints(): op.create_unique_constraint( OLD_UQ, TABLE, ["tenant_id", "client_id", "service_catalogue_id", "financial_year"], ) if "period_label" in _columns(): op.drop_column(TABLE, "period_label")