"""Период консолидации (Срез 4): отчётная дата (OQ-40) и выбор источников ОСВ на пару (ЮЛ, счёт) — D-2, §2.""" import datetime import uuid from dataclasses import dataclass import pytest from src.domain.consolidation_periods import ( QUARTER_ORDER, Gap, Sources, counts_as_cumulative, quarters_through, reporting_date, select_sources, ) from src.models.enums import PeriodQuarter Q1, Q2, Q3, Q4 = PeriodQuarter.Q1, PeriodQuarter.Q2, PeriodQuarter.Q3, PeriodQuarter.Q4 @pytest.mark.parametrize( ("year", "quarter", "expected"), [ (2024, PeriodQuarter.Q1, datetime.date(2024, 4, 1)), (2024, PeriodQuarter.Q2, datetime.date(2024, 7, 1)), (2024, PeriodQuarter.Q3, datetime.date(2024, 10, 1)), (2024, PeriodQuarter.Q4, datetime.date(2025, 1, 1)), (2100, PeriodQuarter.Q4, datetime.date(2101, 1, 1)), ], ) def test_reporting_date_is_first_day_after_quarter(year: int, quarter: PeriodQuarter, expected: datetime.date) -> None: assert reporting_date(year, quarter) == expected @dataclass(frozen=True) class _Doc: """Документ для `select_sources`: структурно подходит под `DocLike` (как `Document` и `SourceDocument`).""" period_quarter: PeriodQuarter | None is_cumulative: bool | None id: uuid.UUID = uuid.UUID(int=0) def _doc(quarter: PeriodQuarter | None, cumulative: bool | None = False) -> _Doc: return _Doc(quarter, cumulative, uuid.uuid4()) def test_quarter_order_and_quarters_through() -> None: assert QUARTER_ORDER == (Q1, Q2, Q3, Q4) assert quarters_through(Q1) == (Q1,) assert quarters_through(Q3) == (Q1, Q2, Q3) assert quarters_through(Q4) == QUARTER_ORDER def test_counts_as_cumulative_null_is_cumulative() -> None: """R-1, решение №10: NULL (ББ/ОФР) — нарастающий; только `False` — квартальный.""" assert counts_as_cumulative(True) is True assert counts_as_cumulative(None) is True assert counts_as_cumulative(False) is False def test_cumulative_of_version_quarter_is_single_source() -> None: cumulative = _doc(Q3, True) assert select_sources(Q3, [cumulative]) == Sources(documents=(cumulative,), cumulative=True) def test_null_cumulative_flag_of_version_quarter_counts_as_cumulative() -> None: document = _doc(Q3, None) assert select_sources(Q3, [document]) == Sources(documents=(document,), cumulative=True) def test_quarterly_q1_to_q3_are_sources_in_quarter_order() -> None: q1, q2, q3 = _doc(Q1), _doc(Q2), _doc(Q3) assert select_sources(Q3, [q3, q1, q2]) == Sources(documents=(q1, q2, q3), cumulative=False) def test_missing_middle_quarter_is_gap() -> None: assert select_sources(Q3, [_doc(Q1), _doc(Q3)]) == Gap(missing_quarters=(Q2,)) def test_q1_version_quarterly_q1_is_enough() -> None: """Для версии Q1 квартальный Q1 равнозначен нарастающему: источник есть, пробела нет.""" q1 = _doc(Q1) assert select_sources(Q1, [q1]) == Sources(documents=(q1,), cumulative=False) def test_cumulative_beats_quarterly_of_same_pair() -> None: """Review Focus 2: квартальные Q1–Q3 и нарастающий Q3 — источник один, нарастающий.""" cumulative = _doc(Q3, True) assert select_sources(Q3, [_doc(Q1), _doc(Q2), _doc(Q3), cumulative]) == Sources((cumulative,), cumulative=True) def test_cumulative_beats_quarterly_with_gap() -> None: cumulative = _doc(Q3, True) assert select_sources(Q3, [_doc(Q1), cumulative]) == Sources((cumulative,), cumulative=True) def test_cumulative_of_earlier_quarter_is_ignored() -> None: """Review Focus 2 (защитно): нарастающий Q2 при версии Q3 не привязывается, но и функция его не берёт.""" assert select_sources(Q3, [_doc(Q2, True)]) == Gap(missing_quarters=(Q1, Q2, Q3)) def test_cumulative_of_earlier_quarter_does_not_fill_gap_of_quarterly() -> None: assert select_sources(Q3, [_doc(Q1), _doc(Q2, True), _doc(Q3)]) == Gap(missing_quarters=(Q2,)) def test_quarterly_after_version_and_without_quarter_are_ignored() -> None: q1, q2, q3 = _doc(Q1), _doc(Q2), _doc(Q3) assert select_sources(Q3, [q1, q2, q3, _doc(Q4), _doc(None), _doc(None, True)]) == Sources((q1, q2, q3), False) assert select_sources(Q2, [_doc(Q3)]) == Gap(missing_quarters=(Q1, Q2)) def test_no_documents_is_gap_of_all_quarters() -> None: assert select_sources(Q3, []) == Gap(missing_quarters=(Q1, Q2, Q3)) assert select_sources(Q1, []) == Gap(missing_quarters=(Q1,))