diff --git a/alembic/versions/20260806_client_related_person_scope_columns.py b/alembic/versions/20260806_client_related_person_scope_columns.py new file mode 100644 index 0000000..34035c1 --- /dev/null +++ b/alembic/versions/20260806_client_related_person_scope_columns.py @@ -0,0 +1,108 @@ +"""Add Business Unit and Client Branch scope to client related persons. + +This migration is intentionally defensive. It adds only columns, foreign keys, +and indexes that are missing, so it is safe for databases where part of the +change may already exist. +""" + +from alembic import op +import sqlalchemy as sa + + +revision = "20260806_related_person_scope" +down_revision = "20260805_scope_aware_services" +branch_labels = None +depends_on = None + + +TABLE_NAME = "client_related_persons" + + +def _column_names(inspector) -> set[str]: + return {column["name"] for column in inspector.get_columns(TABLE_NAME)} + + +def _index_names(inspector) -> set[str]: + return {index["name"] for index in inspector.get_indexes(TABLE_NAME) if index.get("name")} + + +def _foreign_key_names(inspector) -> set[str]: + return { + foreign_key["name"] + for foreign_key in inspector.get_foreign_keys(TABLE_NAME) + if foreign_key.get("name") + } + + +def upgrade(): + bind = op.get_bind() + inspector = sa.inspect(bind) + + columns = _column_names(inspector) + + with op.batch_alter_table(TABLE_NAME) as batch: + if "business_unit_id" not in columns: + batch.add_column(sa.Column("business_unit_id", sa.Integer(), nullable=True)) + if "client_branch_id" not in columns: + batch.add_column(sa.Column("client_branch_id", sa.Integer(), nullable=True)) + + inspector = sa.inspect(bind) + foreign_keys = _foreign_key_names(inspector) + indexes = _index_names(inspector) + + with op.batch_alter_table(TABLE_NAME) as batch: + if "fk_client_related_persons_business_unit" not in foreign_keys: + batch.create_foreign_key( + "fk_client_related_persons_business_unit", + "client_business_units", + ["business_unit_id"], + ["id"], + ondelete="SET NULL", + ) + if "fk_client_related_persons_client_branch" not in foreign_keys: + batch.create_foreign_key( + "fk_client_related_persons_client_branch", + "client_branches", + ["client_branch_id"], + ["id"], + ondelete="SET NULL", + ) + if "ix_client_related_persons_business_unit_id" not in indexes: + batch.create_index( + "ix_client_related_persons_business_unit_id", + ["business_unit_id"], + ) + if "ix_client_related_persons_client_branch_id" not in indexes: + batch.create_index( + "ix_client_related_persons_client_branch_id", + ["client_branch_id"], + ) + + +def downgrade(): + bind = op.get_bind() + inspector = sa.inspect(bind) + + columns = _column_names(inspector) + indexes = _index_names(inspector) + foreign_keys = _foreign_key_names(inspector) + + with op.batch_alter_table(TABLE_NAME) as batch: + if "ix_client_related_persons_client_branch_id" in indexes: + batch.drop_index("ix_client_related_persons_client_branch_id") + if "ix_client_related_persons_business_unit_id" in indexes: + batch.drop_index("ix_client_related_persons_business_unit_id") + if "fk_client_related_persons_client_branch" in foreign_keys: + batch.drop_constraint( + "fk_client_related_persons_client_branch", + type_="foreignkey", + ) + if "fk_client_related_persons_business_unit" in foreign_keys: + batch.drop_constraint( + "fk_client_related_persons_business_unit", + type_="foreignkey", + ) + if "client_branch_id" in columns: + batch.drop_column("client_branch_id") + if "business_unit_id" in columns: + batch.drop_column("business_unit_id")