тех долг
This commit is contained in:
parent
63fa528c48
commit
254002f723
@ -1,11 +1,13 @@
|
|||||||
"""Плоский список счетов плана счетов для выбора статьи расходов."""
|
"""Плоский список счетов плана счетов для выбора статьи расходов."""
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends
|
from typing import Annotated
|
||||||
|
|
||||||
|
from fastapi import APIRouter, Depends, Query
|
||||||
|
|
||||||
from src.api.dependencies.auth import require
|
from src.api.dependencies.auth import require
|
||||||
from src.api.dependencies.db import SessionDep
|
from src.api.dependencies.db import SessionDep
|
||||||
from src.repositories import directories as repo
|
from src.repositories import directories as repo
|
||||||
from src.schemas.directories import AccountOut
|
from src.schemas.directories import AccountFilter, AccountOut
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@ -16,6 +18,12 @@ router = APIRouter()
|
|||||||
dependencies=[Depends(require("DIRECTORY_VIEW"))],
|
dependencies=[Depends(require("DIRECTORY_VIEW"))],
|
||||||
summary="Список счетов плана счетов",
|
summary="Список счетов плана счетов",
|
||||||
)
|
)
|
||||||
async def list_accounts(session: SessionDep) -> list[AccountOut]:
|
async def list_accounts(
|
||||||
"""Все счета одним списком"""
|
session: SessionDep,
|
||||||
return [AccountOut.model_validate(account) for account in await repo.list_accounts(session)]
|
filters: Annotated[AccountFilter, Query()],
|
||||||
|
) -> list[AccountOut]:
|
||||||
|
"""Счета одним списком с необязательными фильтрами."""
|
||||||
|
accounts = await repo.list_accounts(
|
||||||
|
session, kind=filters.kind, row_type=filters.row_type, is_off_balance=filters.is_off_balance
|
||||||
|
)
|
||||||
|
return [AccountOut.model_validate(account) for account in accounts]
|
||||||
|
|||||||
@ -27,15 +27,28 @@ from src.models.directories import (
|
|||||||
RelatedGroup,
|
RelatedGroup,
|
||||||
RelatedGroupMember,
|
RelatedGroupMember,
|
||||||
)
|
)
|
||||||
from src.models.enums import FileStatus, RelatedGroupKind
|
from src.models.enums import AccountKind, AccountRowType, FileStatus, RelatedGroupKind
|
||||||
from src.models.files import Document, StoredFile
|
from src.models.files import Document, StoredFile
|
||||||
|
|
||||||
LIKE_ESCAPE = "\\"
|
LIKE_ESCAPE = "\\"
|
||||||
ALIAS_LOCK_PREFIX = "legal-entity-alias" # пространство ключей advisory-блокировки алиасов
|
ALIAS_LOCK_PREFIX = "legal-entity-alias" # пространство ключей advisory-блокировки алиасов
|
||||||
|
|
||||||
|
|
||||||
async def list_accounts(session: AsyncSession) -> list[ChartOfAccount]:
|
async def list_accounts(
|
||||||
accounts = (await session.scalars(select(ChartOfAccount))).all()
|
session: AsyncSession,
|
||||||
|
*,
|
||||||
|
kind: list[AccountKind] | None = None,
|
||||||
|
row_type: AccountRowType | None = None,
|
||||||
|
is_off_balance: bool | None = None,
|
||||||
|
) -> list[ChartOfAccount]:
|
||||||
|
stmt = select(ChartOfAccount)
|
||||||
|
if kind is not None:
|
||||||
|
stmt = stmt.where(ChartOfAccount.kind.in_(kind))
|
||||||
|
if row_type is not None:
|
||||||
|
stmt = stmt.where(ChartOfAccount.row_type == row_type)
|
||||||
|
if is_off_balance is not None:
|
||||||
|
stmt = stmt.where(ChartOfAccount.is_off_balance.is_(is_off_balance))
|
||||||
|
accounts = (await session.scalars(stmt)).all()
|
||||||
return sorted(accounts, key=lambda account: (account_sort_key(account.code), account.code))
|
return sorted(accounts, key=lambda account: (account_sort_key(account.code), account.code))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -38,6 +38,14 @@ class RegionalOfficeOut(RegionalOfficeBrief):
|
|||||||
is_active: bool
|
is_active: bool
|
||||||
|
|
||||||
|
|
||||||
|
class AccountFilter(BaseModel):
|
||||||
|
"""Необязательные фильтры списка счетов."""
|
||||||
|
|
||||||
|
kind: list[AccountKind] | None = None
|
||||||
|
row_type: AccountRowType | None = None
|
||||||
|
is_off_balance: bool | None = None
|
||||||
|
|
||||||
|
|
||||||
class AccountOut(BaseModel):
|
class AccountOut(BaseModel):
|
||||||
"""Одна строка плана счетов; parent_id сохраняет иерархию в плоском списке."""
|
"""Одна строка плана счетов; parent_id сохраняет иерархию в плоском списке."""
|
||||||
|
|
||||||
|
|||||||
@ -185,7 +185,7 @@ def calculate_matrix(
|
|||||||
variance_amount=variance_amount,
|
variance_amount=variance_amount,
|
||||||
variance_percent=variance_percent,
|
variance_percent=variance_percent,
|
||||||
exceeds_threshold=exceeds_threshold,
|
exceeds_threshold=exceeds_threshold,
|
||||||
one_sided=bool(debit_ids) != bool(credit_ids),
|
one_sided=min(amount_a, amount_b) == 0 and variance_amount > 0,
|
||||||
skipped_osv_entry_count=skipped_entries[(company_a, company_b)]
|
skipped_osv_entry_count=skipped_entries[(company_a, company_b)]
|
||||||
+ skipped_entries[(company_b, company_a)],
|
+ skipped_entries[(company_b, company_a)],
|
||||||
osv_entry_ids_debit=debit_ids,
|
osv_entry_ids_debit=debit_ids,
|
||||||
@ -360,7 +360,7 @@ class IntercompanyMatrixService:
|
|||||||
debit_account_ids: Sequence[uuid.UUID] = (),
|
debit_account_ids: Sequence[uuid.UUID] = (),
|
||||||
credit_account_ids: Sequence[uuid.UUID] = (),
|
credit_account_ids: Sequence[uuid.UUID] = (),
|
||||||
) -> IntercompanyMatrixOut:
|
) -> IntercompanyMatrixOut:
|
||||||
"""ALL использует все счета нужного вида; MANUAL раскрывает выбранные счета и потомков."""
|
"""ALL использует балансовые счета нужного вида; MANUAL раскрывает выбранные счета и потомков."""
|
||||||
matrix, _, _, _ = await self._build_accounts(
|
matrix, _, _, _ = await self._build_accounts(
|
||||||
version_id, mode=mode, debit_account_ids=debit_account_ids, credit_account_ids=credit_account_ids
|
version_id, mode=mode, debit_account_ids=debit_account_ids, credit_account_ids=credit_account_ids
|
||||||
)
|
)
|
||||||
@ -404,8 +404,12 @@ class IntercompanyMatrixService:
|
|||||||
if mode == "ALL":
|
if mode == "ALL":
|
||||||
if debit_account_ids or credit_account_ids:
|
if debit_account_ids or credit_account_ids:
|
||||||
raise DomainValidationError("ACCOUNTS_NOT_ALLOWED", "Для режима ALL не нужно указывать счета")
|
raise DomainValidationError("ACCOUNTS_NOT_ALLOWED", "Для режима ALL не нужно указывать счета")
|
||||||
debit = frozenset(account.id for account in accounts if account.kind in DEBIT_KINDS)
|
debit = frozenset(
|
||||||
credit = frozenset(account.id for account in accounts if account.kind in CREDIT_KINDS)
|
account.id for account in accounts if not account.is_off_balance and account.kind in DEBIT_KINDS
|
||||||
|
)
|
||||||
|
credit = frozenset(
|
||||||
|
account.id for account in accounts if not account.is_off_balance and account.kind in CREDIT_KINDS
|
||||||
|
)
|
||||||
elif mode == "MANUAL":
|
elif mode == "MANUAL":
|
||||||
if not debit_account_ids or not credit_account_ids:
|
if not debit_account_ids or not credit_account_ids:
|
||||||
raise DomainValidationError("ACCOUNTS_REQUIRED", "Выберите дебетовые и кредитовые счета")
|
raise DomainValidationError("ACCOUNTS_REQUIRED", "Выберите дебетовые и кредитовые счета")
|
||||||
|
|||||||
@ -120,7 +120,7 @@ def build_matrix_workbook(matrix: IntercompanyMatrixOut, info: MatrixExportInfo)
|
|||||||
if cell is None:
|
if cell is None:
|
||||||
continue
|
continue
|
||||||
_write_values(sheet, start_row, column, cell)
|
_write_values(sheet, start_row, column, cell)
|
||||||
if cell.exceeds_threshold:
|
if cell.exceeds_threshold or cell.one_sided:
|
||||||
for offset in range(len(METRICS)):
|
for offset in range(len(METRICS)):
|
||||||
marked = sheet.cell(start_row + offset, column)
|
marked = sheet.cell(start_row + offset, column)
|
||||||
marked.fill = THRESHOLD_FILL
|
marked.fill = THRESHOLD_FILL
|
||||||
|
|||||||
@ -4,9 +4,14 @@ import uuid
|
|||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
from unittest.mock import AsyncMock
|
from unittest.mock import AsyncMock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from src.api.endpoints.directories import accounts as accounts_endpoint
|
||||||
from src.api.endpoints.directories.accounts import list_accounts
|
from src.api.endpoints.directories.accounts import list_accounts
|
||||||
|
from src.app import app
|
||||||
from src.models.enums import AccountKind, AccountRowType
|
from src.models.enums import AccountKind, AccountRowType
|
||||||
from src.repositories.directories import list_accounts as query_accounts
|
from src.repositories.directories import list_accounts as query_accounts
|
||||||
|
from src.schemas.directories import AccountFilter
|
||||||
|
|
||||||
|
|
||||||
async def test_accounts_are_flat_and_naturally_sorted() -> None:
|
async def test_accounts_are_flat_and_naturally_sorted() -> None:
|
||||||
@ -43,10 +48,57 @@ async def test_accounts_are_flat_and_naturally_sorted() -> None:
|
|||||||
session = SimpleNamespace(scalars=AsyncMock(return_value=SimpleNamespace(all=lambda: rows)))
|
session = SimpleNamespace(scalars=AsyncMock(return_value=SimpleNamespace(all=lambda: rows)))
|
||||||
|
|
||||||
assert [item.code for item in await query_accounts(session)] == ["60", "60.2", "60.10"]
|
assert [item.code for item in await query_accounts(session)] == ["60", "60.2", "60.10"]
|
||||||
response = await list_accounts(session)
|
response = await list_accounts(session, AccountFilter())
|
||||||
assert [item.code for item in response] == ["60", "60.2", "60.10"]
|
assert [item.code for item in response] == ["60", "60.2", "60.10"]
|
||||||
assert response[0].parent_id is None
|
assert response[0].parent_id is None
|
||||||
assert response[2].parent_id == root_id
|
assert response[2].parent_id == root_id
|
||||||
assert response[2].id == sub_id
|
assert response[2].id == sub_id
|
||||||
assert [item.model_dump(mode="json")["is_debit"] for item in response] == [True, False, True]
|
assert [item.model_dump(mode="json")["is_debit"] for item in response] == [True, False, True]
|
||||||
assert [item.model_dump(mode="json")["is_credit"] for item in response] == [True, True, False]
|
assert [item.model_dump(mode="json")["is_credit"] for item in response] == [True, True, False]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("filters", "expected_columns"),
|
||||||
|
[
|
||||||
|
({}, set()),
|
||||||
|
({"kind": [AccountKind.A]}, {"kind"}),
|
||||||
|
({"kind": [AccountKind.A, AccountKind.AP]}, {"kind"}),
|
||||||
|
({"row_type": AccountRowType.analytic}, {"row_type"}),
|
||||||
|
({"is_off_balance": False}, {"is_off_balance"}),
|
||||||
|
(
|
||||||
|
{"kind": [AccountKind.AP, AccountKind.P], "row_type": AccountRowType.account, "is_off_balance": True},
|
||||||
|
{"kind", "row_type", "is_off_balance"},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_account_filters_apply_only_when_supplied(filters: dict, expected_columns: set[str]) -> None:
|
||||||
|
session = SimpleNamespace(scalars=AsyncMock(return_value=SimpleNamespace(all=list)))
|
||||||
|
|
||||||
|
assert await query_accounts(session, **filters) == []
|
||||||
|
statement = session.scalars.await_args.args[0]
|
||||||
|
where_clause = str(statement.whereclause) if statement.whereclause is not None else ""
|
||||||
|
for column in ("kind", "row_type", "is_off_balance"):
|
||||||
|
assert (f"chart_of_account.{column} " in where_clause) == (column in expected_columns)
|
||||||
|
if "kind" in filters:
|
||||||
|
assert "chart_of_account.kind IN" in where_clause
|
||||||
|
assert statement.compile().params["kind_1"] == filters["kind"]
|
||||||
|
|
||||||
|
|
||||||
|
async def test_account_endpoint_forwards_all_filters(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
query = AsyncMock(return_value=[])
|
||||||
|
monkeypatch.setattr(accounts_endpoint.repo, "list_accounts", query)
|
||||||
|
session = SimpleNamespace()
|
||||||
|
|
||||||
|
filters = AccountFilter(kind=[AccountKind.AP, AccountKind.P], row_type=AccountRowType.account, is_off_balance=False)
|
||||||
|
assert await list_accounts(session, filters) == []
|
||||||
|
query.assert_awaited_once_with(
|
||||||
|
session, kind=[AccountKind.AP, AccountKind.P], row_type=AccountRowType.account, is_off_balance=False
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_account_filters_are_optional_query_parameters() -> None:
|
||||||
|
parameters = app.openapi()["paths"]["/api/v1/directories/accounts"]["get"]["parameters"]
|
||||||
|
assert {item["name"] for item in parameters} == {"kind", "row_type", "is_off_balance"}
|
||||||
|
assert all(item["in"] == "query" and item["required"] is False for item in parameters)
|
||||||
|
kind = next(item for item in parameters if item["name"] == "kind")
|
||||||
|
assert kind["schema"]["anyOf"][0]["type"] == "array"
|
||||||
|
|||||||
@ -138,6 +138,28 @@ async def test_no_osv_for_selected_accounts(auth_client: AuthClientFactory, seed
|
|||||||
assert cell["skipped_osv_entry_count"] == 0
|
assert cell["skipped_osv_entry_count"] == 0
|
||||||
|
|
||||||
|
|
||||||
|
async def test_all_excludes_off_balance_accounts_but_manual_can_select_them(
|
||||||
|
auth_client: AuthClientFactory, seeded: AsyncSession
|
||||||
|
) -> None:
|
||||||
|
world = await _world(seeded)
|
||||||
|
assert world.accounts["001"].is_off_balance is True
|
||||||
|
await _source(seeded, world, credit=False, amount="100", account="001")
|
||||||
|
client = await auth_client(KA_RF)
|
||||||
|
|
||||||
|
all_response = await _request(client, world.version.id, "all", world.selection)
|
||||||
|
assert all_response.status_code == OK, all_response.text
|
||||||
|
assert all(Decimal(cell["amount_a"]) == 0 for cell in all_response.json()["cells"])
|
||||||
|
|
||||||
|
manual_selection = {
|
||||||
|
"debit_account_ids": [str(world.accounts["001"].id)],
|
||||||
|
"credit_account_ids": [str(world.accounts["60.01"].id)],
|
||||||
|
}
|
||||||
|
manual_response = await _request(client, world.version.id, "manual", manual_selection)
|
||||||
|
assert manual_response.status_code == OK, manual_response.text
|
||||||
|
cell = next(c for c in manual_response.json()["cells"] if c["company_a_id"] == str(world.members[0].id))
|
||||||
|
assert Decimal(cell["amount_a"]) == Decimal("100")
|
||||||
|
|
||||||
|
|
||||||
async def test_unmapped_account_source_is_visible_in_cell_count(
|
async def test_unmapped_account_source_is_visible_in_cell_count(
|
||||||
auth_client: AuthClientFactory, seeded: AsyncSession
|
auth_client: AuthClientFactory, seeded: AsyncSession
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -170,7 +192,7 @@ async def test_pair_without_counterpart(
|
|||||||
cell = next(c for c in response.json()["cells"] if c["company_a_id"] == str(world.members[0].id))
|
cell = next(c for c in response.json()["cells"] if c["company_a_id"] == str(world.members[0].id))
|
||||||
assert Decimal(cell["amount_a"]) == (Decimal(0) if credit else Decimal(amount))
|
assert Decimal(cell["amount_a"]) == (Decimal(0) if credit else Decimal(amount))
|
||||||
assert Decimal(cell["amount_b"]) == (Decimal(amount) if credit else Decimal(0))
|
assert Decimal(cell["amount_b"]) == (Decimal(amount) if credit else Decimal(0))
|
||||||
assert cell["one_sided"] is True
|
assert cell["one_sided"] is (Decimal(amount) != 0)
|
||||||
assert cell["variance_percent"] is None
|
assert cell["variance_percent"] is None
|
||||||
assert cell["exceeds_threshold"] is False
|
assert cell["exceeds_threshold"] is False
|
||||||
assert bool(cell["osv_entry_ids_credit"]) is credit
|
assert bool(cell["osv_entry_ids_credit"]) is credit
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
import uuid
|
import uuid
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
|
from unittest.mock import AsyncMock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@ -84,7 +85,50 @@ def test_routes_are_separate_and_matrix_expands_manual_accounts() -> None:
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def test_one_sided_reflects_osv_sources_even_when_amount_is_zero() -> None:
|
async def test_all_excludes_off_balance_accounts_only(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
company_a, company_b = uuid.uuid4(), uuid.uuid4()
|
||||||
|
off_balance = SimpleNamespace(id=uuid.uuid4(), kind=AccountKind.A, parent_id=None, is_off_balance=True)
|
||||||
|
off_balance_credit = SimpleNamespace(id=uuid.uuid4(), kind=AccountKind.P, parent_id=None, is_off_balance=True)
|
||||||
|
debit = SimpleNamespace(id=uuid.uuid4(), kind=AccountKind.A, parent_id=None, is_off_balance=False)
|
||||||
|
credit = SimpleNamespace(id=uuid.uuid4(), kind=AccountKind.P, parent_id=None, is_off_balance=False)
|
||||||
|
accounts = [off_balance, off_balance_credit, debit, credit]
|
||||||
|
monkeypatch.setattr(matrix.directory_repo, "list_accounts", AsyncMock(return_value=accounts))
|
||||||
|
rows = [
|
||||||
|
_row(company_a, company_b, off_balance.id, debit="100"),
|
||||||
|
_row(company_a, company_b, debit.id, debit="30"),
|
||||||
|
_row(company_b, company_a, off_balance_credit.id, credit="40"),
|
||||||
|
_row(company_b, company_a, credit.id, credit="20"),
|
||||||
|
]
|
||||||
|
members = {
|
||||||
|
company_a: SimpleNamespace(id=company_a, name="Компания А"),
|
||||||
|
company_b: SimpleNamespace(id=company_b, name="Компания Б"),
|
||||||
|
}
|
||||||
|
osv_service = SimpleNamespace(
|
||||||
|
build_with_members=AsyncMock(return_value=(SimpleNamespace(rows=rows), None, None, members))
|
||||||
|
)
|
||||||
|
service = matrix.IntercompanyMatrixService(SimpleNamespace(), None, osv_service)
|
||||||
|
|
||||||
|
all_matrix = await service.build_accounts(uuid.uuid4(), mode="ALL")
|
||||||
|
manual_matrix = await service.build_accounts(
|
||||||
|
uuid.uuid4(), mode="MANUAL", debit_account_ids=[off_balance.id], credit_account_ids=[off_balance_credit.id]
|
||||||
|
)
|
||||||
|
assert all_matrix.cells[0].amount_a == Decimal("30")
|
||||||
|
assert all_matrix.cells[0].amount_b == Decimal("20")
|
||||||
|
assert manual_matrix.cells[0].amount_a == Decimal("100")
|
||||||
|
assert manual_matrix.cells[0].amount_b == Decimal("40")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("debit_amount", "credit_amount", "expected"),
|
||||||
|
[
|
||||||
|
("100000000", "0", True),
|
||||||
|
("0", "100000000", True),
|
||||||
|
("0", "0", False),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_one_sided_uses_amounts_even_with_zero_balance_rows(
|
||||||
|
debit_amount: str, credit_amount: str, expected: bool
|
||||||
|
) -> None:
|
||||||
company_a, company_b = uuid.uuid4(), uuid.uuid4()
|
company_a, company_b = uuid.uuid4(), uuid.uuid4()
|
||||||
debit_account, credit_account = uuid.uuid4(), uuid.uuid4()
|
debit_account, credit_account = uuid.uuid4(), uuid.uuid4()
|
||||||
companies = [
|
companies = [
|
||||||
@ -92,14 +136,15 @@ def test_one_sided_reflects_osv_sources_even_when_amount_is_zero() -> None:
|
|||||||
IntercompanyMatrixCompanyOut(id=company_b, name="Компания Б"),
|
IntercompanyMatrixCompanyOut(id=company_b, name="Компания Б"),
|
||||||
]
|
]
|
||||||
selection = matrix.MatrixSelection(frozenset({debit_account}), frozenset({credit_account}), "closing")
|
selection = matrix.MatrixSelection(frozenset({debit_account}), frozenset({credit_account}), "closing")
|
||||||
debit = _row(company_a, company_b, debit_account)
|
debit = _row(company_a, company_b, debit_account, debit=debit_amount)
|
||||||
credit = _row(company_b, company_a, credit_account)
|
credit = _row(company_b, company_a, credit_account, credit=credit_amount)
|
||||||
debit.entry_ids = (uuid.uuid4(),)
|
debit.entry_ids = (uuid.uuid4(),)
|
||||||
credit.entry_ids = (uuid.uuid4(),)
|
credit.entry_ids = (uuid.uuid4(),)
|
||||||
|
|
||||||
for rows, expected in (([], False), ([debit], True), ([credit], True), ([debit, credit], False)):
|
cell = matrix.calculate_matrix([debit, credit], companies, selection).cells[0]
|
||||||
cell = matrix.calculate_matrix(rows, companies, selection).cells[0]
|
|
||||||
assert cell.one_sided is expected
|
assert cell.one_sided is expected
|
||||||
|
assert cell.exceeds_threshold is False
|
||||||
|
assert cell.variance_percent is None
|
||||||
assert cell.model_dump(mode="json")["one_sided"] is expected
|
assert cell.model_dump(mode="json")["one_sided"] is expected
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -142,6 +142,39 @@ def test_matrix_export_has_three_rows_per_debit_company_and_highlights_threshold
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(("amount", "highlighted"), [("100000000", True), ("0", False)])
|
||||||
|
def test_xlsx_highlights_one_sided_amount_but_not_two_zero_balances(amount: str, highlighted: bool) -> None:
|
||||||
|
company_a = UUID("11111111-1111-1111-1111-111111111111")
|
||||||
|
company_b = UUID("22222222-2222-2222-2222-222222222222")
|
||||||
|
matrix = IntercompanyMatrixOut(
|
||||||
|
companies=[
|
||||||
|
IntercompanyMatrixCompanyOut(id=company_a, name="Компания А"),
|
||||||
|
IntercompanyMatrixCompanyOut(id=company_b, name="Компания Б"),
|
||||||
|
],
|
||||||
|
cells=[
|
||||||
|
IntercompanyMatrixCellOut(
|
||||||
|
company_a_id=company_a,
|
||||||
|
company_b_id=company_b,
|
||||||
|
amount_a=amount,
|
||||||
|
amount_b="0",
|
||||||
|
variance_amount=amount,
|
||||||
|
variance_percent=None if highlighted else "0",
|
||||||
|
exceeds_threshold=False,
|
||||||
|
one_sided=highlighted,
|
||||||
|
skipped_osv_entry_count=0,
|
||||||
|
osv_entry_ids_debit=[],
|
||||||
|
osv_entry_ids_credit=[],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
sheet = load_workbook(BytesIO(build_matrix_workbook(matrix, _info(mode="ALL")))).active
|
||||||
|
assert sheet is not None
|
||||||
|
assert all(
|
||||||
|
(sheet.cell(row, 4).fill.fgColor.rgb == THRESHOLD_FILL.fgColor.rgb) is highlighted for row in range(11, 15)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_both_export_routes_use_matrix_calculation() -> None:
|
async def test_both_export_routes_use_matrix_calculation() -> None:
|
||||||
version_id = UUID("11111111-1111-1111-1111-111111111111")
|
version_id = UUID("11111111-1111-1111-1111-111111111111")
|
||||||
account_id = UUID("22222222-2222-2222-2222-222222222222")
|
account_id = UUID("22222222-2222-2222-2222-222222222222")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user