"""Domain events — what happened to a contract, in the past tense (modules/contracts domain_events).
They are WRITTEN as rows the aggregate already owns (ct_signature_event for the signing story, the state and
approval rows for the rest) and REPORTED by contracts.status / show_contract; the application layer also uses
them to run what a commit causes in a later unit of work (a signed renewal supersedes its original). An event is a
value: built once, never changed.
"""
from dataclasses import asdict, dataclass
from typing import Optional
@dataclass(frozen=True)
class DomainEvent:
contract_id: str
at: str # ISO 8601 UTC
@property
def name(self) -> str:
return type(self).__name__
def as_json(self) -> dict:
return {"event": self.name, **asdict(self)}
@dataclass(frozen=True)
class Drafted(DomainEvent):
"""A contract was drafted from an approved template version (missing[] and deviations[] computed)."""
template: str = ""
missing: tuple = ()
deviations: int = 0
@dataclass(frozen=True)
class Approved(DomainEvent):
"""An approval was granted for one draft fingerprint."""
fingerprint: str = ""
by: str = ""
@dataclass(frozen=True)
class Sent(DomainEvent):
"""Version N was frozen and signing links issued."""
version: int = 0
sha256: str = ""
@dataclass(frozen=True)
class Signed(DomainEvent):
"""Every signer of version N signed; the executed copy exists (or a copy signed elsewhere was filed)."""
version: Optional[int] = None
executed_sha256: str = ""
elsewhere: bool = False
@dataclass(frozen=True)
class Declined(DomainEvent):
"""A signer declined, with a reason, or asked for changes."""
signer_no: int = 0
changes_requested: bool = False
@dataclass(frozen=True)
class Expired(DomainEvent):
"""OUT_FOR_SIGNATURE passed its link lifetime unsigned (EXPIRED_UNSIGNED)."""
@dataclass(frozen=True)
class Amended(DomainEvent):
"""An amendment, renewal or termination was signed and the original superseded or terminated."""
original: str = ""
kind: str = ""
__all__ = ["DomainEvent", "Drafted", "Approved", "Sent", "Signed", "Declined", "Expired", "Amended"]