"""software catalog: allow_self_cancel policy flag. Adds ``asset_software_catalog.allow_self_cancel`` (BOOLEAN, default TRUE). Background: every active software seat could be self-cancelled by its holder from the self-service portal / MCP. That is wrong for IT-managed, identity-tier software (Google Workspace, Feishu, …): the account is provisioned and governed by IT and tied to SSO, so an employee must not be able to drop their own seat. This flag lets an admin mark such software ``allow_self_cancel=FALSE`` — the self path is blocked while admin-on-behalf cancellation still works. Default TRUE preserves today's behaviour for every existing row; admins opt specific managed software OUT. Revision ID: 361 Revises: 360 """ from __future__ import annotations import sqlalchemy as sa from alembic import op revision = "361" down_revision = "360" branch_labels = None depends_on = None TABLE = "asset_software_catalog" COLUMN = "allow_self_cancel" def _column_exists(bind, table: str, column: str) -> bool: return bind.execute( sa.text( "SELECT 1 FROM information_schema.columns " "WHERE table_schema = DATABASE() AND table_name = :t " "AND column_name = :c" ), {"t": table, "c": column}, ).scalar() is not None def upgrade() -> None: bind = op.get_bind() if not _column_exists(bind, TABLE, COLUMN): op.add_column( TABLE, sa.Column( COLUMN, sa.Boolean(), nullable=False, server_default=sa.text("1"), ), ) def downgrade() -> None: bind = op.get_bind() if _column_exists(bind, TABLE, COLUMN): op.drop_column(TABLE, COLUMN)