"""ВРЕМЕННЫЙ режим «период ОСВ из версии консолидации» (arm-core#103, настройка `CONSOLIDATION_PERIOD_FROM_VERSION`). Для тестов на произвольных ОСВ: документ ОСВ, загруженный в консолидацию (`stored_file.consolidation_id`), период шапки которого версии НЕ подходит, считается нарастающим за год и квартал этой консолидации. Документ, подходящий версии по шапке, остаётся как есть — квартальные ОСВ по-прежнему склеиваются, уже привязанные не задваиваются. Первичка не мутируется: подменяется только представление документа (`VersionPeriodDocument`) при привязке к версии (`fits`, ключ замены R-1), сборке свода (`select_sources`), чек-листе и колонке «ОСВ» периметра; карточка документа показывает период из шапки. Следствие: подменённые ОСВ одного счёта одного ЮЛ занимают один слот привязки (нарастающий за квартал версии), побеждает подтверждённая последней, и склейки кварталов у них нет. ББ/ОФР и файлы без консолидации (рабочий стол) режим не затрагивает. ASSUMPTION(OQ-161): правила в `05-domain-rules.md` нет — режим временный, по решению владельца 12.10.2026; по умолчанию выключен, без настройки поведение §2 / D-5 Среза 4 не меняется. """ import datetime import uuid from dataclasses import dataclass from typing import Protocol from src.models.enums import DocumentStatus, DocumentType, PeriodQuarter class DocumentFields(Protocol): """Поля документа, которые читают привязка, свод и чек-лист (структурный тип: `Document` подходит).""" @property def id(self) -> uuid.UUID: ... @property def legal_entity_id(self) -> uuid.UUID: ... @property def document_type(self) -> DocumentType: ... @property def osv_account_code(self) -> str | None: ... @property def period_year(self) -> int | None: ... @property def period_quarter(self) -> PeriodQuarter | None: ... @property def is_cumulative(self) -> bool | None: ... @property def status(self) -> DocumentStatus: ... @property def confirmed_at(self) -> datetime.datetime | None: ... @dataclass(frozen=True) class VersionPeriodDocument: """Документ ОСВ с периодом версии: поля документа как есть, период — год и квартал консолидации, нарастающий.""" id: uuid.UUID legal_entity_id: uuid.UUID document_type: DocumentType osv_account_code: str | None period_year: int period_quarter: PeriodQuarter is_cumulative: bool status: DocumentStatus confirmed_at: datetime.datetime | None def takes_version_period( *, enabled: bool, document_type: DocumentType, file_consolidation_id: uuid.UUID | None, consolidation_id: uuid.UUID, fits_by_header: bool, ) -> bool: """Берёт ли документ период версии: режим включён, документ — ОСВ, его файл загружен в эту консолидацию, а период шапки версии не подходит (`fits_by_header` — период шапки подходит версии, как в `fits` без проверки периметра).""" return ( enabled and document_type == DocumentType.OSV and file_consolidation_id == consolidation_id and not fits_by_header ) def with_version_period(document: DocumentFields, *, year: int, quarter: PeriodQuarter) -> VersionPeriodDocument: """Представление `document` как нарастающего за `year`/`quarter` версии; сам документ не меняется.""" return VersionPeriodDocument( id=document.id, legal_entity_id=document.legal_entity_id, document_type=document.document_type, osv_account_code=document.osv_account_code, period_year=year, period_quarter=quarter, is_cumulative=True, status=document.status, confirmed_at=document.confirmed_at, )