diff --git a/app/assets/javascripts/app/controllers/_ui_element/core_workflow_condition.coffee b/app/assets/javascripts/app/controllers/_ui_element/core_workflow_condition.coffee index 6cedd5b90..24d8de723 100644 --- a/app/assets/javascripts/app/controllers/_ui_element/core_workflow_condition.coffee +++ b/app/assets/javascripts/app/controllers/_ui_element/core_workflow_condition.coffee @@ -50,15 +50,15 @@ class App.UiElement.core_workflow_condition extends App.UiElement.ApplicationSel operatorsType = 'active$': ['is'] - 'boolean$': ['is', 'is not', 'is set', 'not set'] - 'integer$': ['is', 'is not', 'is set', 'not set'] - '^select$': ['is', 'is not', 'is set', 'not set'] - '^tree_select$': ['is', 'is not', 'is set', 'not set'] - '^(input|textarea|richtext)$': ['is', 'is not', 'is set', 'not set', 'regex match', 'regex mismatch'] + 'boolean$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to'] + 'integer$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to'] + '^select$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to'] + '^tree_select$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to'] + '^(input|textarea|richtext)$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to', 'regex match', 'regex mismatch'] operatorsName = - '_id$': ['is', 'is not', 'is set', 'not set'] - '_ids$': ['is', 'is not', 'is set', 'not set'] + '_id$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to'] + '_ids$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to'] # merge config elements = {} @@ -175,7 +175,7 @@ class App.UiElement.core_workflow_condition extends App.UiElement.ApplicationSel currentOperator = elementRow.find('.js-operator option:selected').attr('value') name = @buildValueName(elementFull, elementRow, groupAndAttribute, elements, meta, attribute) - if _.contains(['is set', 'not set'], currentOperator) + if _.contains(['is set', 'not set', 'has changed'], currentOperator) elementRow.find('.js-value').addClass('hide').html('') return diff --git a/app/assets/javascripts/app/controllers/_ui_element/core_workflow_perform.coffee b/app/assets/javascripts/app/controllers/_ui_element/core_workflow_perform.coffee index 1e1969ff7..7ac20494c 100644 --- a/app/assets/javascripts/app/controllers/_ui_element/core_workflow_perform.coffee +++ b/app/assets/javascripts/app/controllers/_ui_element/core_workflow_perform.coffee @@ -128,7 +128,7 @@ class App.UiElement.core_workflow_perform extends App.UiElement.ApplicationSelec super(elementFull, elementRow, groupAndAttribute, elements, meta, attribute) @buildValueConfigMultiple: (config, meta) -> - if _.contains(['add_option', 'remove_option', 'set_fixed_to'], meta.operator) + if _.contains(['add_option', 'remove_option', 'set_fixed_to', 'select'], meta.operator) config.multiple = true config.nulloption = true else diff --git a/app/assets/javascripts/app/controllers/ticket_zoom/form_handler_core_workflow.coffee b/app/assets/javascripts/app/controllers/ticket_zoom/form_handler_core_workflow.coffee index fec12385a..558b72406 100644 --- a/app/assets/javascripts/app/controllers/ticket_zoom/form_handler_core_workflow.coffee +++ b/app/assets/javascripts/app/controllers/ticket_zoom/form_handler_core_workflow.coffee @@ -305,6 +305,11 @@ class App.FormHandlerCoreWorkflow screen: ui.screen } + # send last changed attribute only once for has changed condition + if ui.lastChangedAttribute + requestData.last_changed_attribute = ui.lastChangedAttribute + ui.lastChangedAttribute = '-' + if App.FormHandlerCoreWorkflow.useWebSockets() App.WebSocket.send(requestData) else diff --git a/app/models/core_workflow/condition.rb b/app/models/core_workflow/condition.rb index f2dd9676b..01dc07341 100644 --- a/app/models/core_workflow/condition.rb +++ b/app/models/core_workflow/condition.rb @@ -8,7 +8,7 @@ class CoreWorkflow::Condition def initialize(result_object:, workflow:) @user = result_object.user @payload = result_object.payload - @workflow = workflow + @workflow = workflow @attribute_object = result_object.attributes @result_object = result_object @check = nil diff --git a/app/models/core_workflow/condition/backend.rb b/app/models/core_workflow/condition/backend.rb index fa7e474b2..b3bcd7d9c 100644 --- a/app/models/core_workflow/condition/backend.rb +++ b/app/models/core_workflow/condition/backend.rb @@ -10,6 +10,10 @@ class CoreWorkflow::Condition::Backend attr_reader :value + def field + @key.sub(%r{.*\.}, '') + end + def object?(object) @condition_object.attributes.instance_of?(object) end diff --git a/app/models/core_workflow/condition/changed_to.rb b/app/models/core_workflow/condition/changed_to.rb new file mode 100644 index 000000000..4e5827563 --- /dev/null +++ b/app/models/core_workflow/condition/changed_to.rb @@ -0,0 +1,9 @@ +# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/ + +class CoreWorkflow::Condition::ChangedTo < CoreWorkflow::Condition::Backend + def match + return if !CoreWorkflow::Condition::HasChanged.new(condition_object: @condition_object, key: @key, condition: @condition, value: @value).match + + CoreWorkflow::Condition::Is.new(condition_object: @condition_object, key: @key, condition: @condition, value: @value).match + end +end diff --git a/app/models/core_workflow/condition/has_changed.rb b/app/models/core_workflow/condition/has_changed.rb new file mode 100644 index 000000000..150233d1a --- /dev/null +++ b/app/models/core_workflow/condition/has_changed.rb @@ -0,0 +1,9 @@ +# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/ + +class CoreWorkflow::Condition::HasChanged < CoreWorkflow::Condition::Backend + def match + return if @condition_object.payload['last_changed_attribute'] != field + + true + end +end diff --git a/app/models/core_workflow/result/fill_in.rb b/app/models/core_workflow/result/fill_in.rb index cd9c00da4..8b6a34031 100644 --- a/app/models/core_workflow/result/fill_in.rb +++ b/app/models/core_workflow/result/fill_in.rb @@ -11,7 +11,7 @@ class CoreWorkflow::Result::FillIn < CoreWorkflow::Result::Backend end def skip? - return true if fill_in_value.blank? + return true if fill_in_value.nil? return true if params_set? return true if fill_in_set? diff --git a/app/models/core_workflow/result/select.rb b/app/models/core_workflow/result/select.rb index 24ce3e5fc..695151dc9 100644 --- a/app/models/core_workflow/result/select.rb +++ b/app/models/core_workflow/result/select.rb @@ -11,7 +11,7 @@ class CoreWorkflow::Result::Select < CoreWorkflow::Result::Backend end def skip? - return true if select_value.blank? + return true if select_value.nil? return true if params_set? return true if select_set? diff --git a/spec/models/core_workflow_spec.rb b/spec/models/core_workflow_spec.rb index 065bb5c7c..31bc345fd 100644 --- a/spec/models/core_workflow_spec.rb +++ b/spec/models/core_workflow_spec.rb @@ -1752,4 +1752,64 @@ RSpec.describe CoreWorkflow, type: :model do expect(result[:restrict_values]['owner_id']).to eq(['']) end end + + describe 'Add clear selection action or has changed condition #3821' do + let!(:workflow_has_changed) do + create(:core_workflow, + object: 'Ticket', + condition_selected: { + 'ticket.priority_id': { + operator: 'has_changed', + }, + }) + end + let!(:workflow_changed_to) do + create(:core_workflow, + object: 'Ticket', + condition_selected: { + 'ticket.priority_id': { + operator: 'changed_to', + value: [ Ticket::Priority.find_by(name: '3 high').id.to_s ] + }, + }) + end + + context 'when priority changed' do + let(:payload) do + base_payload.merge('last_changed_attribute' => 'priority_id', 'params' => { 'priority_id' => Ticket::Priority.find_by(name: '3 high').id.to_s }) + end + + it 'does match on condition has changed' do + expect(result[:matched_workflows]).to include(workflow_has_changed.id) + end + + it 'does match on condition changed to' do + expect(result[:matched_workflows]).to include(workflow_changed_to.id) + end + end + + context 'when nothing changed' do + it 'does not match on condition has changed' do + expect(result[:matched_workflows]).not_to include(workflow_has_changed.id) + end + + it 'does not match on condition changed to' do + expect(result[:matched_workflows]).not_to include(workflow_changed_to.id) + end + end + + context 'when state changed' do + let(:payload) do + base_payload.merge('last_changed_attribute' => 'state_id') + end + + it 'does not match on condition has changed' do + expect(result[:matched_workflows]).not_to include(workflow_has_changed.id) + end + + it 'does not match on condition changed to' do + expect(result[:matched_workflows]).not_to include(workflow_changed_to.id) + end + end + end end