diff --git a/api/src/repositories/statements.py b/api/src/repositories/statements.py index cb49e5d..3d8d8de 100644 --- a/api/src/repositories/statements.py +++ b/api/src/repositories/statements.py @@ -127,7 +127,9 @@ async def statement_amounts( session: AsyncSession, meta_id: uuid.UUID ) -> tuple[list[int], list[tuple[str, dict[int, Decimal | None]]]]: """Индексы колонок и `(код строки, {колонка: сумма})` по возрастанию кода — вход `run_checks`. Порядок строк - формы в БД не хранится (DBML не хранит), поэтому порядок строк — лексикографический порядок кодов.""" + формы в БД не хранится (DBML не хранит), поэтому порядок строк — лексикографический порядок кодов. + Созданные правкой строки (`line_code_original_value IS NULL`) без единой суммы не участвуют + (spec statement-checks, design D-5).""" columns = ( await session.execute( select(FinancialStatementColumn.id, FinancialStatementColumn.column_index) @@ -138,16 +140,29 @@ async def statement_amounts( index_by_id = {column_id: index for column_id, index in columns} rows = ( await session.execute( - select(FinancialStatementLine.line_code, FinancialStatementValue.column_id, FinancialStatementValue.amount) + select( + FinancialStatementLine.line_code, + FinancialStatementLine.line_code_original_value, + FinancialStatementValue.column_id, + FinancialStatementValue.amount, + ) .join(FinancialStatementValue, FinancialStatementValue.line_id == FinancialStatementLine.id) .where(FinancialStatementLine.meta_id == meta_id) .order_by(FinancialStatementLine.line_code) ) ).all() by_code: dict[str, dict[int, Decimal | None]] = {} - for code, column_id, amount in rows: + added: set[str] = set() # строки, созданные правкой аналитика (design D-5) + for code, original, column_id, amount in rows: by_code.setdefault(code or "", {})[index_by_id[column_id]] = amount - return [index for _, index in columns], list(by_code.items()) + if original is None: + added.add(code or "") + lines = [ + (code, amounts) + for code, amounts in by_code.items() + if code not in added or any(amount is not None for amount in amounts.values()) + ] + return [index for _, index in columns], lines async def value_flags(session: AsyncSession, meta_id: uuid.UUID) -> list[tuple[bool, bool | None, Decimal | None]]: diff --git a/api/tests/test_api_statements.py b/api/tests/test_api_statements.py index cc452ee..03ece4a 100644 --- a/api/tests/test_api_statements.py +++ b/api/tests/test_api_statements.py @@ -1286,3 +1286,39 @@ async def test_patch_duplicate_line_code_rejected( ) assert response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY assert response.json()["detail"]["code"] == "DUPLICATE_CELL" + + +async def test_template_line_checks_follow_filled_amounts( + seeded: AsyncSession, fixtures_dir: Path, auth_client: AuthClientFactory +) -> None: + """Review Focus 4 (spec statement-checks): заполненная в одной колонке строка проверяется по всем колонкам, + очищенная — снова не участвует.""" + document = await form(seeded, fixtures_dir) + sums = await drop_line(seeded, document, "1700") + assert sums[1] not in (None, Decimal(0)) and sums[2] not in (None, Decimal(0)) # колонки 1, 2 непустые + client = await auth_client(KA_RF) + col0 = await _column(client, document.id, 0) + response = await client.patch( + f"/documents/{document.id}/statement", + json={"values": [{"line_code": "1700", "column_id": col0, "amount": str(sums[0])}]}, + ) + failed = response.json()["checks"]["failed"] + relation = "1700 = 1300 + 1400 + 1500" + assert sorted(f["column_index"] for f in failed if f["relation"] == relation) == [1, 2] + assert all(f["column_index"] != 0 for f in failed) + response = await client.patch( + f"/documents/{document.id}/statement", + json={ + "values": [ + { + "line_id": next(i["id"] for i in response.json()["lines"] if i["line_code"] == "1700"), + "column_id": col0, + "amount": None, + } + ] + }, + ) + assert response.status_code == HTTPStatus.OK, response.text + after_clear = response.json()["checks"]["failed"] + assert not any(f["relation"].startswith("1700 =") for f in after_clear) # очищенная 1700 снова не цель + assert {f["column_index"] for f in after_clear if f["relation"] == "1600 = 1700"} == {0, 1, 2}