добавлена информация о искоючения в выгрузку (шахматка пресеты)
This commit is contained in:
parent
967fa01fb6
commit
e5b998df20
@ -90,6 +90,10 @@ class MatrixSelection:
|
|||||||
company_1_account_ids: frozenset[uuid.UUID]
|
company_1_account_ids: frozenset[uuid.UUID]
|
||||||
company_2_account_ids: frozenset[uuid.UUID]
|
company_2_account_ids: frozenset[uuid.UUID]
|
||||||
measure: Measure
|
measure: Measure
|
||||||
|
debit_rule_codes: tuple[str, ...] = ()
|
||||||
|
credit_rule_codes: tuple[str, ...] = ()
|
||||||
|
debit_excluded_codes: tuple[str, ...] = ()
|
||||||
|
credit_excluded_codes: tuple[str, ...] = ()
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
@ -129,6 +133,9 @@ class MatrixExportInfo(VersionExportInfo):
|
|||||||
rule_statement_type: StatementType | None = None
|
rule_statement_type: StatementType | None = None
|
||||||
article_1_line: str | None = None
|
article_1_line: str | None = None
|
||||||
article_2_line: str | None = None
|
article_2_line: str | None = None
|
||||||
|
rule_side_1: str | None = None
|
||||||
|
debit_excluded_codes: tuple[str, ...] = ()
|
||||||
|
credit_excluded_codes: tuple[str, ...] = ()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def preset_details(self) -> tuple[StatementType, int, int, str, str]:
|
def preset_details(self) -> tuple[StatementType, int, int, str, str]:
|
||||||
@ -704,8 +711,8 @@ class IntercompanyMatrixService:
|
|||||||
group_name = await self._group_name(result.consolidation)
|
group_name = await self._group_name(result.consolidation)
|
||||||
by_id = {account.id: account.code for account in result.accounts}
|
by_id = {account.id: account.code for account in result.accounts}
|
||||||
if mode == "PRESET":
|
if mode == "PRESET":
|
||||||
debit_codes = tuple(sorted(by_id[account_id] for account_id in result.selection.company_1_account_ids))
|
debit_codes = result.selection.debit_rule_codes
|
||||||
credit_codes = tuple(sorted(by_id[account_id] for account_id in result.selection.company_2_account_ids))
|
credit_codes = result.selection.credit_rule_codes
|
||||||
else:
|
else:
|
||||||
debit_codes = tuple(by_id[account_id] for account_id in debit_account_ids)
|
debit_codes = tuple(by_id[account_id] for account_id in debit_account_ids)
|
||||||
credit_codes = tuple(by_id[account_id] for account_id in credit_account_ids)
|
credit_codes = tuple(by_id[account_id] for account_id in credit_account_ids)
|
||||||
@ -724,6 +731,9 @@ class IntercompanyMatrixService:
|
|||||||
rule_statement_type=result.rule.statement_type if result.rule is not None else None,
|
rule_statement_type=result.rule.statement_type if result.rule is not None else None,
|
||||||
article_1_line=result.rule.article_1_line if result.rule is not None else None,
|
article_1_line=result.rule.article_1_line if result.rule is not None else None,
|
||||||
article_2_line=result.rule.article_2_line if result.rule is not None else None,
|
article_2_line=result.rule.article_2_line if result.rule is not None else None,
|
||||||
|
rule_side_1=result.rule.side_1 if result.rule is not None else None,
|
||||||
|
debit_excluded_codes=result.selection.debit_excluded_codes,
|
||||||
|
credit_excluded_codes=result.selection.credit_excluded_codes,
|
||||||
)
|
)
|
||||||
return result.matrix, info
|
return result.matrix, info
|
||||||
|
|
||||||
@ -764,7 +774,16 @@ class IntercompanyMatrixService:
|
|||||||
credit = _select_accounts(credit_ids, credit_excluded, "K", by_id, children)
|
credit = _select_accounts(credit_ids, credit_excluded, "K", by_id, children)
|
||||||
measure: Measure = "closing" if rule.measure == "closing" else "turnover"
|
measure: Measure = "closing" if rule.measure == "closing" else "turnover"
|
||||||
|
|
||||||
return MatrixSelection(debit, credit, measure), rule
|
def codes(ids: Sequence[uuid.UUID]) -> tuple[str, ...]:
|
||||||
|
return tuple(sorted(by_id[account_id].code for account_id in ids))
|
||||||
|
|
||||||
|
return MatrixSelection(
|
||||||
|
debit, credit, measure,
|
||||||
|
debit_rule_codes=codes(debit_ids),
|
||||||
|
credit_rule_codes=codes(credit_ids),
|
||||||
|
debit_excluded_codes=codes(debit_excluded),
|
||||||
|
credit_excluded_codes=codes(credit_excluded),
|
||||||
|
), rule
|
||||||
|
|
||||||
async def _build_accounts(
|
async def _build_accounts(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@ -57,17 +57,21 @@ def _write_context(sheet: Worksheet, info: MatrixExportInfo) -> int:
|
|||||||
extra = [
|
extra = [
|
||||||
("Режим", mode_name),
|
("Режим", mode_name),
|
||||||
("Счета дебета", ", ".join(info.debit_account_codes) if info.mode != "ALL" else all_accounts),
|
("Счета дебета", ", ".join(info.debit_account_codes) if info.mode != "ALL" else all_accounts),
|
||||||
("Счета кредита", ", ".join(info.credit_account_codes) if info.mode != "ALL" else all_accounts),
|
|
||||||
("Показатель", "Сальдо" if info.measure == "closing" else "Оборот"),
|
|
||||||
]
|
]
|
||||||
|
if info.mode == "PRESET" and info.debit_excluded_codes:
|
||||||
|
extra.append(("кроме", ", ".join(info.debit_excluded_codes)))
|
||||||
|
extra.append(("Счета кредита", ", ".join(info.credit_account_codes) if info.mode != "ALL" else all_accounts))
|
||||||
|
if info.mode == "PRESET" and info.credit_excluded_codes:
|
||||||
|
extra.append(("кроме", ", ".join(info.credit_excluded_codes)))
|
||||||
|
extra.append(("Показатель", "Сальдо" if info.measure == "closing" else "Оборот"))
|
||||||
if info.mode == "PRESET":
|
if info.mode == "PRESET":
|
||||||
statement_type, rule_no, rule_version_no, article_1_line, article_2_line = info.preset_details
|
statement_type, rule_no, rule_version_no, article_1_line, article_2_line = info.preset_details
|
||||||
rule_type = "ББ" if statement_type.value == "balance" else "ОФР"
|
rule_type = "ББ" if statement_type.value == "balance" else "ОФР"
|
||||||
extra.extend(
|
extra.extend(
|
||||||
(
|
(
|
||||||
("Правило", f"{rule_type} № {rule_no}"),
|
("Правило", f"{rule_type} № {rule_no}"),
|
||||||
("Статья А", article_1_line),
|
("Статья А", article_1_line if info.rule_side_1 != "K" else article_2_line),
|
||||||
("Статья Б", article_2_line),
|
("Статья Б", article_2_line if info.rule_side_1 != "K" else article_1_line),
|
||||||
("Версия правил", f"v{rule_version_no}"),
|
("Версия правил", f"v{rule_version_no}"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@ -168,7 +168,10 @@ async def test_preset_applies_exclusions_to_its_own_side_and_rule_measure(monkey
|
|||||||
_, info = await service.build_export(uuid.uuid4(), mode="PRESET", rule_id=uuid.uuid4())
|
_, info = await service.build_export(uuid.uuid4(), mode="PRESET", rule_id=uuid.uuid4())
|
||||||
assert info.mode == "PRESET"
|
assert info.mode == "PRESET"
|
||||||
assert info.debit_account_codes == ("76",)
|
assert info.debit_account_codes == ("76",)
|
||||||
assert info.credit_account_codes == ("76", "76.01")
|
assert info.credit_account_codes == ("76",)
|
||||||
|
assert info.debit_excluded_codes == ("76.01",)
|
||||||
|
assert info.credit_excluded_codes == ()
|
||||||
|
assert info.rule_side_1 == "D"
|
||||||
assert info.measure == "turnover"
|
assert info.measure == "turnover"
|
||||||
assert info.rule_no == rule.rule_no
|
assert info.rule_no == rule.rule_no
|
||||||
assert info.rule_version_no == rule.version_no
|
assert info.rule_version_no == rule.version_no
|
||||||
@ -181,6 +184,10 @@ async def test_preset_credit_first_uses_second_side_for_debit(monkeypatch: pytes
|
|||||||
credit_child = _account(AccountKind.P, credit_parent.id)
|
credit_child = _account(AccountKind.P, credit_parent.id)
|
||||||
debit_parent = _account(AccountKind.A)
|
debit_parent = _account(AccountKind.A)
|
||||||
debit_child = _account(AccountKind.A, debit_parent.id)
|
debit_child = _account(AccountKind.A, debit_parent.id)
|
||||||
|
credit_parent.code = "76"
|
||||||
|
credit_child.code = "76.07.1"
|
||||||
|
debit_parent.code = "62"
|
||||||
|
debit_child.code = "62.01"
|
||||||
accounts = [credit_parent, credit_child, debit_parent, debit_child]
|
accounts = [credit_parent, credit_child, debit_parent, debit_child]
|
||||||
by_id, children = matrix._make_account_tree(accounts)
|
by_id, children = matrix._make_account_tree(accounts)
|
||||||
rule = SimpleNamespace(
|
rule = SimpleNamespace(
|
||||||
@ -208,6 +215,9 @@ async def test_preset_credit_first_uses_second_side_for_debit(monkeypatch: pytes
|
|||||||
assert selection.company_1_account_ids == {debit_parent.id, debit_child.id}
|
assert selection.company_1_account_ids == {debit_parent.id, debit_child.id}
|
||||||
assert selection.company_2_account_ids == {credit_parent.id}
|
assert selection.company_2_account_ids == {credit_parent.id}
|
||||||
assert selection.measure == "turnover"
|
assert selection.measure == "turnover"
|
||||||
|
assert selection.debit_rule_codes == ("62",)
|
||||||
|
assert selection.credit_rule_codes == ("76",)
|
||||||
|
assert selection.credit_excluded_codes == ("76.07.1",)
|
||||||
|
|
||||||
|
|
||||||
async def test_preset_rejects_income_rule_before_selecting_accounts(monkeypatch: pytest.MonkeyPatch) -> None:
|
async def test_preset_rejects_income_rule_before_selecting_accounts(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
"""XLSX-выгрузка шахматки из готового результата расчёта."""
|
"""XLSX-выгрузка шахматки из готового результата расчёта."""
|
||||||
|
|
||||||
|
from dataclasses import replace
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
from unittest.mock import AsyncMock
|
from unittest.mock import AsyncMock
|
||||||
@ -52,9 +53,35 @@ def _info(*, mode: str = "MANUAL", rule_id: UUID | None = None) -> MatrixExportI
|
|||||||
rule_statement_type=StatementType.balance if mode == "PRESET" else None,
|
rule_statement_type=StatementType.balance if mode == "PRESET" else None,
|
||||||
article_1_line="1230" if mode == "PRESET" else None,
|
article_1_line="1230" if mode == "PRESET" else None,
|
||||||
article_2_line="1520" if mode == "PRESET" else None,
|
article_2_line="1520" if mode == "PRESET" else None,
|
||||||
|
rule_side_1="D" if mode == "PRESET" else None,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_preset_header_shows_rule_codes_exclusions_and_credit_first_articles() -> None:
|
||||||
|
matrix = IntercompanyMatrixOut(companies=[], cells=[])
|
||||||
|
info = replace(
|
||||||
|
_info(mode="PRESET"),
|
||||||
|
debit_account_codes=("60.02", "76"),
|
||||||
|
credit_account_codes=("60.01", "76"),
|
||||||
|
debit_excluded_codes=("76.07.5", "76.07.9"),
|
||||||
|
credit_excluded_codes=("76.07.1",),
|
||||||
|
rule_side_1="K",
|
||||||
|
)
|
||||||
|
|
||||||
|
sheet = load_workbook(BytesIO(build_matrix_workbook(matrix, info))).active
|
||||||
|
assert [(sheet.cell(row, 1).value, sheet.cell(row, 2).value) for row in range(7, 16)] == [
|
||||||
|
("Счета дебета", "60.02, 76"),
|
||||||
|
("кроме", "76.07.5, 76.07.9"),
|
||||||
|
("Счета кредита", "60.01, 76"),
|
||||||
|
("кроме", "76.07.1"),
|
||||||
|
("Показатель", "Оборот"),
|
||||||
|
("Правило", "ББ № 6"),
|
||||||
|
("Статья А", "1520"),
|
||||||
|
("Статья Б", "1230"),
|
||||||
|
("Версия правил", "v3"),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def test_matrix_export_has_three_rows_per_debit_company_and_highlights_threshold() -> None:
|
def test_matrix_export_has_three_rows_per_debit_company_and_highlights_threshold() -> None:
|
||||||
company_a = UUID("11111111-1111-1111-1111-111111111111")
|
company_a = UUID("11111111-1111-1111-1111-111111111111")
|
||||||
company_b = UUID("22222222-2222-2222-2222-222222222222")
|
company_b = UUID("22222222-2222-2222-2222-222222222222")
|
||||||
|
|||||||
@ -280,8 +280,8 @@ SHALL считать исходные строки ОСВ пары с `account_i
|
|||||||
### Requirement: XLSX шахматки пресета
|
### Requirement: XLSX шахматки пресета
|
||||||
`GET …/intercompany-matrix/preset/{rule_id}/export` SHALL возвращать XLSX с теми же строками, что JSON-расчёт
|
`GET …/intercompany-matrix/preset/{rule_id}/export` SHALL возвращать XLSX с теми же строками, что JSON-расчёт
|
||||||
пресета. Имя файла SHALL иметь вид `intercompany-matrix_<ГК>_<год>-<квартал>_v<n>_rule-balance-<rule_no>.xlsx`. Шапка SHALL
|
пресета. Имя файла SHALL иметь вид `intercompany-matrix_<ГК>_<год>-<квартал>_v<n>_rule-balance-<rule_no>.xlsx`. Шапка SHALL
|
||||||
содержать режим «Пресет», коды счетов дебета и кредита (раскрытые по иерархии, с учётом исключений правила), номер
|
содержать режим «Пресет», исходные коды счетов дебета и кредита из правила и отдельную строку «кроме» для исключений каждой стороны, если они есть, номер
|
||||||
правила, статьи А и Б (`article_1_line` / `article_2_line`), фактическую версию правил и строку «Показатель» со значением «Сальдо» для `closing` или «Оборот» для `turnover`. Строка «Показатель» SHALL присутствовать и в выгрузках ALL и MANUAL (значение
|
правила, статьи А и Б по дебетовой и кредитовой сторонам соответственно (`article_1_line` / `article_2_line` при `side_1 = D`, наоборот при `side_1 = K`), фактическую версию правил и строку «Показатель» со значением «Сальдо» для `closing` или «Оборот» для `turnover`. Строка «Показатель» SHALL присутствовать и в выгрузках ALL и MANUAL (значение
|
||||||
«Сальдо»).
|
«Сальдо»).
|
||||||
|
|
||||||
#### Scenario: Выгрузка по оборотам
|
#### Scenario: Выгрузка по оборотам
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user