"""create_internal_tickets Revision ID: a1e6a018af02 Revises: ff6fe5895ea2 Create Date: 2026-05-28 16:29:32.624317 """ from typing import Sequence, Union from alembic import op import sqlalchemy as sa from sqlalchemy.dialects import postgresql # revision identifiers, used by Alembic. revision: str = 'a1e6a018af02' down_revision: Union[str, None] = 'ff6fe5895ea2' branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None _NULL_UUID = "00000000-0000-0000-0000-000000000000" _CURRENT_ACCOUNT = ( f"COALESCE(NULLIF(current_setting('app.current_account_id', TRUE), ''), " f"'{_NULL_UUID}')::uuid" ) def upgrade() -> None: op.create_table( 'internal_tickets', sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), sa.Column('account_id', postgresql.UUID(as_uuid=True), nullable=False), sa.Column('created_by_user_id', postgresql.UUID(as_uuid=True), nullable=False), sa.Column('customer_name', sa.String(120), nullable=True), sa.Column('customer_contact', sa.String(200), nullable=True), sa.Column('problem_statement', sa.Text(), nullable=False), sa.Column('status', sa.String(30), nullable=False, server_default='open'), sa.Column('flow_id', postgresql.UUID(as_uuid=True), nullable=True), sa.Column('flow_proposal_id', postgresql.UUID(as_uuid=True), nullable=True), sa.Column('ai_session_id', postgresql.UUID(as_uuid=True), nullable=True), sa.Column('assigned_user_id', postgresql.UUID(as_uuid=True), nullable=True), sa.Column('resolution_notes', sa.Text(), nullable=True), sa.Column('psa_promoted_ticket_id', sa.String(64), nullable=True), sa.Column('created_at', sa.DateTime(timezone=True), nullable=False, server_default=sa.text('now()')), sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False, server_default=sa.text('now()')), sa.Column('resolved_at', sa.DateTime(timezone=True), nullable=True), sa.PrimaryKeyConstraint('id'), sa.ForeignKeyConstraint(['account_id'], ['accounts.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['created_by_user_id'], ['users.id'], ondelete='RESTRICT'), sa.ForeignKeyConstraint(['flow_id'], ['trees.id'], ondelete='SET NULL'), sa.ForeignKeyConstraint(['flow_proposal_id'], ['flow_proposals.id'], ondelete='SET NULL'), sa.ForeignKeyConstraint(['ai_session_id'], ['ai_sessions.id'], ondelete='SET NULL'), sa.ForeignKeyConstraint(['assigned_user_id'], ['users.id'], ondelete='SET NULL'), sa.CheckConstraint( "status IN ('open', 'walking', 'resolved', 'escalated')", name='ck_internal_tickets_status', ), ) op.create_index('ix_internal_tickets_account_id', 'internal_tickets', ['account_id']) op.create_index('ix_internal_tickets_status', 'internal_tickets', ['status']) op.create_index('ix_internal_tickets_assigned_user_id', 'internal_tickets', ['assigned_user_id']) op.execute("ALTER TABLE internal_tickets ENABLE ROW LEVEL SECURITY") op.execute("ALTER TABLE internal_tickets FORCE ROW LEVEL SECURITY") op.execute(f""" CREATE POLICY tenant_isolation ON internal_tickets USING (account_id = {_CURRENT_ACCOUNT}) WITH CHECK (account_id = {_CURRENT_ACCOUNT}) """) def downgrade() -> None: op.execute("DROP POLICY IF EXISTS tenant_isolation ON internal_tickets") op.execute("ALTER TABLE internal_tickets DISABLE ROW LEVEL SECURITY") op.execute("ALTER TABLE internal_tickets NO FORCE ROW LEVEL SECURITY") op.drop_index('ix_internal_tickets_assigned_user_id', 'internal_tickets') op.drop_index('ix_internal_tickets_status', 'internal_tickets') op.drop_index('ix_internal_tickets_account_id', 'internal_tickets') op.drop_table('internal_tickets')