"""phase33 — contract_notification_recipients table Per-contract additional notification recipients (users or free-form emails) with a per-recipient event_types subscription list. Notified in addition to the global NotificationMatrix routing, scoped to the contract's facilities. Uses INFORMATION_SCHEMA table-existence check — safe to re-run. """ revision = 'phase33_contract_recipients' down_revision = 'phase32_device_token_columns' branch_labels = None depends_on = None from alembic import op import sqlalchemy as sa def _table_exists(conn, table): result = conn.execute(sa.text( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES " "WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = :t" ), {"t": table}) return result.scalar() > 0 def upgrade(): bind = op.get_bind() if _table_exists(bind, 'contract_notification_recipients'): return op.create_table( 'contract_notification_recipients', sa.Column('id', sa.Integer, primary_key=True), sa.Column('project_id', sa.Integer, sa.ForeignKey('projects.id', ondelete='CASCADE'), nullable=False), sa.Column('user_id', sa.Integer, sa.ForeignKey('users.id', ondelete='CASCADE'), nullable=True), sa.Column('email', sa.String(200), nullable=True), sa.Column('event_types', sa.Text, nullable=True), sa.Column('created_at', sa.DateTime, nullable=False), ) op.create_index('ix_cnr_project_id', 'contract_notification_recipients', ['project_id']) op.create_index('ix_cnr_user_id', 'contract_notification_recipients', ['user_id']) def downgrade(): bind = op.get_bind() if _table_exists(bind, 'contract_notification_recipients'): op.drop_table('contract_notification_recipients')