Fixes #864 - Added relative pending_time to Macro/Trigger/Scheduler functionality. Based on the Pull Request #2862 by @fleverest – huge thanks ❤️
This commit is contained in:
parent
e48f49a1a9
commit
22fe0a2a97
12 changed files with 363 additions and 6 deletions
|
@ -35,8 +35,13 @@ class App.UiElement.ticket_perform_action
|
|||
# ignore readonly attributes
|
||||
if !row.readonly
|
||||
config = _.clone(row)
|
||||
if config.tag is 'tag'
|
||||
config.operator = ['add', 'remove']
|
||||
|
||||
switch config.tag
|
||||
when 'datetime'
|
||||
config.operator = ['static', 'relative']
|
||||
when 'tag'
|
||||
config.operator = ['add', 'remove']
|
||||
|
||||
elements["#{groupKey}.#{config.name}"] = config
|
||||
|
||||
# add ticket deletion action
|
||||
|
@ -318,13 +323,22 @@ class App.UiElement.ticket_perform_action
|
|||
item = App.UiElement[tagSearch].render(config, {})
|
||||
else
|
||||
item = App.UiElement[config.tag].render(config, {})
|
||||
if meta.operator is 'before (relative)' || meta.operator is 'within next (relative)' || meta.operator is 'within last (relative)' || meta.operator is 'after (relative)'
|
||||
|
||||
relative_operators = [
|
||||
'before (relative)',
|
||||
'within next (relative)',
|
||||
'within last (relative)',
|
||||
'after (relative)',
|
||||
'relative'
|
||||
]
|
||||
|
||||
if _.include(relative_operators, meta.operator)
|
||||
config['name'] = "#{attribute.name}::#{groupAndAttribute}"
|
||||
if attribute.value && attribute.value[groupAndAttribute]
|
||||
config['value'] = _.clone(attribute.value[groupAndAttribute])
|
||||
item = App.UiElement['time_range'].render(config, {})
|
||||
|
||||
elementRow.find('.js-value').removeClass('hide').html(item)
|
||||
elementRow.find('.js-setAttribute > .flex > .js-value').removeClass('hide').html(item)
|
||||
|
||||
@buildNotificationArea: (notificationType, elementFull, elementRow, groupAndAttribute, elements, meta, attribute) ->
|
||||
|
||||
|
|
|
@ -133,6 +133,25 @@ class App.Ticket extends App.Model
|
|||
else
|
||||
@tagAdd(params.ticket.id, tag)
|
||||
|
||||
# apply pending date changes
|
||||
else if attributes[1] is 'pending_time' && content.operator is 'relative'
|
||||
pendtil = new Date
|
||||
diff = parseInt(content.value, 10)
|
||||
|
||||
switch content.range
|
||||
when 'day'
|
||||
pendtil.setDate(pendtil.getDate() + diff)
|
||||
when 'minute'
|
||||
pendtil.setMinutes(pendtil.getMinutes() + diff)
|
||||
when 'hour'
|
||||
pendtil.setHours(pendtil.getHours() + diff)
|
||||
when 'month'
|
||||
pendtil.setMonth(pendtil.getMonth() + diff)
|
||||
when 'year'
|
||||
pendtil.setYear(pendtil.getYear() + diff)
|
||||
|
||||
params.ticket[attributes[1]] = pendtil.toISOString()
|
||||
|
||||
# apply user changes
|
||||
else if attributes[1] is 'owner_id'
|
||||
if content.pre_condition is 'current_user.id'
|
||||
|
|
|
@ -11,5 +11,5 @@
|
|||
<%- @Icon('arrow-down', 'dropdown-arrow') %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="controls js-value"></div>
|
||||
</div>
|
||||
<div class="controls js-value horizontal"></div>
|
||||
</div>
|
||||
|
|
22
app/models/observer/ticket/pending_time.rb
Normal file
22
app/models/observer/ticket/pending_time.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
# Ensures pending time is always zero-seconds
|
||||
class Observer::Ticket::PendingTime < ActiveRecord::Observer
|
||||
observe 'ticket'
|
||||
|
||||
def before_create(record)
|
||||
_check(record)
|
||||
end
|
||||
|
||||
def before_update(record)
|
||||
_check(record)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def _check(record)
|
||||
return true if record.pending_time.blank?
|
||||
return true if !record.pending_time_changed?
|
||||
return true if record.pending_time.sec.zero?
|
||||
|
||||
record.pending_time = record.pending_time.change sec: 0
|
||||
end
|
||||
end
|
|
@ -907,6 +907,38 @@ perform changes on ticket
|
|||
next
|
||||
end
|
||||
|
||||
# Apply pending_time changes
|
||||
if key == 'ticket.pending_time'
|
||||
new_value = case value['operator']
|
||||
when 'static'
|
||||
value['value']
|
||||
when 'relative'
|
||||
pendtil = Time.zone.now
|
||||
val = value['value'].to_i
|
||||
|
||||
case value['range']
|
||||
when 'day'
|
||||
pendtil += val.days
|
||||
when 'minute'
|
||||
pendtil += val.minutes
|
||||
when 'hour'
|
||||
pendtil += val.hours
|
||||
when 'month'
|
||||
pendtil += val.months
|
||||
when 'year'
|
||||
pendtil += val.years
|
||||
end
|
||||
|
||||
pendtil
|
||||
end
|
||||
|
||||
if new_value
|
||||
self[attribute] = new_value
|
||||
changed = true
|
||||
next
|
||||
end
|
||||
end
|
||||
|
||||
# update tags
|
||||
if key == 'ticket.tags'
|
||||
next if value['value'].blank?
|
||||
|
|
14
app/views/tests/ticket_macro.html.erb
Normal file
14
app/views/tests/ticket_macro.html.erb
Normal file
|
@ -0,0 +1,14 @@
|
|||
|
||||
<link rel="stylesheet" href="/assets/tests/qunit-1.21.0.css">
|
||||
<%= javascript_include_tag "/assets/tests/qunit-1.21.0.js", "/assets/tests/ticket_macro.js", nonce: true %>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding-top: 0px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
|
||||
<div id="qunit" class="u-dontfold"></div>
|
|
@ -29,6 +29,7 @@ module Zammad
|
|||
'observer::_session',
|
||||
'observer::_ticket::_close_time',
|
||||
'observer::_ticket::_last_owner_update',
|
||||
'observer::_ticket::_pending_time',
|
||||
'observer::_ticket::_user_ticket_counter',
|
||||
'observer::_ticket::_article_changes',
|
||||
'observer::_ticket::_article::_fillup_from_origin_by_id',
|
||||
|
|
|
@ -25,6 +25,7 @@ Zammad::Application.routes.draw do
|
|||
match '/tests_table', to: 'tests#table', via: :get
|
||||
match '/tests_table_extended', to: 'tests#table_extended', via: :get
|
||||
match '/tests_html_utils', to: 'tests#html_utils', via: :get
|
||||
match '/tests_ticket_macro', to: 'tests#ticket_macro', via: :get
|
||||
match '/tests_ticket_selector', to: 'tests#ticket_selector', via: :get
|
||||
match '/tests_taskbar', to: 'tests#taskbar', via: :get
|
||||
match '/tests_text_module', to: 'tests#text_module', via: :get
|
||||
|
|
|
@ -237,6 +237,104 @@ test( "ticket_perform_action check", function() {
|
|||
}
|
||||
}
|
||||
deepEqual(params, test_params, 'form param check')
|
||||
|
||||
// add pending time
|
||||
$('[data-attribute-name="ticket_perform_action3"] .js-add').last().click()
|
||||
|
||||
var row = $('[data-attribute-name="ticket_perform_action3"] .js-filterElement').last()
|
||||
|
||||
var date_string = '2010-07-15T12:00:00.000Z'
|
||||
var date_parsed = new Date(date_string) // make sure it works regardless of browser locale
|
||||
|
||||
row.find('.js-attributeSelector .form-control').last().val('ticket.pending_time').trigger('change')
|
||||
row.find('.js-datepicker').val(date_parsed.toLocaleDateString()).trigger('blur')
|
||||
row.find('.js-datepicker').datepicker('setDate')
|
||||
row.find('.js-timepicker').val(date_parsed.getHours() + ':' + date_parsed.getMinutes()).trigger('blur')
|
||||
|
||||
params = App.ControllerForm.params(el)
|
||||
test_params = {
|
||||
ticket_perform_action1: {
|
||||
'ticket.state_id': {
|
||||
value: '2'
|
||||
}
|
||||
},
|
||||
ticket_perform_action2: {
|
||||
'notification.email': {
|
||||
body: 'some body',
|
||||
internal: 'true',
|
||||
recipient: 'ticket_customer',
|
||||
subject: 'some subject'
|
||||
},
|
||||
'ticket.priority_id': {
|
||||
value: '2'
|
||||
},
|
||||
'ticket.state_id': {
|
||||
value: '1'
|
||||
},
|
||||
},
|
||||
ticket_perform_action3: {
|
||||
'notification.email': {
|
||||
body: 'some body',
|
||||
internal: 'false',
|
||||
recipient: 'ticket_owner',
|
||||
subject: 'some subject'
|
||||
},
|
||||
'ticket.pending_time': {
|
||||
operator: 'static',
|
||||
value: date_string
|
||||
},
|
||||
'ticket.state_id': {
|
||||
value: '3'
|
||||
}
|
||||
}
|
||||
}
|
||||
deepEqual(params, test_params, 'form param check')
|
||||
|
||||
// switch pending time to relative
|
||||
|
||||
row.find('.js-operator select').val('relative').trigger('change')
|
||||
row.find('.js-range').val('day').trigger('change')
|
||||
row.find('.js-value').val('10').trigger('change')
|
||||
|
||||
params = App.ControllerForm.params(el)
|
||||
test_params = {
|
||||
ticket_perform_action1: {
|
||||
'ticket.state_id': {
|
||||
value: '2'
|
||||
}
|
||||
},
|
||||
ticket_perform_action2: {
|
||||
'notification.email': {
|
||||
body: 'some body',
|
||||
internal: 'true',
|
||||
recipient: 'ticket_customer',
|
||||
subject: 'some subject'
|
||||
},
|
||||
'ticket.priority_id': {
|
||||
value: '2'
|
||||
},
|
||||
'ticket.state_id': {
|
||||
value: '1'
|
||||
},
|
||||
},
|
||||
ticket_perform_action3: {
|
||||
'notification.email': {
|
||||
body: 'some body',
|
||||
internal: 'false',
|
||||
recipient: 'ticket_owner',
|
||||
subject: 'some subject'
|
||||
},
|
||||
'ticket.pending_time': {
|
||||
operator: 'relative',
|
||||
range: 'day',
|
||||
value: '10'
|
||||
},
|
||||
'ticket.state_id': {
|
||||
value: '3'
|
||||
}
|
||||
}
|
||||
}
|
||||
deepEqual(params, test_params, 'form param check')
|
||||
});
|
||||
|
||||
// Test for backwards compatibility after issue is fixed https://github.com/zammad/zammad/issues/2782
|
||||
|
@ -363,3 +461,46 @@ test( "ticket_perform_action rows manipulation", function() {
|
|||
|
||||
equal($(selector + ' .js-filterElement').length, 1, 'prevents removing last row')
|
||||
});
|
||||
|
||||
// Test for backwards compatibility after PR https://github.com/zammad/zammad/pull/2862
|
||||
test( "ticket_perform_action backwards check after PR#2862", function() {
|
||||
$('#forms').append('<hr><h1>ticket_perform_action check</h1><form id="form3"></form>')
|
||||
|
||||
var el = $('#form3')
|
||||
|
||||
var defaults = {
|
||||
ticket_perform_action4: {
|
||||
'ticket.pending_time': {
|
||||
value: '2010-07-15T05:00:00.000Z'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new App.ControllerForm({
|
||||
el: el,
|
||||
model: {
|
||||
configure_attributes: [
|
||||
{
|
||||
name: 'ticket_perform_action4',
|
||||
display: 'TicketPerformAction4',
|
||||
tag: 'ticket_perform_action',
|
||||
null: true,
|
||||
},
|
||||
]
|
||||
},
|
||||
params: defaults,
|
||||
autofocus: true
|
||||
})
|
||||
|
||||
var params = App.ControllerForm.params(el)
|
||||
var test_params = {
|
||||
ticket_perform_action4: {
|
||||
'ticket.pending_time': {
|
||||
operator: 'static',
|
||||
value: '2010-07-15T05:00:00.000Z'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deepEqual(params, test_params, 'form param check')
|
||||
});
|
||||
|
|
43
public/assets/tests/ticket_macro.js
Normal file
43
public/assets/tests/ticket_macro.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
test( "ticket macro pending time check", function() {
|
||||
var test_relative = function(rules, target, description){
|
||||
var ticket = new App.Ticket()
|
||||
|
||||
App.Ticket.macro({
|
||||
ticket: ticket,
|
||||
macro: {
|
||||
"ticket.pending_time": rules
|
||||
}
|
||||
})
|
||||
|
||||
var compare_against = new Date()
|
||||
var travel = Math.abs( new Date(ticket.pending_time) - compare_against)
|
||||
|
||||
var diff = Math.abs(target - travel)
|
||||
|
||||
ok(diff < 1000, description)
|
||||
}
|
||||
|
||||
var rules = {
|
||||
operator: "relative",
|
||||
range: "day",
|
||||
value: 5
|
||||
}
|
||||
|
||||
test_relative(rules, 60 * 60 * 24 * 5 * 1000, '5 days')
|
||||
|
||||
var rules = {
|
||||
operator: "relative",
|
||||
range: "minute",
|
||||
value: 3
|
||||
}
|
||||
|
||||
test_relative(rules, 60 * 3 * 1000, '5 minutes')
|
||||
|
||||
var rules = {
|
||||
operator: "relative",
|
||||
range: "hour",
|
||||
value: 10
|
||||
}
|
||||
|
||||
test_relative(rules, 60 * 60 * 10 * 1000, '10 hours')
|
||||
})
|
|
@ -153,6 +153,72 @@ RSpec.describe Ticket, type: :model do
|
|||
end
|
||||
end
|
||||
|
||||
# Test for backwards compatibility after PR https://github.com/zammad/zammad/pull/2862
|
||||
context 'with "pending_time" => { "value": DATE } in "perform" hash' do
|
||||
let(:perform) do
|
||||
{
|
||||
'ticket.state_id' => {
|
||||
'value' => Ticket::State.lookup(name: 'pending reminder').id.to_s
|
||||
},
|
||||
'ticket.pending_time' => {
|
||||
'value' => timestamp,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
let(:timestamp) { Time.zone.now }
|
||||
|
||||
it 'changes pending date to given date' do
|
||||
freeze_time do
|
||||
expect { ticket.perform_changes(perform, 'trigger', ticket, User.first) }
|
||||
.to change(ticket, :pending_time).to(be_within(1.minute).of(timestamp))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Test for PR https://github.com/zammad/zammad/pull/2862
|
||||
context 'with "pending_time" => { "operator": "relative" } in "perform" hash' do
|
||||
shared_examples 'verify' do
|
||||
it 'verify relative pending time rule' do
|
||||
freeze_time do
|
||||
interval = relative_value.send(relative_range).from_now
|
||||
|
||||
expect { ticket.perform_changes(perform, 'trigger', ticket, User.first) }
|
||||
.to change(ticket, :pending_time).to(be_within(1.minute).of(interval))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
let(:perform) do
|
||||
{
|
||||
'ticket.state_id' => {
|
||||
'value' => Ticket::State.lookup(name: 'pending reminder').id.to_s
|
||||
},
|
||||
'ticket.pending_time' => {
|
||||
'operator' => 'relative',
|
||||
'value' => relative_value,
|
||||
'range' => relative_range_config
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
let(:relative_range_config) { relative_range.to_s.singularize }
|
||||
|
||||
context 'and value in days' do
|
||||
let(:relative_value) { 2 }
|
||||
let(:relative_range) { :days }
|
||||
|
||||
include_examples 'verify'
|
||||
end
|
||||
|
||||
context 'and value in minutes' do
|
||||
let(:relative_value) { 60 }
|
||||
let(:relative_range) { :minutes }
|
||||
|
||||
include_examples 'verify'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with "ticket.action" => { "value" => "delete" } in "perform" hash' do
|
||||
let(:perform) do
|
||||
{
|
||||
|
|
|
@ -106,6 +106,10 @@ RSpec.describe 'QUnit', type: :system, authenticated_as: false, set_up: true, we
|
|||
q_unit_tests('form_ticket_perform_action')
|
||||
end
|
||||
|
||||
it 'Ticket macro' do
|
||||
q_unit_tests('ticket_macro')
|
||||
end
|
||||
|
||||
it 'Validation' do
|
||||
q_unit_tests('form_validation')
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue