"""operation: валюта, расчётная сумма, связь со строкой файла; типы UWFEE, ADJ Revision ID: 7c2e5a9d1f30 Revises: 3b9f1c2a7d41 Create Date: 2026-10-19 00:00:00 Запись операций из QUIK и ручной ввод (D20, D21, D22). Таблица operation до этой ревизии не заполнялась, поэтому currency_id сразу NOT NULL. """ from typing import Sequence, Union import sqlalchemy as sa from alembic import op from app.core.config import settings # revision identifiers, used by Alembic. revision: str = "7c2e5a9d1f30" down_revision: Union[str, None] = "3b9f1c2a7d41" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None _schema = settings.DATABASE_SCHEMA or None # Типы зафиксированы в ревизии, как и в начальной миграции. _AMOUNT = sa.Numeric(20, 6) _SEED_USER = "system" _NEW_TYPES = [ ("UWFEE", "Комиссия за андеррайтинг"), ("ADJ", "Корректировка"), ] def _trade_number_index(create: bool) -> None: # В SQLite batch пересоздаёт таблицу и теряет WHERE частичного индекса — # снимаем его на время batch и создаём заново. if create: op.create_index( "ux_operation_portfolio_trade_number", "operation", ["portfolio_id", "trade_number"], unique=True, schema=_schema, postgresql_where=sa.text("trade_number IS NOT NULL"), sqlite_where=sa.text("trade_number IS NOT NULL"), ) else: op.drop_index( "ux_operation_portfolio_trade_number", table_name="operation", schema=_schema, ) def upgrade() -> None: """Upgrade schema.""" _trade_number_index(create=False) with op.batch_alter_table("operation", schema=_schema) as batch: batch.add_column(sa.Column("currency_id", sa.BigInteger(), nullable=False)) batch.add_column(sa.Column("settlement_amount", _AMOUNT, nullable=True)) batch.add_column(sa.Column("source_row", sa.Integer(), nullable=True)) batch.create_foreign_key( "fk_operation_currency", "currency", ["currency_id"], ["id"], referent_schema=_schema, ) batch.create_foreign_key( "fk_operation_import_batch", "import_batch", ["import_batch_id"], ["id"], referent_schema=_schema, ) batch.create_unique_constraint( "uq_operation_batch_row", ["import_batch_id", "source_row"] ) _trade_number_index(create=True) op.create_index( "ix_operation_portfolio_security_date", "operation", ["portfolio_id", "security_id", "trade_date"], schema=_schema, ) operation_type = sa.table( "operation_type", sa.column("code", sa.String), sa.column("name", sa.String), sa.column("created_by", sa.String), sa.column("updated_by", sa.String), schema=_schema, ) op.bulk_insert( operation_type, [ { "code": code, "name": name, "created_by": _SEED_USER, "updated_by": _SEED_USER, } for code, name in _NEW_TYPES ], ) def downgrade() -> None: """Downgrade schema.""" codes = ", ".join(f"'{code}'" for code, _ in _NEW_TYPES) table = f"{_schema}.operation_type" if _schema else "operation_type" op.execute(f"DELETE FROM {table} WHERE code IN ({codes})") op.drop_index( "ix_operation_portfolio_security_date", table_name="operation", schema=_schema, ) _trade_number_index(create=False) with op.batch_alter_table("operation", schema=_schema) as batch: batch.drop_constraint("uq_operation_batch_row", type_="unique") batch.drop_constraint("fk_operation_import_batch", type_="foreignkey") batch.drop_constraint("fk_operation_currency", type_="foreignkey") batch.drop_column("source_row") batch.drop_column("settlement_amount") batch.drop_column("currency_id") _trade_number_index(create=True)