"""Event-sourced Revenue Memory for Dealix v3.""" from __future__ import annotations from dataclasses import dataclass, field from datetime import datetime from core._py_compat import UTC from core._py_compat import StrEnum from hashlib import sha256 from typing import Any from uuid import uuid4 class EventType(StrEnum): LEAD_CREATED = "lead.created" SIGNAL_DETECTED = "signal.detected" MESSAGE_SENT = "message.sent" REPLY_RECEIVED = "reply.received" MEETING_BOOKED = "meeting.booked" DEAL_WON = "deal.won" DEAL_LOST = "deal.lost" COMPLIANCE_BLOCKED = "compliance.blocked" AGENT_ACTION_EXECUTED = "agent.action_executed" @dataclass(frozen=True) class RevenueEvent: event_type: EventType customer_id: str aggregate_id: str payload: dict[str, Any] actor: str = "system" occurred_at: datetime = field(default_factory=lambda: datetime.now(UTC)) event_id: str = field(default_factory=lambda: str(uuid4())) def integrity_hash(self) -> str: raw = f"{self.event_id}|{self.event_type.value}|{self.customer_id}|{self.aggregate_id}|{self.occurred_at.isoformat()}|{self.payload}" return sha256(raw.encode()).hexdigest() def to_dict(self) -> dict[str, Any]: return { "event_id": self.event_id, "event_type": self.event_type.value, "customer_id": self.customer_id, "aggregate_id": self.aggregate_id, "payload": self.payload, "actor": self.actor, "occurred_at": self.occurred_at.isoformat(), "integrity_hash": self.integrity_hash(), } class RevenueMemory: def __init__(self) -> None: self._events: list[RevenueEvent] = [] def append(self, event: RevenueEvent) -> RevenueEvent: self._events.append(event) self._events.sort(key=lambda item: item.occurred_at) return event def timeline(self, aggregate_id: str) -> list[dict[str, Any]]: return [event.to_dict() for event in self._events if event.aggregate_id == aggregate_id] def projection(self, aggregate_id: str) -> dict[str, Any]: events = [event for event in self._events if event.aggregate_id == aggregate_id] result: dict[str, Any] = { "aggregate_id": aggregate_id, "events": len(events), "signals": 0, "messages": 0, "replies": 0, "meetings": 0, "revenue_sar": 0.0, "blocked": False, "stage": "unknown", } for event in events: if event.event_type == EventType.SIGNAL_DETECTED: result["signals"] += 1 elif event.event_type == EventType.MESSAGE_SENT: result["messages"] += 1 elif event.event_type == EventType.REPLY_RECEIVED: result["replies"] += 1 elif event.event_type == EventType.MEETING_BOOKED: result["meetings"] += 1 result["stage"] = "meeting_booked" elif event.event_type == EventType.DEAL_WON: result["stage"] = "won" result["revenue_sar"] += float(event.payload.get("amount", 0) or 0) elif event.event_type == EventType.DEAL_LOST: result["stage"] = "lost" elif event.event_type == EventType.COMPLIANCE_BLOCKED: result["blocked"] = True result["stage"] = "blocked" return result def demo_memory() -> RevenueMemory: memory = RevenueMemory() memory.append(RevenueEvent(EventType.SIGNAL_DETECTED, "demo", "clinic_riyadh_01", {"signal": "hiring_sales"})) memory.append(RevenueEvent(EventType.MESSAGE_SENT, "demo", "clinic_riyadh_01", {"channel": "whatsapp"})) memory.append(RevenueEvent(EventType.REPLY_RECEIVED, "demo", "clinic_riyadh_01", {"intent": "positive"})) memory.append(RevenueEvent(EventType.MEETING_BOOKED, "demo", "clinic_riyadh_01", {"date": "next_week"})) return memory