ticket.rb 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. class Ticket < ApplicationModel
  3. include CanBeImported
  4. include HasActivityStreamLog
  5. include ChecksClientNotification
  6. include CanCsvImport
  7. include ChecksHtmlSanitized
  8. include ChecksHumanChanges
  9. include HasHistory
  10. include HasTags
  11. include HasSearchIndexBackend
  12. include HasOnlineNotifications
  13. include HasLinks
  14. include HasObjectManagerAttributes
  15. include HasTaskbars
  16. include Ticket::CallsStatsTicketReopenLog
  17. include Ticket::EnqueuesUserTicketCounterJob
  18. include Ticket::ResetsPendingTimeSeconds
  19. include Ticket::SetsCloseTime
  20. include Ticket::SetsOnlineNotificationSeen
  21. include Ticket::TouchesAssociations
  22. include Ticket::TriggersSubscriptions
  23. include Ticket::ChecksReopenAfterCertainTime
  24. include Ticket::Checklists
  25. include ::Ticket::Escalation
  26. include ::Ticket::Subject
  27. include ::Ticket::Assets
  28. include ::Ticket::SearchIndex
  29. include ::Ticket::CanSelector
  30. include ::Ticket::Search
  31. include ::Ticket::MergeHistory
  32. include ::Ticket::PerformChanges
  33. store :preferences
  34. after_initialize :check_defaults, if: :new_record?
  35. before_create :check_generate, :check_defaults, :check_title, :set_default_state, :set_default_priority
  36. before_update :check_defaults, :check_title, :reset_pending_time, :check_owner_active
  37. # This must be loaded late as it depends on the internal before_create and before_update handlers of ticket.rb.
  38. include Ticket::SetsLastOwnerUpdateTime
  39. # workflow checks should run after before_create and before_update callbacks
  40. # the transaction dispatcher must be run after the workflow checks!
  41. include ChecksCoreWorkflow
  42. include HasTransactionDispatcher
  43. validates :group_id, presence: true
  44. activity_stream_permission 'ticket.agent'
  45. core_workflow_screens 'create_middle', 'edit', 'overview_bulk'
  46. core_workflow_admin_screens 'create_middle', 'edit'
  47. taskbar_entities 'TicketZoom', 'TicketCreate'
  48. taskbar_ignore_state_updates_entities 'TicketZoom'
  49. activity_stream_attributes_ignored :organization_id, # organization_id will change automatically on user update
  50. :create_article_type_id,
  51. :create_article_sender_id,
  52. :article_count,
  53. :first_response_at,
  54. :first_response_escalation_at,
  55. :first_response_in_min,
  56. :first_response_diff_in_min,
  57. :close_at,
  58. :close_escalation_at,
  59. :close_in_min,
  60. :close_diff_in_min,
  61. :update_escalation_at,
  62. :update_in_min,
  63. :update_diff_in_min,
  64. :last_close_at,
  65. :last_contact_at,
  66. :last_contact_agent_at,
  67. :last_contact_customer_at,
  68. :last_owner_update_at,
  69. :preferences
  70. search_index_attributes_relevant :organization_id,
  71. :group_id,
  72. :state_id,
  73. :priority_id
  74. history_attributes_ignored :create_article_type_id,
  75. :create_article_sender_id,
  76. :article_count,
  77. :preferences
  78. history_relation_object 'Ticket::Article', 'Mention', 'Ticket::SharedDraftZoom', 'Checklist', 'Checklist::Item'
  79. validates :note, length: { maximum: 250 }
  80. sanitized_html :note
  81. belongs_to :group, optional: true
  82. belongs_to :organization, optional: true
  83. has_many :articles, -> { reorder(:created_at, :id) }, class_name: 'Ticket::Article', after_add: :cache_update, after_remove: :cache_update, dependent: :destroy, inverse_of: :ticket
  84. has_many :ticket_time_accounting, class_name: 'Ticket::TimeAccounting', dependent: :destroy, inverse_of: :ticket
  85. has_many :mentions, as: :mentionable, dependent: :destroy
  86. has_one :shared_draft, class_name: 'Ticket::SharedDraftZoom', inverse_of: :ticket, dependent: :destroy
  87. belongs_to :state, class_name: 'Ticket::State', optional: true
  88. belongs_to :priority, class_name: 'Ticket::Priority', optional: true
  89. belongs_to :owner, class_name: 'User', optional: true
  90. belongs_to :customer, class_name: 'User', optional: true
  91. belongs_to :created_by, class_name: 'User', optional: true
  92. belongs_to :updated_by, class_name: 'User', optional: true
  93. belongs_to :create_article_type, class_name: 'Ticket::Article::Type', optional: true
  94. belongs_to :create_article_sender, class_name: 'Ticket::Article::Sender', optional: true
  95. association_attributes_ignored :flags, :mentions
  96. attr_accessor :callback_loop
  97. =begin
  98. processes tickets which have reached their pending time and sets next state_id
  99. processed_tickets = Ticket.process_pending
  100. returns
  101. processed_tickets = [<Ticket>, ...]
  102. =end
  103. def self.process_pending
  104. result = []
  105. # process pending action tickets
  106. pending_action = Ticket::StateType.find_by(name: 'pending action')
  107. ticket_states_pending_action = Ticket::State.where(state_type_id: pending_action)
  108. .where.not(next_state_id: nil)
  109. if ticket_states_pending_action.present?
  110. next_state_map = {}
  111. ticket_states_pending_action.each do |state|
  112. next_state_map[state.id] = state.next_state_id
  113. end
  114. where(state_id: next_state_map.keys, pending_time: ..Time.current)
  115. .find_each(batch_size: 500) do |ticket|
  116. Transaction.execute do
  117. ticket.state_id = next_state_map[ticket.state_id]
  118. ticket.updated_at = Time.zone.now
  119. ticket.updated_by_id = 1
  120. ticket.save!
  121. end
  122. result.push ticket
  123. end
  124. end
  125. # process pending reminder tickets
  126. pending_reminder = Ticket::StateType.find_by(name: 'pending reminder')
  127. ticket_states_pending_reminder = Ticket::State.where(state_type_id: pending_reminder)
  128. if ticket_states_pending_reminder.present?
  129. reminder_state_map = {}
  130. ticket_states_pending_reminder.each do |state|
  131. reminder_state_map[state.id] = state.next_state_id
  132. end
  133. where(state_id: reminder_state_map.keys, pending_time: ..Time.current)
  134. .find_each(batch_size: 500) do |ticket|
  135. article_id = nil
  136. article = Ticket::Article.last_customer_agent_article(ticket.id)
  137. if article
  138. article_id = article.id
  139. end
  140. # send notification
  141. TransactionJob.perform_now(
  142. object: 'Ticket',
  143. type: 'reminder_reached',
  144. object_id: ticket.id,
  145. article_id: article_id,
  146. user_id: 1,
  147. )
  148. result.push ticket
  149. end
  150. end
  151. result
  152. end
  153. def auto_assign(user)
  154. return if !persisted?
  155. return if Setting.get('ticket_auto_assignment').blank?
  156. return if owner_id != 1
  157. return if !TicketPolicy.new(user, self).full?
  158. user_ids_ignore = Array(Setting.get('ticket_auto_assignment_user_ids_ignore')).map(&:to_i)
  159. return if user_ids_ignore.include?(user.id)
  160. ticket_auto_assignment_selector = Setting.get('ticket_auto_assignment_selector')
  161. return if ticket_auto_assignment_selector.blank?
  162. condition = ticket_auto_assignment_selector[:condition].merge(
  163. 'ticket.id' => {
  164. 'operator' => 'is',
  165. 'value' => id,
  166. }
  167. )
  168. ticket_count, = Ticket.selectors(condition, limit: 1, current_user: user, access: 'full')
  169. return if ticket_count.to_i.zero?
  170. update!(owner: user)
  171. end
  172. =begin
  173. processes escalated tickets
  174. processed_tickets = Ticket.process_escalation
  175. returns
  176. processed_tickets = [<Ticket>, ...]
  177. =end
  178. def self.process_escalation
  179. result = []
  180. # fetch all escalated and soon to be escalating tickets
  181. where(escalation_at: ..15.minutes.from_now)
  182. .find_each(batch_size: 500) do |ticket|
  183. article_id = nil
  184. article = Ticket::Article.last_customer_agent_article(ticket.id)
  185. if article
  186. article_id = article.id
  187. end
  188. # send escalation
  189. if ticket.escalation_at < Time.zone.now
  190. TransactionJob.perform_now(
  191. object: 'Ticket',
  192. type: 'escalation',
  193. object_id: ticket.id,
  194. article_id: article_id,
  195. user_id: 1,
  196. )
  197. result.push ticket
  198. next
  199. end
  200. # check if warning needs to be sent
  201. TransactionJob.perform_now(
  202. object: 'Ticket',
  203. type: 'escalation_warning',
  204. object_id: ticket.id,
  205. article_id: article_id,
  206. user_id: 1,
  207. )
  208. result.push ticket
  209. end
  210. result
  211. end
  212. =begin
  213. processes tickets which auto unassign time has reached
  214. processed_tickets = Ticket.process_auto_unassign
  215. returns
  216. processed_tickets = [<Ticket>, ...]
  217. =end
  218. def self.process_auto_unassign
  219. # process pending action tickets
  220. state_ids = Ticket::State.by_category_ids(:work_on)
  221. return [] if state_ids.blank?
  222. result = []
  223. groups = Group.where(active: true).where('assignment_timeout IS NOT NULL AND groups.assignment_timeout != 0')
  224. return [] if groups.blank?
  225. groups.each do |group|
  226. next if group.assignment_timeout.blank?
  227. ticket_ids = Ticket.where('state_id IN (?) AND owner_id != 1 AND group_id = ? AND last_owner_update_at IS NOT NULL', state_ids, group.id).limit(600).pluck(:id)
  228. ticket_ids.each do |ticket_id|
  229. ticket = Ticket.find_by(id: ticket_id)
  230. next if !ticket
  231. minutes_since_last_assignment = Time.zone.now - ticket.last_owner_update_at
  232. next if (minutes_since_last_assignment / 60) <= group.assignment_timeout
  233. Transaction.execute do
  234. ticket.owner_id = 1
  235. ticket.updated_at = Time.zone.now
  236. ticket.updated_by_id = 1
  237. ticket.save!
  238. end
  239. result.push ticket
  240. end
  241. end
  242. result
  243. end
  244. =begin
  245. merge tickets
  246. ticket = Ticket.find(123)
  247. result = ticket.merge_to(
  248. ticket_id: 123,
  249. user_id: 123,
  250. )
  251. returns
  252. result = true|false
  253. =end
  254. def merge_to(data)
  255. # prevent cross merging tickets
  256. target_ticket = Ticket.find_by(id: data[:ticket_id])
  257. raise 'no target ticket given' if !target_ticket
  258. raise Exceptions::UnprocessableEntity, __('It is not possible to merge into an already merged ticket.') if target_ticket.state.state_type.name == 'merged'
  259. # check different ticket ids
  260. raise Exceptions::UnprocessableEntity, __('A ticket cannot be merged into itself.') if id == target_ticket.id
  261. # update articles
  262. Transaction.execute context: 'merge' do
  263. Ticket::Article.where(ticket_id: id).each(&:touch)
  264. # quiet update of reassign of articles
  265. Ticket::Article.where(ticket_id: id).update_all(['ticket_id = ?', data[:ticket_id]]) # rubocop:disable Rails/SkipsModelValidations
  266. # mark target ticket as updated
  267. # otherwise the "received_merge" history entry
  268. # will be the same as the last updated_at
  269. # which might be a long time ago
  270. target_ticket.updated_at = Time.zone.now
  271. # add merge event to both ticket's history (Issue #2469 - Add information "Ticket merged" to History)
  272. target_ticket.history_log(
  273. 'received_merge',
  274. data[:user_id],
  275. id_to: target_ticket.id,
  276. id_from: id,
  277. )
  278. history_log(
  279. 'merged_into',
  280. data[:user_id],
  281. id_to: target_ticket.id,
  282. id_from: id,
  283. )
  284. # create new merge article
  285. Ticket::Article.create(
  286. ticket_id: id,
  287. type_id: Ticket::Article::Type.lookup(name: 'note').id,
  288. sender_id: Ticket::Article::Sender.lookup(name: 'Agent').id,
  289. body: 'merged',
  290. internal: false,
  291. created_by_id: data[:user_id],
  292. updated_by_id: data[:user_id],
  293. )
  294. # search for mention duplicates and destroy them before moving mentions
  295. Mention.duplicates(self, target_ticket).destroy_all
  296. Mention.where(mentionable: self).update_all(mentionable_id: target_ticket.id) # rubocop:disable Rails/SkipsModelValidations
  297. # reassign links to the new ticket
  298. # rubocop:disable Rails/SkipsModelValidations
  299. ticket_source_id = Link::Object.find_by(name: 'Ticket').id
  300. # search for all duplicate source and target links and destroy them
  301. # before link merging
  302. Link.duplicates(
  303. object1_id: ticket_source_id,
  304. object1_value: id,
  305. object2_value: data[:ticket_id]
  306. ).destroy_all
  307. Link.where(
  308. link_object_source_id: ticket_source_id,
  309. link_object_source_value: id,
  310. ).update_all(link_object_source_value: data[:ticket_id])
  311. Link.where(
  312. link_object_target_id: ticket_source_id,
  313. link_object_target_value: id,
  314. ).update_all(link_object_target_value: data[:ticket_id])
  315. # rubocop:enable Rails/SkipsModelValidations
  316. # link tickets
  317. Link.add(
  318. link_type: 'parent',
  319. link_object_source: 'Ticket',
  320. link_object_source_value: data[:ticket_id],
  321. link_object_target: 'Ticket',
  322. link_object_target_value: id
  323. )
  324. # external sync references
  325. ExternalSync.migrate('Ticket', id, target_ticket.id)
  326. # set state to 'merged'
  327. self.state_id = Ticket::State.lookup(name: 'merged').id
  328. # rest owner
  329. self.owner_id = 1
  330. # save ticket
  331. save!
  332. # touch new ticket (to broadcast change)
  333. target_ticket.touch # rubocop:disable Rails/SkipsModelValidations
  334. EventBuffer.add('transaction', {
  335. object: target_ticket.class.name,
  336. type: 'update.received_merge',
  337. data: target_ticket,
  338. changes: {},
  339. id: target_ticket.id,
  340. user_id: UserInfo.current_user_id,
  341. created_at: Time.zone.now,
  342. })
  343. EventBuffer.add('transaction', {
  344. object: self.class.name,
  345. type: 'update.merged_into',
  346. data: self,
  347. changes: {},
  348. id: id,
  349. user_id: UserInfo.current_user_id,
  350. created_at: Time.zone.now,
  351. })
  352. end
  353. true
  354. end
  355. =begin
  356. perform active triggers on ticket
  357. Ticket.perform_triggers(ticket, article, triggers, item, triggers, options)
  358. =end
  359. def self.perform_triggers(ticket, article, triggers, item, options = {})
  360. recursive = Setting.get('ticket_trigger_recursive')
  361. type = options[:type] || item[:type]
  362. local_options = options.clone
  363. local_options[:type] = type
  364. local_options[:reset_user_id] = true
  365. local_options[:disable] = ['Transaction::Notification']
  366. local_options[:trigger_ids] ||= {}
  367. local_options[:trigger_ids][ticket.id.to_s] ||= []
  368. local_options[:loop_count] ||= 0
  369. local_options[:loop_count] += 1
  370. ticket_trigger_recursive_max_loop = Setting.get('ticket_trigger_recursive_max_loop')&.to_i || 10
  371. if local_options[:loop_count] > ticket_trigger_recursive_max_loop
  372. message = "Stopped perform_triggers for this object (Ticket/#{ticket.id}), because loop count was #{local_options[:loop_count]}!"
  373. logger.info { message }
  374. return [false, message]
  375. end
  376. return [true, __('No triggers active')] if triggers.blank?
  377. # check if notification should be send because of customer emails
  378. send_notification = true
  379. if local_options[:send_notification] == false
  380. send_notification = false
  381. elsif item[:article_id]
  382. article = Ticket::Article.lookup(id: item[:article_id])
  383. if article&.preferences && article.preferences['send-auto-response'] == false
  384. send_notification = false
  385. end
  386. end
  387. Transaction.execute(local_options) do
  388. triggers.each do |trigger|
  389. logger.debug { "Probe trigger (#{trigger.name}/#{trigger.id}) for this object (Ticket:#{ticket.id}/Loop:#{local_options[:loop_count]})" }
  390. user_id = ticket.updated_by_id
  391. if article
  392. user_id = article.updated_by_id
  393. end
  394. user = User.lookup(id: user_id)
  395. # verify is condition is matching
  396. ticket_count, tickets = Ticket.selectors(
  397. trigger.condition,
  398. limit: 1,
  399. execution_time: true,
  400. current_user: user,
  401. access: 'ignore',
  402. ticket_action: type,
  403. ticket_id: ticket.id,
  404. article_id: article&.id,
  405. changes: item[:changes],
  406. changes_required: trigger.condition_changes_required?
  407. )
  408. next if ticket_count.blank?
  409. next if ticket_count.zero?
  410. next if tickets.take.id != ticket.id
  411. if recursive == false && local_options[:loop_count] > 1
  412. message = "Do not execute recursive triggers per default until Zammad 3.0. With Zammad 3.0 and higher the following trigger is executed '#{trigger.name}' on Ticket:#{ticket.id}. Please review your current triggers and change them if needed."
  413. logger.info { message }
  414. return [true, message]
  415. end
  416. if article && send_notification == false && trigger.perform['notification.email'] && trigger.perform['notification.email']['recipient']
  417. recipient = trigger.perform['notification.email']['recipient']
  418. local_options[:send_notification] = false
  419. if recipient.include?('ticket_customer') || recipient.include?('article_last_sender')
  420. logger.info { "Skip trigger (#{trigger.name}/#{trigger.id}) because sender do not want to get auto responder for object (Ticket/#{ticket.id}/Article/#{article.id})" }
  421. next
  422. end
  423. end
  424. if local_options[:trigger_ids][ticket.id.to_s].include?(trigger.id)
  425. logger.info { "Skip trigger (#{trigger.name}/#{trigger.id}) because was already executed for this object (Ticket:#{ticket.id}/Loop:#{local_options[:loop_count]})" }
  426. next
  427. end
  428. local_options[:trigger_ids][ticket.id.to_s].push trigger.id
  429. logger.info { "Execute trigger (#{trigger.name}/#{trigger.id}) for this object (Ticket:#{ticket.id}/Loop:#{local_options[:loop_count]})" }
  430. ticket.perform_changes(trigger, 'trigger', item, user_id, activator_type: type)
  431. if recursive == true
  432. TransactionDispatcher.commit(local_options)
  433. end
  434. end
  435. end
  436. [true, ticket, local_options]
  437. end
  438. =begin
  439. get all email references headers of a ticket, to exclude some, parse it as array into method
  440. references = ticket.get_references
  441. result
  442. ['message-id-1234', 'message-id-5678']
  443. ignore references header(s)
  444. references = ticket.get_references(['message-id-5678'])
  445. result
  446. ['message-id-1234']
  447. =end
  448. # limited by 32kb (https://github.com/zammad/zammad/issues/5334)
  449. # https://learn.microsoft.com/en-us/office365/servicedescriptions/exchange-online-service-description/exchange-online-limits
  450. def get_references(ignore = [], max_length: 30_000)
  451. references = []
  452. counter = 0
  453. Ticket::Article.select('in_reply_to, message_id').where(ticket_id: id).reorder(id: :desc).each do |article|
  454. new_references = []
  455. if article.message_id.present?
  456. new_references.push article.message_id
  457. end
  458. if article.in_reply_to.present?
  459. new_references.push article.in_reply_to
  460. end
  461. new_references -= ignore
  462. counter += new_references.join.length
  463. break if counter > max_length
  464. references.unshift(*new_references)
  465. end
  466. references
  467. end
  468. # Get whichever #last_contact_* was later
  469. # This is not identical to #last_contact_at
  470. # It returns time to last original (versus follow up) contact
  471. # @return [Time, nil]
  472. def last_original_update_at
  473. [last_contact_agent_at, last_contact_customer_at].compact.max
  474. end
  475. # true if conversation did happen and agent responded
  476. # false if customer is waiting for response or agent reached out and customer did not respond yet
  477. # @return [Bool]
  478. def agent_responded?
  479. return false if last_contact_customer_at.blank?
  480. return false if last_contact_agent_at.blank?
  481. last_contact_customer_at < last_contact_agent_at
  482. end
  483. =begin
  484. Get the color of the state the current ticket is in
  485. ticket.current_state_color
  486. returns a hex color code
  487. =end
  488. def current_state_color
  489. return '#f35912' if escalation_at && escalation_at < Time.zone.now
  490. case state.state_type.name
  491. when 'new', 'open'
  492. return '#faab00'
  493. when 'closed'
  494. return '#38ad69'
  495. when 'pending reminder'
  496. return '#faab00' if pending_time && pending_time < Time.zone.now
  497. end
  498. '#000000'
  499. end
  500. def mention_user_ids
  501. mentions.pluck(:user_id)
  502. end
  503. private
  504. def check_generate
  505. return true if number
  506. self.number = Ticket::Number.generate
  507. true
  508. end
  509. def check_title
  510. return true if !title
  511. title.gsub!(%r{\s|\t|\r}, ' ')
  512. true
  513. end
  514. def check_defaults
  515. check_default_owner
  516. check_default_organization
  517. true
  518. end
  519. def check_default_owner
  520. return if !has_attribute?(:owner_id)
  521. return if owner_id || owner
  522. self.owner_id = 1
  523. end
  524. def check_default_organization
  525. return if !has_attribute?(:organization_id)
  526. return if !customer_id
  527. customer = User.find_by(id: customer_id)
  528. return if !customer
  529. return if organization_id.present? && customer.organization_id?(organization_id)
  530. return if organization.present? && customer.organization_id?(organization.id)
  531. self.organization_id = customer.organization_id
  532. end
  533. def reset_pending_time
  534. # ignore if no state has changed
  535. return true if !changes_to_save['state_id']
  536. # ignore if new state is blank and
  537. # let handle ActiveRecord the error
  538. return if state_id.blank?
  539. # check if new state isn't pending*
  540. current_state = Ticket::State.lookup(id: state_id)
  541. current_state_type = Ticket::StateType.lookup(id: current_state.state_type_id)
  542. # in case, set pending_time to nil
  543. return true if current_state_type.name.match?(%r{^pending}i)
  544. self.pending_time = nil
  545. true
  546. end
  547. def set_default_state
  548. return true if state_id
  549. default_ticket_state = Ticket::State.find_by(default_create: true)
  550. return true if !default_ticket_state
  551. self.state_id = default_ticket_state.id
  552. true
  553. end
  554. def set_default_priority
  555. return true if priority_id
  556. default_ticket_priority = Ticket::Priority.find_by(default_create: true)
  557. return true if !default_ticket_priority
  558. self.priority_id = default_ticket_priority.id
  559. true
  560. end
  561. def check_owner_active
  562. return true if Setting.get('import_mode')
  563. # only change the owner for non closed Tickets for historical/reporting reasons
  564. return true if state.present? && Ticket::StateType.lookup(id: state.state_type_id)&.name == 'closed'
  565. # return when ticket is unassigned
  566. return true if owner_id.blank?
  567. return true if owner_id == 1
  568. # return if owner is active, is agent and has access to group of ticket
  569. return true if owner.active? && owner.permissions?('ticket.agent') && owner.group_access?(group_id, 'full')
  570. # else set the owner of the ticket to the default user as unassigned
  571. self.owner_id = 1
  572. true
  573. end
  574. end