Refactoring: Migrate Rails Observers to Concerns.
This commit is contained in:
parent
5b6f0327c6
commit
cb2286fae4
54 changed files with 903 additions and 556 deletions
|
@ -138,6 +138,18 @@ Metrics/AbcSize:
|
||||||
- 'app/models/concerns/has_rich_text.rb'
|
- 'app/models/concerns/has_rich_text.rb'
|
||||||
- 'app/models/concerns/has_search_index_backend.rb'
|
- 'app/models/concerns/has_search_index_backend.rb'
|
||||||
- 'app/models/concerns/has_search_sortable.rb'
|
- 'app/models/concerns/has_search_sortable.rb'
|
||||||
|
- 'app/models/concerns/ticket/article/adds_metadata_email.rb'
|
||||||
|
- 'app/models/concerns/ticket/article/adds_metadata_general.rb'
|
||||||
|
- 'app/models/concerns/ticket/article/adds_metadata_origin_by_id.rb'
|
||||||
|
- 'app/models/concerns/ticket/article/enqueue_communicate_email_job.rb'
|
||||||
|
- 'app/models/concerns/ticket/article/enqueue_communicate_facebook_job.rb'
|
||||||
|
- 'app/models/concerns/ticket/article/enqueue_communicate_sms_job.rb'
|
||||||
|
- 'app/models/concerns/ticket/article/enqueue_communicate_telegram_job.rb'
|
||||||
|
- 'app/models/concerns/ticket/article/enqueue_communicate_twitter_job.rb'
|
||||||
|
- 'app/models/concerns/ticket/article/resets_ticket_state.rb'
|
||||||
|
- 'app/models/concerns/ticket/sets_last_owner_update_time.rb'
|
||||||
|
- 'app/models/concerns/ticket/touches_associations.rb'
|
||||||
|
- 'app/models/concerns/user/performs_geo_lookup.rb'
|
||||||
- 'app/models/cti/caller_id.rb'
|
- 'app/models/cti/caller_id.rb'
|
||||||
- 'app/models/cti/driver/base.rb'
|
- 'app/models/cti/driver/base.rb'
|
||||||
- 'app/models/cti/driver/placetel.rb'
|
- 'app/models/cti/driver/placetel.rb'
|
||||||
|
@ -156,19 +168,7 @@ Metrics/AbcSize:
|
||||||
- 'app/models/link.rb'
|
- 'app/models/link.rb'
|
||||||
- 'app/models/object_manager/attribute.rb'
|
- 'app/models/object_manager/attribute.rb'
|
||||||
- 'app/models/observer/chat/leave/background_job.rb'
|
- 'app/models/observer/chat/leave/background_job.rb'
|
||||||
- 'app/models/observer/ticket/article/communicate_email.rb'
|
|
||||||
- 'app/models/observer/ticket/article/communicate_facebook.rb'
|
|
||||||
- 'app/models/observer/ticket/article/communicate_sms.rb'
|
|
||||||
- 'app/models/observer/ticket/article/communicate_telegram.rb'
|
|
||||||
- 'app/models/observer/ticket/article/communicate_twitter.rb'
|
|
||||||
- 'app/models/observer/ticket/article/fillup_from_email.rb'
|
|
||||||
- 'app/models/observer/ticket/article/fillup_from_general.rb'
|
|
||||||
- 'app/models/observer/ticket/article/fillup_from_origin_by_id.rb'
|
|
||||||
- 'app/models/observer/ticket/last_owner_update.rb'
|
|
||||||
- 'app/models/observer/ticket/ref_object_touch.rb'
|
|
||||||
- 'app/models/observer/ticket/reset_new_state.rb'
|
|
||||||
- 'app/models/observer/transaction.rb'
|
- 'app/models/observer/transaction.rb'
|
||||||
- 'app/models/observer/user/geo.rb'
|
|
||||||
- 'app/models/online_notification.rb'
|
- 'app/models/online_notification.rb'
|
||||||
- 'app/models/online_notification/assets.rb'
|
- 'app/models/online_notification/assets.rb'
|
||||||
- 'app/models/organization/assets.rb'
|
- 'app/models/organization/assets.rb'
|
||||||
|
@ -533,6 +533,17 @@ Metrics/CyclomaticComplexity:
|
||||||
- 'app/models/concerns/has_rich_text.rb'
|
- 'app/models/concerns/has_rich_text.rb'
|
||||||
- 'app/models/concerns/has_search_index_backend.rb'
|
- 'app/models/concerns/has_search_index_backend.rb'
|
||||||
- 'app/models/concerns/has_search_sortable.rb'
|
- 'app/models/concerns/has_search_sortable.rb'
|
||||||
|
- 'app/models/concerns/ticket/article/adds_metadata_email.rb'
|
||||||
|
- 'app/models/concerns/ticket/article/adds_metadata_general.rb'
|
||||||
|
- 'app/models/concerns/ticket/article/adds_metadata_origin_by_id.rb'
|
||||||
|
- 'app/models/concerns/ticket/article/enqueue_communicate_email_job.rb'
|
||||||
|
- 'app/models/concerns/ticket/article/enqueue_communicate_facebook_job.rb'
|
||||||
|
- 'app/models/concerns/ticket/article/enqueue_communicate_sms_job.rb'
|
||||||
|
- 'app/models/concerns/ticket/article/enqueue_communicate_twitter_job.rb'
|
||||||
|
- 'app/models/concerns/ticket/article/resets_ticket_state.rb'
|
||||||
|
- 'app/models/concerns/ticket/sets_last_owner_update_time.rb'
|
||||||
|
- 'app/models/concerns/ticket/touches_associations.rb'
|
||||||
|
- 'app/models/concerns/user/performs_geo_lookup.rb'
|
||||||
- 'app/models/cti/caller_id.rb'
|
- 'app/models/cti/caller_id.rb'
|
||||||
- 'app/models/cti/driver/base.rb'
|
- 'app/models/cti/driver/base.rb'
|
||||||
- 'app/models/cti/driver/placetel.rb'
|
- 'app/models/cti/driver/placetel.rb'
|
||||||
|
@ -545,18 +556,7 @@ Metrics/CyclomaticComplexity:
|
||||||
- 'app/models/karma/activity_log.rb'
|
- 'app/models/karma/activity_log.rb'
|
||||||
- 'app/models/knowledge_base.rb'
|
- 'app/models/knowledge_base.rb'
|
||||||
- 'app/models/object_manager/attribute.rb'
|
- 'app/models/object_manager/attribute.rb'
|
||||||
- 'app/models/observer/ticket/article/communicate_email.rb'
|
|
||||||
- 'app/models/observer/ticket/article/communicate_facebook.rb'
|
|
||||||
- 'app/models/observer/ticket/article/communicate_sms.rb'
|
|
||||||
- 'app/models/observer/ticket/article/communicate_twitter.rb'
|
|
||||||
- 'app/models/observer/ticket/article/fillup_from_email.rb'
|
|
||||||
- 'app/models/observer/ticket/article/fillup_from_general.rb'
|
|
||||||
- 'app/models/observer/ticket/article/fillup_from_origin_by_id.rb'
|
|
||||||
- 'app/models/observer/ticket/last_owner_update.rb'
|
|
||||||
- 'app/models/observer/ticket/ref_object_touch.rb'
|
|
||||||
- 'app/models/observer/ticket/reset_new_state.rb'
|
|
||||||
- 'app/models/observer/transaction.rb'
|
- 'app/models/observer/transaction.rb'
|
||||||
- 'app/models/observer/user/geo.rb'
|
|
||||||
- 'app/models/online_notification/assets.rb'
|
- 'app/models/online_notification/assets.rb'
|
||||||
- 'app/models/organization/assets.rb'
|
- 'app/models/organization/assets.rb'
|
||||||
- 'app/models/organization/search.rb'
|
- 'app/models/organization/search.rb'
|
||||||
|
@ -763,6 +763,16 @@ Metrics/PerceivedComplexity:
|
||||||
- 'app/models/concerns/has_rich_text.rb'
|
- 'app/models/concerns/has_rich_text.rb'
|
||||||
- 'app/models/concerns/has_search_index_backend.rb'
|
- 'app/models/concerns/has_search_index_backend.rb'
|
||||||
- 'app/models/concerns/has_search_sortable.rb'
|
- 'app/models/concerns/has_search_sortable.rb'
|
||||||
|
- 'app/models/concerns/ticket/article/adds_metadata_email.rb'
|
||||||
|
- 'app/models/concerns/ticket/article/adds_metadata_general.rb'
|
||||||
|
- 'app/models/concerns/ticket/article/adds_metadata_origin_by_id.rb'
|
||||||
|
- 'app/models/concerns/ticket/article/enqueue_communicate_email_job.rb'
|
||||||
|
- 'app/models/concerns/ticket/article/enqueue_communicate_facebook_job.rb'
|
||||||
|
- 'app/models/concerns/ticket/article/enqueue_communicate_twitter_job.rb'
|
||||||
|
- 'app/models/concerns/ticket/article/resets_ticket_state.rb'
|
||||||
|
- 'app/models/concerns/ticket/sets_last_owner_update_time.rb'
|
||||||
|
- 'app/models/concerns/ticket/touches_associations.rb'
|
||||||
|
- 'app/models/concerns/user/performs_geo_lookup.rb'
|
||||||
- 'app/models/cti/caller_id.rb'
|
- 'app/models/cti/caller_id.rb'
|
||||||
- 'app/models/cti/driver/base.rb'
|
- 'app/models/cti/driver/base.rb'
|
||||||
- 'app/models/cti/driver/placetel.rb'
|
- 'app/models/cti/driver/placetel.rb'
|
||||||
|
@ -775,16 +785,6 @@ Metrics/PerceivedComplexity:
|
||||||
- 'app/models/karma/activity_log.rb'
|
- 'app/models/karma/activity_log.rb'
|
||||||
- 'app/models/knowledge_base.rb'
|
- 'app/models/knowledge_base.rb'
|
||||||
- 'app/models/object_manager/attribute.rb'
|
- 'app/models/object_manager/attribute.rb'
|
||||||
- 'app/models/observer/ticket/article/communicate_email.rb'
|
|
||||||
- 'app/models/observer/ticket/article/communicate_facebook.rb'
|
|
||||||
- 'app/models/observer/ticket/article/communicate_sms.rb'
|
|
||||||
- 'app/models/observer/ticket/article/communicate_twitter.rb'
|
|
||||||
- 'app/models/observer/ticket/article/fillup_from_email.rb'
|
|
||||||
- 'app/models/observer/ticket/article/fillup_from_general.rb'
|
|
||||||
- 'app/models/observer/ticket/article/fillup_from_origin_by_id.rb'
|
|
||||||
- 'app/models/observer/ticket/last_owner_update.rb'
|
|
||||||
- 'app/models/observer/ticket/ref_object_touch.rb'
|
|
||||||
- 'app/models/observer/ticket/reset_new_state.rb'
|
|
||||||
- 'app/models/observer/transaction.rb'
|
- 'app/models/observer/transaction.rb'
|
||||||
- 'app/models/online_notification/assets.rb'
|
- 'app/models/online_notification/assets.rb'
|
||||||
- 'app/models/organization/assets.rb'
|
- 'app/models/organization/assets.rb'
|
||||||
|
|
41
app/models/concerns/tag/writes_to_ticket_history.rb
Normal file
41
app/models/concerns/tag/writes_to_ticket_history.rb
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
# Records added/removed tags also in the ticket history.
|
||||||
|
module Tag::WritesToTicketHistory
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
after_create :write_tag_added_to_ticket_history
|
||||||
|
after_destroy :write_tag_removed_to_ticket_history
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def write_tag_added_to_ticket_history
|
||||||
|
|
||||||
|
return true if tag_object.name != 'Ticket'
|
||||||
|
|
||||||
|
History.add(
|
||||||
|
o_id: o_id,
|
||||||
|
history_type: 'added',
|
||||||
|
history_object: 'Ticket',
|
||||||
|
history_attribute: 'tag',
|
||||||
|
value_to: tag_item.name,
|
||||||
|
created_by_id: created_by_id,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_tag_removed_to_ticket_history
|
||||||
|
|
||||||
|
return true if tag_object.name != 'Ticket'
|
||||||
|
|
||||||
|
History.add(
|
||||||
|
o_id: o_id,
|
||||||
|
history_type: 'removed',
|
||||||
|
history_object: 'Ticket',
|
||||||
|
history_attribute: 'tag',
|
||||||
|
value_to: tag_item.name,
|
||||||
|
created_by_id: created_by_id,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,9 +1,16 @@
|
||||||
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
class Observer::Ticket::Article::FillupFromEmail < ActiveRecord::Observer
|
# Adds certain (missing) meta data when creating email articles.
|
||||||
observe 'ticket::_article'
|
module Ticket::Article::AddsMetadataEmail
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
def before_create(record)
|
included do
|
||||||
|
before_create :ticket_article_add_metadata_email
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def ticket_article_add_metadata_email
|
||||||
|
|
||||||
# return if we run import mode
|
# return if we run import mode
|
||||||
return true if Setting.get('import_mode')
|
return true if Setting.get('import_mode')
|
||||||
|
@ -13,36 +20,36 @@ class Observer::Ticket::Article::FillupFromEmail < ActiveRecord::Observer
|
||||||
return if ApplicationHandleInfo.postmaster?
|
return if ApplicationHandleInfo.postmaster?
|
||||||
|
|
||||||
# if sender is customer, do not change anything
|
# if sender is customer, do not change anything
|
||||||
return true if !record.sender_id
|
return true if !sender_id
|
||||||
|
|
||||||
sender = Ticket::Article::Sender.lookup(id: record.sender_id)
|
sender = Ticket::Article::Sender.lookup(id: sender_id)
|
||||||
return true if sender.nil?
|
return true if sender.nil?
|
||||||
return true if sender.name == 'Customer'
|
return true if sender.name == 'Customer'
|
||||||
|
|
||||||
# set email attributes
|
# set email attributes
|
||||||
return true if !record.type_id
|
return true if !type_id
|
||||||
|
|
||||||
type = Ticket::Article::Type.lookup(id: record.type_id)
|
type = Ticket::Article::Type.lookup(id: type_id)
|
||||||
return true if type.nil?
|
return true if type.nil?
|
||||||
return true if type.name != 'email'
|
return true if type.name != 'email'
|
||||||
|
|
||||||
# set subject if empty
|
# set subject if empty
|
||||||
ticket = record.ticket
|
ticket = self.ticket
|
||||||
if !record.subject || record.subject == ''
|
if !subject || subject == ''
|
||||||
record.subject = ticket.title
|
self.subject = ticket.title
|
||||||
end
|
end
|
||||||
|
|
||||||
# clean subject
|
# clean subject
|
||||||
record.subject = ticket.subject_clean(record.subject)
|
self.subject = ticket.subject_clean(subject)
|
||||||
|
|
||||||
# generate message id, force it in production, in test allow to set it for testing reasons
|
# generate message id, force it in production, in test allow to set it for testing reasons
|
||||||
if !record.message_id || Rails.env.production?
|
if !message_id || Rails.env.production?
|
||||||
fqdn = Setting.get('fqdn')
|
fqdn = Setting.get('fqdn')
|
||||||
record.message_id = "<#{DateTime.current.to_s(:number)}.#{record.ticket_id}.#{rand(999_999_999_999)}@#{fqdn}>"
|
self.message_id = "<#{DateTime.current.to_s(:number)}.#{ticket_id}.#{rand(999_999_999_999)}@#{fqdn}>"
|
||||||
end
|
end
|
||||||
|
|
||||||
# generate message_id_md5
|
# generate message_id_md5
|
||||||
record.check_message_id_md5
|
check_message_id_md5
|
||||||
|
|
||||||
# set sender
|
# set sender
|
||||||
email_address = ticket.group.email_address
|
email_address = ticket.group.email_address
|
||||||
|
@ -51,20 +58,20 @@ class Observer::Ticket::Article::FillupFromEmail < ActiveRecord::Observer
|
||||||
end
|
end
|
||||||
|
|
||||||
# remember email address for background job
|
# remember email address for background job
|
||||||
record.preferences['email_address_id'] = email_address.id
|
preferences['email_address_id'] = email_address.id
|
||||||
|
|
||||||
# fill from
|
# fill from
|
||||||
if record.created_by_id != 1 && Setting.get('ticket_define_email_from') == 'AgentNameSystemAddressName'
|
if created_by_id != 1 && Setting.get('ticket_define_email_from') == 'AgentNameSystemAddressName'
|
||||||
separator = Setting.get('ticket_define_email_from_separator')
|
separator = Setting.get('ticket_define_email_from_separator')
|
||||||
sender = User.find(record.created_by_id)
|
sender = User.find(created_by_id)
|
||||||
realname = "#{sender.firstname} #{sender.lastname} #{separator} #{email_address.realname}"
|
realname = "#{sender.firstname} #{sender.lastname} #{separator} #{email_address.realname}"
|
||||||
record.from = Channel::EmailBuild.recipient_line(realname, email_address.email)
|
self.from = Channel::EmailBuild.recipient_line(realname, email_address.email)
|
||||||
elsif Setting.get('ticket_define_email_from') == 'AgentName'
|
elsif Setting.get('ticket_define_email_from') == 'AgentName'
|
||||||
sender = User.find(record.created_by_id)
|
sender = User.find(created_by_id)
|
||||||
realname = "#{sender.firstname} #{sender.lastname}"
|
realname = "#{sender.firstname} #{sender.lastname}"
|
||||||
record.from = Channel::EmailBuild.recipient_line(realname, email_address.email)
|
self.from = Channel::EmailBuild.recipient_line(realname, email_address.email)
|
||||||
else
|
else
|
||||||
record.from = Channel::EmailBuild.recipient_line(email_address.realname, email_address.email)
|
self.from = Channel::EmailBuild.recipient_line(email_address.realname, email_address.email)
|
||||||
end
|
end
|
||||||
true
|
true
|
||||||
end
|
end
|
|
@ -1,9 +1,17 @@
|
||||||
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
class Observer::Ticket::Article::FillupFromGeneral < ActiveRecord::Observer
|
# Adds certain (missing) meta data when creating articles.
|
||||||
observe 'ticket::_article'
|
# This module depends on AddsMetadataOriginById to run before it.
|
||||||
|
module Ticket::Article::AddsMetadataGeneral
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
def before_create(record)
|
included do
|
||||||
|
before_create :ticket_article_add_metadata_general
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def ticket_article_add_metadata_general
|
||||||
|
|
||||||
# return if we run import mode
|
# return if we run import mode
|
||||||
return true if Setting.get('import_mode')
|
return true if Setting.get('import_mode')
|
||||||
|
@ -13,9 +21,9 @@ class Observer::Ticket::Article::FillupFromGeneral < ActiveRecord::Observer
|
||||||
return true if ApplicationHandleInfo.postmaster?
|
return true if ApplicationHandleInfo.postmaster?
|
||||||
|
|
||||||
# set from on all article types excluding email|twitter status|twitter direct-message|facebook feed post|facebook feed comment
|
# set from on all article types excluding email|twitter status|twitter direct-message|facebook feed post|facebook feed comment
|
||||||
return true if record.type_id.blank?
|
return true if type_id.blank?
|
||||||
|
|
||||||
type = Ticket::Article::Type.lookup(id: record.type_id)
|
type = Ticket::Article::Type.lookup(id: type_id)
|
||||||
|
|
||||||
# from will be set by channel backend
|
# from will be set by channel backend
|
||||||
return true if type.nil?
|
return true if type.nil?
|
||||||
|
@ -26,31 +34,31 @@ class Observer::Ticket::Article::FillupFromGeneral < ActiveRecord::Observer
|
||||||
return true if type.name == 'facebook feed comment'
|
return true if type.name == 'facebook feed comment'
|
||||||
return true if type.name == 'sms'
|
return true if type.name == 'sms'
|
||||||
|
|
||||||
user_id = record.created_by_id
|
user_id = created_by_id
|
||||||
|
|
||||||
if record.origin_by_id.present?
|
if origin_by_id.present?
|
||||||
|
|
||||||
# in case the customer is using origin_by_id, force it to current session user
|
# in case the customer is using origin_by_id, force it to current session user
|
||||||
# and set sender to Customer
|
# and set sender to Customer
|
||||||
if !record.created_by.permissions?('ticket.agent')
|
if !created_by.permissions?('ticket.agent')
|
||||||
record.origin_by_id = record.created_by_id
|
self.origin_by_id = created_by_id
|
||||||
record.sender_id = Ticket::Article::Sender.lookup(name: 'Customer').id
|
self.sender_id = Ticket::Article::Sender.lookup(name: 'Customer').id
|
||||||
end
|
end
|
||||||
|
|
||||||
# in case origin_by is different than created_by, set sender to Customer
|
# in case origin_by is different than created_by, set sender to Customer
|
||||||
# Customer in context of this conversation, not as a permission
|
# Customer in context of this conversation, not as a permission
|
||||||
if record.origin_by != record.created_by_id
|
if origin_by != created_by_id
|
||||||
record.sender_id = Ticket::Article::Sender.lookup(name: 'Customer').id
|
self.sender_id = Ticket::Article::Sender.lookup(name: 'Customer').id
|
||||||
user_id = record.origin_by_id
|
user_id = origin_by_id
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return true if user_id.blank?
|
return true if user_id.blank?
|
||||||
|
|
||||||
user = User.find(user_id)
|
user = User.find(user_id)
|
||||||
if type.name == 'web' || type.name == 'phone'
|
if type.name == 'web' || type.name == 'phone'
|
||||||
record.from = "#{user.firstname} #{user.lastname} <#{user.email}>"
|
self.from = "#{user.firstname} #{user.lastname} <#{user.email}>"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
record.from = "#{user.firstname} #{user.lastname}"
|
self.from = "#{user.firstname} #{user.lastname}"
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
# Adds origin_by_id field (if missing) when creating articles.
|
||||||
|
module Ticket::Article::AddsMetadataOriginById
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
before_create :ticket_article_add_metadata_origin_by_id
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def ticket_article_add_metadata_origin_by_id
|
||||||
|
|
||||||
|
# return if we run import mode
|
||||||
|
return true if Setting.get('import_mode')
|
||||||
|
|
||||||
|
# only do fill origin_by_id if article got created via application_server (e. g. not
|
||||||
|
# if article and sender type is set via *.postmaster)
|
||||||
|
return true if ApplicationHandleInfo.postmaster?
|
||||||
|
|
||||||
|
# check if origin_by_id exists
|
||||||
|
return true if origin_by_id.present?
|
||||||
|
return true if ticket.blank?
|
||||||
|
return true if ticket.customer_id.blank?
|
||||||
|
return true if sender_id.blank?
|
||||||
|
return true if sender.name != 'Customer'
|
||||||
|
|
||||||
|
type_name = type.name
|
||||||
|
return true if type_name != 'phone' && type_name != 'note' && type_name != 'web'
|
||||||
|
|
||||||
|
self.origin_by_id = ticket.customer_id
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,9 +1,16 @@
|
||||||
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
class Observer::Ticket::Article::CommunicateEmail < ActiveRecord::Observer
|
# Schedules a backgrond communication job for new email articles.
|
||||||
observe 'ticket::_article'
|
module Ticket::Article::EnqueueCommunicateEmailJob
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
def after_create(record)
|
included do
|
||||||
|
after_create :ticket_article_enqueue_communicate_email_job
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def ticket_article_enqueue_communicate_email_job
|
||||||
|
|
||||||
# return if we run import mode
|
# return if we run import mode
|
||||||
return true if Setting.get('import_mode')
|
return true if Setting.get('import_mode')
|
||||||
|
@ -13,20 +20,20 @@ class Observer::Ticket::Article::CommunicateEmail < ActiveRecord::Observer
|
||||||
return true if ApplicationHandleInfo.postmaster?
|
return true if ApplicationHandleInfo.postmaster?
|
||||||
|
|
||||||
# if sender is customer, do not communicate
|
# if sender is customer, do not communicate
|
||||||
return true if !record.sender_id
|
return true if !sender_id
|
||||||
|
|
||||||
sender = Ticket::Article::Sender.lookup(id: record.sender_id)
|
sender = Ticket::Article::Sender.lookup(id: sender_id)
|
||||||
return true if sender.nil?
|
return true if sender.nil?
|
||||||
return true if sender.name == 'Customer'
|
return true if sender.name == 'Customer'
|
||||||
|
|
||||||
# only apply on emails
|
# only apply on emails
|
||||||
return true if !record.type_id
|
return true if !type_id
|
||||||
|
|
||||||
type = Ticket::Article::Type.lookup(id: record.type_id)
|
type = Ticket::Article::Type.lookup(id: type_id)
|
||||||
return true if type.nil?
|
return true if type.nil?
|
||||||
return true if type.name != 'email'
|
return true if type.name != 'email'
|
||||||
|
|
||||||
# send background job
|
# send background job
|
||||||
TicketArticleCommunicateEmailJob.perform_later(record.id)
|
TicketArticleCommunicateEmailJob.perform_later(id)
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -1,8 +1,16 @@
|
||||||
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||||
class Observer::Ticket::Article::CommunicateFacebook < ActiveRecord::Observer
|
|
||||||
observe 'ticket::_article'
|
|
||||||
|
|
||||||
def after_create(record)
|
# Schedules a backgrond communication job for new facebook articles.
|
||||||
|
module Ticket::Article::EnqueueCommunicateFacebookJob
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
after_create :ticket_article_enqueue_communicate_facebook_job
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def ticket_article_enqueue_communicate_facebook_job
|
||||||
|
|
||||||
# return if we run import mode
|
# return if we run import mode
|
||||||
return true if Setting.get('import_mode')
|
return true if Setting.get('import_mode')
|
||||||
|
@ -12,20 +20,20 @@ class Observer::Ticket::Article::CommunicateFacebook < ActiveRecord::Observer
|
||||||
return true if ApplicationHandleInfo.postmaster?
|
return true if ApplicationHandleInfo.postmaster?
|
||||||
|
|
||||||
# if sender is customer, do not communicate
|
# if sender is customer, do not communicate
|
||||||
return true if !record.sender_id
|
return true if !sender_id
|
||||||
|
|
||||||
sender = Ticket::Article::Sender.lookup(id: record.sender_id)
|
sender = Ticket::Article::Sender.lookup(id: sender_id)
|
||||||
return true if sender.nil?
|
return true if sender.nil?
|
||||||
return true if sender.name == 'Customer'
|
return true if sender.name == 'Customer'
|
||||||
|
|
||||||
# only apply for facebook
|
# only apply for facebook
|
||||||
return true if !record.type_id
|
return true if !type_id
|
||||||
|
|
||||||
type = Ticket::Article::Type.lookup(id: record.type_id)
|
type = Ticket::Article::Type.lookup(id: type_id)
|
||||||
return true if type.nil?
|
return true if type.nil?
|
||||||
return true if !type.name.start_with?('facebook')
|
return true if !type.name.start_with?('facebook')
|
||||||
|
|
||||||
CommunicateFacebookJob.perform_later(record.id)
|
CommunicateFacebookJob.perform_later(id)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
# Schedules a backgrond communication job for new SMS articles.
|
||||||
|
module Ticket::Article::EnqueueCommunicateSmsJob
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
after_create :ticket_article_enqueue_communicate_sms_job
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def ticket_article_enqueue_communicate_sms_job
|
||||||
|
|
||||||
|
# return if we run import mode
|
||||||
|
return true if Setting.get('import_mode')
|
||||||
|
|
||||||
|
# if sender is customer, do not communicate
|
||||||
|
return true if !sender_id
|
||||||
|
|
||||||
|
sender = Ticket::Article::Sender.lookup(id: sender_id)
|
||||||
|
return true if sender.nil?
|
||||||
|
return true if sender.name == 'Customer'
|
||||||
|
|
||||||
|
# only apply on sms
|
||||||
|
return true if !type_id
|
||||||
|
|
||||||
|
type = Ticket::Article::Type.lookup(id: type_id)
|
||||||
|
return true if type.nil?
|
||||||
|
return true if type.name != 'sms'
|
||||||
|
|
||||||
|
CommunicateSmsJob.perform_later(id)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
# Schedules a backgrond communication job for new telegram articles.
|
||||||
|
module Ticket::Article::EnqueueCommunicateTelegramJob
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
after_create :ticket_article_enqueue_communicate_telegram_job
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def ticket_article_enqueue_communicate_telegram_job
|
||||||
|
|
||||||
|
# return if we run import mode
|
||||||
|
return true if Setting.get('import_mode')
|
||||||
|
|
||||||
|
# if sender is customer, do not communicate
|
||||||
|
return true if !sender_id
|
||||||
|
|
||||||
|
sender = Ticket::Article::Sender.lookup(id: sender_id)
|
||||||
|
return true if sender.nil?
|
||||||
|
return true if sender.name == 'Customer'
|
||||||
|
|
||||||
|
# only apply on telegram messages
|
||||||
|
return true if !type_id
|
||||||
|
|
||||||
|
type = Ticket::Article::Type.lookup(id: type_id)
|
||||||
|
return true if !type.name.match?(/\Atelegram/i)
|
||||||
|
|
||||||
|
CommunicateTelegramJob.perform_later(id)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -1,9 +1,16 @@
|
||||||
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
class Observer::Ticket::Article::CommunicateTwitter < ActiveRecord::Observer
|
# Schedules a backgrond communication job for new twitter articles.
|
||||||
observe 'ticket::_article'
|
module Ticket::Article::EnqueueCommunicateTwitterJob
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
def after_create(record)
|
included do
|
||||||
|
after_create :ticket_article_enqueue_communicate_twitter_job
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def ticket_article_enqueue_communicate_twitter_job
|
||||||
|
|
||||||
# return if we run import mode
|
# return if we run import mode
|
||||||
return true if Setting.get('import_mode')
|
return true if Setting.get('import_mode')
|
||||||
|
@ -13,22 +20,22 @@ class Observer::Ticket::Article::CommunicateTwitter < ActiveRecord::Observer
|
||||||
return true if ApplicationHandleInfo.postmaster?
|
return true if ApplicationHandleInfo.postmaster?
|
||||||
|
|
||||||
# if sender is customer, do not communicate
|
# if sender is customer, do not communicate
|
||||||
return true if !record.sender_id
|
return true if !sender_id
|
||||||
|
|
||||||
sender = Ticket::Article::Sender.lookup(id: record.sender_id)
|
sender = Ticket::Article::Sender.lookup(id: sender_id)
|
||||||
return true if sender.nil?
|
return true if sender.nil?
|
||||||
return true if sender.name == 'Customer'
|
return true if sender.name == 'Customer'
|
||||||
|
|
||||||
# only apply on tweets
|
# only apply on tweets
|
||||||
return true if !record.type_id
|
return true if !type_id
|
||||||
|
|
||||||
type = Ticket::Article::Type.lookup(id: record.type_id)
|
type = Ticket::Article::Type.lookup(id: type_id)
|
||||||
return true if type.nil?
|
return true if type.nil?
|
||||||
return true if !type.name.match?(/\Atwitter/i)
|
return true if !type.name.match?(/\Atwitter/i)
|
||||||
|
|
||||||
raise Exceptions::UnprocessableEntity, 'twitter to: parameter is missing' if record.to.blank? && type['name'] == 'twitter direct-message'
|
raise Exceptions::UnprocessableEntity, 'twitter to: parameter is missing' if to.blank? && type['name'] == 'twitter direct-message'
|
||||||
|
|
||||||
CommunicateTwitterJob.perform_later(record.id)
|
CommunicateTwitterJob.perform_later(id)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
|
@ -1,9 +1,16 @@
|
||||||
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
class Observer::Ticket::ResetNewState < ActiveRecord::Observer
|
# Reopens the ticket in case certain new articles are created.
|
||||||
observe 'ticket::_article'
|
module Ticket::Article::ResetsTicketState
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
def after_create(record)
|
included do
|
||||||
|
after_create :ticket_article_reset_ticket_state
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def ticket_article_reset_ticket_state
|
||||||
|
|
||||||
# return if we run import mode
|
# return if we run import mode
|
||||||
return true if Setting.get('import_mode')
|
return true if Setting.get('import_mode')
|
||||||
|
@ -12,16 +19,16 @@ class Observer::Ticket::ResetNewState < ActiveRecord::Observer
|
||||||
return true if ApplicationHandleInfo.postmaster?
|
return true if ApplicationHandleInfo.postmaster?
|
||||||
|
|
||||||
# if article in internal
|
# if article in internal
|
||||||
return true if record.internal
|
return true if internal
|
||||||
|
|
||||||
# if sender is agent
|
# if sender is agent
|
||||||
return true if Ticket::Article::Sender.lookup(id: record.sender_id).name != 'Agent'
|
return true if Ticket::Article::Sender.lookup(id: sender_id).name != 'Agent'
|
||||||
|
|
||||||
# if article is a message to customer
|
# if article is a message to customer
|
||||||
return true if !Ticket::Article::Type.lookup(id: record.type_id).communication
|
return true if !Ticket::Article::Type.lookup(id: type_id).communication
|
||||||
|
|
||||||
# if current ticket state is still new
|
# if current ticket state is still new
|
||||||
ticket = Ticket.find_by(id: record.ticket_id)
|
ticket = Ticket.find_by(id: ticket_id)
|
||||||
return true if !ticket
|
return true if !ticket
|
||||||
|
|
||||||
new_state = Ticket::State.find_by(default_create: true)
|
new_state = Ticket::State.find_by(default_create: true)
|
23
app/models/concerns/ticket/calls_stats_ticket_reopen_log.rb
Normal file
23
app/models/concerns/ticket/calls_stats_ticket_reopen_log.rb
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
require_dependency 'stats/ticket_reopen'
|
||||||
|
|
||||||
|
# Adds new and updated tickets to the reopen log processing.
|
||||||
|
module Ticket::CallsStatsTicketReopenLog
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
before_create :ticket_call_stats_ticket_reopen_log
|
||||||
|
before_update :ticket_call_stats_ticket_reopen_log
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def ticket_call_stats_ticket_reopen_log
|
||||||
|
|
||||||
|
# return if we run import mode
|
||||||
|
return if Setting.get('import_mode')
|
||||||
|
|
||||||
|
Stats::TicketReopen.log('Ticket', id, saved_changes, updated_by_id)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,30 @@
|
||||||
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
# Adds a background job to update the user's ticket counter on ticket changes.
|
||||||
|
module Ticket::EnqueuesUserTicketCounterJob
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
after_commit :enqueue_user_ticket_counter_job
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def enqueue_user_ticket_counter_job
|
||||||
|
# return if we run import mode
|
||||||
|
return true if Setting.get('import_mode')
|
||||||
|
|
||||||
|
return true if BulkImportInfo.enabled?
|
||||||
|
|
||||||
|
return true if destroyed?
|
||||||
|
|
||||||
|
return true if !customer_id
|
||||||
|
|
||||||
|
# send background job
|
||||||
|
TicketUserTicketCounterJob.perform_later(
|
||||||
|
customer_id,
|
||||||
|
UserInfo.current_user_id || updated_by_id,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
21
app/models/concerns/ticket/resets_pending_time_seconds.rb
Normal file
21
app/models/concerns/ticket/resets_pending_time_seconds.rb
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
# Ensures pending time is always zero-seconds.
|
||||||
|
module Ticket::ResetsPendingTimeSeconds
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
before_create :ticket_reset_pending_time_seconds
|
||||||
|
before_update :ticket_reset_pending_time_seconds
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def ticket_reset_pending_time_seconds
|
||||||
|
return true if pending_time.blank?
|
||||||
|
return true if !pending_time_changed?
|
||||||
|
return true if pending_time.sec.zero?
|
||||||
|
|
||||||
|
self.pending_time = pending_time.change sec: 0
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,34 +1,32 @@
|
||||||
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
class Observer::Ticket::CloseTime < ActiveRecord::Observer
|
# Adds close time (if missing) when tickets are closed.
|
||||||
observe 'ticket'
|
module Ticket::SetsCloseTime
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
def before_create(record)
|
included do
|
||||||
_check(record)
|
before_create :ticket_set_close_time
|
||||||
end
|
before_update :ticket_set_close_time
|
||||||
|
|
||||||
def before_update(record)
|
|
||||||
_check(record)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def _check(record)
|
def ticket_set_close_time
|
||||||
|
|
||||||
# return if we run import mode
|
# return if we run import mode
|
||||||
return true if Setting.get('import_mode')
|
return true if Setting.get('import_mode')
|
||||||
|
|
||||||
# check if close_at is already set
|
# check if close_at is already set
|
||||||
return true if record.close_at
|
return true if close_at
|
||||||
|
|
||||||
# check if ticket is closed now
|
# check if ticket is closed now
|
||||||
return true if !record.state_id
|
return true if !state_id
|
||||||
|
|
||||||
state = Ticket::State.lookup(id: record.state_id)
|
state = Ticket::State.lookup(id: state_id)
|
||||||
state_type = Ticket::StateType.lookup(id: state.state_type_id)
|
state_type = Ticket::StateType.lookup(id: state.state_type_id)
|
||||||
return true if state_type.name != 'closed'
|
return true if state_type.name != 'closed'
|
||||||
|
|
||||||
# set close_at
|
# set close_at
|
||||||
record.close_at = Time.zone.now
|
self.close_at = Time.zone.now
|
||||||
end
|
end
|
||||||
end
|
end
|
49
app/models/concerns/ticket/sets_last_owner_update_time.rb
Normal file
49
app/models/concerns/ticket/sets_last_owner_update_time.rb
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
# Adds a last_owner_update time on ticket changes.
|
||||||
|
module Ticket::SetsLastOwnerUpdateTime
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
before_create :ticket_set_last_owner_update_time
|
||||||
|
before_update :ticket_set_last_owner_update_time
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def ticket_set_last_owner_update_time
|
||||||
|
|
||||||
|
# return if we run import mode
|
||||||
|
return true if Setting.get('import_mode')
|
||||||
|
# check if owner, state or group has changed
|
||||||
|
return true if changes_to_save['owner_id'].blank? && changes_to_save['state_id'].blank? && changes_to_save['group_id'].blank? && changes_to_save['last_contact_agent_at'].blank?
|
||||||
|
|
||||||
|
# check if owner is nobody
|
||||||
|
if changes_to_save['owner_id'].present? && changes_to_save['owner_id'][1] == 1
|
||||||
|
self.last_owner_update_at = nil
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
# check if group is change
|
||||||
|
if changes_to_save['group_id'].present?
|
||||||
|
group = Group.lookup(id: changes_to_save['group_id'][1])
|
||||||
|
return true if !group
|
||||||
|
|
||||||
|
if group.assignment_timeout.blank? || group.assignment_timeout.zero?
|
||||||
|
self.last_owner_update_at = nil
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# check if state is not new/open
|
||||||
|
if changes_to_save['state_id'].present?
|
||||||
|
state_ids = Ticket::State.by_category(:work_on).pluck(:id)
|
||||||
|
if state_ids.exclude?(changes_to_save['state_id'][1])
|
||||||
|
self.last_owner_update_at = nil
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.last_owner_update_at = Time.zone.now
|
||||||
|
end
|
||||||
|
end
|
30
app/models/concerns/ticket/sets_online_notification_seen.rb
Normal file
30
app/models/concerns/ticket/sets_online_notification_seen.rb
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
# Schedules a background job to update the user's ticket seen information on ticket changes.
|
||||||
|
module Ticket::SetsOnlineNotificationSeen
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
after_create :ticket_set_online_notification_seen
|
||||||
|
after_update :ticket_set_online_notification_seen
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def ticket_set_online_notification_seen
|
||||||
|
|
||||||
|
# return if we run import mode
|
||||||
|
return false if Setting.get('import_mode')
|
||||||
|
|
||||||
|
# set seen only if state has changes
|
||||||
|
return false if !saved_changes?
|
||||||
|
return false if saved_changes['state_id'].blank?
|
||||||
|
|
||||||
|
# check if existing online notifications for this ticket should be set to seen
|
||||||
|
return true if !online_notification_seen_state
|
||||||
|
|
||||||
|
# set all online notifications to seen
|
||||||
|
# send background job
|
||||||
|
TicketOnlineNotificationSeenJob.perform_later(id, updated_by_id)
|
||||||
|
end
|
||||||
|
end
|
37
app/models/concerns/ticket/touches_associations.rb
Normal file
37
app/models/concerns/ticket/touches_associations.rb
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
# Update assigned customer and organization change_time information on ticket changes.
|
||||||
|
module Ticket::TouchesAssociations
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
after_create :ticket_touch_associations
|
||||||
|
after_update :ticket_touch_associations
|
||||||
|
after_destroy :ticket_touch_associations
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def ticket_touch_associations
|
||||||
|
|
||||||
|
# return if we run import mode
|
||||||
|
return true if Setting.get('import_mode')
|
||||||
|
|
||||||
|
# touch old customer if changed
|
||||||
|
customer_id_changed = saved_changes['customer_id']
|
||||||
|
if customer_id_changed && customer_id_changed[0] != customer_id_changed[1] && customer_id_changed[0]
|
||||||
|
User.find(customer_id_changed[0]).touch # rubocop:disable Rails/SkipsModelValidations
|
||||||
|
end
|
||||||
|
|
||||||
|
# touch new/current customer
|
||||||
|
customer&.touch # rubocop:disable Rails/SkipsModelValidations
|
||||||
|
|
||||||
|
# touch old organization if changed
|
||||||
|
organization_id_changed = saved_changes['organization_id']
|
||||||
|
if organization_id_changed && organization_id_changed[0] != organization_id_changed[1] && organization_id_changed[0]
|
||||||
|
Organization.find(organization_id_changed[0]).touch # rubocop:disable Rails/SkipsModelValidations
|
||||||
|
end
|
||||||
|
|
||||||
|
organization&.touch # rubocop:disable Rails/SkipsModelValidations
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,26 +1,23 @@
|
||||||
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
class Observer::User::Geo < ActiveRecord::Observer
|
# Perform geo data lookup on user changes.
|
||||||
observe 'user'
|
module User::PerformsGeoLookup
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
def before_create(record)
|
included do
|
||||||
check_geo(record)
|
before_create :user_check_geo_location
|
||||||
true
|
before_update :user_check_geo_location
|
||||||
end
|
end
|
||||||
|
|
||||||
def before_update(record)
|
private
|
||||||
check_geo(record)
|
|
||||||
true
|
|
||||||
end
|
|
||||||
|
|
||||||
# check if geo need to be updated
|
def user_check_geo_location
|
||||||
def check_geo(record)
|
|
||||||
|
|
||||||
location = %w[address street zip city country]
|
location = %w[address street zip city country]
|
||||||
|
|
||||||
# check if geo update is needed based on old/new location
|
# check if geo update is needed based on old/new location
|
||||||
if record.id
|
if id
|
||||||
current = User.find_by(id: record.id)
|
current = User.find_by(id: id)
|
||||||
return if !current
|
return if !current
|
||||||
|
|
||||||
current_location = {}
|
current_location = {}
|
||||||
|
@ -32,27 +29,26 @@ class Observer::User::Geo < ActiveRecord::Observer
|
||||||
# get full address
|
# get full address
|
||||||
next_location = {}
|
next_location = {}
|
||||||
location.each do |item|
|
location.each do |item|
|
||||||
next_location[item] = record[item]
|
next_location[item] = attributes[item]
|
||||||
end
|
end
|
||||||
|
|
||||||
# return if address hasn't changed and geo data is already available
|
# return if address hasn't changed and geo data is already available
|
||||||
return if (current_location == next_location) && record.preferences['lat'] && record.preferences['lng']
|
return if (current_location == next_location) && preferences['lat'] && preferences['lng']
|
||||||
|
|
||||||
# geo update
|
# geo update
|
||||||
geo_update(record)
|
user_update_geo_location
|
||||||
end
|
end
|
||||||
|
|
||||||
# update geo data of user
|
def user_update_geo_location
|
||||||
def geo_update(record)
|
|
||||||
address = ''
|
address = ''
|
||||||
location = %w[address street zip city country]
|
location = %w[address street zip city country]
|
||||||
location.each do |item|
|
location.each do |item|
|
||||||
next if record[item].blank?
|
next if attributes[item].blank?
|
||||||
|
|
||||||
if address.present?
|
if address.present?
|
||||||
address += ', '
|
address += ', '
|
||||||
end
|
end
|
||||||
address += record[item]
|
address += attributes[item]
|
||||||
end
|
end
|
||||||
|
|
||||||
# return if no address is given
|
# return if no address is given
|
||||||
|
@ -60,10 +56,11 @@ class Observer::User::Geo < ActiveRecord::Observer
|
||||||
|
|
||||||
# lookup
|
# lookup
|
||||||
latlng = Service::GeoLocation.geocode(address)
|
latlng = Service::GeoLocation.geocode(address)
|
||||||
|
|
||||||
return if !latlng
|
return if !latlng
|
||||||
|
|
||||||
# store data
|
# store data
|
||||||
record.preferences['lat'] = latlng[0]
|
preferences['lat'] = latlng[0]
|
||||||
record.preferences['lng'] = latlng[1]
|
preferences['lng'] = latlng[1]
|
||||||
end
|
end
|
||||||
end
|
end
|
36
app/models/concerns/user/touches_organization.rb
Normal file
36
app/models/concerns/user/touches_organization.rb
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
# Update assigned organization change_time information on user changes.
|
||||||
|
module User::TouchesOrganization
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
after_create :touch_user_organization
|
||||||
|
after_update :touch_user_organization
|
||||||
|
after_destroy :touch_user_organization
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def touch_user_organization
|
||||||
|
|
||||||
|
# return if we run import mode
|
||||||
|
return true if Setting.get('import_mode')
|
||||||
|
|
||||||
|
organization_id_changed = saved_changes['organization_id']
|
||||||
|
return true if !organization_id_changed
|
||||||
|
|
||||||
|
return true if organization_id_changed[0] == organization_id_changed[1]
|
||||||
|
|
||||||
|
# touch old organization
|
||||||
|
if organization_id_changed[0]
|
||||||
|
old_organization = Organization.find(organization_id_changed[0])
|
||||||
|
old_organization&.touch # rubocop:disable Rails/SkipsModelValidations
|
||||||
|
end
|
||||||
|
|
||||||
|
# touch new/current organization
|
||||||
|
organization&.touch # rubocop:disable Rails/SkipsModelValidations
|
||||||
|
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
28
app/models/concerns/user/updates_ticket_organization.rb
Normal file
28
app/models/concerns/user/updates_ticket_organization.rb
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
# If a user is assigned to another organization, also assign their latest tickets to it.
|
||||||
|
module User::UpdatesTicketOrganization
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
after_create :user_update_ticket_organization
|
||||||
|
after_update :user_update_ticket_organization
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def user_update_ticket_organization
|
||||||
|
|
||||||
|
# check if organization has changed
|
||||||
|
return true if !saved_change_to_attribute?('organization_id')
|
||||||
|
|
||||||
|
# update last 100 tickets of user
|
||||||
|
tickets = Ticket.where(customer_id: id).limit(100)
|
||||||
|
tickets.each do |ticket|
|
||||||
|
if ticket.organization_id != organization_id
|
||||||
|
ticket.organization_id = organization_id
|
||||||
|
ticket.save
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,36 +0,0 @@
|
||||||
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
|
||||||
class Observer::Tag::TicketHistory < ActiveRecord::Observer
|
|
||||||
observe 'tag'
|
|
||||||
|
|
||||||
def after_create(record)
|
|
||||||
|
|
||||||
# just process ticket object tags
|
|
||||||
return true if record.tag_object.name != 'Ticket'
|
|
||||||
|
|
||||||
# add ticket history
|
|
||||||
History.add(
|
|
||||||
o_id: record.o_id,
|
|
||||||
history_type: 'added',
|
|
||||||
history_object: 'Ticket',
|
|
||||||
history_attribute: 'tag',
|
|
||||||
value_to: record.tag_item.name,
|
|
||||||
created_by_id: record.created_by_id,
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def after_destroy(record)
|
|
||||||
|
|
||||||
# just process ticket object tags
|
|
||||||
return true if record.tag_object.name != 'Ticket'
|
|
||||||
|
|
||||||
# add ticket history
|
|
||||||
History.add(
|
|
||||||
o_id: record.o_id,
|
|
||||||
history_type: 'removed',
|
|
||||||
history_object: 'Ticket',
|
|
||||||
history_attribute: 'tag',
|
|
||||||
value_to: record.tag_item.name,
|
|
||||||
created_by_id: record.created_by_id,
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,25 +0,0 @@
|
||||||
class Observer::Ticket::Article::CommunicateSms < ActiveRecord::Observer
|
|
||||||
observe 'ticket::_article'
|
|
||||||
|
|
||||||
def after_create(record)
|
|
||||||
|
|
||||||
# return if we run import mode
|
|
||||||
return true if Setting.get('import_mode')
|
|
||||||
|
|
||||||
# if sender is customer, do not communicate
|
|
||||||
return true if !record.sender_id
|
|
||||||
|
|
||||||
sender = Ticket::Article::Sender.lookup(id: record.sender_id)
|
|
||||||
return true if sender.nil?
|
|
||||||
return true if sender.name == 'Customer'
|
|
||||||
|
|
||||||
# only apply on sms
|
|
||||||
return true if !record.type_id
|
|
||||||
|
|
||||||
type = Ticket::Article::Type.lookup(id: record.type_id)
|
|
||||||
return true if type.nil?
|
|
||||||
return true if type.name != 'sms'
|
|
||||||
|
|
||||||
CommunicateSmsJob.perform_later(record.id)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,27 +0,0 @@
|
||||||
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
|
||||||
|
|
||||||
class Observer::Ticket::Article::CommunicateTelegram < ActiveRecord::Observer
|
|
||||||
observe 'ticket::_article'
|
|
||||||
|
|
||||||
def after_create(record)
|
|
||||||
|
|
||||||
# return if we run import mode
|
|
||||||
return true if Setting.get('import_mode')
|
|
||||||
|
|
||||||
# if sender is customer, do not communicate
|
|
||||||
return true if !record.sender_id
|
|
||||||
|
|
||||||
sender = Ticket::Article::Sender.lookup(id: record.sender_id)
|
|
||||||
return true if sender.nil?
|
|
||||||
return true if sender.name == 'Customer'
|
|
||||||
|
|
||||||
# only apply on telegram messages
|
|
||||||
return true if !record.type_id
|
|
||||||
|
|
||||||
type = Ticket::Article::Type.lookup(id: record.type_id)
|
|
||||||
return true if !type.name.match?(/\Atelegram/i)
|
|
||||||
|
|
||||||
CommunicateTelegramJob.perform_later(record.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
|
@ -1,27 +0,0 @@
|
||||||
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
|
||||||
|
|
||||||
class Observer::Ticket::Article::FillupFromOriginById < ActiveRecord::Observer
|
|
||||||
observe 'ticket::_article'
|
|
||||||
|
|
||||||
def before_create(record)
|
|
||||||
|
|
||||||
# return if we run import mode
|
|
||||||
return true if Setting.get('import_mode')
|
|
||||||
|
|
||||||
# only do fill origin_by_id if article got created via application_server (e. g. not
|
|
||||||
# if article and sender type is set via *.postmaster)
|
|
||||||
return true if ApplicationHandleInfo.postmaster?
|
|
||||||
|
|
||||||
# check if origin_by_id exists
|
|
||||||
return true if record.origin_by_id.present?
|
|
||||||
return true if record.ticket.blank?
|
|
||||||
return true if record.ticket.customer_id.blank?
|
|
||||||
return true if record.sender_id.blank?
|
|
||||||
return true if record.sender.name != 'Customer'
|
|
||||||
|
|
||||||
type_name = record.type.name
|
|
||||||
return true if type_name != 'phone' && type_name != 'note' && type_name != 'web'
|
|
||||||
|
|
||||||
record.origin_by_id = record.ticket.customer_id
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,52 +0,0 @@
|
||||||
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
|
||||||
|
|
||||||
class Observer::Ticket::LastOwnerUpdate < ActiveRecord::Observer
|
|
||||||
observe 'ticket'
|
|
||||||
|
|
||||||
def before_create(record)
|
|
||||||
_check(record)
|
|
||||||
end
|
|
||||||
|
|
||||||
def before_update(record)
|
|
||||||
_check(record)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def _check(record)
|
|
||||||
|
|
||||||
# return if we run import mode
|
|
||||||
return true if Setting.get('import_mode')
|
|
||||||
|
|
||||||
# check if owner, state or group has changed
|
|
||||||
return true if record.changes_to_save['owner_id'].blank? && record.changes_to_save['state_id'].blank? && record.changes_to_save['group_id'].blank? && record.changes_to_save['last_contact_agent_at'].blank?
|
|
||||||
|
|
||||||
# check if owner is nobody
|
|
||||||
if record.changes_to_save['owner_id'].present? && record.changes_to_save['owner_id'][1] == 1
|
|
||||||
record.last_owner_update_at = nil
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
# check if group is change
|
|
||||||
if record.changes_to_save['group_id'].present?
|
|
||||||
group = Group.lookup(id: record.changes_to_save['group_id'][1])
|
|
||||||
return true if !group
|
|
||||||
|
|
||||||
if group.assignment_timeout.blank? || group.assignment_timeout.zero?
|
|
||||||
record.last_owner_update_at = nil
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# check if state is not new/open
|
|
||||||
if record.changes_to_save['state_id'].present?
|
|
||||||
state_ids = Ticket::State.by_category(:work_on).pluck(:id)
|
|
||||||
if state_ids.exclude?(record.changes_to_save['state_id'][1])
|
|
||||||
record.last_owner_update_at = nil
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
record.last_owner_update_at = Time.zone.now
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,32 +0,0 @@
|
||||||
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
|
||||||
|
|
||||||
class Observer::Ticket::OnlineNotificationSeen < ActiveRecord::Observer
|
|
||||||
observe 'ticket'
|
|
||||||
|
|
||||||
def after_create(record)
|
|
||||||
_check(record)
|
|
||||||
end
|
|
||||||
|
|
||||||
def after_update(record)
|
|
||||||
_check(record)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def _check(record)
|
|
||||||
|
|
||||||
# return if we run import mode
|
|
||||||
return false if Setting.get('import_mode')
|
|
||||||
|
|
||||||
# set seen only if state has changes
|
|
||||||
return false if !record.saved_changes?
|
|
||||||
return false if record.saved_changes['state_id'].blank?
|
|
||||||
|
|
||||||
# check if existing online notifications for this ticket should be set to seen
|
|
||||||
return true if !record.online_notification_seen_state
|
|
||||||
|
|
||||||
# set all online notifications to seen
|
|
||||||
# send background job
|
|
||||||
TicketOnlineNotificationSeenJob.perform_later(record.id, record.updated_by_id)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,22 +0,0 @@
|
||||||
# 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
|
|
|
@ -1,43 +0,0 @@
|
||||||
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
|
||||||
|
|
||||||
class Observer::Ticket::RefObjectTouch < ActiveRecord::Observer
|
|
||||||
observe 'ticket'
|
|
||||||
|
|
||||||
def after_create(record)
|
|
||||||
ref_object_touch(record)
|
|
||||||
end
|
|
||||||
|
|
||||||
def after_update(record)
|
|
||||||
ref_object_touch(record)
|
|
||||||
end
|
|
||||||
|
|
||||||
def after_destroy(record)
|
|
||||||
ref_object_touch(record)
|
|
||||||
end
|
|
||||||
|
|
||||||
def ref_object_touch(record)
|
|
||||||
|
|
||||||
# return if we run import mode
|
|
||||||
return true if Setting.get('import_mode')
|
|
||||||
|
|
||||||
# touch old customer if changed
|
|
||||||
cutomer_id_changed = record.saved_changes['customer_id']
|
|
||||||
if cutomer_id_changed && cutomer_id_changed[0] != cutomer_id_changed[1] && cutomer_id_changed[0]
|
|
||||||
User.find(cutomer_id_changed[0]).touch # rubocop:disable Rails/SkipsModelValidations
|
|
||||||
end
|
|
||||||
|
|
||||||
# touch new/current customer
|
|
||||||
record.customer&.touch # rubocop:disable Rails/SkipsModelValidations
|
|
||||||
|
|
||||||
# touch old organization if changed
|
|
||||||
organization_id_changed = record.saved_changes['organization_id']
|
|
||||||
if organization_id_changed && organization_id_changed[0] != organization_id_changed[1] && organization_id_changed[0]
|
|
||||||
Organization.find(organization_id_changed[0]).touch # rubocop:disable Rails/SkipsModelValidations
|
|
||||||
end
|
|
||||||
|
|
||||||
# touch new/current organization
|
|
||||||
return true if !record.organization
|
|
||||||
|
|
||||||
record.organization.touch # rubocop:disable Rails/SkipsModelValidations
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,26 +0,0 @@
|
||||||
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
|
||||||
|
|
||||||
require_dependency 'stats/ticket_reopen'
|
|
||||||
|
|
||||||
class Observer::Ticket::StatsReopen < ActiveRecord::Observer
|
|
||||||
|
|
||||||
observe 'ticket'
|
|
||||||
|
|
||||||
def after_create(record)
|
|
||||||
_check(record)
|
|
||||||
end
|
|
||||||
|
|
||||||
def after_update(record)
|
|
||||||
_check(record)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def _check(record)
|
|
||||||
|
|
||||||
# return if we run import mode
|
|
||||||
return if Setting.get('import_mode')
|
|
||||||
|
|
||||||
Stats::TicketReopen.log('Ticket', record.id, record.saved_changes, record.updated_by_id)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,28 +0,0 @@
|
||||||
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
|
||||||
|
|
||||||
class Observer::Ticket::UserTicketCounter < ActiveRecord::Observer
|
|
||||||
observe 'ticket'
|
|
||||||
|
|
||||||
def after_commit(record)
|
|
||||||
user_ticket_counter_update(record)
|
|
||||||
end
|
|
||||||
|
|
||||||
def user_ticket_counter_update(record)
|
|
||||||
|
|
||||||
# return if we run import mode
|
|
||||||
return true if Setting.get('import_mode')
|
|
||||||
|
|
||||||
return true if BulkImportInfo.enabled?
|
|
||||||
|
|
||||||
return true if record.destroyed?
|
|
||||||
|
|
||||||
return true if !record.customer_id
|
|
||||||
|
|
||||||
# send background job
|
|
||||||
TicketUserTicketCounterJob.perform_later(
|
|
||||||
record.customer_id,
|
|
||||||
UserInfo.current_user_id || record.updated_by_id,
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
|
@ -1,41 +0,0 @@
|
||||||
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
|
||||||
|
|
||||||
class Observer::User::RefObjectTouch < ActiveRecord::Observer
|
|
||||||
observe 'user'
|
|
||||||
|
|
||||||
def after_create(record)
|
|
||||||
ref_object_touch(record)
|
|
||||||
end
|
|
||||||
|
|
||||||
def after_update(record)
|
|
||||||
ref_object_touch(record)
|
|
||||||
end
|
|
||||||
|
|
||||||
def after_destroy(record)
|
|
||||||
ref_object_touch(record)
|
|
||||||
end
|
|
||||||
|
|
||||||
def ref_object_touch(record)
|
|
||||||
|
|
||||||
# return if we run import mode
|
|
||||||
return true if Setting.get('import_mode')
|
|
||||||
|
|
||||||
organization_id_changed = record.saved_changes['organization_id']
|
|
||||||
return true if !organization_id_changed
|
|
||||||
|
|
||||||
return true if organization_id_changed[0] == organization_id_changed[1]
|
|
||||||
|
|
||||||
# touch old organization
|
|
||||||
if organization_id_changed[0]
|
|
||||||
organization = Organization.find(organization_id_changed[0])
|
|
||||||
organization.touch # rubocop:disable Rails/SkipsModelValidations
|
|
||||||
end
|
|
||||||
|
|
||||||
# touch new/current organization
|
|
||||||
if record&.organization
|
|
||||||
record.organization.touch # rubocop:disable Rails/SkipsModelValidations
|
|
||||||
end
|
|
||||||
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,30 +0,0 @@
|
||||||
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
|
||||||
|
|
||||||
class Observer::User::TicketOrganization < ActiveRecord::Observer
|
|
||||||
observe 'user'
|
|
||||||
|
|
||||||
def after_create(record)
|
|
||||||
check_organization(record)
|
|
||||||
end
|
|
||||||
|
|
||||||
def after_update(record)
|
|
||||||
check_organization(record)
|
|
||||||
end
|
|
||||||
|
|
||||||
# check if organization need to be updated
|
|
||||||
def check_organization(record)
|
|
||||||
|
|
||||||
# check if organization has changed
|
|
||||||
return true if !record.saved_change_to_attribute?('organization_id')
|
|
||||||
|
|
||||||
# update last 100 tickets of user
|
|
||||||
tickets = Ticket.where(customer_id: record.id).limit(100)
|
|
||||||
tickets.each do |ticket|
|
|
||||||
if ticket.organization_id != record.organization_id
|
|
||||||
ticket.organization_id = record.organization_id
|
|
||||||
ticket.save
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
|
@ -1,6 +1,7 @@
|
||||||
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
class Tag < ApplicationModel
|
class Tag < ApplicationModel
|
||||||
|
include Tag::WritesToTicketHistory
|
||||||
|
|
||||||
belongs_to :tag_object, class_name: 'Tag::Object', optional: true
|
belongs_to :tag_object, class_name: 'Tag::Object', optional: true
|
||||||
belongs_to :tag_item, class_name: 'Tag::Item', optional: true
|
belongs_to :tag_item, class_name: 'Tag::Item', optional: true
|
||||||
|
|
|
@ -15,6 +15,12 @@ class Ticket < ApplicationModel
|
||||||
include HasLinks
|
include HasLinks
|
||||||
include HasObjectManagerAttributesValidation
|
include HasObjectManagerAttributesValidation
|
||||||
include HasTaskbars
|
include HasTaskbars
|
||||||
|
include Ticket::CallsStatsTicketReopenLog
|
||||||
|
include Ticket::EnqueuesUserTicketCounterJob
|
||||||
|
include Ticket::ResetsPendingTimeSeconds
|
||||||
|
include Ticket::SetsCloseTime
|
||||||
|
include Ticket::SetsOnlineNotificationSeen
|
||||||
|
include Ticket::TouchesAssociations
|
||||||
|
|
||||||
include ::Ticket::Escalation
|
include ::Ticket::Escalation
|
||||||
include ::Ticket::Subject
|
include ::Ticket::Subject
|
||||||
|
@ -27,6 +33,9 @@ class Ticket < ApplicationModel
|
||||||
before_create :check_generate, :check_defaults, :check_title, :set_default_state, :set_default_priority
|
before_create :check_generate, :check_defaults, :check_title, :set_default_state, :set_default_priority
|
||||||
before_update :check_defaults, :check_title, :reset_pending_time, :check_owner_active
|
before_update :check_defaults, :check_title, :reset_pending_time, :check_owner_active
|
||||||
|
|
||||||
|
# This must be loaded late as it depends on the internal before_create and before_update handlers of ticket.rb.
|
||||||
|
include Ticket::SetsLastOwnerUpdateTime
|
||||||
|
|
||||||
validates :group_id, presence: true
|
validates :group_id, presence: true
|
||||||
|
|
||||||
activity_stream_permission 'ticket.agent'
|
activity_stream_permission 'ticket.agent'
|
||||||
|
|
|
@ -10,7 +10,18 @@ class Ticket::Article < ApplicationModel
|
||||||
include HasObjectManagerAttributesValidation
|
include HasObjectManagerAttributesValidation
|
||||||
|
|
||||||
include Ticket::Article::Assets
|
include Ticket::Article::Assets
|
||||||
|
include Ticket::Article::EnqueueCommunicateEmailJob
|
||||||
|
include Ticket::Article::EnqueueCommunicateFacebookJob
|
||||||
|
include Ticket::Article::EnqueueCommunicateSmsJob
|
||||||
|
include Ticket::Article::EnqueueCommunicateTelegramJob
|
||||||
|
include Ticket::Article::EnqueueCommunicateTwitterJob
|
||||||
include Ticket::Article::HasTicketContactAttributesImpact
|
include Ticket::Article::HasTicketContactAttributesImpact
|
||||||
|
include Ticket::Article::ResetsTicketState
|
||||||
|
|
||||||
|
# AddsMetadataGeneral depends on AddsMetadataOriginById, so load that first
|
||||||
|
include Ticket::Article::AddsMetadataOriginById
|
||||||
|
include Ticket::Article::AddsMetadataGeneral
|
||||||
|
include Ticket::Article::AddsMetadataEmail
|
||||||
|
|
||||||
belongs_to :ticket, optional: true
|
belongs_to :ticket, optional: true
|
||||||
has_one :ticket_time_accounting, class_name: 'Ticket::TimeAccounting', foreign_key: :ticket_article_id, dependent: :destroy, inverse_of: :ticket_article
|
has_one :ticket_time_accounting, class_name: 'Ticket::TimeAccounting', foreign_key: :ticket_article_id, dependent: :destroy, inverse_of: :ticket_article
|
||||||
|
|
|
@ -19,6 +19,9 @@ class User < ApplicationModel
|
||||||
include User::Assets
|
include User::Assets
|
||||||
include User::Search
|
include User::Search
|
||||||
include User::SearchIndex
|
include User::SearchIndex
|
||||||
|
include User::TouchesOrganization
|
||||||
|
include User::PerformsGeoLookup
|
||||||
|
include User::UpdatesTicketOrganization
|
||||||
|
|
||||||
has_and_belongs_to_many :organizations, after_add: :cache_update, after_remove: :cache_update, class_name: 'Organization'
|
has_and_belongs_to_many :organizations, after_add: :cache_update, after_remove: :cache_update, class_name: 'Organization'
|
||||||
has_and_belongs_to_many :overviews, dependent: :nullify
|
has_and_belongs_to_many :overviews, dependent: :nullify
|
||||||
|
|
|
@ -27,26 +27,6 @@ module Zammad
|
||||||
# config.active_record.observers = :cacher, :garbage_collector, :forum_observer
|
# config.active_record.observers = :cacher, :garbage_collector, :forum_observer
|
||||||
config.active_record.observers =
|
config.active_record.observers =
|
||||||
'observer::_session',
|
'observer::_session',
|
||||||
'observer::_ticket::_close_time',
|
|
||||||
'observer::_ticket::_last_owner_update',
|
|
||||||
'observer::_ticket::_pending_time',
|
|
||||||
'observer::_ticket::_user_ticket_counter',
|
|
||||||
'observer::_ticket::_article::_fillup_from_origin_by_id',
|
|
||||||
'observer::_ticket::_article::_fillup_from_general',
|
|
||||||
'observer::_ticket::_article::_fillup_from_email',
|
|
||||||
'observer::_ticket::_article::_communicate_email',
|
|
||||||
'observer::_ticket::_article::_communicate_facebook',
|
|
||||||
'observer::_ticket::_article::_communicate_sms',
|
|
||||||
'observer::_ticket::_article::_communicate_twitter',
|
|
||||||
'observer::_ticket::_article::_communicate_telegram',
|
|
||||||
'observer::_ticket::_reset_new_state',
|
|
||||||
'observer::_ticket::_ref_object_touch',
|
|
||||||
'observer::_ticket::_online_notification_seen',
|
|
||||||
'observer::_ticket::_stats_reopen',
|
|
||||||
'observer::_tag::_ticket_history',
|
|
||||||
'observer::_user::_ref_object_touch',
|
|
||||||
'observer::_user::_ticket_organization',
|
|
||||||
'observer::_user::_geo',
|
|
||||||
'observer::_transaction'
|
'observer::_transaction'
|
||||||
|
|
||||||
config.active_job.queue_adapter = :delayed_job
|
config.active_job.queue_adapter = :delayed_job
|
||||||
|
|
|
@ -5,13 +5,6 @@ RSpec.describe CommunicateTwitterJob, type: :job do
|
||||||
let(:article) { create(:twitter_article, **(try(:factory_options) || {})) }
|
let(:article) { create(:twitter_article, **(try(:factory_options) || {})) }
|
||||||
|
|
||||||
describe 'core behavior', :use_vcr do
|
describe 'core behavior', :use_vcr do
|
||||||
# This job runs automatically whenever an article is created.
|
|
||||||
# We disable this auto-execution so we can invoke it manually in the tests below.
|
|
||||||
around do |example|
|
|
||||||
ActiveRecord::Base.observers.disable('observer::_ticket::_article::_communicate_twitter')
|
|
||||||
example.run
|
|
||||||
ActiveRecord::Base.observers.enable('observer::_ticket::_article::_communicate_twitter')
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'for tweets' do
|
context 'for tweets' do
|
||||||
let(:tweet_attributes) do
|
let(:tweet_attributes) do
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
RSpec.shared_examples 'TagWritesToTicketHistory' do
|
||||||
|
subject { create(described_class.name.underscore) }
|
||||||
|
|
||||||
|
# The concern is for the tag model, but the shared example needs to be loaded in the ticket test.
|
||||||
|
it 'can only be loaded for tickets' do
|
||||||
|
expect(described_class).to eq Ticket
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a ticket history entry for tag_add' do # rubocop:disable RSpec/ExampleLength
|
||||||
|
subject.tag_add('foo', 1)
|
||||||
|
expect(subject.history_get.last).to include(
|
||||||
|
'object' => described_class.name,
|
||||||
|
'o_id' => subject.id,
|
||||||
|
'type' => 'added',
|
||||||
|
'attribute' => 'tag',
|
||||||
|
'value_to' => 'foo',
|
||||||
|
'value_from' => nil
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a ticket history entry for tag_remove' do # rubocop:disable RSpec/ExampleLength
|
||||||
|
subject.tag_add('foo', 1)
|
||||||
|
subject.tag_remove('foo', 1)
|
||||||
|
expect(subject.history_get.last).to include(
|
||||||
|
'object' => described_class.name,
|
||||||
|
'o_id' => subject.id,
|
||||||
|
'type' => 'removed',
|
||||||
|
'attribute' => 'tag',
|
||||||
|
'value_to' => 'foo',
|
||||||
|
'value_from' => nil
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,6 +1,6 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe Observer::Ticket::Article::FillupFromGeneral, current_user_id: -> { agent.id } do
|
RSpec.describe Ticket::Article::AddsMetadataGeneral, current_user_id: -> { agent.id } do
|
||||||
let(:agent) { create(:agent) }
|
let(:agent) { create(:agent) }
|
||||||
|
|
||||||
context 'when customer is agent' do
|
context 'when customer is agent' do
|
|
@ -0,0 +1,41 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe Ticket::Article::EnqueueCommunicateEmailJob, performs_jobs: true do
|
||||||
|
before { allow(Delayed::Job).to receive(:enqueue).and_call_original }
|
||||||
|
|
||||||
|
let(:article) { create(:ticket_article, **(try(:factory_options) || {})) }
|
||||||
|
|
||||||
|
shared_examples 'for no-op' do
|
||||||
|
it 'is a no-op' do
|
||||||
|
expect { article }.not_to have_enqueued_job(TicketArticleCommunicateEmailJob)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'for success' do
|
||||||
|
it 'enqueues the Email background job' do
|
||||||
|
expect { article }.to have_enqueued_job(TicketArticleCommunicateEmailJob)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when in Import Mode' do
|
||||||
|
before { Setting.set('import_mode', true) }
|
||||||
|
|
||||||
|
include_examples 'for no-op'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when article is created during Channel::EmailParser#process', application_handle: 'scheduler.postmaster' do
|
||||||
|
include_examples 'for no-op'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when article is from a customer' do
|
||||||
|
let(:factory_options) { { sender_name: 'Customer' } }
|
||||||
|
|
||||||
|
include_examples 'for no-op'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when article is an email' do
|
||||||
|
let(:factory_options) { { sender_name: 'Agent', type_name: 'email' } }
|
||||||
|
|
||||||
|
include_examples 'for success'
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,41 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe Ticket::Article::EnqueueCommunicateFacebookJob, performs_jobs: true do
|
||||||
|
before { allow(Delayed::Job).to receive(:enqueue).and_call_original }
|
||||||
|
|
||||||
|
let(:article) { create(:ticket_article, **(try(:factory_options) || {})) }
|
||||||
|
|
||||||
|
shared_examples 'for no-op' do
|
||||||
|
it 'is a no-op' do
|
||||||
|
expect { article }.not_to have_enqueued_job(CommunicateFacebookJob)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'for success' do
|
||||||
|
it 'enqueues the Facebook background job' do
|
||||||
|
expect { article }.to have_enqueued_job(CommunicateFacebookJob)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when in Import Mode' do
|
||||||
|
before { Setting.set('import_mode', true) }
|
||||||
|
|
||||||
|
include_examples 'for no-op'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when article is created during Channel::EmailParser#process', application_handle: 'scheduler.postmaster' do
|
||||||
|
include_examples 'for no-op'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when article is from a customer' do
|
||||||
|
let(:factory_options) { { sender_name: 'Customer' } }
|
||||||
|
|
||||||
|
include_examples 'for no-op'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when article is a facebook post' do
|
||||||
|
let(:factory_options) { { sender_name: 'Agent', type_name: 'facebook feed post' } }
|
||||||
|
|
||||||
|
include_examples 'for success'
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,41 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe Ticket::Article::EnqueueCommunicateSmsJob, performs_jobs: true do
|
||||||
|
before { allow(Delayed::Job).to receive(:enqueue).and_call_original }
|
||||||
|
|
||||||
|
let(:article) { create(:ticket_article, **(try(:factory_options) || {})) }
|
||||||
|
|
||||||
|
shared_examples 'for no-op' do
|
||||||
|
it 'is a no-op' do
|
||||||
|
expect { article }.not_to have_enqueued_job(CommunicateSmsJob)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'for success' do
|
||||||
|
it 'enqueues the SMS background job' do
|
||||||
|
expect { article }.to have_enqueued_job(CommunicateSmsJob)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when in Import Mode' do
|
||||||
|
before { Setting.set('import_mode', true) }
|
||||||
|
|
||||||
|
include_examples 'for no-op'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when article is created during Channel::EmailParser#process', application_handle: 'scheduler.postmaster' do
|
||||||
|
include_examples 'for no-op'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when article is from a customer' do
|
||||||
|
let(:factory_options) { { sender_name: 'Customer' } }
|
||||||
|
|
||||||
|
include_examples 'for no-op'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when article is an sms' do
|
||||||
|
let(:factory_options) { { sender_name: 'Agent', type_name: 'sms' } }
|
||||||
|
|
||||||
|
include_examples 'for success'
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,41 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe 'Ticket::Article::EnqueueCommunicateTelegramJob', performs_jobs: true do
|
||||||
|
before { allow(Delayed::Job).to receive(:enqueue).and_call_original }
|
||||||
|
|
||||||
|
let(:article) { create(:ticket_article, **(try(:factory_options) || {})) }
|
||||||
|
|
||||||
|
shared_examples 'for no-op' do
|
||||||
|
it 'is a no-op' do
|
||||||
|
expect { article }.not_to have_enqueued_job(CommunicateTelegramJob)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'for success' do
|
||||||
|
it 'enqueues the Telegram background job' do
|
||||||
|
expect { article }.to have_enqueued_job(CommunicateTelegramJob)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when in Import Mode' do
|
||||||
|
before { Setting.set('import_mode', true) }
|
||||||
|
|
||||||
|
include_examples 'for no-op'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when article is created during Channel::EmailParser#process', application_handle: 'scheduler.postmaster' do
|
||||||
|
include_examples 'for no-op'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when article is from a customer' do
|
||||||
|
let(:factory_options) { { sender_name: 'Customer' } }
|
||||||
|
|
||||||
|
include_examples 'for no-op'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when article is a Telegram message' do
|
||||||
|
let(:factory_options) { { sender_name: 'Agent', type_name: 'telegram personal-message' } }
|
||||||
|
|
||||||
|
include_examples 'for success'
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,6 +1,6 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe Observer::Ticket::Article::CommunicateTwitter, performs_jobs: true do
|
RSpec.describe Ticket::Article::EnqueueCommunicateTwitterJob, performs_jobs: true do
|
||||||
before { allow(Delayed::Job).to receive(:enqueue).and_call_original }
|
before { allow(Delayed::Job).to receive(:enqueue).and_call_original }
|
||||||
|
|
||||||
let(:article) { create(:ticket_article, **(try(:factory_options) || {})) }
|
let(:article) { create(:ticket_article, **(try(:factory_options) || {})) }
|
||||||
|
@ -17,7 +17,7 @@ RSpec.describe Observer::Ticket::Article::CommunicateTwitter, performs_jobs: tru
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'in Import Mode' do
|
context 'when in Import Mode' do
|
||||||
before { Setting.set('import_mode', true) }
|
before { Setting.set('import_mode', true) }
|
||||||
|
|
||||||
include_examples 'for no-op'
|
include_examples 'for no-op'
|
||||||
|
@ -50,7 +50,7 @@ RSpec.describe Observer::Ticket::Article::CommunicateTwitter, performs_jobs: tru
|
||||||
|
|
||||||
include_examples 'for success'
|
include_examples 'for success'
|
||||||
|
|
||||||
context 'but #to attribute is missing' do
|
context 'when #to attribute is missing' do
|
||||||
let(:factory_options) { { sender_name: 'Agent', type_name: 'twitter direct-message', to: nil } }
|
let(:factory_options) { { sender_name: 'Agent', type_name: 'twitter direct-message', to: nil } }
|
||||||
|
|
||||||
it 'raises an error' do
|
it 'raises an error' do
|
|
@ -0,0 +1,12 @@
|
||||||
|
RSpec.shared_examples 'TicketCallsStatsTicketReopenLog' do
|
||||||
|
|
||||||
|
it 'can only be loaded for Ticket' do
|
||||||
|
expect(described_class).to eq Ticket
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'calls Stats::TicketReopen.log' do
|
||||||
|
allow(Stats::TicketReopen).to receive(:log)
|
||||||
|
create(described_class.name.underscore)
|
||||||
|
expect(Stats::TicketReopen).to have_received(:log)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,10 @@
|
||||||
|
RSpec.shared_examples 'TicketEnqueuesTicketUserTicketCounterJob', type: :job do
|
||||||
|
subject { create(described_class.name.underscore) }
|
||||||
|
|
||||||
|
let(:customer) { create('customer') }
|
||||||
|
|
||||||
|
it 'enqueues a job for the customer' do
|
||||||
|
subject.customer = customer
|
||||||
|
expect { subject.save }.to have_enqueued_job(TicketUserTicketCounterJob)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,12 @@
|
||||||
|
RSpec.shared_examples 'TicketResetsPendingTimeSeconds' do
|
||||||
|
subject { create(described_class.name.underscore) }
|
||||||
|
|
||||||
|
it 'can only be loaded for tickets' do
|
||||||
|
expect(described_class).to eq Ticket
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'resets pending_time seconds' do
|
||||||
|
subject.update(pending_time: Time.zone.parse('2007-02-10 15:30:45'))
|
||||||
|
expect(subject.pending_time).to eq(Time.zone.parse('2007-02-10 15:30:00'))
|
||||||
|
end
|
||||||
|
end
|
18
spec/models/concerns/ticket/sets_close_time_examples.rb
Normal file
18
spec/models/concerns/ticket/sets_close_time_examples.rb
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.shared_examples 'TicketSetsCloseTime' do
|
||||||
|
subject { create(described_class.name.underscore) }
|
||||||
|
|
||||||
|
it 'can only be loaded for tickets' do
|
||||||
|
expect(described_class).to eq Ticket
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
travel_to Time.zone.now
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'resets pending_time seconds' do
|
||||||
|
subject.update(state: Ticket::State.lookup(name: 'closed'))
|
||||||
|
expect(subject.close_at).to eq(Time.zone.now)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,24 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.shared_examples 'TicketSetsLastOwnerUpdateTime' do
|
||||||
|
subject { create(described_class.name.underscore) }
|
||||||
|
|
||||||
|
let(:new_owner) { create(:agent, groups: [subject.group]) }
|
||||||
|
|
||||||
|
it 'can only be loaded for tickets' do
|
||||||
|
expect(described_class).to eq Ticket
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
travel_to Time.zone.now
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'has no last_owner_update_at initially' do
|
||||||
|
expect(subject.last_owner_update_at).to be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'gets last_owner_update_at after user change' do
|
||||||
|
subject.update(owner: new_owner)
|
||||||
|
expect(subject.last_owner_update_at).to eq(Time.zone.now)
|
||||||
|
end
|
||||||
|
end
|
16
spec/models/concerns/user/performs_geo_lookup_examples.rb
Normal file
16
spec/models/concerns/user/performs_geo_lookup_examples.rb
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
RSpec.shared_examples 'UserPerformsGeoLookup' do
|
||||||
|
|
||||||
|
it 'can only be loaded for User' do
|
||||||
|
expect(described_class).to eq User
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'performs geo lookup' do
|
||||||
|
|
||||||
|
# Mock the geo lookup as it requires an API key.
|
||||||
|
allow(Service::GeoLocation).to receive(:geocode).with('Marienstraße 18, 10117, Berlin, Germany').and_return([10.0, 20.0])
|
||||||
|
|
||||||
|
user = create(described_class.name.underscore, street: 'Marienstraße 18', zip: '10117', city: 'Berlin', country: 'Germany')
|
||||||
|
|
||||||
|
expect(user.preferences).to include(lat: 10.0, lng: 20.0)
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,9 +4,15 @@ require 'models/concerns/can_be_imported_examples'
|
||||||
require 'models/concerns/can_csv_import_examples'
|
require 'models/concerns/can_csv_import_examples'
|
||||||
require 'models/concerns/has_history_examples'
|
require 'models/concerns/has_history_examples'
|
||||||
require 'models/concerns/has_tags_examples'
|
require 'models/concerns/has_tags_examples'
|
||||||
|
require 'models/concerns/tag/writes_to_ticket_history_examples'
|
||||||
require 'models/concerns/has_taskbars_examples'
|
require 'models/concerns/has_taskbars_examples'
|
||||||
require 'models/concerns/has_xss_sanitized_note_examples'
|
require 'models/concerns/has_xss_sanitized_note_examples'
|
||||||
require 'models/concerns/has_object_manager_attributes_validation_examples'
|
require 'models/concerns/has_object_manager_attributes_validation_examples'
|
||||||
|
require 'models/concerns/ticket/calls_stats_ticket_reopen_log_examples'
|
||||||
|
require 'models/concerns/ticket/enqueues_user_ticket_counter_job_examples'
|
||||||
|
require 'models/concerns/ticket/resets_pending_time_seconds_examples'
|
||||||
|
require 'models/concerns/ticket/sets_close_time_examples'
|
||||||
|
require 'models/concerns/ticket/sets_last_owner_update_time_examples'
|
||||||
require 'models/ticket/escalation_examples'
|
require 'models/ticket/escalation_examples'
|
||||||
|
|
||||||
RSpec.describe Ticket, type: :model do
|
RSpec.describe Ticket, type: :model do
|
||||||
|
@ -17,10 +23,16 @@ RSpec.describe Ticket, type: :model do
|
||||||
it_behaves_like 'CanCsvImport'
|
it_behaves_like 'CanCsvImport'
|
||||||
it_behaves_like 'HasHistory', history_relation_object: 'Ticket::Article'
|
it_behaves_like 'HasHistory', history_relation_object: 'Ticket::Article'
|
||||||
it_behaves_like 'HasTags'
|
it_behaves_like 'HasTags'
|
||||||
|
it_behaves_like 'TagWritesToTicketHistory'
|
||||||
it_behaves_like 'HasTaskbars'
|
it_behaves_like 'HasTaskbars'
|
||||||
it_behaves_like 'HasXssSanitizedNote', model_factory: :ticket
|
it_behaves_like 'HasXssSanitizedNote', model_factory: :ticket
|
||||||
it_behaves_like 'HasObjectManagerAttributesValidation'
|
it_behaves_like 'HasObjectManagerAttributesValidation'
|
||||||
it_behaves_like 'Ticket::Escalation'
|
it_behaves_like 'Ticket::Escalation'
|
||||||
|
it_behaves_like 'TicketCallsStatsTicketReopenLog'
|
||||||
|
it_behaves_like 'TicketEnqueuesTicketUserTicketCounterJob'
|
||||||
|
it_behaves_like 'TicketResetsPendingTimeSeconds'
|
||||||
|
it_behaves_like 'TicketSetsCloseTime'
|
||||||
|
it_behaves_like 'TicketSetsLastOwnerUpdateTime'
|
||||||
|
|
||||||
describe 'Class methods:' do
|
describe 'Class methods:' do
|
||||||
describe '.selectors' do
|
describe '.selectors' do
|
||||||
|
|
|
@ -7,6 +7,7 @@ require 'models/concerns/has_groups_permissions_examples'
|
||||||
require 'models/concerns/has_xss_sanitized_note_examples'
|
require 'models/concerns/has_xss_sanitized_note_examples'
|
||||||
require 'models/concerns/can_be_imported_examples'
|
require 'models/concerns/can_be_imported_examples'
|
||||||
require 'models/concerns/has_object_manager_attributes_validation_examples'
|
require 'models/concerns/has_object_manager_attributes_validation_examples'
|
||||||
|
require 'models/concerns/user/performs_geo_lookup_examples'
|
||||||
require 'models/user/has_ticket_create_screen_impact_examples'
|
require 'models/user/has_ticket_create_screen_impact_examples'
|
||||||
require 'models/user/can_lookup_search_index_attributes_examples'
|
require 'models/user/can_lookup_search_index_attributes_examples'
|
||||||
require 'models/concerns/has_taskbars_examples'
|
require 'models/concerns/has_taskbars_examples'
|
||||||
|
@ -29,6 +30,7 @@ RSpec.describe User, type: :model do
|
||||||
it_behaves_like 'User::HasTicketCreateScreenImpact'
|
it_behaves_like 'User::HasTicketCreateScreenImpact'
|
||||||
it_behaves_like 'CanLookupSearchIndexAttributes'
|
it_behaves_like 'CanLookupSearchIndexAttributes'
|
||||||
it_behaves_like 'HasTaskbars'
|
it_behaves_like 'HasTaskbars'
|
||||||
|
it_behaves_like 'UserPerformsGeoLookup'
|
||||||
|
|
||||||
describe 'Class methods:' do
|
describe 'Class methods:' do
|
||||||
describe '.authenticate' do
|
describe '.authenticate' do
|
||||||
|
|
Loading…
Reference in a new issue