feat: add per-inbox toggle to disable incoming calls (#14645)
## Description Adds a per-inbox "Allow incoming calls" toggle for voice-enabled WhatsApp and Twilio inboxes. When turned off, the setting is persisted on the channel; actually rejecting inbound calls is handled in a follow-up PR. ## Type of change - [ ] New feature (non-breaking change which adds functionality) ## Screenshot <img width="804" height="384" alt="Screenshot 2026-06-04 at 11 44 07 AM" src="https://github.com/user-attachments/assets/df8bb026-0387-4031-bcba-6d9a56872eb7" /> ## Checklist: - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules
This commit is contained in:
@@ -60,6 +60,12 @@ class Inboxes extends CacheEnabledApiClient {
|
||||
disableWhatsappCalling(inboxId) {
|
||||
return axios.post(`${this.url}/${inboxId}/disable_whatsapp_calling`);
|
||||
}
|
||||
|
||||
setInboundCalls(inboxId, enabled) {
|
||||
return axios.post(`${this.url}/${inboxId}/set_inbound_calls`, {
|
||||
inbound_calls_enabled: enabled,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default new Inboxes();
|
||||
|
||||
@@ -653,6 +653,10 @@
|
||||
},
|
||||
"CREDENTIALS": {
|
||||
"DESCRIPTION": "Voice calling requires Twilio API Key credentials. These are used to generate tokens for agent voice connections."
|
||||
},
|
||||
"INBOUND": {
|
||||
"LABEL": "Allow incoming calls",
|
||||
"DESCRIPTION": "Let customers call this number. When turned off, incoming calls are declined automatically — agents aren't notified and no conversation is created. Agents can still place outgoing calls."
|
||||
}
|
||||
},
|
||||
"WHATSAPP_CALLING": {
|
||||
|
||||
+42
@@ -1,9 +1,11 @@
|
||||
<script>
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import InboxesAPI from 'dashboard/api/inboxes';
|
||||
import SettingsFieldSection from 'dashboard/components-next/Settings/SettingsFieldSection.vue';
|
||||
import SettingsToggleSection from 'dashboard/components-next/Settings/SettingsToggleSection.vue';
|
||||
import NextInput from 'dashboard/components-next/input/Input.vue';
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
import Spinner from 'dashboard/components-next/spinner/Spinner.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -11,6 +13,7 @@ export default {
|
||||
SettingsToggleSection,
|
||||
NextInput,
|
||||
NextButton,
|
||||
Spinner,
|
||||
},
|
||||
props: {
|
||||
inbox: {
|
||||
@@ -21,9 +24,11 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
voiceEnabled: this.inbox.voice_enabled || false,
|
||||
inboundCallsEnabled: this.inbox.inbound_calls_enabled !== false,
|
||||
apiKeySid: this.inbox.api_key_sid || '',
|
||||
apiKeySecret: '',
|
||||
isUpdating: false,
|
||||
isTogglingInbound: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -62,8 +67,27 @@ export default {
|
||||
'inbox.api_key_sid'(val) {
|
||||
this.apiKeySid = val || '';
|
||||
},
|
||||
'inbox.inbound_calls_enabled'(val) {
|
||||
this.inboundCallsEnabled = val !== false;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async handleInboundToggle(newValue) {
|
||||
if (this.isTogglingInbound) return;
|
||||
const previousValue = this.inboundCallsEnabled;
|
||||
this.inboundCallsEnabled = newValue;
|
||||
this.isTogglingInbound = true;
|
||||
try {
|
||||
await InboxesAPI.setInboundCalls(this.inbox.id, newValue);
|
||||
await this.$store.dispatch('inboxes/get', this.inbox.id);
|
||||
useAlert(this.$t('INBOX_MGMT.EDIT.API.SUCCESS_MESSAGE'));
|
||||
} catch (_) {
|
||||
this.inboundCallsEnabled = previousValue;
|
||||
useAlert(this.$t('INBOX_MGMT.EDIT.API.ERROR_MESSAGE'));
|
||||
} finally {
|
||||
this.isTogglingInbound = false;
|
||||
}
|
||||
},
|
||||
async updateVoiceSettings() {
|
||||
this.isUpdating = true;
|
||||
try {
|
||||
@@ -123,6 +147,24 @@ export default {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="inbox.voice_enabled"
|
||||
class="relative"
|
||||
:class="{ 'pointer-events-none opacity-60': isTogglingInbound }"
|
||||
>
|
||||
<SettingsToggleSection
|
||||
:model-value="inboundCallsEnabled"
|
||||
:header="$t('INBOX_MGMT.VOICE_CONFIGURATION.INBOUND.LABEL')"
|
||||
:description="$t('INBOX_MGMT.VOICE_CONFIGURATION.INBOUND.DESCRIPTION')"
|
||||
:hide-toggle="isTogglingInbound"
|
||||
@update:model-value="handleInboundToggle"
|
||||
>
|
||||
<template v-if="isTogglingInbound" #hiddenToggle>
|
||||
<Spinner class="size-4 text-n-slate-11" />
|
||||
</template>
|
||||
</SettingsToggleSection>
|
||||
</div>
|
||||
|
||||
<div v-if="inbox.voice_enabled && inbox.voice_call_webhook_url">
|
||||
<SettingsFieldSection
|
||||
:label="$t('INBOX_MGMT.ADD.VOICE.CONFIGURATION.TWILIO_VOICE_URL_TITLE')"
|
||||
|
||||
+41
@@ -24,10 +24,13 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
callingEnabled: this.inbox.provider_config?.calling_enabled || false,
|
||||
inboundCallsEnabled:
|
||||
this.inbox.provider_config?.inbound_calls_enabled !== false,
|
||||
permissionRequestBody:
|
||||
this.inbox.provider_config?.call_permission_request_body || '',
|
||||
isUpdating: false,
|
||||
isTogglingCalling: false,
|
||||
isTogglingInbound: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -44,8 +47,27 @@ export default {
|
||||
'inbox.provider_config.call_permission_request_body'(val) {
|
||||
this.permissionRequestBody = val || '';
|
||||
},
|
||||
'inbox.provider_config.inbound_calls_enabled'(val) {
|
||||
this.inboundCallsEnabled = val !== false;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async handleInboundToggle(newValue) {
|
||||
if (this.isTogglingInbound) return;
|
||||
const previousValue = this.inboundCallsEnabled;
|
||||
this.inboundCallsEnabled = newValue;
|
||||
this.isTogglingInbound = true;
|
||||
try {
|
||||
await InboxesAPI.setInboundCalls(this.inbox.id, newValue);
|
||||
await this.$store.dispatch('inboxes/get', this.inbox.id);
|
||||
useAlert(this.$t('INBOX_MGMT.EDIT.API.SUCCESS_MESSAGE'));
|
||||
} catch (_) {
|
||||
this.inboundCallsEnabled = previousValue;
|
||||
useAlert(this.$t('INBOX_MGMT.EDIT.API.ERROR_MESSAGE'));
|
||||
} finally {
|
||||
this.isTogglingInbound = false;
|
||||
}
|
||||
},
|
||||
async handleCallingToggle(newValue) {
|
||||
if (this.isTogglingCalling) return;
|
||||
const previousValue = this.callingEnabled;
|
||||
@@ -117,6 +139,25 @@ export default {
|
||||
</div>
|
||||
|
||||
<template v-if="callingEnabled">
|
||||
<div
|
||||
class="relative"
|
||||
:class="{ 'pointer-events-none opacity-60': isTogglingInbound }"
|
||||
>
|
||||
<SettingsToggleSection
|
||||
:model-value="inboundCallsEnabled"
|
||||
:header="$t('INBOX_MGMT.VOICE_CONFIGURATION.INBOUND.LABEL')"
|
||||
:description="
|
||||
$t('INBOX_MGMT.VOICE_CONFIGURATION.INBOUND.DESCRIPTION')
|
||||
"
|
||||
:hide-toggle="isTogglingInbound"
|
||||
@update:model-value="handleInboundToggle"
|
||||
>
|
||||
<template v-if="isTogglingInbound" #hiddenToggle>
|
||||
<Spinner class="size-4 text-n-slate-11" />
|
||||
</template>
|
||||
</SettingsToggleSection>
|
||||
</div>
|
||||
|
||||
<SettingsFieldSection
|
||||
v-if="phoneNumber"
|
||||
:label="$t('INBOX_MGMT.WHATSAPP_CALLING.PHONE_NUMBER.LABEL')"
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
# medium :integer default("sms")
|
||||
# messaging_service_sid :string
|
||||
# phone_number :string
|
||||
# provider_config :jsonb
|
||||
# twiml_app_sid :string
|
||||
# voice_enabled :boolean default(FALSE), not null
|
||||
# created_at :datetime not null
|
||||
@@ -54,6 +55,11 @@ class Channel::TwilioSms < ApplicationRecord
|
||||
medium == 'sms' ? 'Twilio SMS' : 'Whatsapp'
|
||||
end
|
||||
|
||||
# Mutes only the incoming side of calling; default on, so only an explicit false disables inbound.
|
||||
def inbound_calls_enabled?
|
||||
provider_config['inbound_calls_enabled'] != false
|
||||
end
|
||||
|
||||
def send_message(to:, body:, media_url: nil)
|
||||
params = send_message_from.merge(to: to, body: body)
|
||||
params[:media_url] = media_url if media_url.present?
|
||||
|
||||
@@ -49,6 +49,11 @@ class Channel::Whatsapp < ApplicationRecord
|
||||
account.feature_enabled?('channel_voice')
|
||||
end
|
||||
|
||||
# Mutes only the incoming side of calling; default on, so only an explicit false disables inbound.
|
||||
def inbound_calls_enabled?
|
||||
provider_config['inbound_calls_enabled'] != false
|
||||
end
|
||||
|
||||
# Whether this inbox can do WhatsApp calling at all. Meta's Calling API is
|
||||
# reachable by any whatsapp_cloud inbox, so 360dialog inboxes can't be toggled
|
||||
# on even though calling_enabled would persist.
|
||||
|
||||
@@ -77,4 +77,8 @@ class InboxPolicy < ApplicationPolicy
|
||||
def disable_whatsapp_calling?
|
||||
@account_user.administrator?
|
||||
end
|
||||
|
||||
def set_inbound_calls?
|
||||
@account_user.administrator?
|
||||
end
|
||||
end
|
||||
|
||||
@@ -140,6 +140,7 @@ end
|
||||
## Voice attributes for TwilioSms
|
||||
if resource.twilio? && resource.channel.respond_to?(:voice_enabled?)
|
||||
json.voice_enabled resource.channel.voice_enabled?
|
||||
json.inbound_calls_enabled resource.channel.inbound_calls_enabled?
|
||||
json.voice_configured resource.channel.try(:twiml_app_sid).present?
|
||||
json.has_api_key_secret resource.channel.try(:api_key_secret).present?
|
||||
if resource.channel.try(:twiml_app_sid).present?
|
||||
@@ -149,4 +150,7 @@ if resource.twilio? && resource.channel.respond_to?(:voice_enabled?)
|
||||
end
|
||||
|
||||
## Voice attribute for WhatsApp Cloud (only embedded-signup channels surface true)
|
||||
json.voice_enabled resource.channel.voice_enabled? if resource.channel_type == 'Channel::Whatsapp' && resource.channel.respond_to?(:voice_enabled?)
|
||||
if resource.channel_type == 'Channel::Whatsapp' && resource.channel.respond_to?(:voice_enabled?)
|
||||
json.voice_enabled resource.channel.voice_enabled?
|
||||
json.inbound_calls_enabled resource.channel.inbound_calls_enabled?
|
||||
end
|
||||
|
||||
@@ -267,6 +267,7 @@ Rails.application.routes.draw do
|
||||
end
|
||||
post :enable_whatsapp_calling, on: :member
|
||||
post :disable_whatsapp_calling, on: :member
|
||||
post :set_inbound_calls, on: :member
|
||||
end
|
||||
|
||||
resource :csat_template, only: [:show, :create], controller: 'inbox_csat_templates' do
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
class AddProviderConfigToChannelTwilioSms < ActiveRecord::Migration[7.1]
|
||||
def change
|
||||
add_column :channel_twilio_sms, :provider_config, :jsonb, default: {}
|
||||
end
|
||||
end
|
||||
+2
-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_05_25_093000) do
|
||||
ActiveRecord::Schema[7.1].define(version: 2026_06_04_000000) do
|
||||
# These extensions should be enabled to support this database
|
||||
enable_extension "pg_stat_statements"
|
||||
enable_extension "pg_trgm"
|
||||
@@ -557,6 +557,7 @@ ActiveRecord::Schema[7.1].define(version: 2026_05_25_093000) do
|
||||
t.boolean "voice_enabled", default: false, null: false
|
||||
t.string "twiml_app_sid"
|
||||
t.string "api_key_secret"
|
||||
t.jsonb "provider_config", default: {}
|
||||
t.index ["account_sid", "phone_number"], name: "index_channel_twilio_sms_on_account_sid_and_phone_number", unique: true
|
||||
t.index ["messaging_service_sid"], name: "index_channel_twilio_sms_on_messaging_service_sid", unique: true
|
||||
t.index ["phone_number"], name: "index_channel_twilio_sms_on_phone_number", unique: true
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
module Enterprise::Api::V1::Accounts::InboxesController
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def inbox_attributes
|
||||
super + ee_inbox_attributes
|
||||
end
|
||||
@@ -21,6 +23,24 @@ module Enterprise::Api::V1::Accounts::InboxesController
|
||||
render_could_not_create_error(e.message)
|
||||
end
|
||||
|
||||
# Toggles only the inbound-calls flag in provider_config. Saved with validate: false
|
||||
# so WhatsApp's remote credential re-check (validate_provider_config) can't reject a
|
||||
# simple toggle, mirroring enable_voice_calling!. Voice support (WhatsApp calling or
|
||||
# Twilio voice) is guarded inline by ensure_inbound_calls_supported.
|
||||
def set_inbound_calls
|
||||
return unless ensure_inbound_calls_supported
|
||||
|
||||
channel = @inbox.channel
|
||||
channel.provider_config = (channel.provider_config || {}).merge(
|
||||
'inbound_calls_enabled' => ActiveModel::Type::Boolean.new.cast(params[:inbound_calls_enabled])
|
||||
)
|
||||
channel.save!(validate: false)
|
||||
@inbox.update_account_cache # bump inbox cache key so the cached inbox list refetches the new flag
|
||||
head :ok
|
||||
rescue StandardError => e
|
||||
render_could_not_create_error(e.message)
|
||||
end
|
||||
|
||||
def ee_inbox_attributes
|
||||
[auto_assignment_config: [:max_assignment_limit]]
|
||||
end
|
||||
@@ -35,6 +55,14 @@ module Enterprise::Api::V1::Accounts::InboxesController
|
||||
false
|
||||
end
|
||||
|
||||
# Inbound calls can be toggled on any voice-enabled inbox (WhatsApp calling or Twilio voice).
|
||||
def ensure_inbound_calls_supported
|
||||
return true if @inbox.channel.try(:voice_enabled?)
|
||||
|
||||
render_could_not_create_error('Inbox does not support calling')
|
||||
false
|
||||
end
|
||||
|
||||
def allowed_channel_types
|
||||
super + ['voice']
|
||||
end
|
||||
|
||||
@@ -24,6 +24,8 @@ class Twilio::VoiceController < ApplicationController
|
||||
"TWILIO_VOICE_TWIML account=#{current_account.id} call_sid=#{twilio_call_sid} from=#{twilio_from} direction=#{twilio_direction}"
|
||||
)
|
||||
|
||||
return render xml: reject_twiml if reject_inbound?
|
||||
|
||||
call = resolve_call
|
||||
render xml: conference_twiml(call)
|
||||
end
|
||||
@@ -88,6 +90,16 @@ class Twilio::VoiceController < ApplicationController
|
||||
from_number.start_with?('client:')
|
||||
end
|
||||
|
||||
# A fresh contact-initiated leg on an inbox with inbound calls turned off.
|
||||
# Reject it so no conference, conversation, or Call row is created.
|
||||
def reject_inbound?
|
||||
twilio_direction == 'inbound' && !agent_leg?(twilio_from) && !inbox.channel.inbound_calls_enabled?
|
||||
end
|
||||
|
||||
def reject_twiml
|
||||
Twilio::TwiML::VoiceResponse.new(&:reject).to_s
|
||||
end
|
||||
|
||||
def resolve_call
|
||||
return find_call_for_agent if agent_leg?(twilio_from)
|
||||
|
||||
|
||||
@@ -72,6 +72,12 @@ class Whatsapp::IncomingCallService
|
||||
end
|
||||
|
||||
def create_inbound_call(payload)
|
||||
unless inbox.channel.inbound_calls_enabled?
|
||||
Rails.logger.info "[WHATSAPP CALL] Inbound calls disabled for inbox #{inbox.id}; rejecting call #{payload[:id]}"
|
||||
inbox.channel.provider_service.reject_call(payload[:id])
|
||||
return
|
||||
end
|
||||
|
||||
sdp_offer = payload.dig(:session, :sdp)
|
||||
extra_meta = { 'sdp_offer' => sdp_offer, 'ice_servers' => Call.default_ice_servers }
|
||||
name = caller_profile_name(payload)
|
||||
|
||||
@@ -49,6 +49,59 @@ RSpec.describe 'Enterprise Inboxes API', type: :request do
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v1/accounts/{account.id}/inboxes/:id/set_inbound_calls' do
|
||||
before do
|
||||
allow(Twilio::VoiceWebhookSetupService).to receive(:new)
|
||||
.and_return(instance_double(Twilio::VoiceWebhookSetupService, perform: "AP#{SecureRandom.hex(16)}"))
|
||||
end
|
||||
|
||||
context 'when administrator' do
|
||||
it 'disables inbound calls on a Twilio voice inbox' do
|
||||
channel = create(:channel_twilio_sms, :with_voice, account: account)
|
||||
|
||||
post "/api/v1/accounts/#{account.id}/inboxes/#{channel.inbox.id}/set_inbound_calls",
|
||||
headers: admin.create_new_auth_token,
|
||||
params: { inbound_calls_enabled: false },
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(channel.reload.inbound_calls_enabled?).to be false
|
||||
end
|
||||
|
||||
it 'enables inbound calls on a WhatsApp inbox without re-validating provider config' do
|
||||
account.enable_features('channel_voice')
|
||||
account.save!
|
||||
channel = create(:channel_whatsapp, account: account, provider: 'whatsapp_cloud',
|
||||
validate_provider_config: false, sync_templates: false)
|
||||
channel.update!(provider_config: channel.provider_config.merge('calling_enabled' => true, 'inbound_calls_enabled' => false))
|
||||
|
||||
post "/api/v1/accounts/#{account.id}/inboxes/#{channel.inbox.id}/set_inbound_calls",
|
||||
headers: admin.create_new_auth_token,
|
||||
params: { inbound_calls_enabled: true },
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(channel.reload.inbound_calls_enabled?).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when agent' do
|
||||
let(:agent) { create(:user, account: account, role: :agent) }
|
||||
|
||||
it 'is forbidden' do
|
||||
channel = create(:channel_twilio_sms, :with_voice, account: account)
|
||||
|
||||
post "/api/v1/accounts/#{account.id}/inboxes/#{channel.inbox.id}/set_inbound_calls",
|
||||
headers: agent.create_new_auth_token,
|
||||
params: { inbound_calls_enabled: false },
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
expect(channel.reload.inbound_calls_enabled?).to be true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PATCH /api/v1/accounts/{account.id}/inboxes/:id' do
|
||||
let(:inbox) { create(:inbox, account: account, auto_assignment_config: { max_assignment_limit: 5 }) }
|
||||
|
||||
|
||||
@@ -112,6 +112,23 @@ RSpec.describe 'Twilio::VoiceController', type: :request do
|
||||
}
|
||||
expect(response).to have_http_status(:not_found)
|
||||
end
|
||||
|
||||
it 'rejects the inbound contact leg without building a call when inbound calls are disabled' do
|
||||
channel.update!(provider_config: { 'inbound_calls_enabled' => false })
|
||||
expect(Voice::InboundCallBuilder).not_to receive(:perform!)
|
||||
|
||||
expect do
|
||||
post "/twilio/voice/call/#{digits}", params: {
|
||||
'CallSid' => call_sid,
|
||||
'From' => from_number,
|
||||
'To' => to_number,
|
||||
'Direction' => 'inbound'
|
||||
}
|
||||
end.not_to change(Call, :count)
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.body).to include('<Reject')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /twilio/voice/status/:phone' do
|
||||
|
||||
@@ -38,6 +38,19 @@ RSpec.describe Channel::TwilioSms do
|
||||
end
|
||||
end
|
||||
|
||||
describe '#inbound_calls_enabled?' do
|
||||
it 'returns true by default when nothing has been toggled' do
|
||||
channel = create(:channel_twilio_sms, :with_voice, account: account)
|
||||
expect(channel.inbound_calls_enabled?).to be true
|
||||
end
|
||||
|
||||
it 'returns false only when explicitly disabled in provider_config' do
|
||||
channel = create(:channel_twilio_sms, :with_voice, account: account,
|
||||
provider_config: { 'inbound_calls_enabled' => false })
|
||||
expect(channel.inbound_calls_enabled?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
describe '#voice_call_webhook_url' do
|
||||
it 'returns the webhook URL based on phone number' do
|
||||
channel = create(:channel_twilio_sms, :with_voice)
|
||||
|
||||
@@ -30,6 +30,20 @@ describe Whatsapp::IncomingCallService do
|
||||
end
|
||||
end
|
||||
|
||||
context 'when inbound calls are disabled on the channel' do
|
||||
it 'rejects the call with Meta without creating a Call or Conversation' do
|
||||
channel.provider_config = channel.provider_config.merge('inbound_calls_enabled' => false)
|
||||
channel.save!
|
||||
provider_service = instance_double(Whatsapp::Providers::WhatsappCloudService, reject_call: true)
|
||||
allow(inbox.channel).to receive(:provider_service).and_return(provider_service)
|
||||
|
||||
params = call_payload(event: 'connect', session: { sdp: "v=0\r\n...sdp...", sdp_type: 'offer' })
|
||||
expect { described_class.new(inbox: inbox, params: params).perform }
|
||||
.to not_change(Call, :count).and not_change(Conversation, :count)
|
||||
expect(provider_service).to have_received(:reject_call).with(provider_call_id)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'inbound connect' do
|
||||
let(:sdp_offer) { "v=0\r\n...sdp..." }
|
||||
let!(:agent) { create(:user, account: account) }
|
||||
|
||||
@@ -248,4 +248,22 @@ RSpec.describe Channel::Whatsapp do
|
||||
expect(channel.voice_enabled?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
describe '#inbound_calls_enabled?' do
|
||||
let(:account) { create(:account) }
|
||||
|
||||
it 'returns true by default when nothing has been toggled' do
|
||||
channel = create(:channel_whatsapp, account: account, provider: 'whatsapp_cloud',
|
||||
validate_provider_config: false, sync_templates: false)
|
||||
expect(channel.inbound_calls_enabled?).to be true
|
||||
end
|
||||
|
||||
it 'returns false only when explicitly disabled in provider_config' do
|
||||
channel = create(:channel_whatsapp, account: account, provider: 'whatsapp_cloud',
|
||||
validate_provider_config: false, sync_templates: false)
|
||||
channel.update!(provider_config: channel.provider_config.merge('inbound_calls_enabled' => false))
|
||||
|
||||
expect(channel.inbound_calls_enabled?).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user