Modèle de données

Base SQLite elynav.db

Une seule base, trois familles de tables — Identity, App (meetings/conversations/reports), et CRM (accounts/opps/activities/séquences). Partagée avec les MCP servers du dossier Salesforce/.

⚠ Règle absolue

Les tables CRM (accounts, contacts, opportunities, etc.) sont excluded from EF migrations. Leur schéma est la propriété de Salesforce/crm/schema.sql. Toute évolution doit passer par ce fichier et être coordonnée avec les MCP servers Node qui écrivent dans la même DB.

Tables App (EF Core migrations)

Owned by Elynav Command. Évoluent via dotnet ef migrations dans ElynavCommand.Data/Migrations/.

erDiagram meetings ||--o{ meeting_participants : has meetings ||--o{ meeting_messages : has conversations ||--o{ conversation_participants : has conversations ||--o{ conversation_messages : has AspNetUsers ||--o{ meetings : "created_by" AspNetUsers ||--o{ conversations : "created_by" meetings { int Id PK string Title datetime ScheduledAt string Status "Scheduled|Live|Ended|Cancelled" string CreatedByUserId FK datetime CreatedAt datetime EndedAt string SummaryMd } meeting_participants { int Id PK int MeetingId FK string AgentName } meeting_messages { int Id PK int MeetingId FK string Role "User|Agent|System" string AgentName string ContentMd string ToolCallsJson int TurnIndex datetime CreatedAt } conversations { int Id PK string Title string Status "Active|Archived" string CreatedByUserId FK datetime CreatedAt datetime UpdatedAt datetime ArchivedAt } conversation_participants { int Id PK int ConversationId FK string AgentName bool IsActive datetime AddedAt } conversation_messages { int Id PK int ConversationId FK string Role string AgentName string ContentMd string ToolCallsJson int TurnIndex datetime CreatedAt } agent_daily_reports { int Id PK string AgentName date ReportDate string ContentMd string MetricsJson string Status "Pending|Ready|Failed" datetime GeneratedAt }
Meetings vs Conversations

Les meetings ont une heure prévue (ScheduledAt) et un cycle Scheduled → Live → Ended. Les conversations sont ad-hoc, sans horaire, et restent dans Active jusqu'à archivage.

TurnIndex

Incrément monotonique par meeting/conversation. Permet de garantir l'ordre d'affichage même si plusieurs messages arrivent en parallèle.

Tables CRM (schéma SQL pur)

Schéma authoritatif : Salesforce/crm/schema.sql. Read/write depuis EF, mais aussi depuis les MCP servers Node.

erDiagram accounts ||--o{ contacts : "has many" accounts ||--o{ opportunities : "has many" accounts ||--o{ activities : "logs" contacts ||--o{ opportunities : "primary contact" contacts ||--o{ activities : "involved in" contacts ||--o{ sequence_enrollments : "enrolled in" contacts ||--o{ email_events : "receives" opportunities ||--o{ activities : "moves" sequences ||--o{ sequence_steps : "ordered" sequences ||--o{ sequence_enrollments : "enrolls contacts" campaigns ||--o{ sequence_enrollments : "tied to" campaigns ||--o{ email_events : "tracks" accounts { int id PK text siren UK text name text industry text status "new|researching|contacted|engaged|qualified|customer|lost|excluded" int icp_score text icp_tier "A|B|C|D|OUT" text target_product "fleet|driver|both" int fleet_size_est int revenue_eur text department_code } contacts { int id PK int account_id FK text email text email_status text role_category bool do_not_contact int brevo_contact_id text language } opportunities { int id PK int account_id FK int primary_contact_id FK text name text product text stage "mql|sql|demo|proposal|negotiation|won|lost" int amount_eur int probability date expected_close } activities { int id PK int account_id FK int contact_id FK int opportunity_id FK text type text direction text agent datetime occurred_at } sequences { int id PK text name text product bool active } sequence_steps { int id PK int sequence_id FK int step_order int day_offset text channel text template_key } sequence_enrollments { int id PK int sequence_id FK int contact_id FK int campaign_id FK int current_step text status } campaigns { int id PK text name text channel text brevo_campaign_id int recipient_count int open_count } email_events { int id PK int contact_id FK int campaign_id FK text event_type "sent|delivered|opened|clicked|bounced|..." text brevo_message_id datetime occurred_at }

Vues SQL utilitaires

v_pipeline

Agrège les opportunités non closes par étape (stage) avec total et montant pondéré par la probabilité. Utilisé par la page /pipeline.

v_hot_accounts

Comptes en statut engaged/qualified ou ICP tier A, triés par score, avec nombre d'activités sur 14 jours et date de dernière activité. Utilisé par les agents SDR/AE pour prioriser.

Concurrence et SQLite

⚠ SQLite = 1 seule instance writer. Le conteneur Docker ne doit jamais être scalé à plus de 1 réplica. Le mode WAL (PRAGMA journal_mode = WAL) permet plusieurs lecteurs concurrents mais un seul écrivain. Si tu vois database is locked : c'est probablement un cron parallèle ou un second conteneur qui touche le même fichier.