"""Cancelled orders must never reach `event` (plan §1.4). `GetOperationsByCursor` returns cancelled orders alongside executed ones. A cancelled one carries `quantity_done = 0`, the quantity the order only ASKED for in `quantity`, and no payment — so taken at face value it reads as a free acquisition, and the derived position drifts above the broker's snapshot. The raw tier keeps them; only `event` filters. """ from __future__ import annotations from datetime import UTC, datetime from decimal import Decimal from sqlalchemy import select from factories import make_account from fintracker.db import get_sessionmaker from fintracker.models import AccountKind, AccountRole, Event, EventKind from fintracker.sources.tinvest.client import Operation from fintracker.sources.tinvest.sync import _is_executed, _write_events D = Decimal def op(op_id: str, state: str, *, quantity: str = "2") -> Operation: return Operation( id=op_id, account_id="tinv-1", operation_type="OPERATION_TYPE_BUY", state=state, ts=datetime(2026, 4, 2, 10, 0, tzinfo=UTC), instrument_uid=None, position_uid=None, figi=None, quantity=D(quantity), price=D("11325"), price_currency="RUB", payment=D("-22650.95"), payment_currency="RUB", commission=None, accrued_int=None, description="Покупка", payload={}, ) def test_is_executed_reads_the_state_not_the_quantity(): assert _is_executed(op("1", "OPERATION_STATE_EXECUTED")) assert not _is_executed(op("2", "OPERATION_STATE_CANCELED")) # an older payload that states no state at all is trusted, not silently dropped assert _is_executed(op("3", "")) async def broker_account() -> int: return await make_account( name="Брокерский", kind=AccountKind.broker, role=AccountRole.investment, balance=None, include_in_net_worth=False, source="tinvest", source_id="tinv-1", ) async def dedupe_keys() -> list[str]: async with get_sessionmaker()() as session: return list((await session.execute(select(Event.dedupe_key))).scalars().all()) async def write(account_id: int, *operations: Operation) -> int: async with get_sessionmaker()() as session: written, _ = await _write_events(session, account_id, list(operations), {}) await session.commit() return written async def test_a_cancelled_order_never_becomes_an_event(app): account = await broker_account() written = await write( account, op("exec-1", "OPERATION_STATE_EXECUTED", quantity="1"), op("cancel-1", "OPERATION_STATE_CANCELED"), ) assert written == 1 assert await dedupe_keys() == ["tinvest:tinv-1:exec-1"] async def test_re_reading_removes_a_cancelled_order_imported_before_this_filter(app): account = await broker_account() async with get_sessionmaker()() as session: session.add( Event( account_id=account, kind=EventKind.buy, ts=datetime(2026, 4, 2, 10, 0, tzinfo=UTC), trade_date=datetime(2026, 4, 2, tzinfo=UTC).date(), quantity=D(2), amount=D("22650.95"), currency="RUB", source="tinvest", source_id="cancel-1", dedupe_key="tinvest:tinv-1:cancel-1", ) ) await session.commit() await write(account, op("cancel-1", "OPERATION_STATE_CANCELED")) assert await dedupe_keys() == []