feat(statements): подтверждение формы — период и проверки §7, reviewed_by; форма в чек-листе и в переходе версии (D-9)
Для BALANCE/INCOME подтверждение требует определённого периода (PERIOD_REQUIRED) и пройденных либо акцептованных жёстких проверок §7 (CHECKS_NOT_PASSED, нарушения в detail.failures); записывает financial_statement_meta.reviewed_by/at. Шапка читается под блокировкой документа с refresh, чтобы не использовать устаревший акцепт. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
This commit is contained in:
parent
6c00a8705f
commit
167969c7c9
@ -59,7 +59,8 @@ async def list_entries(
|
|||||||
**NOT_FOUND,
|
**NOT_FOUND,
|
||||||
status.HTTP_409_CONFLICT: {
|
status.HTTP_409_CONFLICT: {
|
||||||
"model": ErrorDetail,
|
"model": ErrorDetail,
|
||||||
"description": "`ALREADY_CONFIRMED`, `FILE_SUPERSEDED`, `DOCUMENT_NOT_CONFIRMABLE`",
|
"description": "`ALREADY_CONFIRMED`, `FILE_SUPERSEDED`, `DOCUMENT_NOT_CONFIRMABLE`; для формы ББ/ОФР — "
|
||||||
|
"`PERIOD_REQUIRED`, `CHECKS_NOT_PASSED` (нарушения проверок — в `detail.failures`)",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
summary="Подтвердить документ",
|
summary="Подтвердить документ",
|
||||||
|
|||||||
@ -23,7 +23,9 @@ from src.domain.policy import VisibilityScope
|
|||||||
from src.models.enums import AuditAction, AuditSection, DocumentStatus, FileStatus
|
from src.models.enums import AuditAction, AuditSection, DocumentStatus, FileStatus
|
||||||
from src.models.files import Document, RecognitionJob, StoredFile
|
from src.models.files import Document, RecognitionJob, StoredFile
|
||||||
from src.models.osv import OsvEntry
|
from src.models.osv import OsvEntry
|
||||||
|
from src.models.statements import FinancialStatementMeta
|
||||||
from src.repositories import files as files_repo
|
from src.repositories import files as files_repo
|
||||||
|
from src.repositories import statements as statements_repo
|
||||||
from src.schemas.documents import DocumentOut, EntriesPageParams, GroupOut
|
from src.schemas.documents import DocumentOut, EntriesPageParams, GroupOut
|
||||||
from src.schemas.files import DocumentBrief, FileOut, JobBrief
|
from src.schemas.files import DocumentBrief, FileOut, JobBrief
|
||||||
from src.services import consolidation_documents
|
from src.services import consolidation_documents
|
||||||
@ -35,6 +37,8 @@ WARNINGS_KEY = "warnings" # `recognition_job.metrics.warnings` — предуп
|
|||||||
FILE_SUPERSEDED = "FILE_SUPERSEDED"
|
FILE_SUPERSEDED = "FILE_SUPERSEDED"
|
||||||
ALREADY_CONFIRMED = "ALREADY_CONFIRMED"
|
ALREADY_CONFIRMED = "ALREADY_CONFIRMED"
|
||||||
DOCUMENT_NOT_CONFIRMABLE = "DOCUMENT_NOT_CONFIRMABLE"
|
DOCUMENT_NOT_CONFIRMABLE = "DOCUMENT_NOT_CONFIRMABLE"
|
||||||
|
PERIOD_REQUIRED = "PERIOD_REQUIRED"
|
||||||
|
CHECKS_NOT_PASSED = "CHECKS_NOT_PASSED"
|
||||||
CONFIRMABLE_STATUSES = frozenset({DocumentStatus.RECOGNIZED, DocumentStatus.NEEDS_CHECK})
|
CONFIRMABLE_STATUSES = frozenset({DocumentStatus.RECOGNIZED, DocumentStatus.NEEDS_CHECK})
|
||||||
|
|
||||||
|
|
||||||
@ -146,13 +150,47 @@ class DocumentService:
|
|||||||
document = await self._visible(document_id)
|
document = await self._visible(document_id)
|
||||||
return await files_repo.list_entries(self._session, document.id, limit=page.limit, offset=page.offset)
|
return await files_repo.list_entries(self._session, document.id, limit=page.limit, offset=page.offset)
|
||||||
|
|
||||||
|
async def _check_statement(self, document: Document) -> FinancialStatementMeta | None:
|
||||||
|
"""Условия подтверждения формы ББ/ОФР (D-9): период определён, жёсткие проверки §7 пройдены или акцептованы.
|
||||||
|
|
||||||
|
Шапка читается под блокировками документа с `refresh` (иначе в сессии мог остаться устаревший акцепт).
|
||||||
|
Для не-формы и документа без шапки — `None`, условий нет.
|
||||||
|
"""
|
||||||
|
from src.services.statements import FORM_TYPES, StatementService # noqa: PLC0415 - цикл импорта
|
||||||
|
|
||||||
|
if document.document_type not in FORM_TYPES:
|
||||||
|
return None
|
||||||
|
meta = await statements_repo.get_meta(self._session, document.id, refresh=True)
|
||||||
|
if meta is None:
|
||||||
|
return None
|
||||||
|
if document.period_year is None or document.period_quarter is None:
|
||||||
|
raise ConflictError(PERIOD_REQUIRED, "у формы не определён период: поправьте дату отчёта")
|
||||||
|
failures = await StatementService(self._session, self._audit, self._scope).hard_failures(document, meta)
|
||||||
|
if failures:
|
||||||
|
raise ConflictError(
|
||||||
|
CHECKS_NOT_PASSED,
|
||||||
|
"не пройдены логические проверки формы: исправьте значения или акцептуйте с комментарием",
|
||||||
|
failures=[
|
||||||
|
{
|
||||||
|
"column_index": failure.column_index,
|
||||||
|
"relation": failure.relation,
|
||||||
|
"expected": str(failure.expected),
|
||||||
|
"actual": str(failure.actual),
|
||||||
|
}
|
||||||
|
for failure in failures
|
||||||
|
],
|
||||||
|
)
|
||||||
|
return meta
|
||||||
|
|
||||||
async def confirm(self, document_id: uuid.UUID) -> DocumentOut:
|
async def confirm(self, document_id: uuid.UUID) -> DocumentOut:
|
||||||
"""`RECOGNIZED`/`NEEDS_CHECK` → `CONFIRMED` (`confirmed_by/at`), аудит `DOCUMENT_CONFIRM`.
|
"""`RECOGNIZED`/`NEEDS_CHECK` → `CONFIRMED` (`confirmed_by/at`), аудит `DOCUMENT_CONFIRM`.
|
||||||
|
|
||||||
409: файл `SUPERSEDED` — `FILE_SUPERSEDED`; уже `CONFIRMED` — `ALREADY_CONFIRMED`; иной статус —
|
409: файл `SUPERSEDED` — `FILE_SUPERSEDED`; уже `CONFIRMED` — `ALREADY_CONFIRMED`; иной статус —
|
||||||
`DOCUMENT_NOT_CONFIRMABLE`. Порядок блокировок и перечитывание — см. docstring модуля. В той же транзакции
|
`DOCUMENT_NOT_CONFIRMABLE`; для формы ББ/ОФР далее — `PERIOD_REQUIRED` (период не определён) и
|
||||||
документ привязывается к подходящим версиям «в работе», версии из `INITIATED`/`DOCUMENTS_PROCESSING` переходят
|
`CHECKS_NOT_PASSED` (неакцептованные нарушения проверок §7 в `failures`; D-9). Порядок блокировок и
|
||||||
в `DATA_CORRECTION` (`on_document_confirmed`, spec documents-api «Подтверждение документа»).
|
перечитывание — см. docstring модуля. В той же транзакции документ привязывается к подходящим версиям
|
||||||
|
«в работе», версии из `INITIATED`/`DOCUMENTS_PROCESSING` переходят в `DATA_CORRECTION`
|
||||||
|
(`on_document_confirmed`, spec documents-api «Подтверждение документа»).
|
||||||
"""
|
"""
|
||||||
visible = await self._visible(document_id)
|
visible = await self._visible(document_id)
|
||||||
file = await files_repo.lock_file(self._session, visible.stored_file_id)
|
file = await files_repo.lock_file(self._session, visible.stored_file_id)
|
||||||
@ -170,12 +208,16 @@ class DocumentService:
|
|||||||
DOCUMENT_NOT_CONFIRMABLE,
|
DOCUMENT_NOT_CONFIRMABLE,
|
||||||
f"документ в статусе {document.status.value} подтвердить нельзя: нужен RECOGNIZED или NEEDS_CHECK",
|
f"документ в статусе {document.status.value} подтвердить нельзя: нужен RECOGNIZED или NEEDS_CHECK",
|
||||||
)
|
)
|
||||||
|
meta = await self._check_statement(document)
|
||||||
before = document.status
|
before = document.status
|
||||||
now = utc_now()
|
now = utc_now()
|
||||||
document.status = DocumentStatus.CONFIRMED
|
document.status = DocumentStatus.CONFIRMED
|
||||||
document.confirmed_by = self._audit.user_id
|
document.confirmed_by = self._audit.user_id
|
||||||
document.confirmed_at = now
|
document.confirmed_at = now
|
||||||
document.updated_at = now
|
document.updated_at = now
|
||||||
|
if meta is not None:
|
||||||
|
meta.reviewed_by = self._audit.user_id
|
||||||
|
meta.reviewed_at = now
|
||||||
await self._session.flush()
|
await self._session.flush()
|
||||||
await consolidation_documents.on_document_confirmed(
|
await consolidation_documents.on_document_confirmed(
|
||||||
self._session, self._audit.service, document=document, user_id=self._audit.user_id, now=now
|
self._session, self._audit.service, document=document, user_id=self._audit.user_id, now=now
|
||||||
|
|||||||
@ -15,8 +15,8 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
|||||||
from src.domain.statement_checks import CROSS_FORM_RELATION
|
from src.domain.statement_checks import CROSS_FORM_RELATION
|
||||||
from src.models.access import AppUser
|
from src.models.access import AppUser
|
||||||
from src.models.audit_ex import AuditEvent
|
from src.models.audit_ex import AuditEvent
|
||||||
from src.models.directories import LegalEntity, RegionalOffice
|
from src.models.directories import CompanyGroup, LegalEntity, RegionalOffice
|
||||||
from src.models.enums import AuditAction, DocumentStatus, DocumentType, FileStatus, RecognitionStatus
|
from src.models.enums import AuditAction, DocumentStatus, DocumentType, FileStatus, PeriodQuarter, RecognitionStatus
|
||||||
from src.models.files import Document, StoredFile
|
from src.models.files import Document, StoredFile
|
||||||
from src.models.statements import (
|
from src.models.statements import (
|
||||||
FinancialStatementColumn,
|
FinancialStatementColumn,
|
||||||
@ -25,7 +25,13 @@ from src.models.statements import (
|
|||||||
FinancialStatementValue,
|
FinancialStatementValue,
|
||||||
)
|
)
|
||||||
from tests.conftest import AuthClientFactory
|
from tests.conftest import AuthClientFactory
|
||||||
from tests.factories import make_company_group, make_document, make_legal_entity, make_stored_file
|
from tests.factories import (
|
||||||
|
make_company_group,
|
||||||
|
make_consolidation,
|
||||||
|
make_document,
|
||||||
|
make_legal_entity,
|
||||||
|
make_stored_file,
|
||||||
|
)
|
||||||
from tests.fixtures_helpers import GOLDEN_STATEMENTS, map_golden_snapshot
|
from tests.fixtures_helpers import GOLDEN_STATEMENTS, map_golden_snapshot
|
||||||
|
|
||||||
pytestmark = [pytest.mark.integration, pytest.mark.fixtures]
|
pytestmark = [pytest.mark.integration, pytest.mark.fixtures]
|
||||||
@ -600,3 +606,110 @@ async def test_patch_osv_is_not_statement(
|
|||||||
client = await auth_client(KA_RF)
|
client = await auth_client(KA_RF)
|
||||||
response = await client.patch(f"/documents/{osv.id}/statement", json={"header": {"units": "руб."}})
|
response = await client.patch(f"/documents/{osv.id}/statement", json={"header": {"units": "руб."}})
|
||||||
assert response.status_code == HTTPStatus.CONFLICT and response.json()["detail"]["code"] == "DOCUMENT_NOT_STATEMENT"
|
assert response.status_code == HTTPStatus.CONFLICT and response.json()["detail"]["code"] == "DOCUMENT_NOT_STATEMENT"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_confirm_requires_checks(
|
||||||
|
seeded: AsyncSession, fixtures_dir: Path, auth_client: AuthClientFactory
|
||||||
|
) -> None:
|
||||||
|
document = await form(seeded, fixtures_dir)
|
||||||
|
client = await auth_client(KA_RF)
|
||||||
|
await _break_balance(client, document.id)
|
||||||
|
blocked = await client.post(f"/documents/{document.id}/confirm")
|
||||||
|
assert blocked.status_code == HTTPStatus.CONFLICT and blocked.json()["detail"]["code"] == "CHECKS_NOT_PASSED"
|
||||||
|
failures = blocked.json()["detail"]["failures"]
|
||||||
|
assert failures and set(failures[0]) == {"column_index", "relation", "expected", "actual"}
|
||||||
|
assert all(isinstance(failure["expected"], str) and isinstance(failure["actual"], str) for failure in failures)
|
||||||
|
status = await seeded.scalar(select(Document.status).where(Document.id == document.id))
|
||||||
|
assert status is not DocumentStatus.CONFIRMED
|
||||||
|
await client.post(f"/documents/{document.id}/statement/checks/accept", json={"comment": "округление"})
|
||||||
|
confirmed = await client.post(f"/documents/{document.id}/confirm")
|
||||||
|
assert confirmed.status_code == HTTPStatus.OK and confirmed.json()["status"] == "CONFIRMED"
|
||||||
|
meta = (
|
||||||
|
await seeded.execute(
|
||||||
|
select(FinancialStatementMeta)
|
||||||
|
.where(FinancialStatementMeta.document_id == document.id)
|
||||||
|
.execution_options(populate_existing=True)
|
||||||
|
)
|
||||||
|
).scalar_one()
|
||||||
|
assert meta.reviewed_by is not None and meta.reviewed_at is not None
|
||||||
|
|
||||||
|
|
||||||
|
async def test_confirm_after_non_quarter_date_edit(
|
||||||
|
seeded: AsyncSession, fixtures_dir: Path, auth_client: AuthClientFactory
|
||||||
|
) -> None:
|
||||||
|
"""Review Focus 5: дата не конец квартала → квартал NULL, NEEDS_CHECK, PERIOD_REQUIRED; исправление даты снимает."""
|
||||||
|
document = await form(seeded, fixtures_dir)
|
||||||
|
client = await auth_client(KA_RF)
|
||||||
|
edit = await client.patch(f"/documents/{document.id}/statement", json={"header": {"report_date": "2025-10-31"}})
|
||||||
|
body = edit.json()
|
||||||
|
assert body["document"]["period_quarter"] is None and body["document"]["status"] == "NEEDS_CHECK"
|
||||||
|
blocked = await client.post(f"/documents/{document.id}/confirm")
|
||||||
|
assert blocked.status_code == HTTPStatus.CONFLICT and blocked.json()["detail"]["code"] == "PERIOD_REQUIRED"
|
||||||
|
await client.patch(f"/documents/{document.id}/statement", json={"header": {"report_date": "2025-09-30"}})
|
||||||
|
assert (await client.post(f"/documents/{document.id}/confirm")).status_code == HTTPStatus.OK
|
||||||
|
|
||||||
|
|
||||||
|
async def test_period_required_precedes_checks_not_passed(
|
||||||
|
seeded: AsyncSession, fixtures_dir: Path, auth_client: AuthClientFactory
|
||||||
|
) -> None:
|
||||||
|
"""Порядок D-9: при пустом периоде и нарушенной проверке — `PERIOD_REQUIRED`."""
|
||||||
|
document = await form(seeded, fixtures_dir)
|
||||||
|
client = await auth_client(KA_RF)
|
||||||
|
await _break_balance(client, document.id)
|
||||||
|
await client.patch(f"/documents/{document.id}/statement", json={"header": {"report_date": "2025-10-31"}})
|
||||||
|
blocked = await client.post(f"/documents/{document.id}/confirm")
|
||||||
|
assert blocked.status_code == HTTPStatus.CONFLICT and blocked.json()["detail"]["code"] == "PERIOD_REQUIRED"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_confirm_form_rereads_accept_under_lock(
|
||||||
|
seeded: AsyncSession, fixtures_dir: Path, auth_client: AuthClientFactory
|
||||||
|
) -> None:
|
||||||
|
"""Акцепт, поставленный параллельным запросом после загрузки шапки в сессию, виден подтверждению (T6.I1)."""
|
||||||
|
document = await form(seeded, fixtures_dir)
|
||||||
|
client = await auth_client(KA_RF)
|
||||||
|
await _break_balance(client, document.id)
|
||||||
|
held = ( # карта идентичности слабая: удерживаем шапку без акцепта, как это делает ждущий запрос
|
||||||
|
await seeded.execute(select(FinancialStatementMeta).where(FinancialStatementMeta.document_id == document.id))
|
||||||
|
).scalar_one()
|
||||||
|
assert held.checks_accepted_at is None
|
||||||
|
user_id = (await seeded.execute(select(AppUser.id).where(AppUser.email == KA_RF))).scalar_one()
|
||||||
|
await seeded.execute(
|
||||||
|
update(FinancialStatementMeta)
|
||||||
|
.where(FinancialStatementMeta.document_id == document.id)
|
||||||
|
.values(
|
||||||
|
checks_accepted_by=user_id,
|
||||||
|
checks_accepted_at=datetime.datetime.now(datetime.UTC),
|
||||||
|
checks_accepted_comment="x",
|
||||||
|
)
|
||||||
|
.execution_options(synchronize_session=False) # объект в сессии остаётся устаревшим
|
||||||
|
)
|
||||||
|
confirmed = await client.post(f"/documents/{document.id}/confirm")
|
||||||
|
assert confirmed.status_code == HTTPStatus.OK, confirmed.text
|
||||||
|
|
||||||
|
|
||||||
|
async def test_confirmed_form_in_checklist(
|
||||||
|
seeded: AsyncSession, fixtures_dir: Path, auth_client: AuthClientFactory
|
||||||
|
) -> None:
|
||||||
|
document = await form(seeded, fixtures_dir)
|
||||||
|
entity = await seeded.get(LegalEntity, document.legal_entity_id)
|
||||||
|
assert entity is not None
|
||||||
|
group = await seeded.get(CompanyGroup, entity.company_group_id)
|
||||||
|
assert group is not None
|
||||||
|
user = (await seeded.execute(select(AppUser).where(AppUser.email == KA_RF))).scalar_one()
|
||||||
|
_, version = await make_consolidation(
|
||||||
|
seeded, group=group, year=2025, quarter=PeriodQuarter.Q3, members=[entity], initiator=user
|
||||||
|
)
|
||||||
|
client = await auth_client(KA_RF)
|
||||||
|
assert (await client.post(f"/documents/{document.id}/confirm")).status_code == HTTPStatus.OK
|
||||||
|
checklist = (await client.get(f"/consolidations/versions/{version.id}/checklist")).json()
|
||||||
|
balance_index = next(i for i, rule in enumerate(checklist["rules"]) if rule["document_type"] == "BALANCE")
|
||||||
|
row = next(row for row in checklist["rows"] if row["legal_entity_id"] == str(entity.id))
|
||||||
|
assert row["cells"][balance_index]["status"] == "CONFIRMED"
|
||||||
|
meta = (
|
||||||
|
await seeded.execute(
|
||||||
|
select(FinancialStatementMeta)
|
||||||
|
.where(FinancialStatementMeta.document_id == document.id)
|
||||||
|
.execution_options(populate_existing=True)
|
||||||
|
)
|
||||||
|
).scalar_one()
|
||||||
|
assert meta.reviewed_by == user.id
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import asyncio
|
|||||||
import datetime
|
import datetime
|
||||||
import uuid
|
import uuid
|
||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@ -57,6 +58,7 @@ from tests.factories import (
|
|||||||
make_related_group,
|
make_related_group,
|
||||||
make_stored_file,
|
make_stored_file,
|
||||||
)
|
)
|
||||||
|
from tests.fixtures_helpers import GOLDEN_STATEMENTS, map_golden_snapshot
|
||||||
|
|
||||||
HTTP_OK = 200
|
HTTP_OK = 200
|
||||||
HTTP_CREATED = 201
|
HTTP_CREATED = 201
|
||||||
@ -64,6 +66,8 @@ ROSTOV = "Ростовский региональный филиал"
|
|||||||
KA_RF = "ka_rf@armka.local"
|
KA_RF = "ka_rf@armka.local"
|
||||||
ACCOUNT = "60"
|
ACCOUNT = "60"
|
||||||
YEAR = 2024
|
YEAR = 2024
|
||||||
|
FORM_YEAR = 2025 # период golden-формы `FORM_SNAPSHOT`
|
||||||
|
FORM_SNAPSHOT = GOLDEN_STATEMENTS / "mezheninovskaya" / "2025-Q3" / "1_Форма1_бух_09.json"
|
||||||
LOCK_WAIT_SECONDS = 5
|
LOCK_WAIT_SECONDS = 5
|
||||||
ROWS = [{"name": "ООО Контрагент", "inn": "7701234567", "closing_debit": 10}]
|
ROWS = [{"name": "ООО Контрагент", "inn": "7701234567", "closing_debit": 10}]
|
||||||
Q1, Q2, Q3, Q4 = PeriodQuarter.Q1, PeriodQuarter.Q2, PeriodQuarter.Q3, PeriodQuarter.Q4
|
Q1, Q2, Q3, Q4 = PeriodQuarter.Q1, PeriodQuarter.Q2, PeriodQuarter.Q3, PeriodQuarter.Q4
|
||||||
@ -1138,3 +1142,35 @@ async def test_on_documents_mapped_waits_for_transition_to_documents_processing(
|
|||||||
assert moved == [data["version_id"]]
|
assert moved == [data["version_id"]]
|
||||||
async with maker() as session:
|
async with maker() as session:
|
||||||
assert await _status(session, data["version_id"]) is ConsolidationStatus.DATA_CORRECTION
|
assert await _status(session, data["version_id"]) is ConsolidationStatus.DATA_CORRECTION
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.integration
|
||||||
|
@pytest.mark.fixtures
|
||||||
|
async def test_mapped_statement_moves_version_without_binding(seeded: AsyncSession, fixtures_dir: Path) -> None:
|
||||||
|
"""Форма двигает версию: `BALANCE` Q3 в `NEEDS_CHECK` (ИНН шапки ≠ ИНН владельца) → `DATA_CORRECTION`."""
|
||||||
|
group, entities = await _group(seeded)
|
||||||
|
user = await _user(seeded)
|
||||||
|
_, version = await make_consolidation(
|
||||||
|
seeded,
|
||||||
|
group=group,
|
||||||
|
year=FORM_YEAR,
|
||||||
|
quarter=Q3,
|
||||||
|
members=entities,
|
||||||
|
initiator=user,
|
||||||
|
status=ConsolidationStatus.DOCUMENTS_PROCESSING,
|
||||||
|
)
|
||||||
|
documents = await map_golden_snapshot(
|
||||||
|
seeded, entity=entities[0], path=fixtures_dir / FORM_SNAPSHOT, uploaded_by=user.id
|
||||||
|
)
|
||||||
|
balance = next(d for d in documents if d.document_type is DocumentType.BALANCE)
|
||||||
|
assert (balance.status, balance.period_year, balance.period_quarter) == (DocumentStatus.NEEDS_CHECK, FORM_YEAR, Q3)
|
||||||
|
file = await seeded.get(StoredFile, balance.stored_file_id)
|
||||||
|
assert file is not None
|
||||||
|
# `map_file` сам перехода не делает: он идёт после него, в `map_job_result`
|
||||||
|
assert await _status(seeded, version.id) is ConsolidationStatus.DOCUMENTS_PROCESSING
|
||||||
|
|
||||||
|
moved = await consolidation_documents.on_documents_mapped(seeded, file=file, documents=documents, now=utc_now())
|
||||||
|
|
||||||
|
assert moved == [version.id]
|
||||||
|
assert await _status(seeded, version.id) is ConsolidationStatus.DATA_CORRECTION
|
||||||
|
assert await _bound(seeded, version.id) == set()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user