1# frozen_string_literal: true
2
3# == TimeTrackable concern
4#
5# Contains functionality related to objects that support time tracking.
6#
7# Used by Issue and MergeRequest.
8#
9
10module TimeTrackable
11  extend ActiveSupport::Concern
12
13  included do
14    attr_reader :time_spent, :time_spent_user, :spent_at, :summary
15
16    alias_method :time_spent?, :time_spent
17
18    default_value_for :time_estimate, value: 0, allows_nil: false
19
20    validates :time_estimate, numericality: { message: 'has an invalid format' }, allow_nil: false
21    validate  :check_negative_time_spent
22
23    has_many :timelogs, dependent: :destroy, autosave: true # rubocop:disable Cop/ActiveRecordDependent
24  end
25
26  # rubocop:disable Gitlab/ModuleWithInstanceVariables
27  def spend_time(options)
28    @time_spent = options[:duration]
29    @time_spent_note_id = options[:note_id]
30    @time_spent_user = User.find(options[:user_id])
31    @spent_at = options[:spent_at]
32    @summary = options[:summary]
33    @original_total_time_spent = nil
34
35    return if @time_spent == 0
36
37    @timelog = if @time_spent == :reset
38                 reset_spent_time
39               else
40                 add_or_subtract_spent_time
41               end
42  end
43  alias_method :spend_time=, :spend_time
44  # rubocop:enable Gitlab/ModuleWithInstanceVariables
45
46  def total_time_spent
47    timelogs.sum(:time_spent)
48  end
49
50  def human_total_time_spent
51    Gitlab::TimeTrackingFormatter.output(total_time_spent)
52  end
53
54  def time_change
55    @timelog&.time_spent.to_i # rubocop:disable Gitlab/ModuleWithInstanceVariables
56  end
57
58  def human_time_change
59    Gitlab::TimeTrackingFormatter.output(time_change)
60  end
61
62  def human_time_estimate
63    Gitlab::TimeTrackingFormatter.output(time_estimate)
64  end
65
66  def time_estimate=(val)
67    val.is_a?(Integer) ? super([val, Gitlab::Database::MAX_INT_VALUE].min) : super(val)
68  end
69
70  private
71
72  def reset_spent_time
73    timelogs.new(time_spent: total_time_spent * -1, user: @time_spent_user) # rubocop:disable Gitlab/ModuleWithInstanceVariables
74  end
75
76  # rubocop:disable Gitlab/ModuleWithInstanceVariables
77  def add_or_subtract_spent_time
78    timelogs.new(
79      time_spent: time_spent,
80      note_id: @time_spent_note_id,
81      user: @time_spent_user,
82      spent_at: @spent_at,
83      summary: @summary
84    )
85  end
86  # rubocop:enable Gitlab/ModuleWithInstanceVariables
87
88  def check_negative_time_spent
89    return if time_spent.nil? || time_spent == :reset
90
91    if time_spent < 0 && (time_spent.abs > original_total_time_spent)
92      errors.add(:base, _('Time to subtract exceeds the total time spent'))
93    end
94  end
95
96  # we need to cache the total time spent so multiple calls to #valid?
97  # doesn't give a false error
98  def original_total_time_spent
99    @original_total_time_spent ||= total_time_spent
100  end
101end
102