Снимки приёма и исхода получают необязательные kwargs: header_source (сырой текст шапки из financial_statement_meta.header_source) кладётся в header для ББ/ОФР, osv_row_count — в payload ОСВ как row_count. Без kwargs ключи не эмитятся. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
166 lines
6.2 KiB
Python
166 lines
6.2 KiB
Python
"""Чистые снимки payload фактов качества (§14.1): форма для ОБ/ОФР и для ОСВ."""
|
||
|
||
import uuid
|
||
from decimal import Decimal
|
||
|
||
from src.domain.quality_facts import build_intake_payload, build_outcome_payload
|
||
from src.models.enums import (
|
||
DocumentIssueKind,
|
||
DocumentStatus,
|
||
DocumentType,
|
||
IssueSeverity,
|
||
IssueSource,
|
||
StatementLineStatus,
|
||
)
|
||
from src.models.files import Document
|
||
from src.models.statements import DocumentIssue, FinancialStatementLine, FinancialStatementValue
|
||
|
||
ROW_COUNT = 4_200
|
||
|
||
|
||
def make_document(document_type: DocumentType = DocumentType.BALANCE) -> Document:
|
||
return Document(
|
||
id=uuid.uuid4(),
|
||
document_type=document_type,
|
||
status=DocumentStatus.RECOGNIZED,
|
||
)
|
||
|
||
|
||
def test_build_intake_payload_statement_has_lines_and_values():
|
||
document = make_document(document_type=DocumentType.BALANCE)
|
||
payload = build_intake_payload(document, lines=[...], values=[...], issues=[])
|
||
assert "lines" in payload and "values" in payload
|
||
assert "osv_warnings" not in payload
|
||
|
||
|
||
def test_build_intake_payload_osv_has_no_lines_or_values():
|
||
document = make_document(document_type=DocumentType.OSV)
|
||
payload = build_intake_payload(document, osv_warnings=["OSV_SOMETHING"])
|
||
assert "lines" not in payload and "values" not in payload
|
||
assert payload["osv_warnings"] == ["OSV_SOMETHING"]
|
||
|
||
|
||
def test_build_intake_payload_statement_serializes_line_value_and_issue_fields():
|
||
document = make_document(document_type=DocumentType.INCOME)
|
||
line_id = uuid.uuid4()
|
||
column_id = uuid.uuid4()
|
||
line = FinancialStatementLine(
|
||
id=line_id,
|
||
source_ref="row:3",
|
||
row_index=3,
|
||
line_code="2110",
|
||
line_code_text="2110",
|
||
line_status=StatementLineStatus.ok,
|
||
needs_review=True,
|
||
)
|
||
value = FinancialStatementValue(
|
||
line_id=line_id,
|
||
column_id=column_id,
|
||
amount=Decimal("1500.00"),
|
||
string_value=None,
|
||
source_text="1 500",
|
||
confidence=Decimal("95.00"),
|
||
is_recognized=True,
|
||
needs_review=False,
|
||
)
|
||
issue = DocumentIssue(
|
||
document_id=document.id,
|
||
kind=DocumentIssueKind.relation,
|
||
code="RELATION_BROKEN",
|
||
severity=IssueSeverity.error,
|
||
message="нарушено",
|
||
source=IssueSource.core,
|
||
relation="2110 = 2120",
|
||
codes=["2110"],
|
||
)
|
||
payload = build_intake_payload(document, lines=[line], values=[value], issues=[issue])
|
||
assert payload["lines"] == [
|
||
{
|
||
"id": str(line_id),
|
||
"ref": "row:3",
|
||
"row_index": 3,
|
||
"line_code": "2110",
|
||
"line_code_text": "2110",
|
||
"line_status": "ok",
|
||
"needs_review": True,
|
||
}
|
||
]
|
||
assert payload["values"] == [
|
||
{
|
||
"line_id": str(line_id),
|
||
"column_id": str(column_id),
|
||
"amount": "1500.00",
|
||
"string_value": None,
|
||
"source_text": "1 500",
|
||
"confidence": "95.00",
|
||
"is_recognized": True,
|
||
"needs_review": False,
|
||
}
|
||
]
|
||
assert payload["issues"] == [
|
||
{
|
||
"kind": "relation",
|
||
"code": "RELATION_BROKEN",
|
||
"severity": "error",
|
||
"source": "core",
|
||
"field": None,
|
||
"relation": "2110 = 2120",
|
||
"codes": ["2110"],
|
||
"line_ids": None,
|
||
}
|
||
]
|
||
|
||
|
||
def test_build_outcome_payload_statement_has_fixes_and_no_osv_warnings():
|
||
document = make_document(document_type=DocumentType.BALANCE)
|
||
fix = {"what": "line_code", "was": "2110", "became": "2120", "source": "service", "rule": "§5"}
|
||
payload = build_outcome_payload(document, lines=[], values=[], issues=[], fixes=[fix])
|
||
assert payload["fixes"] == [fix]
|
||
assert "lines" in payload and "values" in payload
|
||
assert "osv_warnings" not in payload
|
||
|
||
|
||
def test_build_outcome_payload_osv_has_fixes_and_no_lines():
|
||
document = make_document(document_type=DocumentType.OSV)
|
||
fix = {"what": "period", "was": "2024 Q1", "became": "2024 Q2", "source": "ours", "rule": "§5"}
|
||
payload = build_outcome_payload(document, osv_warnings=["OSV_SOMETHING"], fixes=[fix])
|
||
assert payload["osv_warnings"] == ["OSV_SOMETHING"]
|
||
assert payload["fixes"] == [fix]
|
||
assert "lines" not in payload and "values" not in payload
|
||
|
||
|
||
def test_build_intake_payload_includes_header_source_for_statement():
|
||
document = make_document(document_type=DocumentType.BALANCE)
|
||
header_source = {"inn": {"text": "7701234567", "confidence": Decimal("100.00")}}
|
||
payload = build_intake_payload(document, lines=[], values=[], header_source=header_source)
|
||
assert payload["header"]["header_source"] == {"inn": {"text": "7701234567", "confidence": "100.00"}}
|
||
|
||
|
||
def test_build_outcome_payload_includes_header_source_for_statement():
|
||
document = make_document(document_type=DocumentType.INCOME)
|
||
header_source = {"okpo": {"text": "12345678", "confidence": None}}
|
||
payload = build_outcome_payload(document, lines=[], values=[], fixes=[], header_source=header_source)
|
||
assert payload["header"]["header_source"] == {"okpo": {"text": "12345678", "confidence": None}}
|
||
|
||
|
||
def test_build_intake_payload_includes_osv_row_count():
|
||
document = make_document(document_type=DocumentType.OSV)
|
||
payload = build_intake_payload(document, osv_warnings=["OSV_SOMETHING"], osv_row_count=ROW_COUNT)
|
||
assert payload["row_count"] == ROW_COUNT
|
||
assert payload["osv_warnings"] == ["OSV_SOMETHING"]
|
||
|
||
|
||
def test_build_outcome_payload_includes_osv_row_count():
|
||
document = make_document(document_type=DocumentType.OSV)
|
||
payload = build_outcome_payload(document, osv_warnings=[], fixes=[], osv_row_count=0)
|
||
assert payload["row_count"] == 0
|
||
|
||
|
||
def test_header_source_and_row_count_absent_when_kwargs_omitted():
|
||
balance = build_intake_payload(make_document(document_type=DocumentType.BALANCE), lines=[], values=[])
|
||
assert "header_source" not in balance["header"]
|
||
osv = build_intake_payload(make_document(document_type=DocumentType.OSV), osv_warnings=[])
|
||
assert "row_count" not in osv
|
||
outcome_osv = build_outcome_payload(make_document(document_type=DocumentType.OSV), osv_warnings=[], fixes=[])
|
||
assert "row_count" not in outcome_osv
|