Source code for aeat.application._workflow_review_models
"""Shared leaf models for the workflow and review packages.:mod:`~application.workflow` and :mod:`~application.review` need eachother's pydantic models at runtime: :class:`WorkflowEvent` is instantiated byreview actions and embedded as a field type on:class:`~application.review.InvoiceReviewRecord` and:class:`~application.review.LedgerReviewRecord`; those two review recordsare in turn embedded as field types on:class:`~application.workflow.WorkflowState`. Neither side can import theother's public facade without re-entering a partially-initialised packageduring Python's import machinery (the facade `__init__` for either packagepulls in the other), and pydantic's eager field-type resolution makes thedependency runtime-bound rather than annotation-only, so neither side of theformer direct cross-import was ``TYPE_CHECKING``-deferrable.This module is the structural fix: it is a leaf with no dependency on either:mod:`~application.workflow` or :mod:`~application.review`, so both packages importthese four names from here instead of from each other. :mod:`~application.workflow`re-exports :class:`WorkflowEvent` and :func:`utc_now` from its facade;:mod:`~application.review` re-exports :class:`InvoiceReviewRecord` and:class:`LedgerReviewRecord` fromits facade. Consumers outside these two packages are unaffected — they alreadyimport through the public facades, which keep re-exporting the same names.This module is private application-layer plumbing consumed only by:mod:`~application.workflow` and :mod:`~application.review`; it is notpart of the :mod:`~application` public surface and carries no `__all__`.See Also: :class:`~application.workflow.WorkflowState` Workflow aggregate that embeds review records from this leaf module. :class:`~application.review.InvoiceReviewRecord` Public review facade export for invoice annotations. :class:`~application.review.LedgerReviewRecord` Public review facade export for ledger transaction annotations. :class:`~core.identity.BucketId` Bucket identifier type carried by workflow events."""from__future__importannotationsfromdatetimeimportdatetimefrompydanticimportBaseModel,Field,field_validatorfrom..coreimportSTRICT_FROZEN_CONFIGas_STRICT_FROZENfrom..core.identityimportBucketIdfrom..core.timeimportnowasutc_nowfrom..domain.contribuyenteimportnormalise_key
[docs]classWorkflowEvent(BaseModel):"""One operator-visible event emitted by a mutating workflow verb. Events are appended to :attr:`~application.workflow.WorkflowState.bucket_events` so the operator can audit which actions ran, when, and against which object. ``action`` names the verb (e.g. ``"profile.created"``); ``reason`` carries a free-form human-readable annotation; ``bucket_id`` and ``object_id`` are optional pointers to the affected resource. """model_config=_STRICT_FROZENaction:str=Field(min_length=1)reason:str=""bucket_id:BucketId|None=Noneobject_id:str|None=Noneat:datetime=Field(default_factory=utc_now)@field_validator("action","reason")@classmethoddef_trim_text(cls,value:str)->str:returnvalue.strip()@field_validator("bucket_id","object_id")@classmethoddef_trim_optional_text(cls,value:str|None)->str|None:ifvalueisNone:returnNonetrimmed=value.strip()returntrimmedorNone
[docs]classLedgerReviewRecord(BaseModel):"""Workflow attention annotation for one persisted transaction. Durable transaction facts are not stored here. Classification, category, business percentage, tax fields, evidence references, skip/final-disposition state, and corrections live on the bucket-scoped transaction catalogue. """model_config=_STRICT_FROZENtransaction_id:str=Field(min_length=1)history:tuple[WorkflowEvent,...]=()updated_at:datetime=Field(default_factory=utc_now)
[docs]classInvoiceReviewRecord(BaseModel):"""Workflow annotations for one persisted invoice."""model_config=_STRICT_FROZENinvoice_id:str=Field(min_length=1)fields:dict[str,str]=Field(default_factory=dict)history:tuple[WorkflowEvent,...]=()updated_at:datetime=Field(default_factory=utc_now)@field_validator("fields")@classmethoddef_normalise_fields(cls,value:dict[str,str])->dict[str,str]:return{normalise_key(str(key)):str(raw).strip()forkey,rawinvalue.items()}