Сквозной тест на фикстурах РБ показал: парсер помечает ОСВ за Q1 нарастающей, а привязка (`fits`) брала нарастающий только за квартал версии, `select_sources` не считал его квартальным Q1 — для версии Q3 привязано 17 из 22, пары 60/62(/76) в пробеле [Q1], свод пуст. - consolidation_periods.effective_cumulative: для Q1 — квартальный при любом флаге; одна функция для `fits`, ключа замены `_key`, `select_sources` (версии Q2+) и адаптера чек-листа; для версии Q1 поведение прежнее (№50) - тесты: таблица effective_cumulative, select_sources/build/чек-лист с нарастающим Q1, fits любой версии года, подтверждение и подхват (нарастающий и квартальный Q1 — один слот, побеждает поздний) - test_consolidation_fixtures: xfail снят, сценарий зелёный (22 привязки, 1385 строк) - спеки documents-api и consolidated-osv: фраза о равнозначности Q1; design #81; план Task 13 Step 2–4 и tasks.md 5.1 отмечены Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
169 lines
7.4 KiB
Python
169 lines
7.4 KiB
Python
"""Период консолидации (Срез 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,
|
||
effective_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,))
|
||
|
||
|
||
# ------------------------------------------------------------------------- R-14: документ за Q1 равнозначен обоим видам
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
("flag", "quarter", "expected"),
|
||
[
|
||
(True, Q1, False), # нарастающий с начала года за Q1 — это и есть квартал Q1
|
||
(None, Q1, False),
|
||
(False, Q1, False),
|
||
(True, Q2, True),
|
||
(None, Q3, True), # ББ/ОФР вне Q1 — нарастающие (решение №10)
|
||
(False, Q3, False),
|
||
(True, None, True), # квартал не определён — по флагу (такой документ не привязывается)
|
||
],
|
||
)
|
||
def test_effective_cumulative(flag: bool | None, quarter: PeriodQuarter | None, expected: bool) -> None:
|
||
assert effective_cumulative(flag, quarter) is expected
|
||
|
||
|
||
def test_cumulative_q1_is_quarterly_q1_source_for_later_version() -> None:
|
||
"""Фикстуры РБ: парсер помечает ОСВ за Q1 нарастающей, Q2/Q3 — квартальными; для версии Q3 это полный комплект."""
|
||
q1, q2, q3 = _doc(Q1, True), _doc(Q2), _doc(Q3)
|
||
assert select_sources(Q3, [q3, q2, q1]) == Sources(documents=(q1, q2, q3), cumulative=False)
|
||
assert select_sources(Q2, [q2, q1]) == Sources(documents=(q1, q2), cumulative=False)
|
||
|
||
|
||
def test_null_flag_q1_is_quarterly_q1_source_for_later_version() -> None:
|
||
q1, q2 = _doc(Q1, None), _doc(Q2)
|
||
assert select_sources(Q2, [q1, q2]) == Sources(documents=(q1, q2), cumulative=False)
|
||
|
||
|
||
def test_cumulative_q1_does_not_fill_other_quarters() -> None:
|
||
assert select_sources(Q2, [_doc(Q1, True)]) == Gap(missing_quarters=(Q2,))
|
||
# нарастающий Q2 при версии Q3 — не квартальный Q2: не хватает квартальных Q2 и Q3
|
||
assert select_sources(Q3, [_doc(Q1, True), _doc(Q2, True)]) == Gap(missing_quarters=(Q2, Q3))
|
||
|
||
|
||
def test_cumulative_of_version_quarter_still_beats_cumulative_q1() -> None:
|
||
cumulative = _doc(Q3, True)
|
||
assert select_sources(Q3, [_doc(Q1, True), _doc(Q2), _doc(Q3), cumulative]) == Sources((cumulative,), True)
|
||
|
||
|
||
def test_q1_version_cumulative_q1_is_taken_as_is() -> None:
|
||
"""Версия Q1: нарастающий Q1 — источник «как есть» (решение №50), квартальный — склейка из одного квартала."""
|
||
cumulative, quarterly = _doc(Q1, True), _doc(Q1, False)
|
||
assert select_sources(Q1, [cumulative]) == Sources((cumulative,), cumulative=True)
|
||
assert select_sources(Q1, [quarterly, cumulative]) == Sources((cumulative,), cumulative=True)
|
||
assert select_sources(Q1, [quarterly]) == Sources((quarterly,), cumulative=False)
|