From 1957cd15dc4a0377aea81c07f2619db9ebc3f208 Mon Sep 17 00:00:00 2001 From: Tanmay Deep Sharma Date: Wed, 15 Jul 2026 18:56:15 +0530 Subject: [PATCH] feat(automations): allow inbox filter on delayed conversation rules inbox_id never changes after a conversation is created, so filtering a delayed conversation-level rule by inbox is safe: the status episode key still tracks the only mutable dimension, and inbox rides along as a static fire-time re-check. Allow status + inbox_id in the validation and the narrowed condition dropdown. --- .../settings/automation/AutomationRuleForm.vue | 11 ++++++++--- app/models/automation_rule.rb | 11 +++++++---- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/app/javascript/dashboard/routes/dashboard/settings/automation/AutomationRuleForm.vue b/app/javascript/dashboard/routes/dashboard/settings/automation/AutomationRuleForm.vue index 2d7c37dc5..23eb5463a 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/automation/AutomationRuleForm.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/automation/AutomationRuleForm.vue @@ -82,6 +82,8 @@ const MIN_DELAY_MINUTES = 10; const MAX_DELAY_MINUTES = 43200; // 30 days // Events where a delayed rule is meaningful: status-based waits and awaiting-agent/reply-chase. const DELAYED_EVENT_KEYS = ['conversation_updated', 'message_created']; +// Conversation-level delayed rules can filter on status plus immutable attributes (inbox). +const DELAYED_CONVERSATION_ATTRS = ['status', 'inbox_id']; const { t } = useI18n(); const { isCloudFeatureEnabled } = useAccount(); @@ -129,9 +131,11 @@ const filterTypes = computed(() => { if (!event || !props.automationTypes[event]) return []; let attributes = getTranslatedAttributes(props.automationTypes, event); - // A delayed conversation-level rule can only key its episode on status, so offer status alone. + // A delayed conversation-level rule can filter only on status and immutable attributes (inbox). if (isDelayed.value && event !== 'message_created') { - attributes = attributes.filter(attr => attr.key === 'status'); + attributes = attributes.filter(attr => + DELAYED_CONVERSATION_ATTRS.includes(attr.key) + ); } return attributes.map(attr => { @@ -229,7 +233,8 @@ const delayRestrictionReason = computed(() => { eventName.value !== 'message_created' && conditions.some( condition => - condition.attribute_key && condition.attribute_key !== 'status' + condition.attribute_key && + !DELAYED_CONVERSATION_ATTRS.includes(condition.attribute_key) ) ) { return 'CONVERSATION_NON_STATUS'; diff --git a/app/models/automation_rule.rb b/app/models/automation_rule.rb index 74c5295c0..e4aa56da6 100644 --- a/app/models/automation_rule.rb +++ b/app/models/automation_rule.rb @@ -23,6 +23,9 @@ class AutomationRule < ApplicationRecord include Reauthorizable EXECUTION_DELAY_RANGE = (10..43_200) # minutes: 10 min to 30 days + # Conversation-level delayed rules key their episode on status; only status and attributes + # that never change after the delay (inbox) are safe to also filter on. + DELAYED_CONVERSATION_ATTRIBUTES = %w[status inbox_id].freeze belongs_to :account has_many :pending_executions, class_name: 'AutomationRulePendingExecution', dependent: :delete_all @@ -113,13 +116,13 @@ class AutomationRule < ApplicationRecord errors.add(:execution_delay, 'cannot be used with attribute_changed conditions.') end - # Conversation-level episodes key on status_changed_at alone, so only status conditions - # can be delayed; other attributes would collapse distinct periods into one episode. + # Conversation-level episodes key on status_changed_at alone. Mutable attributes would collapse + # distinct periods into one episode, so only status and immutable filters (inbox) are allowed. def execution_delay_supported_event return if execution_delay.blank? || conditions.blank? || event_name == 'message_created' - return if conditions.all? { |obj| obj['attribute_key'] == 'status' } + return if conditions.all? { |obj| DELAYED_CONVERSATION_ATTRIBUTES.include?(obj['attribute_key']) } - errors.add(:execution_delay, 'only supports status conditions for conversation-level events.') + errors.add(:execution_delay, 'only supports status and inbox conditions for conversation-level events.') end def execution_config_changed?