feat: add call model
This commit is contained in:
@@ -108,6 +108,7 @@ class Conversation < ApplicationRecord
|
||||
|
||||
has_many :mentions, dependent: :destroy_async
|
||||
has_many :messages, dependent: :destroy_async, autosave: true
|
||||
has_many :calls, dependent: :destroy_async
|
||||
has_one :csat_survey_response, dependent: :destroy_async
|
||||
has_many :conversation_participants, dependent: :destroy_async
|
||||
has_many :notifications, as: :primary_actor, dependent: :destroy_async
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
class CreateCalls < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
create_table :calls do |t|
|
||||
t.bigint :account_id, null: false
|
||||
t.bigint :inbox_id, null: false
|
||||
t.bigint :conversation_id, null: false
|
||||
t.bigint :message_id
|
||||
t.bigint :accepted_by_agent_id
|
||||
t.string :provider_call_id, null: false
|
||||
t.integer :provider, null: false, default: 0
|
||||
t.integer :direction, null: false
|
||||
t.string :status, null: false, default: 'ringing'
|
||||
t.integer :duration_seconds
|
||||
t.string :end_reason
|
||||
t.jsonb :meta, default: {}
|
||||
t.text :transcript
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
add_index :calls, [:provider, :provider_call_id], unique: true
|
||||
add_index :calls, [:account_id, :conversation_id]
|
||||
add_index :calls, :message_id
|
||||
add_index :calls, :meta, using: :gin
|
||||
end
|
||||
end
|
||||
+23
-1
@@ -10,7 +10,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[7.1].define(version: 2026_03_24_102005) do
|
||||
ActiveRecord::Schema[7.1].define(version: 2026_04_08_170902) do
|
||||
# These extensions should be enabled to support this database
|
||||
enable_extension "pg_stat_statements"
|
||||
enable_extension "pg_trgm"
|
||||
@@ -261,6 +261,28 @@ ActiveRecord::Schema[7.1].define(version: 2026_03_24_102005) do
|
||||
t.index ["account_id"], name: "index_automation_rules_on_account_id"
|
||||
end
|
||||
|
||||
create_table "calls", force: :cascade do |t|
|
||||
t.bigint "account_id", null: false
|
||||
t.bigint "inbox_id", null: false
|
||||
t.bigint "conversation_id", null: false
|
||||
t.bigint "message_id"
|
||||
t.bigint "accepted_by_agent_id"
|
||||
t.string "provider_call_id", null: false
|
||||
t.integer "provider", default: 0, null: false
|
||||
t.integer "direction", null: false
|
||||
t.string "status", default: "ringing", null: false
|
||||
t.integer "duration_seconds"
|
||||
t.string "end_reason"
|
||||
t.jsonb "meta", default: {}
|
||||
t.text "transcript"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["account_id", "conversation_id"], name: "index_calls_on_account_id_and_conversation_id"
|
||||
t.index ["message_id"], name: "index_calls_on_message_id"
|
||||
t.index ["meta"], name: "index_calls_on_meta", using: :gin
|
||||
t.index ["provider", "provider_call_id"], name: "index_calls_on_provider_and_provider_call_id", unique: true
|
||||
end
|
||||
|
||||
create_table "campaigns", force: :cascade do |t|
|
||||
t.integer "display_id", null: false
|
||||
t.string "title", null: false
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
class Call < ApplicationRecord
|
||||
# All valid call statuses
|
||||
STATUSES = %w[ringing in_progress completed no_answer failed].freeze
|
||||
# Statuses where the call is finished and won't change again
|
||||
TERMINAL_STATUSES = %w[completed no_answer failed].freeze
|
||||
|
||||
enum :provider, { twilio: 0, whatsapp: 1 }
|
||||
enum :direction, { incoming: 0, outgoing: 1 }
|
||||
|
||||
belongs_to :account
|
||||
belongs_to :inbox
|
||||
belongs_to :conversation
|
||||
belongs_to :message, optional: true
|
||||
belongs_to :accepted_by_agent, class_name: 'User', optional: true
|
||||
|
||||
has_one_attached :recording
|
||||
|
||||
validates :provider_call_id, presence: true
|
||||
validates :provider, presence: true
|
||||
validates :direction, presence: true
|
||||
validates :status, presence: true, inclusion: { in: STATUSES }
|
||||
|
||||
scope :active, -> { where.not(status: TERMINAL_STATUSES) }
|
||||
end
|
||||
@@ -17,6 +17,7 @@ module Enterprise::Concerns::Account
|
||||
has_many :copilot_threads, dependent: :destroy_async
|
||||
has_many :companies, dependent: :destroy_async
|
||||
has_many :voice_channels, dependent: :destroy_async, class_name: '::Channel::Voice'
|
||||
has_many :calls, dependent: :destroy_async
|
||||
|
||||
has_one :saml_settings, dependent: :destroy_async, class_name: 'AccountSamlSettings'
|
||||
end
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
# Voice as Twilio SMS Capability — QA Test Cases
|
||||
## Prerequisites
|
||||
|
||||
* Enterprise edition
|
||||
* `channel_voice` feature flag enabled: `Account.find(<id>).enable_features('channel_voice'); Account.find(<id>).save!`
|
||||
* Twilio account with a voice-capable phone number
|
||||
* Twilio API Key SID + API Key Secret
|
||||
|
||||
---
|
||||
|
||||
## Voice inbox creation
|
||||
|
||||
1. ~~Create Voice inbox via Voice tile with valid credentials — Channel created with voice_enabled: true, twiml_app_sid auto-provisioned, SMS and calls work~~
|
||||
2. ~~Create Voice inbox with a number that doesn't support voice (e.g., short code) — Fails with "This phone number does not support voice calls" error~~
|
||||
3. ~~Create Voice inbox with invalid Twilio credentials — Fails with clear error message~~
|
||||
|
||||
## Enable voice on existing SMS inbox
|
||||
|
||||
5. ~~SMS inbox (phone number, no ~~`~~api_key_sid~~`~~) → enable voice — Prompts for both ~~`~~api_key_sid~~`~~ and ~~`~~api_key_secret~~`~~, provisions TwiML app~~
|
||||
6. ~~SMS inbox (phone number, has ~~`~~api_key_sid~~`~~, no ~~`~~api_key_secret~~`~~) → enable voice — Prompts for ~~`~~api_key_secret~~`~~ only, provisions TwiML app~~
|
||||
7. ~~SMS inbox (phone number, has api_key_sid + api_key_secret) → enable voice — No credential fields, just toggle and update~~
|
||||
8. ~~SMS inbox (messaging_service_sid only) — Voice tab not shown (voice requires phone number)~~
|
||||
9. ~~Twilio WhatsApp inbox — Voice tab not shown (~~`~~medium !== 'sms'~~`~~)~~
|
||||
10. ~~SMS inbox without ~~`~~channel_voice~~`~~ feature flag — Voice tab not shown~~
|
||||
11. ~~Enable voice via API when ~~`~~channel_voice~~`~~ flag is off — Voice fields silently ignored~~
|
||||
|
||||
## Disable voice
|
||||
|
||||
12. ~~SMS+Voice channel → disable voice — TwiML app deleted from Twilio, ~~`~~twiml_app_sid~~`~~ cleared, ~~`~~api_key_sid~~`~~ and ~~`~~api_key_secret~~`~~ preserved, SMS still works~~
|
||||
13. ~~Disable voice when Twilio API fails (e.g., TwiML app already deleted) — Local cleanup still happens, no error shown to user~~
|
||||
|
||||
## Re-enable voice
|
||||
|
||||
14. ~~Previously disabled voice channel (has api_key_sid + api_key_secret) → re-enable — No credentials needed, new TwiML app provisioned~~
|
||||
15. ~~Previously disabled voice channel (has api_key_sid, api_key_secret is nil) → re-enable — Prompts for api_key_secret~~
|
||||
|
||||
## Voice settings tab UI
|
||||
|
||||
16. ~~Voice enabled + configured — Shows toggle (on) + voice webhook URLs~~
|
||||
17. ~~Voice disabled + credentials exist — Shows toggle (off) only, no credential fields~~
|
||||
18. ~~Voice disabled + no credentials → toggle on — Shows credential fields when toggled on~~
|
||||
19. ~~Click Update button — Shows spinner while saving~~
|
||||
20. ~~After update completes — Tab stays selected (doesn't jump to another tab)~~
|
||||
21. ~~Voice-enabled inbox still shows Business Hours, CSAT, Help Center, and Configuration tabs~~
|
||||
|
||||
## Inbox list and naming
|
||||
|
||||
22. ~~Voice-enabled TwilioSms inbox in inbox list — Shows "Voice" as channel name~~
|
||||
23. ~~Voice-disabled TwilioSms inbox in inbox list — Shows "Twilio SMS" as channel name~~
|
||||
24. ~~Voice-enabled inbox icon — Shows voice icon (phone)~~
|
||||
|
||||
## Inbox creation UI
|
||||
|
||||
25. ~~Voice tile shows "Coming Soon" when ~~`~~channel_voice~~`~~ feature flag is off~~
|
||||
26. ~~Voice tile is clickable when ~~`~~channel_voice~~`~~ feature flag is on~~
|
||||
27. ~~Voice tile description reads "Handle voice calls and SMS with Twilio"~~
|
||||
Reference in New Issue
Block a user