from __future__ import annotations from dataclasses import asdict, dataclass, field from typing import Any @dataclass(frozen=True) class WorkflowAction: code: str label: str description: str = "" priority: str = "normal" target_url: str | None = None can_execute: bool = True reason_if_blocked: str | None = None document_id: str | None = None document_number: str | None = None def to_dict(self) -> dict[str, Any]: return asdict(self) @dataclass(frozen=True) class OpportunityDecision: """Single operational decision consumed by UI, tasks and audits.""" next_action: WorkflowAction reason: str available_actions: list[WorkflowAction] = field(default_factory=list) blocked_actions: list[WorkflowAction] = field(default_factory=list) warnings: list[str] = field(default_factory=list) commercial_stage: str = "REVIEW" financial_state: str = "unknown" physical_state: str = "unknown" ui_hints: dict[str, Any] = field(default_factory=dict) decision_version: str = "opportunity-flow-engine-v1" profile_name: str = "default" def to_dict(self) -> dict[str, Any]: data = asdict(self) # Backward-compatible aliases used by older UI/service code. data["action_code"] = self.next_action.code data["label"] = self.next_action.label data["description"] = self.next_action.description or self.reason data["priority"] = self.next_action.priority data["target_url"] = self.next_action.target_url data["can_execute"] = self.next_action.can_execute data["reason_if_blocked"] = self.next_action.reason_if_blocked data["document_id"] = self.next_action.document_id data["document_number"] = self.next_action.document_number return data