Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BJKMLHdxMwSUeYcdkwqX4W
98 lines
3.5 KiB
Python
98 lines
3.5 KiB
Python
"""Статус документа и конверта (спека «Шаг 8 — статус документа и конверта»).
|
||
|
||
Одна функция на сервис и ядро: вход не зависит от источника строк.
|
||
"""
|
||
|
||
from collections.abc import Sequence
|
||
from dataclasses import dataclass
|
||
|
||
from arm_statements.model import (
|
||
DocStatus,
|
||
DocumentType,
|
||
EnvelopeStatus,
|
||
LineStatus,
|
||
PeriodKind,
|
||
Severity,
|
||
StatementDocument,
|
||
StatementWarning,
|
||
)
|
||
|
||
_ACCEPTABLE_LINES = frozenset({LineStatus.OK, LineStatus.DETAIL})
|
||
|
||
|
||
@dataclass(frozen=True, slots=True)
|
||
class StatusLine:
|
||
line_status: LineStatus
|
||
needs_review: bool
|
||
|
||
|
||
@dataclass(frozen=True, slots=True)
|
||
class StatusValue:
|
||
line_status: LineStatus
|
||
needs_review: bool
|
||
|
||
|
||
@dataclass(frozen=True, slots=True)
|
||
class StatusInput:
|
||
document_type: DocumentType
|
||
layout_found: bool
|
||
has_organization_or_inn: bool
|
||
period_known: bool
|
||
needs_review_fields: tuple[str, ...]
|
||
lines: tuple[StatusLine, ...]
|
||
values: tuple[StatusValue, ...]
|
||
hard_failures: int
|
||
accepted: bool
|
||
warning_severities: tuple[Severity, ...]
|
||
|
||
|
||
def document_status(data: StatusInput) -> DocStatus:
|
||
"""`DISCLOSURE` — всегда `done`; иначе `done` только при выполнении всех условий спеки."""
|
||
if data.document_type is DocumentType.DISCLOSURE:
|
||
return DocStatus.DONE
|
||
done = (
|
||
data.layout_found
|
||
and data.has_organization_or_inn
|
||
and data.period_known
|
||
and all(line.line_status in _ACCEPTABLE_LINES for line in data.lines)
|
||
and not any(line.needs_review for line in data.lines)
|
||
# значения строк detail в статус не входят (Р-23)
|
||
and not any(v.needs_review for v in data.values if v.line_status is not LineStatus.DETAIL)
|
||
and not data.needs_review_fields
|
||
and (data.hard_failures == 0 or data.accepted)
|
||
and Severity.ERROR not in data.warning_severities
|
||
)
|
||
return DocStatus.DONE if done else DocStatus.NEEDS_CHECK
|
||
|
||
|
||
def envelope_status(
|
||
document_statuses: Sequence[DocStatus], file_warnings: Sequence[StatementWarning]
|
||
) -> EnvelopeStatus:
|
||
if not document_statuses:
|
||
return EnvelopeStatus.ERROR
|
||
if all(s is DocStatus.DONE for s in document_statuses) and all(
|
||
w.severity is not Severity.ERROR for w in file_warnings
|
||
):
|
||
return EnvelopeStatus.DONE
|
||
return EnvelopeStatus.NEEDS_CHECK
|
||
|
||
|
||
def status_input(document: StatementDocument) -> StatusInput:
|
||
"""Вход функции статуса из документа шагов 2–8."""
|
||
header = document.header
|
||
period_known = header.report_date.value is not None or any(
|
||
c.period_kind is PeriodKind.ABSOLUTE and c.period_date is not None for c in document.columns
|
||
)
|
||
return StatusInput(
|
||
document_type=document.document_type,
|
||
layout_found=document.lines != (),
|
||
has_organization_or_inn=bool(header.organization_name.value or header.inn.value),
|
||
period_known=period_known,
|
||
needs_review_fields=header.needs_review_fields,
|
||
lines=tuple(StatusLine(line.line_status, line.needs_review) for line in document.lines),
|
||
values=tuple(StatusValue(line.line_status, v.needs_review) for line in document.lines for v in line.values),
|
||
hard_failures=0 if document.checks is None else len(document.checks.relations_failed),
|
||
accepted=False,
|
||
warning_severities=tuple(w.severity for w in document.warnings),
|
||
)
|