Moved to new query condition syntax to support is, is not, contains, contains not, ...
This commit is contained in:
parent
3d47199ec5
commit
d825cc288b
16 changed files with 618 additions and 333 deletions
|
@ -1,111 +1,58 @@
|
||||||
class App.UiElement.ticket_selector
|
class App.UiElement.ticket_selector
|
||||||
@render: (attribute, params = {}) ->
|
@defaults: ->
|
||||||
|
defaults = ['ticket.state_id']
|
||||||
|
|
||||||
# list of attributes
|
|
||||||
groups =
|
groups =
|
||||||
tickets:
|
ticket:
|
||||||
name: 'Ticket'
|
name: 'Ticket'
|
||||||
model: 'Ticket'
|
model: 'Ticket'
|
||||||
users:
|
customer:
|
||||||
name: 'Customer'
|
name: 'Customer'
|
||||||
model: 'User'
|
model: 'User'
|
||||||
organizations:
|
organization:
|
||||||
name: 'Organization'
|
name: 'Organization'
|
||||||
model: 'Organization'
|
model: 'Organization'
|
||||||
|
|
||||||
elements =
|
operators_type =
|
||||||
tickets:
|
'^datetime$': ['before (absolute)', 'after (absolute)', 'before (relative)', 'after (relative)']
|
||||||
title:
|
'^timestamp$': ['before (absolute)', 'after (absolute)', 'before (relative)', 'after (relative)']
|
||||||
tag: 'input'
|
'boolean$': ['is', 'is not']
|
||||||
operator: ['contains', 'contains not']
|
'^input$': ['contains', 'contains not']
|
||||||
number:
|
'^textarea$': ['contains', 'contains not']
|
||||||
tag: 'input'
|
|
||||||
operator: ['contains', 'contains not']
|
operators_name =
|
||||||
group_id:
|
'_id$': ['is', 'is not']
|
||||||
relation: 'Group'
|
'_ids$': ['is', 'is not']
|
||||||
tag: 'select'
|
|
||||||
multible: true
|
|
||||||
operator: ['is', 'is not']
|
|
||||||
priority_id:
|
|
||||||
relation: 'Priority'
|
|
||||||
tag: 'select'
|
|
||||||
multible: true
|
|
||||||
operator: ['is', 'is not']
|
|
||||||
state_id:
|
|
||||||
relation: 'State'
|
|
||||||
tag: 'select'
|
|
||||||
multible: true
|
|
||||||
operator: ['is', 'is not']
|
|
||||||
owner_id:
|
|
||||||
tag: 'user_selection'
|
|
||||||
relation: 'User'
|
|
||||||
operator: ['is', 'is not']
|
|
||||||
customer_id:
|
|
||||||
tag: 'user_selection'
|
|
||||||
relation: 'User'
|
|
||||||
operator: ['is', 'is not']
|
|
||||||
organization_id:
|
|
||||||
tag: ''
|
|
||||||
relation: 'Organization'
|
|
||||||
operator: ['is', 'is not']
|
|
||||||
tag:
|
|
||||||
tag: 'tag'
|
|
||||||
multible: true
|
|
||||||
operator: ['is', 'is not']
|
|
||||||
created_at:
|
|
||||||
tag: 'timestamp'
|
|
||||||
operator: ['before', 'after']
|
|
||||||
updated_at:
|
|
||||||
tag: 'timestamp'
|
|
||||||
operator: ['before', 'after']
|
|
||||||
escalation_time:
|
|
||||||
tag: 'timestamp'
|
|
||||||
operator: ['before', 'after']
|
|
||||||
users:
|
|
||||||
firstname:
|
|
||||||
tag: 'input'
|
|
||||||
operator: ['contains', 'contains not']
|
|
||||||
lastname:
|
|
||||||
tag: 'input'
|
|
||||||
operator: ['contains', 'contains not']
|
|
||||||
email:
|
|
||||||
tag: 'input'
|
|
||||||
operator: ['contains', 'contains not']
|
|
||||||
login:
|
|
||||||
tag: 'input'
|
|
||||||
operator: ['contains', 'contains not']
|
|
||||||
created_at:
|
|
||||||
tag: 'time_selector_enhanced'
|
|
||||||
operator: ['before', 'after']
|
|
||||||
updated_at:
|
|
||||||
tag: 'time_selector_enhanced'
|
|
||||||
operator: ['before', 'after']
|
|
||||||
organizations:
|
|
||||||
name:
|
|
||||||
tag: 'input'
|
|
||||||
operator: ['contains', 'contains not']
|
|
||||||
shared:
|
|
||||||
tag: 'boolean'
|
|
||||||
operator: ['is', 'is not']
|
|
||||||
created_at:
|
|
||||||
tag: 'time_selector_enhanced'
|
|
||||||
operator: ['before', 'after']
|
|
||||||
updated_at:
|
|
||||||
tag: 'time_selector_enhanced'
|
|
||||||
operator: ['before', 'after']
|
|
||||||
|
|
||||||
# megre config
|
# megre config
|
||||||
|
elements = {}
|
||||||
for groupKey, groupMeta of groups
|
for groupKey, groupMeta of groups
|
||||||
for elementKey, elementGroup of elements
|
for row in App[groupMeta.model].configure_attributes
|
||||||
if elementKey is groupKey
|
|
||||||
configure_attributes = App[groupMeta.model].configure_attributes
|
# ignore passwords and relations
|
||||||
for attributeName, attributeConfig of elementGroup
|
if row.type isnt 'password' && row.name.substr(row.name.length-4,4) isnt '_ids'
|
||||||
for attribute in configure_attributes
|
config = _.clone(row)
|
||||||
if attribute.name is attributeName
|
for operatorRegEx, operator of operators_type
|
||||||
attributeConfig.config = attribute
|
myRegExp = new RegExp(operatorRegEx, 'i')
|
||||||
|
if config.tag && config.tag.match(myRegExp)
|
||||||
|
config.operator = operator
|
||||||
|
elements["#{groupKey}.#{config.name}"] = config
|
||||||
|
for operatorRegEx, operator of operators_name
|
||||||
|
myRegExp = new RegExp(operatorRegEx, 'i')
|
||||||
|
if config.name && config.name.match(myRegExp)
|
||||||
|
config.operator = operator
|
||||||
|
elements["#{groupKey}.#{config.name}"] = config
|
||||||
|
[defaults, groups, elements]
|
||||||
|
|
||||||
|
@render: (attribute, params = {}) ->
|
||||||
|
|
||||||
|
[defaults, groups, elements] = @defaults()
|
||||||
|
|
||||||
selector = @buildAttributeSelector(groups, elements)
|
selector = @buildAttributeSelector(groups, elements)
|
||||||
|
|
||||||
|
search = =>
|
||||||
|
@preview(item)
|
||||||
|
|
||||||
# return item
|
# return item
|
||||||
item = $( App.view('generic/ticket_selector')( attribute: attribute ) )
|
item = $( App.view('generic/ticket_selector')( attribute: attribute ) )
|
||||||
item.find('.js-attributeSelector').prepend(selector)
|
item.find('.js-attributeSelector').prepend(selector)
|
||||||
|
@ -116,12 +63,14 @@ class App.UiElement.ticket_selector
|
||||||
elementClone = element.clone(true)
|
elementClone = element.clone(true)
|
||||||
element.after(elementClone)
|
element.after(elementClone)
|
||||||
elementClone.find('.js-attributeSelector select').trigger('change')
|
elementClone.find('.js-attributeSelector select').trigger('change')
|
||||||
|
@preview(item)
|
||||||
)
|
)
|
||||||
|
|
||||||
# remove filter
|
# remove filter
|
||||||
item.find('.js-remove').bind('click', (e) =>
|
item.find('.js-remove').bind('click', (e) =>
|
||||||
$(e.target).closest('.js-filterElement').remove()
|
$(e.target).closest('.js-filterElement').remove()
|
||||||
@rebuildAttributeSelectors(item)
|
@rebuildAttributeSelectors(item)
|
||||||
|
@preview(item)
|
||||||
)
|
)
|
||||||
|
|
||||||
# change filter
|
# change filter
|
||||||
|
@ -158,9 +107,20 @@ class App.UiElement.ticket_selector
|
||||||
if selectorExists
|
if selectorExists
|
||||||
item.find('.js-filterElement').first().remove()
|
item.find('.js-filterElement').first().remove()
|
||||||
|
|
||||||
|
else
|
||||||
|
for default_row in defaults
|
||||||
|
|
||||||
|
# get selector rows
|
||||||
|
elementFirst = item.find('.js-filterElement').first()
|
||||||
|
elementLast = item.find('.js-filterElement').last()
|
||||||
|
|
||||||
|
# clone, rebuild and append
|
||||||
|
elementClone = elementFirst.clone(true)
|
||||||
|
@rebuildAttributeSelectors(item, elementClone, default_row)
|
||||||
|
elementLast.after(elementClone)
|
||||||
|
item.find('.js-filterElement').first().remove()
|
||||||
|
|
||||||
# bind for preview
|
# bind for preview
|
||||||
search = =>
|
|
||||||
@preview(item)
|
|
||||||
item.on('change', 'select.form-control', (e) =>
|
item.on('change', 'select.form-control', (e) =>
|
||||||
App.Delay.set(
|
App.Delay.set(
|
||||||
search,
|
search,
|
||||||
|
@ -168,7 +128,7 @@ class App.UiElement.ticket_selector
|
||||||
'preview',
|
'preview',
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
item.on('keyup', 'input.form-control', (e) =>
|
item.on('change keyup', 'input.form-control', (e) =>
|
||||||
App.Delay.set(
|
App.Delay.set(
|
||||||
search,
|
search,
|
||||||
600,
|
600,
|
||||||
|
@ -200,13 +160,6 @@ class App.UiElement.ticket_selector
|
||||||
ticket_ids: ticket_ids
|
ticket_ids: ticket_ids
|
||||||
)
|
)
|
||||||
|
|
||||||
@getElementConfig: (groupAndAttribute, elements) ->
|
|
||||||
for elementGroup, elementConfig of elements
|
|
||||||
for elementKey, elementItem of elementConfig
|
|
||||||
if "#{elementGroup}.#{elementKey}" is groupAndAttribute
|
|
||||||
return elementItem
|
|
||||||
false
|
|
||||||
|
|
||||||
@buildValue: (elementFull, elementRow, groupAndAttribute, elements, value) ->
|
@buildValue: (elementFull, elementRow, groupAndAttribute, elements, value) ->
|
||||||
|
|
||||||
# do nothing if item already exists
|
# do nothing if item already exists
|
||||||
|
@ -214,16 +167,32 @@ class App.UiElement.ticket_selector
|
||||||
return if elementRow.find("[name=\"#{name}\"]").get(0)
|
return if elementRow.find("[name=\"#{name}\"]").get(0)
|
||||||
|
|
||||||
# build new item
|
# build new item
|
||||||
attributeConfig = @getElementConfig(groupAndAttribute, elements)
|
attributeConfig = elements[groupAndAttribute]
|
||||||
|
config = _.clone(attributeConfig)
|
||||||
|
|
||||||
|
# force to use auto compition on user lookup
|
||||||
|
if config.relation is 'User'
|
||||||
|
config.tag = 'user_autocompletion'
|
||||||
|
|
||||||
|
# render ui element
|
||||||
item = ''
|
item = ''
|
||||||
if attributeConfig && attributeConfig.config && App.UiElement[attributeConfig.config.tag]
|
if config && App.UiElement[config.tag]
|
||||||
config = _.clone(attributeConfig.config)
|
|
||||||
config['name'] = name
|
config['name'] = name
|
||||||
config['value'] = value
|
config['value'] = value
|
||||||
if 'multiple' of config
|
if 'multiple' of config
|
||||||
config.multiple = true
|
config.multiple = true
|
||||||
config.nulloption = false
|
config.nulloption = false
|
||||||
item = App.UiElement[attributeConfig.config.tag].render(config, {})
|
if config.tag is 'checkbox'
|
||||||
|
config.tag = 'select'
|
||||||
|
#config.type = 'datetime-local'
|
||||||
|
#if config.tag is 'datetime'
|
||||||
|
# config.tag = 'input'
|
||||||
|
# config.type = 'datetime-local'
|
||||||
|
tagSearch = "#{config.tag}_search"
|
||||||
|
if App.UiElement[tagSearch]
|
||||||
|
item = App.UiElement[tagSearch].render(config, {})
|
||||||
|
else
|
||||||
|
item = App.UiElement[config.tag].render(config, {})
|
||||||
elementRow.find('.js-value').html(item)
|
elementRow.find('.js-value').html(item)
|
||||||
|
|
||||||
@buildAttributeSelector: (groups, elements) ->
|
@buildAttributeSelector: (groups, elements) ->
|
||||||
|
@ -233,13 +202,12 @@ class App.UiElement.ticket_selector
|
||||||
selection.closest('select').append("<optgroup label=\"#{displayName}\" class=\"js-#{groupKey}\"></optgroup>")
|
selection.closest('select').append("<optgroup label=\"#{displayName}\" class=\"js-#{groupKey}\"></optgroup>")
|
||||||
optgroup = selection.find("optgroup.js-#{groupKey}")
|
optgroup = selection.find("optgroup.js-#{groupKey}")
|
||||||
for elementKey, elementGroup of elements
|
for elementKey, elementGroup of elements
|
||||||
if elementKey is groupKey
|
spacer = elementKey.split(/\./)
|
||||||
for attributeName, attributeConfig of elementGroup
|
if spacer[0] is groupKey
|
||||||
if attributeConfig.config && attributeConfig.config.display
|
attributeConfig = elements[elementKey]
|
||||||
displayName = App.i18n.translateInline(attributeConfig.config.display)
|
if attributeConfig.operator
|
||||||
else
|
displayName = App.i18n.translateInline(attributeConfig.display)
|
||||||
displayName = App.i18n.translateInline(attributeName)
|
optgroup.append("<option value=\"#{elementKey}\">#{displayName}</option>")
|
||||||
optgroup.append("<option value=\"#{groupKey}.#{attributeName}\">#{displayName}</option>")
|
|
||||||
selection
|
selection
|
||||||
|
|
||||||
@rebuildAttributeSelectors: (elementFull, elementRow, groupAndAttribute) ->
|
@rebuildAttributeSelectors: (elementFull, elementRow, groupAndAttribute) ->
|
||||||
|
@ -267,7 +235,9 @@ class App.UiElement.ticket_selector
|
||||||
|
|
||||||
@buildOperator: (elementFull, elementRow, groupAndAttribute, elements, current_operator) ->
|
@buildOperator: (elementFull, elementRow, groupAndAttribute, elements, current_operator) ->
|
||||||
selection = $("<select class=\"form-control\" name=\"condition::#{groupAndAttribute}::operator\"></select>")
|
selection = $("<select class=\"form-control\" name=\"condition::#{groupAndAttribute}::operator\"></select>")
|
||||||
attributeConfig = @getElementConfig(groupAndAttribute, elements)
|
|
||||||
|
attributeConfig = elements[groupAndAttribute]
|
||||||
|
if attributeConfig.operator
|
||||||
for operator in attributeConfig.operator
|
for operator in attributeConfig.operator
|
||||||
operatorName = App.i18n.translateInline(operator)
|
operatorName = App.i18n.translateInline(operator)
|
||||||
selected = ''
|
selected = ''
|
||||||
|
@ -290,16 +260,43 @@ class App.UiElement.ticket_selector
|
||||||
@humanText: (condition) ->
|
@humanText: (condition) ->
|
||||||
none = App.i18n.translateContent('No filter.')
|
none = App.i18n.translateContent('No filter.')
|
||||||
return [none] if _.isEmpty(condition)
|
return [none] if _.isEmpty(condition)
|
||||||
|
[defaults, groups, elements] = @defaults()
|
||||||
rules = []
|
rules = []
|
||||||
for position of condition.attribute
|
for attribute, meta of condition
|
||||||
|
|
||||||
|
objectAttribute = attribute.split(/\./)
|
||||||
|
|
||||||
# get stored params
|
# get stored params
|
||||||
groupAndAttribute = condition.attribute[position]
|
if meta && objectAttribute[1]
|
||||||
if condition[groupAndAttribute]
|
|
||||||
selectorExists = true
|
selectorExists = true
|
||||||
operator = condition[groupAndAttribute].operator
|
operator = meta.operator
|
||||||
value = condition[groupAndAttribute].value
|
value = meta.value
|
||||||
rules.push "#{App.i18n.translateContent('Where')} <b>#{App.i18n.translateContent(groupAndAttribute)}</b> #{App.i18n.translateContent(operator)} <b>#{App.i18n.translateContent(value)}</b>."
|
model = toCamelCase(objectAttribute[0])
|
||||||
|
modelAttribute = objectAttribute[1]
|
||||||
|
|
||||||
|
config = elements[attribute]
|
||||||
|
|
||||||
|
if modelAttribute.substr(modelAttribute.length-4,4) is '_ids'
|
||||||
|
modelAttribute = modelAttribute.substr(0, modelAttribute.length-4)
|
||||||
|
if modelAttribute.substr(modelAttribute.length-3,3) is '_id'
|
||||||
|
modelAttribute = modelAttribute.substr(0, modelAttribute.length-3)
|
||||||
|
valueHuman = []
|
||||||
|
if _.isArray(value)
|
||||||
|
for data in value
|
||||||
|
r = @humanTextLookup(config, data)
|
||||||
|
valueHuman.push r
|
||||||
|
else
|
||||||
|
valueHuman.push @humanTextLookup(config, value)
|
||||||
|
rules.push "#{App.i18n.translateContent('Where')} <b>#{App.i18n.translateContent(model)} -> #{App.i18n.translateContent(toCamelCase(modelAttribute))}</b> #{App.i18n.translateContent(operator)} <b>#{valueHuman}</b>."
|
||||||
|
|
||||||
return [none] if _.isEmpty(rules)
|
return [none] if _.isEmpty(rules)
|
||||||
rules
|
rules
|
||||||
|
|
||||||
|
@humanTextLookup: (config, value) ->
|
||||||
|
return value if !App[config.relation]
|
||||||
|
return value if !App[config.relation].exists(value)
|
||||||
|
data = App[config.relation].fullLocal(value)
|
||||||
|
return value if !data
|
||||||
|
if data.displayName
|
||||||
|
return App.i18n.translateContent( data.displayName() )
|
||||||
|
valueHuman.push App.i18n.translateContent( data.name )
|
||||||
|
|
|
@ -22,6 +22,7 @@ class Index extends App.ControllerContent
|
||||||
{ name: 'New Overview', 'data-type': 'new', class: 'btn--success' }
|
{ name: 'New Overview', 'data-type': 'new', class: 'btn--success' }
|
||||||
]
|
]
|
||||||
container: @el.closest('.content')
|
container: @el.closest('.content')
|
||||||
|
large: true,
|
||||||
)
|
)
|
||||||
|
|
||||||
App.Config.set( 'Overview', { prio: 2300, name: 'Overviews', parent: '#manage', target: '#manage/overviews', controller: Index, role: ['Admin'] }, 'NavBarAdmin' )
|
App.Config.set( 'Overview', { prio: 2300, name: 'Overviews', parent: '#manage', target: '#manage/overviews', controller: Index, role: ['Admin'] }, 'NavBarAdmin' )
|
|
@ -1,16 +1,15 @@
|
||||||
class App.Overview extends App.Model
|
class App.Overview extends App.Model
|
||||||
@configure 'Overview', 'name', 'link', 'prio', 'condition', 'order', 'group_by', 'view', 'user_id', 'organization_shared', 'role_id', 'order', 'group_by', 'active', 'updated_at'
|
@configure 'Overview', 'name', 'prio', 'condition', 'order', 'group_by', 'view', 'user_id', 'organization_shared', 'role_id', 'order', 'group_by', 'active', 'updated_at'
|
||||||
@extend Spine.Model.Ajax
|
@extend Spine.Model.Ajax
|
||||||
@url: @apiPath + '/overviews'
|
@url: @apiPath + '/overviews'
|
||||||
@configure_attributes = [
|
@configure_attributes = [
|
||||||
{ name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, 'null': false, 'class': 'span4' },
|
{ name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, 'null': false },
|
||||||
{ name: 'link', display: 'URL', tag: 'input', type: 'text', limit: 100, 'null': false, 'class': 'span4' },
|
{ name: 'role_id', display: 'Available for Role', tag: 'select', multiple: false, nulloption: true, null: false, relation: 'Role', translate: true },
|
||||||
{ name: 'role_id', display: 'Available for Role', tag: 'select', multiple: false, nulloption: true, null: false, relation: 'Role', translate: true, class: 'span4' },
|
{ name: 'user_id', display: 'Available for User', tag: 'select', multiple: true, nulloption: true, null: true, relation: 'User', sortBy: 'firstname' },
|
||||||
{ name: 'user_id', display: 'Available for User', tag: 'select', multiple: true, nulloption: true, null: true, relation: 'User', sortBy: 'firstname', class: 'span4' },
|
{ name: 'organization_shared', display: 'Only available for Users with shared Organization', tag: 'select', options: { true: 'yes', false: 'no' }, default: false, null: true },
|
||||||
{ name: 'organization_shared', display: 'Only available for Users with shared Organization', tag: 'select', options: { true: 'yes', false: 'no' }, default: false, null: true, 'class': 'span4' },
|
# { name: 'content', display: 'Content', tag: 'textarea', limit: 250, 'null': false },
|
||||||
# { name: 'content', display: 'Content', tag: 'textarea', limit: 250, 'null': false, 'class': 'span4' },
|
{ name: 'condition', display: 'Conditions for shown Tickets', tag: 'ticket_selector', null: false },
|
||||||
{ name: 'condition', display: 'Conditions for shown Tickets', tag: 'ticket_attribute_selection', null: true, class: 'span4' },
|
{ name: 'prio', display: 'Prio', tag: 'input', type: 'text', limit: 10, 'null': false },
|
||||||
{ name: 'prio', display: 'Prio', tag: 'input', type: 'text', limit: 10, 'null': false, 'class': 'span4' },
|
|
||||||
{
|
{
|
||||||
name: 'view::s'
|
name: 'view::s'
|
||||||
display: 'Attributes'
|
display: 'Attributes'
|
||||||
|
|
|
@ -11,18 +11,18 @@ class App.Ticket extends App.Model
|
||||||
{ name: 'title', display: 'Title', tag: 'input', type: 'text', limit: 100, null: false, parentClass: 'noTruncate' },
|
{ name: 'title', display: 'Title', tag: 'input', type: 'text', limit: 100, null: false, parentClass: 'noTruncate' },
|
||||||
{ name: 'state_id', display: 'State', tag: 'select', multiple: false, null: false, relation: 'TicketState', default: 'new', style: 'width: 12%', edit: true, customer: true, },
|
{ name: 'state_id', display: 'State', tag: 'select', multiple: false, null: false, relation: 'TicketState', default: 'new', style: 'width: 12%', edit: true, customer: true, },
|
||||||
{ name: 'priority_id', display: 'Priority', tag: 'select', multiple: false, null: false, relation: 'TicketPriority', default: '2 normal', style: 'width: 12%', edit: true, customer: true, },
|
{ name: 'priority_id', display: 'Priority', tag: 'select', multiple: false, null: false, relation: 'TicketPriority', default: '2 normal', style: 'width: 12%', edit: true, customer: true, },
|
||||||
|
{ name: 'article_count', display: 'Article#', style: 'width: 12%' },
|
||||||
|
{ name: 'escalation_time', display: 'Escalation', tag: 'datetime', null: true, style: 'width: 12%', class: 'escalation', parentClass: 'noTruncate' },
|
||||||
{ name: 'last_contact', display: 'Last contact', tag: 'datetime', null: true, style: 'width: 12%', parentClass: 'noTruncate' },
|
{ name: 'last_contact', display: 'Last contact', tag: 'datetime', null: true, style: 'width: 12%', parentClass: 'noTruncate' },
|
||||||
{ name: 'last_contact_agent', display: 'Last contact (Agent)', tag: 'datetime', null: true, style: 'width: 12%', parentClass: 'noTruncate' },
|
{ name: 'last_contact_agent', display: 'Last contact (Agent)', tag: 'datetime', null: true, style: 'width: 12%', parentClass: 'noTruncate' },
|
||||||
{ name: 'last_contact_customer', display: 'Last contact (Customer)', tag: 'datetime', null: true, style: 'width: 12%', parentClass: 'noTruncate' },
|
{ name: 'last_contact_customer', display: 'Last contact (Customer)', tag: 'datetime', null: true, style: 'width: 12%', parentClass: 'noTruncate' },
|
||||||
{ name: 'first_response', display: 'First response', tag: 'datetime', null: true, style: 'width: 12%', parentClass: 'noTruncate' },
|
{ name: 'first_response', display: 'First response', tag: 'datetime', null: true, style: 'width: 12%', parentClass: 'noTruncate' },
|
||||||
{ name: 'close_time', display: 'Close time', tag: 'datetime', null: true, style: 'width: 12%', parentClass: 'noTruncate' },
|
{ name: 'close_time', display: 'Close time', tag: 'datetime', null: true, style: 'width: 12%', parentClass: 'noTruncate' },
|
||||||
{ name: 'pending_time', display: 'Pending Time', tag: 'datetime', null: true, style: 'width: 12%', parentClass: 'noTruncate' },
|
{ name: 'pending_time', display: 'Pending Time', tag: 'datetime', null: true, style: 'width: 12%', parentClass: 'noTruncate' },
|
||||||
{ name: 'escalation_time', display: 'Escalation', tag: 'datetime', null: true, style: 'width: 12%', class: 'escalation', parentClass: 'noTruncate' },
|
|
||||||
{ name: 'article_count', display: 'Article#', style: 'width: 12%' },
|
|
||||||
{ name: 'created_by_id', display: 'Created by', relation: 'User', readonly: 1 },
|
{ name: 'created_by_id', display: 'Created by', relation: 'User', readonly: 1 },
|
||||||
{ name: 'created_at', display: 'Created', tag: 'datetime', style: 'width: 120px', readonly: 1, parentClass: 'noTruncate' },
|
{ name: 'created_at', display: 'Created at', tag: 'datetime', style: 'width: 120px', readonly: 1, parentClass: 'noTruncate' },
|
||||||
{ name: 'updated_by_id', display: 'Updated by', relation: 'User', readonly: 1 },
|
{ name: 'updated_by_id', display: 'Updated by', relation: 'User', readonly: 1 },
|
||||||
{ name: 'updated_at', display: 'Updated', tag: 'datetime', style: 'width: 120px', readonly: 1, parentClass: 'noTruncate' },
|
{ name: 'updated_at', display: 'Updated at', tag: 'datetime', style: 'width: 120px', readonly: 1, parentClass: 'noTruncate' },
|
||||||
]
|
]
|
||||||
|
|
||||||
uiUrl: ->
|
uiUrl: ->
|
||||||
|
|
|
@ -9,15 +9,7 @@ class App.User extends App.Model
|
||||||
{ name: 'firstname', display: 'Firstname', tag: 'input', type: 'text', limit: 100, null: false, signup: true, info: true, invite_agent: true },
|
{ name: 'firstname', display: 'Firstname', tag: 'input', type: 'text', limit: 100, null: false, signup: true, info: true, invite_agent: true },
|
||||||
{ name: 'lastname', display: 'Lastname', tag: 'input', type: 'text', limit: 100, null: false, signup: true, info: true, invite_agent: true },
|
{ name: 'lastname', display: 'Lastname', tag: 'input', type: 'text', limit: 100, null: false, signup: true, info: true, invite_agent: true },
|
||||||
{ name: 'email', display: 'Email', tag: 'input', type: 'email', limit: 100, null: false, signup: true, info: true, invite_agent: true },
|
{ name: 'email', display: 'Email', tag: 'input', type: 'email', limit: 100, null: false, signup: true, info: true, invite_agent: true },
|
||||||
{ name: 'web', display: 'Web', tag: 'input', type: 'url', limit: 100, null: true, signup: false, info: true },
|
|
||||||
{ name: 'phone', display: 'Phone', tag: 'input', type: 'phone', limit: 100, null: true, signup: false, info: true },
|
|
||||||
{ name: 'mobile', display: 'Mobile', tag: 'input', type: 'phone', limit: 100, null: true, signup: false, info: true },
|
|
||||||
{ name: 'fax', display: 'Fax', tag: 'input', type: 'phone', limit: 100, null: true, signup: false, info: true },
|
|
||||||
{ name: 'organization_id', display: 'Organization', tag: 'select', multiple: false, nulloption: true, null: true, relation: 'Organization', signup: false, info: true },
|
{ name: 'organization_id', display: 'Organization', tag: 'select', multiple: false, nulloption: true, null: true, relation: 'Organization', signup: false, info: true },
|
||||||
{ name: 'department', display: 'Department', tag: 'input', type: 'text', limit: 200, null: true, signup: false, info: true },
|
|
||||||
{ name: 'street', display: 'Street', tag: 'input', type: 'text', limit: 100, null: true, signup: false, info: true },
|
|
||||||
{ name: 'zip', display: 'Zip', tag: 'input', type: 'text', limit: 100, null: true, signup: false, info: true },
|
|
||||||
{ name: 'city', display: 'City', tag: 'input', type: 'text', limit: 100, null: true, signup: false, info: true },
|
|
||||||
{ name: 'password', display: 'Password', tag: 'input', type: 'password', limit: 50, null: true, autocomplete: 'off', signup: true, },
|
{ name: 'password', display: 'Password', tag: 'input', type: 'password', limit: 50, null: true, autocomplete: 'off', signup: true, },
|
||||||
{ name: 'note', display: 'Note', tag: 'textarea', note: 'Notes are visible to agents only, never to customers.', limit: 250, null: true, info: true },
|
{ name: 'note', display: 'Note', tag: 'textarea', note: 'Notes are visible to agents only, never to customers.', limit: 250, null: true, info: true },
|
||||||
{ name: 'role_ids', display: 'Roles', tag: 'checkbox', multiple: true, null: false, relation: 'Role' },
|
{ name: 'role_ids', display: 'Roles', tag: 'checkbox', multiple: true, null: false, relation: 'Role' },
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
<div class="sub_attribute well">
|
|
||||||
<div class="ticket_attribute_item"></div>
|
|
||||||
<hr>
|
|
||||||
<div class="ticket_attribute_list"></div>
|
|
||||||
</div>
|
|
|
@ -208,6 +208,13 @@ function underscored (str) {
|
||||||
return str.trim().replace(/([a-z\d])([A-Z]+)/g, '$1_$2').replace(/[-\s]+/g, '_').toLowerCase();
|
return str.trim().replace(/([a-z\d])([A-Z]+)/g, '$1_$2').replace(/[-\s]+/g, '_').toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toCamelCase (str) {
|
||||||
|
return str
|
||||||
|
.replace(/\s(.)/g, function($1) { return $1.toUpperCase(); })
|
||||||
|
.replace(/\s/g, '')
|
||||||
|
.replace(/^(.)/, function($1) { return $1.toUpperCase(); });
|
||||||
|
};
|
||||||
|
|
||||||
jQuery.event.special.remove = {
|
jQuery.event.special.remove = {
|
||||||
remove: function(e) {
|
remove: function(e) {
|
||||||
if (e.handler) e.handler();
|
if (e.handler) e.handler();
|
||||||
|
|
|
@ -60,9 +60,33 @@ curl http://localhost/api/v1/slas.json -v -u #{login}:#{password}
|
||||||
|
|
||||||
# slas
|
# slas
|
||||||
sla_ids = []
|
sla_ids = []
|
||||||
|
models = Models.all
|
||||||
Sla.all.order(:name).each {|sla|
|
Sla.all.order(:name).each {|sla|
|
||||||
sla_ids.push sla.id
|
sla_ids.push sla.id
|
||||||
assets = sla.assets(assets)
|
assets = sla.assets(assets)
|
||||||
|
|
||||||
|
# get assets of condition
|
||||||
|
sla.condition.each {|item, content|
|
||||||
|
attribute = item.split(/\./)
|
||||||
|
next if !attribute[1]
|
||||||
|
attribute_class = attribute[0].to_classname.constantize
|
||||||
|
reflection = attribute[1].sub(/_id$/, '')
|
||||||
|
reflection = reflection.to_sym
|
||||||
|
next if !models[attribute_class]
|
||||||
|
next if !models[attribute_class][:reflections]
|
||||||
|
next if !models[attribute_class][:reflections][reflection]
|
||||||
|
next if !models[attribute_class][:reflections][reflection].klass
|
||||||
|
attribute_ref_class = models[attribute_class][:reflections][reflection].klass
|
||||||
|
if content['value'].class == Array
|
||||||
|
content['value'].each {|item_id|
|
||||||
|
attribute_object = attribute_ref_class.find_by(id: item_id)
|
||||||
|
assets = attribute_object.assets(assets)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
attribute_object = attribute_ref_class.find_by(id: content['value'])
|
||||||
|
assets = attribute_object.assets(assets)
|
||||||
|
end
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render json: {
|
render json: {
|
||||||
|
|
|
@ -389,8 +389,14 @@ class TicketsController < ApplicationController
|
||||||
if params[:user_id]
|
if params[:user_id]
|
||||||
user = User.find( params[:user_id] )
|
user = User.find( params[:user_id] )
|
||||||
condition = {
|
condition = {
|
||||||
'tickets.state_id' => Ticket::State.by_category('open'),
|
'tickets.state_id' => {
|
||||||
'tickets.customer_id' => user.id,
|
operator: 'is',
|
||||||
|
value: Ticket::State.by_category('open').map(&:id),
|
||||||
|
},
|
||||||
|
'tickets.customer_id' => {
|
||||||
|
operator: 'is',
|
||||||
|
value: user.id,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
user_tickets_open = Ticket.search(
|
user_tickets_open = Ticket.search(
|
||||||
limit: limit,
|
limit: limit,
|
||||||
|
@ -401,8 +407,14 @@ class TicketsController < ApplicationController
|
||||||
|
|
||||||
# lookup closed user tickets
|
# lookup closed user tickets
|
||||||
condition = {
|
condition = {
|
||||||
'tickets.state_id' => Ticket::State.by_category('closed'),
|
'tickets.state_id' => {
|
||||||
'tickets.customer_id' => user.id,
|
operator: 'is',
|
||||||
|
value: Ticket::State.by_category('closed').map(&:id),
|
||||||
|
},
|
||||||
|
'tickets.customer_id' => {
|
||||||
|
operator: 'is',
|
||||||
|
value: user.id,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
user_tickets_closed = Ticket.search(
|
user_tickets_closed = Ticket.search(
|
||||||
limit: limit,
|
limit: limit,
|
||||||
|
@ -451,8 +463,14 @@ class TicketsController < ApplicationController
|
||||||
if params[:organization_id] && !params[:organization_id].empty?
|
if params[:organization_id] && !params[:organization_id].empty?
|
||||||
|
|
||||||
condition = {
|
condition = {
|
||||||
'tickets.state_id' => Ticket::State.by_category('open'),
|
'tickets.state_id' => {
|
||||||
'tickets.organization_id' => params[:organization_id],
|
operator: 'is',
|
||||||
|
value: Ticket::State.by_category('open').map(&:id),
|
||||||
|
},
|
||||||
|
'tickets.organization_id' => {
|
||||||
|
operator: 'is',
|
||||||
|
value: params[:organization_id],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
org_tickets_open = Ticket.search(
|
org_tickets_open = Ticket.search(
|
||||||
limit: limit,
|
limit: limit,
|
||||||
|
@ -463,8 +481,14 @@ class TicketsController < ApplicationController
|
||||||
|
|
||||||
# lookup closed org tickets
|
# lookup closed org tickets
|
||||||
condition = {
|
condition = {
|
||||||
'tickets.state_id' => Ticket::State.by_category('closed'),
|
'tickets.state_id' => {
|
||||||
'tickets.organization_id' => params[:organization_id],
|
operator: 'is',
|
||||||
|
value: Ticket::State.by_category('closed').map(&:id),
|
||||||
|
},
|
||||||
|
'tickets.organization_id' => {
|
||||||
|
operator: 'is',
|
||||||
|
value: params[:organization_id],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
org_tickets_closed = Ticket.search(
|
org_tickets_closed = Ticket.search(
|
||||||
limit: limit,
|
limit: limit,
|
||||||
|
|
|
@ -6,5 +6,19 @@ class Overview < ApplicationModel
|
||||||
store :view
|
store :view
|
||||||
validates :name, presence: true
|
validates :name, presence: true
|
||||||
validates :prio, presence: true
|
validates :prio, presence: true
|
||||||
validates :link, presence: true
|
|
||||||
|
before_create :fill_link
|
||||||
|
before_update :fill_link
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# fill link
|
||||||
|
def fill_link
|
||||||
|
return true if link && !link.empty?
|
||||||
|
self.link = name.downcase
|
||||||
|
link.gsub!(/\s/, '_')
|
||||||
|
link.gsub!(/[^0-9a-z]/i, '_')
|
||||||
|
link.gsub!(/_+/, '_')
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -270,9 +270,10 @@ returns
|
||||||
|
|
||||||
def self.selectors(selectors, limit = 10)
|
def self.selectors(selectors, limit = 10)
|
||||||
return if !selectors
|
return if !selectors
|
||||||
query, bind_params = _selectors(selectors)
|
query, bind_params, tables = _selectors(selectors)
|
||||||
ticket_count = Ticket.where(query, *bind_params).count
|
return [] if !query
|
||||||
tickets = Ticket.where(query, *bind_params).limit(limit)
|
ticket_count = Ticket.where(query, *bind_params).joins(tables).count
|
||||||
|
tickets = Ticket.where(query, *bind_params).joins(tables).limit(limit)
|
||||||
[ticket_count, tickets]
|
[ticket_count, tickets]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -281,6 +282,15 @@ returns
|
||||||
query = ''
|
query = ''
|
||||||
bind_params = []
|
bind_params = []
|
||||||
|
|
||||||
|
tables = []
|
||||||
|
selectors.each {|attribute, selector|
|
||||||
|
selector = attribute.split(/\./)
|
||||||
|
next if !selector[1]
|
||||||
|
next if selector[0] == 'ticket'
|
||||||
|
next if tables.include?(selector[0])
|
||||||
|
tables.push selector[0].to_sym
|
||||||
|
}
|
||||||
|
|
||||||
selectors.each {|attribute, selector|
|
selectors.each {|attribute, selector|
|
||||||
if query != ''
|
if query != ''
|
||||||
query += ' AND '
|
query += ' AND '
|
||||||
|
@ -289,7 +299,9 @@ returns
|
||||||
next if !selector.respond_to?(:key?)
|
next if !selector.respond_to?(:key?)
|
||||||
next if !selector['operator']
|
next if !selector['operator']
|
||||||
return nil if !selector['value']
|
return nil if !selector['value']
|
||||||
return nil if selector['value'].respond_to?(:key?) && selector['value'].empty?
|
return nil if selector['value'].respond_to?(:empty?) && selector['value'].empty?
|
||||||
|
attributes = attribute.split(/\./)
|
||||||
|
attribute = "#{attributes[0]}s.#{attributes[1]}"
|
||||||
if selector['operator'] == 'is'
|
if selector['operator'] == 'is'
|
||||||
query += "#{attribute} IN (?)"
|
query += "#{attribute} IN (?)"
|
||||||
bind_params.push selector['value']
|
bind_params.push selector['value']
|
||||||
|
@ -304,14 +316,23 @@ returns
|
||||||
query += "#{attribute} NOT LIKE (?)"
|
query += "#{attribute} NOT LIKE (?)"
|
||||||
value = "%#{selector['value']}%"
|
value = "%#{selector['value']}%"
|
||||||
bind_params.push value
|
bind_params.push value
|
||||||
elsif selector['operator'] == 'before'
|
elsif selector['operator'] == 'before (absolute)'
|
||||||
query += "#{attribute} <= (?)"
|
query += "#{attribute} <= ?"
|
||||||
bind_params.push selector['value']
|
bind_params.push selector['value']
|
||||||
|
elsif selector['operator'] == 'after (absolute)'
|
||||||
|
query += "#{attribute} >= ?"
|
||||||
|
bind_params.push selector['value']
|
||||||
|
elsif selector['operator'] == 'before (relative)'
|
||||||
|
query += "#{attribute} <= ?"
|
||||||
|
bind_params.push Time.zone.now - selector['value'].to_i.minutes
|
||||||
|
elsif selector['operator'] == 'after (relative)'
|
||||||
|
query += "#{attribute} >= ?"
|
||||||
|
bind_params.push Time.zone.now + selector['value'].to_i.minutes
|
||||||
else
|
else
|
||||||
fail "Invalid operator '#{selector['operator']}' for '#{selector['value'].inspect}'"
|
fail "Invalid operator '#{selector['operator']}' for '#{selector['value'].inspect}'"
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
[query, bind_params]
|
[query, bind_params, tables]
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -39,16 +39,16 @@ returns
|
||||||
selected overview by user
|
selected overview by user
|
||||||
|
|
||||||
result = Ticket::Overviews.list(
|
result = Ticket::Overviews.list(
|
||||||
:current_user => User.find(123),
|
current_user: User.find(123),
|
||||||
:view => 'some_view_url',
|
view: 'some_view_url',
|
||||||
)
|
)
|
||||||
|
|
||||||
returns
|
returns
|
||||||
|
|
||||||
result = {
|
result = {
|
||||||
:tickets => tickets, # [ticket1, ticket2, ticket3]
|
tickets: tickets, # [ticket1, ticket2, ticket3]
|
||||||
:tickets_count => tickets_count, # count of tickets
|
tickets_count: tickets_count, # count of tickets
|
||||||
:overview => overview_selected_raw, # overview attributes
|
overview: overview_selected_raw, # overview attributes
|
||||||
}
|
}
|
||||||
|
|
||||||
=end
|
=end
|
||||||
|
@ -71,18 +71,29 @@ returns
|
||||||
end
|
end
|
||||||
|
|
||||||
# replace e.g. 'current_user.id' with current_user.id
|
# replace e.g. 'current_user.id' with current_user.id
|
||||||
overview.condition.each { |item, value|
|
overview.condition.each { |attribute, content|
|
||||||
|
next if !content
|
||||||
next if !value
|
next if !content.respond_to?(:key?)
|
||||||
next if value.class.to_s != 'String'
|
next if !content['value']
|
||||||
|
next if content['value'].class != String && content['value'].class != Array
|
||||||
parts = value.split( '.', 2 )
|
|
||||||
|
|
||||||
|
if content['value'].class == String
|
||||||
|
parts = content['value'].split( '.', 2 )
|
||||||
next if !parts[0]
|
next if !parts[0]
|
||||||
next if !parts[1]
|
next if !parts[1]
|
||||||
next if parts[0] != 'current_user'
|
next if parts[0] != 'current_user'
|
||||||
|
overview.condition[attribute]['value'] = data[:current_user][parts[1].to_sym]
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
overview.condition[item] = data[:current_user][parts[1].to_sym]
|
content['value'].each {|item|
|
||||||
|
next if item.class != String
|
||||||
|
parts = item.split('.', 2)
|
||||||
|
next if !parts[0]
|
||||||
|
next if !parts[1]
|
||||||
|
next if parts[0] != 'current_user'
|
||||||
|
item = data[:current_user][parts[1].to_sym]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,37 +101,8 @@ returns
|
||||||
fail "No such view '#{data[:view]}'"
|
fail "No such view '#{data[:view]}'"
|
||||||
end
|
end
|
||||||
|
|
||||||
# sortby
|
|
||||||
# prio
|
|
||||||
# state
|
|
||||||
# group
|
|
||||||
# customer
|
|
||||||
|
|
||||||
# order
|
|
||||||
# asc
|
|
||||||
# desc
|
|
||||||
|
|
||||||
# groupby
|
|
||||||
# prio
|
|
||||||
# state
|
|
||||||
# group
|
|
||||||
# customer
|
|
||||||
|
|
||||||
# all = attributes[:myopenassigned]
|
|
||||||
# all.merge( { :group_id => groups } )
|
|
||||||
|
|
||||||
# @tickets = Ticket.where(:group_id => groups, attributes[:myopenassigned] ).limit(params[:limit])
|
|
||||||
# get only tickets with permissions
|
# get only tickets with permissions
|
||||||
if data[:current_user].role?('Customer')
|
access_condition = Ticket.access_condition( data[:current_user] )
|
||||||
group_ids = Group.select( 'groups.id' )
|
|
||||||
.where( 'groups.active = ?', true )
|
|
||||||
.map( &:id )
|
|
||||||
else
|
|
||||||
group_ids = Group.select( 'groups.id' ).joins(:users)
|
|
||||||
.where( 'groups_users.user_id = ?', [ data[:current_user].id ] )
|
|
||||||
.where( 'groups.active = ?', true )
|
|
||||||
.map( &:id )
|
|
||||||
end
|
|
||||||
|
|
||||||
# overview meta for navbar
|
# overview meta for navbar
|
||||||
if !overview_selected
|
if !overview_selected
|
||||||
|
@ -129,8 +111,10 @@ returns
|
||||||
result = []
|
result = []
|
||||||
overviews.each { |overview|
|
overviews.each { |overview|
|
||||||
|
|
||||||
|
query_condition, bind_condition = Ticket._selectors(overview.condition)
|
||||||
|
|
||||||
# get count
|
# get count
|
||||||
count = Ticket.where( group_id: group_ids ).where( _condition( overview.condition ) ).count()
|
count = Ticket.where( access_condition ).where( query_condition, *bind_condition ).count()
|
||||||
|
|
||||||
# get meta info
|
# get meta info
|
||||||
all = {
|
all = {
|
||||||
|
@ -151,9 +135,12 @@ returns
|
||||||
if overview_selected.group_by && !overview_selected.group_by.empty?
|
if overview_selected.group_by && !overview_selected.group_by.empty?
|
||||||
order_by = overview_selected.group_by + '_id, ' + order_by
|
order_by = overview_selected.group_by + '_id, ' + order_by
|
||||||
end
|
end
|
||||||
|
|
||||||
|
query_condition, bind_condition = Ticket._selectors(overview_selected.condition)
|
||||||
|
|
||||||
tickets = Ticket.select('id')
|
tickets = Ticket.select('id')
|
||||||
.where( group_id: group_ids )
|
.where( access_condition )
|
||||||
.where( _condition( overview_selected.condition ) )
|
.where( query_condition, *bind_condition )
|
||||||
.order( order_by )
|
.order( order_by )
|
||||||
.limit( 500 )
|
.limit( 500 )
|
||||||
|
|
||||||
|
@ -162,9 +149,7 @@ returns
|
||||||
ticket_ids.push ticket.id
|
ticket_ids.push ticket.id
|
||||||
}
|
}
|
||||||
|
|
||||||
tickets_count = Ticket.where( group_id: group_ids )
|
tickets_count = Ticket.where( access_condition ).where( query_condition, *bind_condition ).count()
|
||||||
.where( _condition( overview_selected.condition ) )
|
|
||||||
.count()
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ticket_ids: ticket_ids,
|
ticket_ids: ticket_ids,
|
||||||
|
@ -175,15 +160,12 @@ returns
|
||||||
|
|
||||||
# get tickets for overview
|
# get tickets for overview
|
||||||
data[:start_page] ||= 1
|
data[:start_page] ||= 1
|
||||||
tickets = Ticket.where( group_id: group_ids )
|
query_condition, bind_condition = Ticket._selectors(overview_selected.condition)
|
||||||
.where( _condition( overview_selected.condition ) )
|
tickets = Ticket.where( access_condition )
|
||||||
|
.where( query_condition, *bind_condition )
|
||||||
.order( overview_selected[:order][:by].to_s + ' ' + overview_selected[:order][:direction].to_s )
|
.order( overview_selected[:order][:by].to_s + ' ' + overview_selected[:order][:direction].to_s )
|
||||||
# .limit( overview_selected.view[ data[:view_mode].to_sym ][:per_page] )
|
|
||||||
# .offset( overview_selected.view[ data[:view_mode].to_sym ][:per_page].to_i * ( data[:start_page].to_i - 1 ) )
|
|
||||||
|
|
||||||
tickets_count = Ticket.where( group_id: group_ids )
|
tickets_count = Ticket.where( access_condition ).where( query_condition, *bind_condition ).count()
|
||||||
.where( _condition( overview_selected.condition ) )
|
|
||||||
.count()
|
|
||||||
|
|
||||||
{
|
{
|
||||||
tickets: tickets,
|
tickets: tickets,
|
||||||
|
@ -192,64 +174,4 @@ returns
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def self._condition(condition)
|
|
||||||
sql = ''
|
|
||||||
bind = [nil]
|
|
||||||
condition.each {|key, value|
|
|
||||||
if sql != ''
|
|
||||||
sql += ' AND '
|
|
||||||
end
|
|
||||||
if value.class == Array
|
|
||||||
sql += " #{key} IN (?)"
|
|
||||||
bind.push value
|
|
||||||
elsif value.class == Hash || value.class == ActiveSupport::HashWithIndifferentAccess
|
|
||||||
time = Time.zone.now
|
|
||||||
if value['area'] == 'minute'
|
|
||||||
if value['direction'] == 'last'
|
|
||||||
time -= value['count'].to_i * 60
|
|
||||||
else
|
|
||||||
time += value['count'].to_i * 60
|
|
||||||
end
|
|
||||||
elsif value['area'] == 'hour'
|
|
||||||
if value['direction'] == 'last'
|
|
||||||
time -= value['count'].to_i * 60 * 60
|
|
||||||
else
|
|
||||||
time += value['count'].to_i * 60 * 60
|
|
||||||
end
|
|
||||||
elsif value['area'] == 'day'
|
|
||||||
if value['direction'] == 'last'
|
|
||||||
time -= value['count'].to_i * 60 * 60 * 24
|
|
||||||
else
|
|
||||||
time += value['count'].to_i * 60 * 60 * 24
|
|
||||||
end
|
|
||||||
elsif value['area'] == 'month'
|
|
||||||
if value['direction'] == 'last'
|
|
||||||
time -= value['count'].to_i * 60 * 60 * 24 * 31
|
|
||||||
else
|
|
||||||
time += value['count'].to_i * 60 * 60 * 24 * 31
|
|
||||||
end
|
|
||||||
elsif value['area'] == 'year'
|
|
||||||
if value['direction'] == 'last'
|
|
||||||
time -= value['count'].to_i * 60 * 60 * 24 * 365
|
|
||||||
else
|
|
||||||
time += value['count'].to_i * 60 * 60 * 24 * 365
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if value['direction'] == 'last'
|
|
||||||
sql += " #{key} > ?"
|
|
||||||
bind.push time
|
|
||||||
else
|
|
||||||
sql += " #{key} < ?"
|
|
||||||
bind.push time
|
|
||||||
end
|
|
||||||
else
|
|
||||||
sql += " #{key} = ?"
|
|
||||||
bind.push value
|
|
||||||
end
|
|
||||||
}
|
|
||||||
bind[0] = sql
|
|
||||||
bind
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -59,14 +59,20 @@ search tickets via database
|
||||||
result = Ticket.search(
|
result = Ticket.search(
|
||||||
current_user: User.find(123),
|
current_user: User.find(123),
|
||||||
condition: {
|
condition: {
|
||||||
'tickets.owner_id' => user.id,
|
'tickets.owner_id' => {
|
||||||
'tickets.state_id' => Ticket::State.where(
|
operator: 'is',
|
||||||
|
value: user.id,
|
||||||
|
},
|
||||||
|
'tickets.state_id' => {
|
||||||
|
operator: 'is',
|
||||||
|
value: Ticket::State.where(
|
||||||
state_type_id: Ticket::StateType.where(
|
state_type_id: Ticket::StateType.where(
|
||||||
name: [
|
name: [
|
||||||
'pending reminder',
|
'pending reminder',
|
||||||
'pending action',
|
'pending action',
|
||||||
],
|
],
|
||||||
),
|
).map(&:id),
|
||||||
|
},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
limit: 15,
|
limit: 15,
|
||||||
|
@ -154,9 +160,10 @@ returns
|
||||||
.order('`tickets`.`created_at` DESC')
|
.order('`tickets`.`created_at` DESC')
|
||||||
.limit(limit)
|
.limit(limit)
|
||||||
else
|
else
|
||||||
|
query_condition, bind_condition = _selectors(params[:condition])
|
||||||
tickets_all = Ticket.select('DISTINCT(tickets.id)')
|
tickets_all = Ticket.select('DISTINCT(tickets.id)')
|
||||||
.where(access_condition)
|
.where(access_condition)
|
||||||
.where(params[:condition])
|
.where(query_condition, *bind_condition)
|
||||||
.order('`tickets`.`created_at` DESC')
|
.order('`tickets`.`created_at` DESC')
|
||||||
.limit(limit)
|
.limit(limit)
|
||||||
end
|
end
|
||||||
|
|
218
db/migrate/20150973000001_update_overview3.rb
Normal file
218
db/migrate/20150973000001_update_overview3.rb
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
class UpdateOverview3 < ActiveRecord::Migration
|
||||||
|
def up
|
||||||
|
UserInfo.current_user_id = 1
|
||||||
|
overview_role = Role.where( name: 'Agent' ).first
|
||||||
|
Overview.create_or_update(
|
||||||
|
name: 'My assigned Tickets',
|
||||||
|
link: 'my_assigned',
|
||||||
|
prio: 1000,
|
||||||
|
role_id: overview_role.id,
|
||||||
|
condition: {
|
||||||
|
'ticket.state_id' => {
|
||||||
|
operator: 'is',
|
||||||
|
value: [ 1, 2, 3, 7 ],
|
||||||
|
},
|
||||||
|
'ticket.owner_id' => {
|
||||||
|
operator: 'is',
|
||||||
|
value: 'current_user.id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
order: {
|
||||||
|
by: 'created_at',
|
||||||
|
direction: 'ASC',
|
||||||
|
},
|
||||||
|
view: {
|
||||||
|
d: %w(title customer group created_at),
|
||||||
|
s: %w(title customer group created_at),
|
||||||
|
m: %w(number title customer group created_at),
|
||||||
|
view_mode_default: 's',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
Overview.create_or_update(
|
||||||
|
name: 'My pending reached Tickets',
|
||||||
|
link: 'my_pending_reached',
|
||||||
|
prio: 1010,
|
||||||
|
role_id: overview_role.id,
|
||||||
|
condition: {
|
||||||
|
'ticket.state_id' => {
|
||||||
|
operator: 'is',
|
||||||
|
value: 3,
|
||||||
|
},
|
||||||
|
'ticket.owner_id' => {
|
||||||
|
operator: 'is',
|
||||||
|
value: 'current_user.id',
|
||||||
|
},
|
||||||
|
'ticket.pending_time' => {
|
||||||
|
operator: 'after (relative)',
|
||||||
|
value: '1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
order: {
|
||||||
|
by: 'created_at',
|
||||||
|
direction: 'ASC',
|
||||||
|
},
|
||||||
|
view: {
|
||||||
|
d: %w(title customer group created_at),
|
||||||
|
s: %w(title customer group created_at),
|
||||||
|
m: %w(number title customer group created_at),
|
||||||
|
view_mode_default: 's',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
Overview.create_or_update(
|
||||||
|
name: 'Unassigned & Open Tickets',
|
||||||
|
link: 'all_unassigned',
|
||||||
|
prio: 1020,
|
||||||
|
role_id: overview_role.id,
|
||||||
|
condition: {
|
||||||
|
'ticket.state_id' => {
|
||||||
|
operator: 'is',
|
||||||
|
value: [1, 2, 3],
|
||||||
|
},
|
||||||
|
'ticket.owner_id' => {
|
||||||
|
operator: 'is',
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
order: {
|
||||||
|
by: 'created_at',
|
||||||
|
direction: 'ASC',
|
||||||
|
},
|
||||||
|
view: {
|
||||||
|
d: %w(title customer group created_at),
|
||||||
|
s: %w(title customer group created_at),
|
||||||
|
m: %w(number title customer group created_at),
|
||||||
|
view_mode_default: 's',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
Overview.create_or_update(
|
||||||
|
name: 'All Open Tickets',
|
||||||
|
link: 'all_open',
|
||||||
|
prio: 1030,
|
||||||
|
role_id: overview_role.id,
|
||||||
|
condition: {
|
||||||
|
'ticket.state_id' => {
|
||||||
|
operator: 'is',
|
||||||
|
value: [1, 2, 3],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
order: {
|
||||||
|
by: 'created_at',
|
||||||
|
direction: 'ASC',
|
||||||
|
},
|
||||||
|
view: {
|
||||||
|
d: %w(title customer group state owner created_at),
|
||||||
|
s: %w(title customer group state owner created_at),
|
||||||
|
m: %w(number title customer group state owner created_at),
|
||||||
|
view_mode_default: 's',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
Overview.create_or_update(
|
||||||
|
name: 'All pending reached Tickets',
|
||||||
|
link: 'all_pending_reached',
|
||||||
|
prio: 1035,
|
||||||
|
role_id: overview_role.id,
|
||||||
|
condition: {
|
||||||
|
'ticket.state_id' => {
|
||||||
|
operator: 'is',
|
||||||
|
value: [3],
|
||||||
|
},
|
||||||
|
'ticket.pending_time' => {
|
||||||
|
operator: 'after (relative)',
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
order: {
|
||||||
|
by: 'created_at',
|
||||||
|
direction: 'ASC',
|
||||||
|
},
|
||||||
|
view: {
|
||||||
|
d: %w(title customer group owner created_at),
|
||||||
|
s: %w(title customer group owner created_at),
|
||||||
|
m: %w(number title customer group owner created_at),
|
||||||
|
view_mode_default: 's',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
Overview.create_or_update(
|
||||||
|
name: 'Escalated Tickets',
|
||||||
|
link: 'all_escalated',
|
||||||
|
prio: 1040,
|
||||||
|
role_id: overview_role.id,
|
||||||
|
condition: {
|
||||||
|
'ticket.escalation_time' => {
|
||||||
|
operator: 'before (relative)',
|
||||||
|
value: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
order: {
|
||||||
|
by: 'escalation_time',
|
||||||
|
direction: 'ASC',
|
||||||
|
},
|
||||||
|
view: {
|
||||||
|
d: %w(title customer group owner escalation_time),
|
||||||
|
s: %w(title customer group owner escalation_time),
|
||||||
|
m: %w(number title customer group owner escalation_time),
|
||||||
|
view_mode_default: 's',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
overview_role = Role.where( name: 'Customer' ).first
|
||||||
|
Overview.create_or_update(
|
||||||
|
name: 'My Tickets',
|
||||||
|
link: 'my_tickets',
|
||||||
|
prio: 1000,
|
||||||
|
role_id: overview_role.id,
|
||||||
|
condition: {
|
||||||
|
'ticket.state_id' => {
|
||||||
|
operator: 'is',
|
||||||
|
value: [ 1, 2, 3, 4, 6 ],
|
||||||
|
},
|
||||||
|
'ticket.customer_id' => {
|
||||||
|
operator: 'is',
|
||||||
|
value: 'current_user.id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
order: {
|
||||||
|
by: 'created_at',
|
||||||
|
direction: 'DESC',
|
||||||
|
},
|
||||||
|
view: {
|
||||||
|
d: %w(title customer state created_at),
|
||||||
|
s: %w(number title state created_at),
|
||||||
|
m: %w(number title state created_at),
|
||||||
|
view_mode_default: 's',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
Overview.create_or_update(
|
||||||
|
name: 'My Organization Tickets',
|
||||||
|
link: 'my_organization_tickets',
|
||||||
|
prio: 1100,
|
||||||
|
role_id: overview_role.id,
|
||||||
|
organization_shared: true,
|
||||||
|
condition: {
|
||||||
|
'ticket.state_id' => {
|
||||||
|
operator: 'is',
|
||||||
|
value: [ 1, 2, 3, 4, 6 ],
|
||||||
|
},
|
||||||
|
'ticket.organization_id' => {
|
||||||
|
operator: 'is',
|
||||||
|
value: 'current_user.organization_id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
order: {
|
||||||
|
by: 'created_at',
|
||||||
|
direction: 'DESC',
|
||||||
|
},
|
||||||
|
view: {
|
||||||
|
d: %w(title customer state created_at),
|
||||||
|
s: %w(number title customer state created_at),
|
||||||
|
m: %w(number title customer state created_at),
|
||||||
|
view_mode_default: 's',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
75
db/seeds.rb
75
db/seeds.rb
|
@ -1575,8 +1575,14 @@ Overview.create_if_not_exists(
|
||||||
prio: 1000,
|
prio: 1000,
|
||||||
role_id: overview_role.id,
|
role_id: overview_role.id,
|
||||||
condition: {
|
condition: {
|
||||||
'tickets.state_id' => [ 1, 2, 3, 7 ],
|
'ticket.state_id' => {
|
||||||
'tickets.owner_id' => 'current_user.id',
|
operator: 'is',
|
||||||
|
value: [ 1, 2, 3, 7 ],
|
||||||
|
},
|
||||||
|
'ticket.owner_id' => {
|
||||||
|
operator: 'is',
|
||||||
|
value: current_user.id,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
order: {
|
order: {
|
||||||
by: 'created_at',
|
by: 'created_at',
|
||||||
|
@ -1596,9 +1602,18 @@ Overview.create_if_not_exists(
|
||||||
prio: 1010,
|
prio: 1010,
|
||||||
role_id: overview_role.id,
|
role_id: overview_role.id,
|
||||||
condition: {
|
condition: {
|
||||||
'tickets.state_id' => [3],
|
'ticket.state_id' => {
|
||||||
'tickets.owner_id' => 'current_user.id',
|
operator: 'is',
|
||||||
'tickets.pending_time' => { 'direction' => 'before', 'count' => 1, 'area' => 'minute' },
|
value: 3,
|
||||||
|
},
|
||||||
|
'ticket.owner_id' => {
|
||||||
|
operator: 'is',
|
||||||
|
value: 'current_user.id',
|
||||||
|
},
|
||||||
|
'ticket.pending_time' => {
|
||||||
|
operator: 'after (relative)',
|
||||||
|
value: '1',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
order: {
|
order: {
|
||||||
by: 'created_at',
|
by: 'created_at',
|
||||||
|
@ -1618,8 +1633,14 @@ Overview.create_if_not_exists(
|
||||||
prio: 1020,
|
prio: 1020,
|
||||||
role_id: overview_role.id,
|
role_id: overview_role.id,
|
||||||
condition: {
|
condition: {
|
||||||
'tickets.state_id' => [1, 2, 3],
|
'ticket.state_id' => {
|
||||||
'tickets.owner_id' => 1,
|
operator: 'is',
|
||||||
|
value: [1, 2, 3],
|
||||||
|
},
|
||||||
|
'ticket.owner_id' => {
|
||||||
|
operator: 'is',
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
order: {
|
order: {
|
||||||
by: 'created_at',
|
by: 'created_at',
|
||||||
|
@ -1639,7 +1660,10 @@ Overview.create_if_not_exists(
|
||||||
prio: 1030,
|
prio: 1030,
|
||||||
role_id: overview_role.id,
|
role_id: overview_role.id,
|
||||||
condition: {
|
condition: {
|
||||||
'tickets.state_id' => [1, 2, 3],
|
'ticket.state_id' => {
|
||||||
|
operator: 'is',
|
||||||
|
value: [1, 2, 3],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
order: {
|
order: {
|
||||||
by: 'created_at',
|
by: 'created_at',
|
||||||
|
@ -1659,8 +1683,14 @@ Overview.create_if_not_exists(
|
||||||
prio: 1035,
|
prio: 1035,
|
||||||
role_id: overview_role.id,
|
role_id: overview_role.id,
|
||||||
condition: {
|
condition: {
|
||||||
'tickets.state_id' => [3],
|
'ticket.state_id' => {
|
||||||
'tickets.pending_time' => { 'direction' => 'before', 'count' => 1, 'area' => 'minute' },
|
operator: 'is',
|
||||||
|
value: [3],
|
||||||
|
},
|
||||||
|
'ticket.pending_time' => {
|
||||||
|
operator: 'after (relative)',
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
order: {
|
order: {
|
||||||
by: 'created_at',
|
by: 'created_at',
|
||||||
|
@ -1680,7 +1710,10 @@ Overview.create_if_not_exists(
|
||||||
prio: 1040,
|
prio: 1040,
|
||||||
role_id: overview_role.id,
|
role_id: overview_role.id,
|
||||||
condition: {
|
condition: {
|
||||||
'tickets.escalation_time' => { 'direction' => 'before', 'count' => 5, 'area' => 'minute' },
|
'ticket.escalation_time' => {
|
||||||
|
operator: 'before (relative)',
|
||||||
|
value: 5,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
order: {
|
order: {
|
||||||
by: 'escalation_time',
|
by: 'escalation_time',
|
||||||
|
@ -1701,8 +1734,14 @@ Overview.create_if_not_exists(
|
||||||
prio: 1000,
|
prio: 1000,
|
||||||
role_id: overview_role.id,
|
role_id: overview_role.id,
|
||||||
condition: {
|
condition: {
|
||||||
'tickets.state_id' => [ 1, 2, 3, 4, 6 ],
|
'ticket.state_id' => {
|
||||||
'tickets.customer_id' => 'current_user.id',
|
operator: 'is',
|
||||||
|
value: [ 1, 2, 3, 4, 6 ],
|
||||||
|
},
|
||||||
|
'ticket.customer_id' => {
|
||||||
|
operator: 'is',
|
||||||
|
value: 'current_user.id',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
order: {
|
order: {
|
||||||
by: 'created_at',
|
by: 'created_at',
|
||||||
|
@ -1722,8 +1761,14 @@ Overview.create_if_not_exists(
|
||||||
role_id: overview_role.id,
|
role_id: overview_role.id,
|
||||||
organization_shared: true,
|
organization_shared: true,
|
||||||
condition: {
|
condition: {
|
||||||
'tickets.state_id' => [ 1, 2, 3, 4, 6 ],
|
'ticket.state_id' => {
|
||||||
'tickets.organization_id' => 'current_user.organization_id',
|
operator: 'is',
|
||||||
|
value: [ 1, 2, 3, 4, 6 ],
|
||||||
|
},
|
||||||
|
'ticket.organization_id' => {
|
||||||
|
operator: 'is',
|
||||||
|
value: 'current_user.organization_id',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
order: {
|
order: {
|
||||||
by: 'created_at',
|
by: 'created_at',
|
||||||
|
|
|
@ -46,12 +46,18 @@ class CalendarSubscriptions::Tickets
|
||||||
return events_data if owner_ids.empty?
|
return events_data if owner_ids.empty?
|
||||||
|
|
||||||
condition = {
|
condition = {
|
||||||
'tickets.owner_id' => owner_ids,
|
'tickets.owner_id' => {
|
||||||
'tickets.state_id' => Ticket::State.where(
|
operator: 'is',
|
||||||
|
value: owner_ids,
|
||||||
|
},
|
||||||
|
'tickets.state_id' => {
|
||||||
|
operator: 'is',
|
||||||
|
value: Ticket::State.where(
|
||||||
state_type_id: Ticket::StateType.where(
|
state_type_id: Ticket::StateType.where(
|
||||||
name: %w(new open),
|
name: %w(new open),
|
||||||
),
|
),
|
||||||
),
|
).map(&:id),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
tickets = Ticket.search(
|
tickets = Ticket.search(
|
||||||
|
@ -87,15 +93,21 @@ class CalendarSubscriptions::Tickets
|
||||||
return events_data if owner_ids.empty?
|
return events_data if owner_ids.empty?
|
||||||
|
|
||||||
condition = {
|
condition = {
|
||||||
'tickets.owner_id' => owner_ids,
|
'tickets.owner_id' => {
|
||||||
'tickets.state_id' => Ticket::State.where(
|
operator: 'is',
|
||||||
|
value: owner_ids,
|
||||||
|
},
|
||||||
|
'tickets.state_id' => {
|
||||||
|
operator: 'is',
|
||||||
|
value: Ticket::State.where(
|
||||||
state_type_id: Ticket::StateType.where(
|
state_type_id: Ticket::StateType.where(
|
||||||
name: [
|
name: [
|
||||||
'pending reminder',
|
'pending reminder',
|
||||||
'pending action',
|
'pending action',
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
).map(&:id),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
tickets = Ticket.search(
|
tickets = Ticket.search(
|
||||||
|
@ -137,9 +149,16 @@ class CalendarSubscriptions::Tickets
|
||||||
owner_ids = owner_ids(:escalation)
|
owner_ids = owner_ids(:escalation)
|
||||||
return events_data if owner_ids.empty?
|
return events_data if owner_ids.empty?
|
||||||
|
|
||||||
condition = [
|
condition = {
|
||||||
'tickets.owner_id IN (?) AND tickets.escalation_time IS NOT NULL', owner_ids
|
'tickets.owner_id' => {
|
||||||
]
|
operator: 'is',
|
||||||
|
value: owner_ids,
|
||||||
|
},
|
||||||
|
'tickets.escalation_time' => {
|
||||||
|
operator: 'is not',
|
||||||
|
value: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tickets = Ticket.search(
|
tickets = Ticket.search(
|
||||||
current_user: @user,
|
current_user: @user,
|
||||||
|
|
Loading…
Reference in a new issue