fix(consolidation): режим «период из версии» подменяет период только у ОСВ «не того» периода (#103)
Квартальные ОСВ, загруженные в консолидацию своего периода и уже привязанные, в режиме становились «нарастающими за квартал версии»: select_sources возвращал все такие документы, и свод брал их строки как есть — консолидация РБ 2024 Q3 на стенде задвоилась бы. Теперь подмена — только если период шапки версии не подходит (fits_by_header); регрессионный тест test_mode_keeps_header_period_when_it_fits_version. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
a1614b05ef
commit
a6418ca475
@ -714,10 +714,11 @@ Q2–Q4 не подходят (R-14a). Привязываются только `
|
|||||||
- `period_check` документа остаётся `UNKNOWN` — подходящесть — функция (документ, версия), а не свойство документа (OQ-120).
|
- `period_check` документа остаётся `UNKNOWN` — подходящесть — функция (документ, версия), а не свойство документа (OQ-120).
|
||||||
- **Временный режим «период ОСВ из версии»** (`CONSOLIDATION_PERIOD_FROM_VERSION=true`, в контуре —
|
- **Временный режим «период ОСВ из версии»** (`CONSOLIDATION_PERIOD_FROM_VERSION=true`, в контуре —
|
||||||
`OPENBAO__APP__CONSOLIDATION_PERIOD_FROM_VERSION`; по умолчанию выключен; arm-core#103, OQ-161): ОСВ, загруженная в
|
`OPENBAO__APP__CONSOLIDATION_PERIOD_FROM_VERSION`; по умолчанию выключен; arm-core#103, OQ-161): ОСВ, загруженная в
|
||||||
консолидацию (`stored_file.consolidation_id`), считается нарастающей за год и квартал консолидации, какой бы период ни
|
консолидацию (`stored_file.consolidation_id`), период шапки которой версии **не подходит**, считается нарастающей за год
|
||||||
стоял в шапке, — для привязки, свода, чек-листа и колонки «ОСВ» периметра (`src/domain/version_period.py`). Документ
|
и квартал консолидации — для привязки, свода, чек-листа и колонки «ОСВ» периметра (`src/domain/version_period.py`).
|
||||||
не меняется (карточка показывает период из шапки); склейки кварталов нет — на счёт ЮЛ один слот, побеждает
|
ОСВ, подходящие по шапке, не трогаются (квартальные склеиваются как раньше). Документ не меняется (карточка показывает
|
||||||
подтверждённая последней; ББ/ОФР и файлы с рабочего стола режим не затрагивает. Только для тестов на произвольных ОСВ.
|
период из шапки); у подменённых ОСВ склейки нет — на счёт ЮЛ один слот, побеждает подтверждённая последней; ББ/ОФР и
|
||||||
|
файлы с рабочего стола режим не затрагивает. Только для тестов на произвольных ОСВ.
|
||||||
|
|
||||||
### Чек-лист
|
### Чек-лист
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +1,15 @@
|
|||||||
"""ВРЕМЕННЫЙ режим «период ОСВ из версии консолидации» (arm-core#103, настройка `CONSOLIDATION_PERIOD_FROM_VERSION`).
|
"""ВРЕМЕННЫЙ режим «период ОСВ из версии консолидации» (arm-core#103, настройка `CONSOLIDATION_PERIOD_FROM_VERSION`).
|
||||||
|
|
||||||
Для тестов на произвольных ОСВ: документ ОСВ, загруженный в консолидацию (`stored_file.consolidation_id`), считается
|
Для тестов на произвольных ОСВ: документ ОСВ, загруженный в консолидацию (`stored_file.consolidation_id`), период шапки
|
||||||
нарастающим за год и квартал этой консолидации, какой бы период ни стоял в его шапке. Первичка не мутируется:
|
которого версии НЕ подходит, считается нарастающим за год и квартал этой консолидации. Документ, подходящий версии по
|
||||||
подменяется только представление документа (`VersionPeriodDocument`) при привязке к версии (`fits`, ключ замены R-1),
|
шапке, остаётся как есть — квартальные ОСВ по-прежнему склеиваются, уже привязанные не задваиваются. Первичка не
|
||||||
сборке свода (`select_sources`), чек-листе и колонке «ОСВ» периметра; карточка документа показывает период из шапки.
|
мутируется: подменяется только представление документа (`VersionPeriodDocument`) при привязке к версии (`fits`, ключ
|
||||||
|
замены R-1), сборке свода (`select_sources`), чек-листе и колонке «ОСВ» периметра; карточка документа показывает период
|
||||||
|
из шапки.
|
||||||
|
|
||||||
Следствие: склейки кварталов нет — все ОСВ одного счёта одного ЮЛ в консолидации занимают один слот привязки, побеждает
|
Следствие: подменённые ОСВ одного счёта одного ЮЛ занимают один слот привязки (нарастающий за квартал версии), побеждает
|
||||||
подтверждённая последней. ББ/ОФР и файлы без консолидации (рабочий стол) режим не затрагивает.
|
подтверждённая последней, и склейки кварталов у них нет. ББ/ОФР и файлы без консолидации (рабочий стол) режим не
|
||||||
|
затрагивает.
|
||||||
|
|
||||||
ASSUMPTION(OQ-161): правила в `05-domain-rules.md` нет — режим временный, по решению владельца 12.10.2026; по умолчанию
|
ASSUMPTION(OQ-161): правила в `05-domain-rules.md` нет — режим временный, по решению владельца 12.10.2026; по умолчанию
|
||||||
выключен, без настройки поведение §2 / D-5 Среза 4 не меняется.
|
выключен, без настройки поведение §2 / D-5 Среза 4 не меняется.
|
||||||
@ -64,9 +67,16 @@ def takes_version_period(
|
|||||||
document_type: DocumentType,
|
document_type: DocumentType,
|
||||||
file_consolidation_id: uuid.UUID | None,
|
file_consolidation_id: uuid.UUID | None,
|
||||||
consolidation_id: uuid.UUID,
|
consolidation_id: uuid.UUID,
|
||||||
|
fits_by_header: bool,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Берёт ли документ период версии: режим включён, документ — ОСВ, его файл загружен в эту консолидацию."""
|
"""Берёт ли документ период версии: режим включён, документ — ОСВ, его файл загружен в эту консолидацию, а период
|
||||||
return enabled and document_type == DocumentType.OSV and file_consolidation_id == consolidation_id
|
шапки версии не подходит (`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:
|
def with_version_period(document: DocumentFields, *, year: int, quarter: PeriodQuarter) -> VersionPeriodDocument:
|
||||||
|
|||||||
@ -71,9 +71,9 @@ def period_from_version() -> bool:
|
|||||||
async def for_consolidation(
|
async def for_consolidation(
|
||||||
session: AsyncSession, documents: Sequence[Document], consolidation: Consolidation
|
session: AsyncSession, documents: Sequence[Document], consolidation: Consolidation
|
||||||
) -> list[DocumentFields]:
|
) -> list[DocumentFields]:
|
||||||
"""Документы так, как их видит консолидация: при режиме `period_from_version` ОСВ, загруженные в неё, — с периодом
|
"""Документы так, как их видит консолидация: при режиме `period_from_version` ОСВ, загруженные в неё и не подходящие
|
||||||
версии (`with_version_period`), прочие — как есть. Режим выключен — `documents` без запросов к БД; включён — один
|
версии по периоду шапки, — с периодом версии (`with_version_period`), прочие — как есть. Режим выключен —
|
||||||
запрос `consolidation_id` файлов."""
|
`documents` без запросов к БД; включён — один запрос `consolidation_id` файлов."""
|
||||||
if not period_from_version() or not documents:
|
if not period_from_version() or not documents:
|
||||||
return list(documents)
|
return list(documents)
|
||||||
file_ids = {document.stored_file_id for document in documents}
|
file_ids = {document.stored_file_id for document in documents}
|
||||||
@ -85,6 +85,7 @@ async def for_consolidation(
|
|||||||
document_type=document.document_type,
|
document_type=document.document_type,
|
||||||
file_consolidation_id=file_consolidations.get(document.stored_file_id),
|
file_consolidation_id=file_consolidations.get(document.stored_file_id),
|
||||||
consolidation_id=consolidation.id,
|
consolidation_id=consolidation.id,
|
||||||
|
fits_by_header=_period_fits(document, consolidation),
|
||||||
)
|
)
|
||||||
else document
|
else document
|
||||||
for document in documents
|
for document in documents
|
||||||
@ -128,7 +129,13 @@ def fits(
|
|||||||
(R-14; ББ/ОФР за `Q1` — только версии `Q1`, R-14a). Статус документа и файла не проверяется — это
|
(R-14; ББ/ОФР за `Q1` — только версии `Q1`, R-14a). Статус документа и файла не проверяется — это
|
||||||
дело вызывающего (привязываются только `CONFIRMED` незаменённых файлов). Период — у переданного представления:
|
дело вызывающего (привязываются только `CONFIRMED` незаменённых файлов). Период — у переданного представления:
|
||||||
в режиме «период из версии» вызывающий передаёт `for_consolidation(...)`. Чистая функция, без БД."""
|
в режиме «период из версии» вызывающий передаёт `for_consolidation(...)`. Чистая функция, без БД."""
|
||||||
if document.legal_entity_id not in member_ids or document.period_year != consolidation.year:
|
return document.legal_entity_id in member_ids and _period_fits(document, consolidation)
|
||||||
|
|
||||||
|
|
||||||
|
def _period_fits(document: DocumentFields, consolidation: Consolidation) -> bool:
|
||||||
|
"""Периодная часть `fits` (D-5, R-14, R-14a): год версии, квартал определён, нарастающий — за квартал версии,
|
||||||
|
квартальный — не позже него."""
|
||||||
|
if document.period_year != consolidation.year:
|
||||||
return False
|
return False
|
||||||
if document.period_quarter is None:
|
if document.period_quarter is None:
|
||||||
return False
|
return False
|
||||||
|
|||||||
@ -17,18 +17,23 @@ MEMBER = uuid.uuid4()
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("enabled", "document_type", "file_consolidation_id", "expected"),
|
("enabled", "document_type", "file_consolidation_id", "fits_by_header", "expected"),
|
||||||
[
|
[
|
||||||
(True, DocumentType.OSV, CONSOLIDATION, True), # ОСВ загружена в эту консолидацию
|
(True, DocumentType.OSV, CONSOLIDATION, False, True), # ОСВ «не того» периода загружена в эту консолидацию
|
||||||
(False, DocumentType.OSV, CONSOLIDATION, False), # режим выключен — период из шапки
|
(True, DocumentType.OSV, CONSOLIDATION, True, False), # период шапки и так подходит — склейка кварталов цела
|
||||||
(True, DocumentType.OSV, OTHER, False), # загружена в другую консолидацию
|
(False, DocumentType.OSV, CONSOLIDATION, False, False), # режим выключен — период из шапки
|
||||||
(True, DocumentType.OSV, None, False), # загружена с рабочего стола, без консолидации
|
(True, DocumentType.OSV, OTHER, False, False), # загружена в другую консолидацию
|
||||||
(True, DocumentType.BALANCE, CONSOLIDATION, False), # ББ/ОФР — только период из шапки
|
(True, DocumentType.OSV, None, False, False), # загружена с рабочего стола, без консолидации
|
||||||
(True, DocumentType.INCOME, CONSOLIDATION, False),
|
(True, DocumentType.BALANCE, CONSOLIDATION, False, False), # ББ/ОФР — только период из шапки
|
||||||
|
(True, DocumentType.INCOME, CONSOLIDATION, False, False),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_takes_version_period(
|
def test_takes_version_period(
|
||||||
enabled: bool, document_type: DocumentType, file_consolidation_id: uuid.UUID | None, expected: bool
|
enabled: bool,
|
||||||
|
document_type: DocumentType,
|
||||||
|
file_consolidation_id: uuid.UUID | None,
|
||||||
|
fits_by_header: bool,
|
||||||
|
expected: bool,
|
||||||
) -> None:
|
) -> None:
|
||||||
assert (
|
assert (
|
||||||
takes_version_period(
|
takes_version_period(
|
||||||
@ -36,6 +41,7 @@ def test_takes_version_period(
|
|||||||
document_type=document_type,
|
document_type=document_type,
|
||||||
file_consolidation_id=file_consolidation_id,
|
file_consolidation_id=file_consolidation_id,
|
||||||
consolidation_id=CONSOLIDATION,
|
consolidation_id=CONSOLIDATION,
|
||||||
|
fits_by_header=fits_by_header,
|
||||||
)
|
)
|
||||||
is expected
|
is expected
|
||||||
)
|
)
|
||||||
|
|||||||
@ -194,3 +194,44 @@ async def test_perimeter_pickup_binds_one_osv_per_account_latest_confirmed(seede
|
|||||||
|
|
||||||
assert bound == 1
|
assert bound == 1
|
||||||
assert await _bound(seeded, version.id) == {latest.id}
|
assert await _bound(seeded, version.id) == {latest.id}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.integration
|
||||||
|
async def test_mode_keeps_header_period_when_it_fits_version(
|
||||||
|
seeded: AsyncSession, auth_client: AuthClientFactory, monkeypatch: pytest.MonkeyPatch
|
||||||
|
) -> None:
|
||||||
|
"""Регрессия: квартальные ОСВ Q1–Q3, загруженные в консолидацию Q3 и привязанные до включения режима, в режиме
|
||||||
|
по-прежнему склеиваются в одну строку свода (их период из шапки подходит версии — подмены нет), а не идут «как есть»
|
||||||
|
трижды как нарастающие за Q3."""
|
||||||
|
office = (await seeded.execute(select(RegionalOffice).where(RegionalOffice.name == ROSTOV))).scalar_one()
|
||||||
|
group = await make_company_group(seeded, name=f"ГК {uuid.uuid4().hex[:6]}", office=office)
|
||||||
|
entities = [await make_legal_entity(seeded, name=f"ООО Член {n}", group=group) for n in range(2)]
|
||||||
|
user = (await seeded.execute(select(AppUser).where(AppUser.email == KA_RF))).scalar_one()
|
||||||
|
consolidation, version = await make_consolidation(
|
||||||
|
seeded, group=group, year=HEADER_YEAR, quarter=PeriodQuarter.Q3, members=entities, initiator=user
|
||||||
|
)
|
||||||
|
quarters = (Q1, Q2, PeriodQuarter.Q3)
|
||||||
|
for quarter in quarters:
|
||||||
|
document = await make_confirmed_osv(
|
||||||
|
seeded,
|
||||||
|
entity=entities[0],
|
||||||
|
account=ACCOUNT,
|
||||||
|
year=HEADER_YEAR,
|
||||||
|
quarter=quarter,
|
||||||
|
is_cumulative=False,
|
||||||
|
rows=[{"name": "ООО Контрагент", "inn": "7701234567", "turnover_debit": 10}],
|
||||||
|
)
|
||||||
|
file = await seeded.get(StoredFile, document.stored_file_id)
|
||||||
|
assert file is not None
|
||||||
|
file.consolidation_id = consolidation.id
|
||||||
|
await seeded.flush()
|
||||||
|
assert await consolidation_documents.bind_confirmed(
|
||||||
|
seeded, version=version, consolidation=consolidation, entity_ids=[entities[0].id]
|
||||||
|
) == len(quarters) # режим выключен: три квартальных источника
|
||||||
|
monkeypatch.setattr(settings, "CONSOLIDATION_PERIOD_FROM_VERSION", True)
|
||||||
|
client = await auth_client(KA_RF)
|
||||||
|
|
||||||
|
body = (await client.get(f"/consolidations/versions/{version.id}/osv?limit=100")).json()
|
||||||
|
|
||||||
|
assert body["total"] == 1, body
|
||||||
|
assert body["items"][0]["turnover_debit"] == "30.00"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user