refactor(whatsapp-call): migrate to unified Call model from PR #14026

Replace the WhatsappCall model with the unified Call model introduced
in feat/voice-call-model (PR #14026), enabling multi-provider support
(Twilio + WhatsApp) under a single data model.

Key changes:
- Delete WhatsappCall model and its 3 migrations (whatsapp_calls table)
- Enhance Call model with WhatsApp-specific helpers (sdp_offer,
  ice_servers, recording_url, direction_label, ringing?, in_progress?)
- Update all backend services, controller, and jobs to use Call model
- Map statuses: accepted→in_progress, rejected→failed, missed→no_answer,
  ended→completed
- Map directions: inbound→incoming, outbound→outgoing (enums)
- Rename call_id column to provider_call_id, always set provider: :whatsapp
- Add authorization check on initiate action
- Add race condition guard in outbound call_connect webhook handler
- Consolidate direction_label helper into Call model (DRY)
- Update frontend data key from waCallId to callId in VoiceCall bubble
- Remove whatsapp_calls table from schema.rb, fix missing started_at

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
root
2026-04-12 17:17:30 +00:00
co-authored by Claude Opus 4.6
parent 0a0915ace8
commit f49032610f
15 changed files with 210 additions and 266 deletions
+9 -5
View File
@@ -908,7 +908,9 @@ If any prerequisite is missing, the recording is still saved — only transcript
## 7. Data Model
### `whatsapp_calls` table
### `calls` table (unified Call model)
WhatsApp calls use the unified `Call` model (from PR #14026) with `provider: :whatsapp`.
| Column | Type | Description |
|---|---|---|
@@ -918,9 +920,11 @@ If any prerequisite is missing, the recording is still saved — only transcript
| `conversation_id` | bigint | FK → conversations |
| `accepted_by_agent_id` | bigint | FK → users (nullable) |
| `message_id` | bigint | FK → messages (nullable) — the voice_call message |
| `call_id` | string | Unique Meta call ID |
| `direction` | string | `inbound` or `outbound` |
| `status` | string | `ringing`, `accepted`, `rejected`, `missed`, `ended`, `failed` |
| `provider_call_id` | string | Unique provider call ID (Meta call ID for WhatsApp) |
| `provider` | integer | Enum: `twilio` (0), `whatsapp` (1) |
| `direction` | integer | Enum: `incoming` (0), `outgoing` (1) |
| `status` | string | `ringing`, `in_progress`, `completed`, `no_answer`, `failed` |
| `started_at` | datetime | When the call was answered |
| `duration_seconds` | integer | Call duration (set on termination) |
| `end_reason` | string | Reason from Meta (e.g., `caller_hangup`) |
| `meta` | jsonb | Stores `sdp_offer`, `sdp_answer`, `ice_servers` |
@@ -930,7 +934,7 @@ If any prerequisite is missing, the recording is still saved — only transcript
**ActiveStorage attachment:** `has_one_attached :recording` (audio/webm)
**Indexes:** `call_id` (unique), `[account_id, conversation_id]`, `[inbox_id, status]`, `message_id`
**Indexes:** `[provider, provider_call_id]` (unique), `[account_id, conversation_id]`, `message_id`
### Voice call message structure