ticket.rb 23 KB

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