diff --git a/api/src/schemas/consolidations.py b/api/src/schemas/consolidations.py index a4472e3..65c22ab 100644 --- a/api/src/schemas/consolidations.py +++ b/api/src/schemas/consolidations.py @@ -702,7 +702,6 @@ class IntercompanyResultTableOut(BaseModel): companies: list[IntercompanyMatrixCompanyOut] articles: list[IntercompanyResultArticleOut] - company_totals: list[IntercompanyResultAmountOut] grand_total: Decimal @field_validator("grand_total") diff --git a/api/src/services/intercompany_matrix.py b/api/src/services/intercompany_matrix.py index 4b34224..84cda3d 100644 --- a/api/src/services/intercompany_matrix.py +++ b/api/src/services/intercompany_matrix.py @@ -374,17 +374,9 @@ def calculate_result_table( group_total=sum((item.amount for item in company_amounts), ZERO), ) ) - company_totals = [ - IntercompanyResultAmountOut( - company_id=company.id, - amount=sum((article.amounts[index].amount for article in articles), ZERO), - ) - for index, company in enumerate(companies) - ] return IntercompanyResultTableOut( companies=list(companies), articles=articles, - company_totals=company_totals, grand_total=sum((article.group_total for article in articles), ZERO), ) diff --git a/api/src/services/intercompany_matrix_export.py b/api/src/services/intercompany_matrix_export.py index 7a4ca7c..76d8a1e 100644 --- a/api/src/services/intercompany_matrix_export.py +++ b/api/src/services/intercompany_matrix_export.py @@ -213,11 +213,6 @@ def build_result_table_workbook(result: IntercompanyResultTableOut, info: Result summary_row = header_row + len(result.articles) + 1 _text(sheet, summary_row, 1, "Итого") sheet.cell(summary_row, 1).font = Font(bold=True) - totals = {item.company_id: item.amount for item in result.company_totals} - for column, company in enumerate(result.companies, start=2): - cell = sheet.cell(summary_row, column, totals[company.id]) - cell.number_format = MONEY_FORMAT - cell.font = Font(bold=True) grand_total = sheet.cell(summary_row, total_column, result.grand_total) grand_total.number_format = MONEY_FORMAT grand_total.font = Font(bold=True) diff --git a/api/tests/test_api_intercompany_matrix.py b/api/tests/test_api_intercompany_matrix.py index 1490c34..38fcd78 100644 --- a/api/tests/test_api_intercompany_matrix.py +++ b/api/tests/test_api_intercompany_matrix.py @@ -730,7 +730,7 @@ async def test_result_table_export_matches_json(auth_client: AuthClientFactory, assert sorted(item["amount"] for item in receivables["amounts"]) == ["0.00", "100.25"] assert receivables["group_total"] == "100.25" assert all(article["group_total"] == "0.00" for article in data["articles"] if article != receivables) - assert sorted(item["amount"] for item in data["company_totals"]) == ["0.00", "100.25"] + assert "company_totals" not in data assert data["grand_total"] == "100.25" sheet = load_workbook(BytesIO(export.content))["Результирующая таблица"] assert [sheet.cell(7, col).value for col in range(2, len(data["companies"]) + 2)] == [ @@ -744,9 +744,8 @@ async def test_result_table_export_matches_json(auth_client: AuthClientFactory, assert Decimal(str(sheet.cell(row, len(data["companies"]) + 2).value)) == Decimal(article["group_total"]) summary_row = 8 + len(data["articles"]) assert sheet.cell(summary_row, 1).value == "Итого" - totals = {item["company_id"]: Decimal(item["amount"]) for item in data["company_totals"]} - for col, company in enumerate(data["companies"], start=2): - assert Decimal(str(sheet.cell(summary_row, col).value)) == totals[company["id"]] + for col in range(2, len(data["companies"]) + 2): + assert sheet.cell(summary_row, col).value is None assert Decimal(str(sheet.cell(summary_row, len(data["companies"]) + 2).value)) == Decimal(data["grand_total"]) event = (await seeded.scalars(select(AuditEvent).where(AuditEvent.action == AuditAction.EXPORT))).one() assert event.after_value == {"report": "intercompany_result_table", "articles": len(data["articles"])} diff --git a/api/tests/test_intercompany_matrix.py b/api/tests/test_intercompany_matrix.py index 618492d..fe7d2ae 100644 --- a/api/tests/test_intercompany_matrix.py +++ b/api/tests/test_intercompany_matrix.py @@ -500,10 +500,7 @@ def test_result_table_uses_fixed_articles_and_group_totals() -> None: assert isinstance(result, IntercompanyResultTableOut) response = result assert response.articles[0].amounts[0].company_id == company_a - assert [(item.company_id, item.amount) for item in result.company_totals] == [ - (company_a, Decimal("140.00")), - (company_b, Decimal("110.00")), - ] + assert "company_totals" not in result.model_dump(mode="json") assert result.grand_total == Decimal("250.00") diff --git a/api/tests/test_intercompany_matrix_export.py b/api/tests/test_intercompany_matrix_export.py index d3b50ab..5ee8b3f 100644 --- a/api/tests/test_intercompany_matrix_export.py +++ b/api/tests/test_intercompany_matrix_export.py @@ -410,10 +410,6 @@ async def test_result_table_export_matches_result_and_records_audit() -> None: group_total="120.25", ) ], - company_totals=[ - IntercompanyResultAmountOut(company_id=version_id, amount="100.25"), - IntercompanyResultAmountOut(company_id=other_id, amount="20.00"), - ], grand_total="120.25", ) info = ResultTableExportInfo('ГК "Кавычки" / Бисквит', 2024, PeriodQuarter.Q3, 2) @@ -435,7 +431,7 @@ async def test_result_table_export_matches_result_and_records_audit() -> None: assert sheet["B7"].data_type == "s" assert [sheet.cell(8, column).value for column in range(1, 5)] == ["Дебиторская задолженность", 100.25, 20, 120.25] assert all(sheet.cell(8, column).number_format == "#,##0.00" for column in range(2, 5)) - assert [sheet.cell(9, column).value for column in range(1, 5)] == ["Итого", 100.25, 20, 120.25] + assert [sheet.cell(9, column).value for column in range(1, 5)] == ["Итого", None, None, 120.25] assert sheet.freeze_panes == "B8" assert audit.record.await_args.kwargs == { "action": AuditAction.EXPORT, @@ -456,7 +452,7 @@ async def test_failed_result_table_export_does_not_write_audit(monkeypatch: pyte service = SimpleNamespace( build_result_export=AsyncMock( return_value=( - IntercompanyResultTableOut(companies=[], articles=[], company_totals=[], grand_total="0"), + IntercompanyResultTableOut(companies=[], articles=[], grand_total="0"), info, ) ) @@ -479,7 +475,6 @@ def test_result_table_workbook_rejects_article_without_company_amount() -> None: result = IntercompanyResultTableOut( companies=[company], articles=[IntercompanyResultArticleOut(code="receivables", name="ДЗ", amounts=[], group_total="0")], - company_totals=[IntercompanyResultAmountOut(company_id=company.id, amount="0")], grand_total="0", ) with pytest.raises(KeyError): diff --git a/openspec/specs/consolidations-api/spec.md b/openspec/specs/consolidations-api/spec.md index ccece5c..5586911 100644 --- a/openspec/specs/consolidations-api/spec.md +++ b/openspec/specs/consolidations-api/spec.md @@ -253,12 +253,11 @@ account, status, missing_quarters, document_ids}`: `document_type` и `account` ### Requirement: Результирующая таблица ВГО `GET /api/v1/consolidations/versions/{version_id}/intercompany-matrix/result-table` -(`CONSOLIDATION_VIEW`, policy) SHALL возвращать `{companies[], articles[], company_totals[], grand_total}`. Компании — все члены периметра +(`CONSOLIDATION_VIEW`, policy) SHALL возвращать `{companies[], articles[], grand_total}`. Компании — все члены периметра по имени; статья содержит `code`, `name`, `amounts[] {company_id, amount}` и `group_total`. Пять фиксированных статей §6: дебиторская и кредиторская задолженность, финансовые вложения, долгосрочные и краткосрочные займы. Сумма компании SHALL считаться по всем остальным компаниям периметра из конечного сальдо нужной стороны и выбранных счетов статьи; `group_total` — сумма по компаниям. -`company_totals[] {company_id, amount}` SHALL содержать сумму всех статей для каждого ЮЛ в порядке `companies[]`; `grand_total` — сумму `group_total` всех статей. Итоги считаются из округлённых значений ответа. `amount`, `group_total` и `grand_total` SHALL возвращаться строками с двумя знаками после округления `ROUND_HALF_UP` (например, `"0.00"`). @@ -267,7 +266,6 @@ account, status, missing_quarters, document_ids}`: `document_type` и `account` #### Scenario: Компания без ОСВ - **WHEN** в периметре есть компания без подходящих строк ОСВ - **THEN** она есть в `companies`, её сумма по каждой статье — строка `"0.00"` -- **AND** её итог в `company_totals` — строка `"0.00"` #### Scenario: Нет обязательного счёта - **WHEN** в плане счетов отсутствует счёт 58.03 @@ -276,7 +274,8 @@ account, status, missing_quarters, document_ids}`: `document_type` и `account` ### Requirement: XLSX результирующей таблицы ВГО `GET /api/v1/consolidations/versions/{version_id}/intercompany-matrix/result-table/export` (`CONSOLIDATION_VIEW`, policy) SHALL возвращать XLSX с теми же статьями, компаниями и итогами, что `GET …/intercompany-matrix/result-table`, -из того же расчёта сводной ОСВ версии. Последняя строка «Итого» содержит `company_totals` и `grand_total`. +из того же расчёта сводной ОСВ версии. Последняя строка «Итого» содержит только `grand_total` в колонке группы; +ячейки этой строки под отдельными ЮЛ пусты. Книга — лист «Результирующая таблица» с шапкой (ГК, период, версия, единицы «руб.») и таблицей «Статья ВГО» × компании периметра + колонка «Итого по группе»; суммы — числа с двумя знаками. Имя файла — `intercompany-result-table_<ГК>_<год>-<квартал>_v.xlsx`. Ошибки — как у JSON-маршрута: невидимая или