ticket.rb 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736
  1. # Copyright (C) 2012-2025 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. state_type = Ticket::StateType.lookup(name: 'merged')
  328. self.state_id = Ticket::State.lookup(state_type_id: state_type.id).id
  329. # rest owner
  330. self.owner_id = 1
  331. # save ticket
  332. save!
  333. # touch new ticket (to broadcast change)
  334. target_ticket.touch # rubocop:disable Rails/SkipsModelValidations
  335. EventBuffer.add('transaction', {
  336. object: target_ticket.class.name,
  337. type: 'update.received_merge',
  338. data: target_ticket,
  339. changes: {},
  340. id: target_ticket.id,
  341. user_id: UserInfo.current_user_id,
  342. created_at: Time.zone.now,
  343. })
  344. EventBuffer.add('transaction', {
  345. object: self.class.name,
  346. type: 'update.merged_into',
  347. data: self,
  348. changes: {},
  349. id: id,
  350. user_id: UserInfo.current_user_id,
  351. created_at: Time.zone.now,
  352. })
  353. end
  354. true
  355. end
  356. =begin
  357. perform active triggers on ticket
  358. Ticket.perform_triggers(ticket, article, triggers, item, triggers, options)
  359. =end
  360. def self.perform_triggers(ticket, article, triggers, item, options = {})
  361. recursive = Setting.get('ticket_trigger_recursive')
  362. type = options[:type] || item[:type]
  363. local_options = options.clone
  364. local_options[:type] = type
  365. local_options[:reset_user_id] = true
  366. local_options[:disable] = ['Transaction::Notification']
  367. local_options[:trigger_ids] ||= {}
  368. local_options[:trigger_ids][ticket.id.to_s] ||= []
  369. local_options[:loop_count] ||= 0
  370. local_options[:loop_count] += 1
  371. ticket_trigger_recursive_max_loop = Setting.get('ticket_trigger_recursive_max_loop')&.to_i || 10
  372. if local_options[:loop_count] > ticket_trigger_recursive_max_loop
  373. message = "Stopped perform_triggers for this object (Ticket/#{ticket.id}), because loop count was #{local_options[:loop_count]}!"
  374. logger.info { message }
  375. return [false, message]
  376. end
  377. return [true, __('No triggers active')] if triggers.blank?
  378. # check if notification should be send because of customer emails
  379. send_notification = true
  380. if local_options[:send_notification] == false
  381. send_notification = false
  382. elsif item[:article_id]
  383. article = Ticket::Article.lookup(id: item[:article_id])
  384. if article&.preferences && article.preferences['send-auto-response'] == false
  385. send_notification = false
  386. end
  387. end
  388. Transaction.execute(local_options) do
  389. triggers.each do |trigger|
  390. logger.debug { "Probe trigger (#{trigger.name}/#{trigger.id}) for this object (Ticket:#{ticket.id}/Loop:#{local_options[:loop_count]})" }
  391. user_id = ticket.updated_by_id
  392. if article
  393. user_id = article.updated_by_id
  394. end
  395. user = User.lookup(id: user_id)
  396. # verify is condition is matching
  397. ticket_count, tickets = Ticket.selectors(
  398. trigger.condition,
  399. limit: 1,
  400. execution_time: true,
  401. current_user: user,
  402. access: 'ignore',
  403. ticket_action: type,
  404. ticket_id: ticket.id,
  405. article_id: article&.id,
  406. changes: item[:changes],
  407. changes_required: trigger.condition_changes_required?
  408. )
  409. next if ticket_count.blank?
  410. next if ticket_count.zero?
  411. next if tickets.take.id != ticket.id
  412. if recursive == false && local_options[:loop_count] > 1
  413. 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."
  414. logger.info { message }
  415. return [true, message]
  416. end
  417. if article && send_notification == false && trigger.perform['notification.email'] && trigger.perform['notification.email']['recipient']
  418. recipient = trigger.perform['notification.email']['recipient']
  419. local_options[:send_notification] = false
  420. if recipient.include?('ticket_customer') || recipient.include?('article_last_sender')
  421. 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})" }
  422. next
  423. end
  424. end
  425. if local_options[:trigger_ids][ticket.id.to_s].include?(trigger.id)
  426. logger.info { "Skip trigger (#{trigger.name}/#{trigger.id}) because was already executed for this object (Ticket:#{ticket.id}/Loop:#{local_options[:loop_count]})" }
  427. next
  428. end
  429. local_options[:trigger_ids][ticket.id.to_s].push trigger.id
  430. logger.info { "Execute trigger (#{trigger.name}/#{trigger.id}) for this object (Ticket:#{ticket.id}/Loop:#{local_options[:loop_count]})" }
  431. ticket.perform_changes(trigger, 'trigger', item, user_id, activator_type: type)
  432. if recursive == true
  433. TransactionDispatcher.commit(local_options)
  434. end
  435. end
  436. end
  437. [true, ticket, local_options]
  438. end
  439. =begin
  440. get all email references headers of a ticket, to exclude some, parse it as array into method
  441. references = ticket.get_references
  442. result
  443. ['message-id-1234', 'message-id-5678']
  444. ignore references header(s)
  445. references = ticket.get_references(['message-id-5678'])
  446. result
  447. ['message-id-1234']
  448. =end
  449. # limited by 32kb (https://github.com/zammad/zammad/issues/5334)
  450. # https://learn.microsoft.com/en-us/office365/servicedescriptions/exchange-online-service-description/exchange-online-limits
  451. def get_references(ignore = [], max_length: 30_000)
  452. references = []
  453. counter = 0
  454. Ticket::Article.select('in_reply_to, message_id').where(ticket_id: id).reorder(id: :desc).each do |article|
  455. new_references = []
  456. if article.message_id.present?
  457. new_references.push article.message_id
  458. end
  459. if article.in_reply_to.present?
  460. new_references.push article.in_reply_to
  461. end
  462. new_references -= ignore
  463. counter += new_references.join.length
  464. break if counter > max_length
  465. references.unshift(*new_references)
  466. end
  467. references
  468. end
  469. # Get whichever #last_contact_* was later
  470. # This is not identical to #last_contact_at
  471. # It returns time to last original (versus follow up) contact
  472. # @return [Time, nil]
  473. def last_original_update_at
  474. [last_contact_agent_at, last_contact_customer_at].compact.max
  475. end
  476. # true if conversation did happen and agent responded
  477. # false if customer is waiting for response or agent reached out and customer did not respond yet
  478. # @return [Bool]
  479. def agent_responded?
  480. return false if last_contact_customer_at.blank?
  481. return false if last_contact_agent_at.blank?
  482. last_contact_customer_at < last_contact_agent_at
  483. end
  484. =begin
  485. Get the color of the state the current ticket is in
  486. ticket.current_state_color
  487. returns a hex color code
  488. =end
  489. def current_state_color
  490. return '#f35912' if escalation_at && escalation_at < Time.zone.now
  491. case state.state_type.name
  492. when 'new', 'open'
  493. return '#faab00'
  494. when 'closed'
  495. return '#38ad69'
  496. when 'pending reminder'
  497. return '#faab00' if pending_time && pending_time < Time.zone.now
  498. end
  499. '#000000'
  500. end
  501. def mention_user_ids
  502. mentions.pluck(:user_id)
  503. end
  504. private
  505. def check_generate
  506. return true if number
  507. self.number = Ticket::Number.generate
  508. true
  509. end
  510. def check_title
  511. return true if !title
  512. title.gsub!(%r{\s|\t|\r}, ' ')
  513. true
  514. end
  515. def check_defaults
  516. check_default_owner
  517. check_default_organization
  518. true
  519. end
  520. def check_default_owner
  521. return if !has_attribute?(:owner_id)
  522. return if owner_id || owner
  523. self.owner_id = 1
  524. end
  525. def check_default_organization
  526. return if !has_attribute?(:organization_id)
  527. return if !customer_id
  528. customer = User.find_by(id: customer_id)
  529. return if !customer
  530. return if organization_id.present? && customer.organization_id?(organization_id)
  531. return if organization.present? && customer.organization_id?(organization.id)
  532. self.organization_id = customer.organization_id
  533. end
  534. def reset_pending_time
  535. # ignore if no state has changed
  536. return true if !changes_to_save['state_id']
  537. # ignore if new state is blank and
  538. # let handle ActiveRecord the error
  539. return if state_id.blank?
  540. # check if new state isn't pending*
  541. current_state = Ticket::State.lookup(id: state_id)
  542. current_state_type = Ticket::StateType.lookup(id: current_state.state_type_id)
  543. # in case, set pending_time to nil
  544. return true if current_state_type.name.match?(%r{^pending}i)
  545. self.pending_time = nil
  546. true
  547. end
  548. def set_default_state
  549. return true if state_id
  550. default_ticket_state = Ticket::State.find_by(default_create: true)
  551. return true if !default_ticket_state
  552. self.state_id = default_ticket_state.id
  553. true
  554. end
  555. def set_default_priority
  556. return true if priority_id
  557. default_ticket_priority = Ticket::Priority.find_by(default_create: true)
  558. return true if !default_ticket_priority
  559. self.priority_id = default_ticket_priority.id
  560. true
  561. end
  562. def check_owner_active
  563. return true if Setting.get('import_mode')
  564. # only change the owner for non closed Tickets for historical/reporting reasons
  565. return true if state.present? && Ticket::StateType.lookup(id: state.state_type_id)&.name == 'closed'
  566. # return when ticket is unassigned
  567. return true if owner_id.blank?
  568. return true if owner_id == 1
  569. # return if owner is active, is agent and has access to group of ticket
  570. return true if owner.active? && owner.permissions?('ticket.agent') && owner.group_access?(group_id, 'full')
  571. # else set the owner of the ticket to the default user as unassigned
  572. self.owner_id = 1
  573. true
  574. end
  575. end