Class: TimeEntry

Inherits:
ActiveRecord::Base
  • Object
show all
Includes:
Redmine::SafeAttributes
Defined in:
app/models/time_entry.rb

Overview

Redmine - project management software Copyright © 2006-2023 Jean-Philippe Lang

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Redmine::SafeAttributes

#delete_unsafe_attributes, included, #safe_attribute?, #safe_attribute_names

Constructor Details

#initialize(attributes = nil, *args) ⇒ TimeEntry

Returns a new instance of TimeEntry.



107
108
109
110
111
112
113
# File 'app/models/time_entry.rb', line 107

def initialize(attributes=nil, *args)
  super
  if new_record? && self.activity.nil?
    self.activity_id = TimeEntryActivity.default_activity_id(User.current, self.project)
    self.hours = nil if hours == 0
  end
end

Class Method Details

.visible_condition(user, options = {}) ⇒ Object

Returns a SQL conditions string used to find all time entries visible by the specified user



82
83
84
85
86
87
88
89
90
91
92
# File 'app/models/time_entry.rb', line 82

def self.visible_condition(user, options={})
  Project.allowed_to_condition(user, :view_time_entries, options) do |role, user|
    if role.time_entries_visibility == 'all'
      nil
    elsif role.time_entries_visibility == 'own' && user.id && user.logged?
      "#{table_name}.user_id = #{user.id}"
    else
      '1=0'
    end
  end
end

Instance Method Details

#assignable_usersObject



233
234
235
236
237
238
239
240
241
# File 'app/models/time_entry.rb', line 233

def assignable_users
  users = []
  if project
    users = project.members.active.preload(:user)
    users = users.map(&:user).select{|u| u.allowed_to?(:log_time, project)}
  end
  users << User.current if User.current.logged? && !users.include?(User.current)
  users
end

#editable_by?(usr) ⇒ Boolean

Returns true if the time entry can be edited by usr, otherwise false

Returns:

  • (Boolean)


210
211
212
213
214
# File 'app/models/time_entry.rb', line 210

def editable_by?(usr)
  visible?(usr) && (
    (usr == user && usr.allowed_to?(:edit_own_time_entries, project)) || usr.allowed_to?(:edit_time_entries, project)
  )
end

#editable_custom_field_values(user = nil) ⇒ Object

Returns the custom_field_values that can be edited by the given user



217
218
219
# File 'app/models/time_entry.rb', line 217

def editable_custom_field_values(user=nil)
  visible_custom_field_values(user)
end

#editable_custom_fields(user = nil) ⇒ Object

Returns the custom fields that can be edited by the given user



222
223
224
# File 'app/models/time_entry.rb', line 222

def editable_custom_fields(user=nil)
  editable_custom_field_values(user).map(&:custom_field).uniq
end

#hoursObject



191
192
193
194
195
196
197
198
# File 'app/models/time_entry.rb', line 191

def hours
  h = read_attribute(:hours)
  if h.is_a?(Float)
    h.round(2)
  else
    h
  end
end

#hours=(h) ⇒ Object



187
188
189
# File 'app/models/time_entry.rb', line 187

def hours=(h)
  write_attribute :hours, (h.is_a?(String) ? (h.to_hours || h) : h)
end

#safe_attributes=(attrs, user = User.current) ⇒ Object



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'app/models/time_entry.rb', line 115

def safe_attributes=(attrs, user=User.current)
  if attrs
    attrs = super(attrs)
    if issue_id_changed? && issue
      if issue.visible?(user) && user.allowed_to?(:log_time, issue.project)
        if attrs[:project_id].blank? && issue.project_id != project_id
          self.project_id = issue.project_id
        end
        @invalid_issue_id = nil
      elsif user.allowed_to?(:log_time, issue.project) && issue.assigned_to_id_changed? && issue.previous_assignee == User.current
        current_assignee = issue.assigned_to
        issue.assigned_to = issue.previous_assignee
        unless issue.visible?(user)
          @invalid_issue_id = issue_id
        end
        issue.assigned_to = current_assignee
      else
        @invalid_issue_id = issue_id
      end
    end
    if user_id_changed? && user_id != author_id && !user.allowed_to?(:log_time_for_other_users, project)
      @invalid_user_id = user_id
    else
      @invalid_user_id = nil
    end

    # Delete assigned custom fields not visible by the user
    editable_custom_field_ids = editable_custom_field_values(user).map {|v| v.custom_field_id.to_s}
    self.custom_field_values.delete_if do |c|
      !editable_custom_field_ids.include?(c.custom_field.id.to_s)
    end
  end

  attrs
end

#set_author_if_nilObject



155
156
157
# File 'app/models/time_entry.rb', line 155

def set_author_if_nil
  self.author = User.current if author.nil?
end

#set_project_if_nilObject



151
152
153
# File 'app/models/time_entry.rb', line 151

def set_project_if_nil
  self.project = issue.project if issue && project.nil?
end

#spent_on=(date) ⇒ Object

tyear, tmonth, tweek assigned where setting spent_on attributes these attributes make time aggregations easier



202
203
204
205
206
207
# File 'app/models/time_entry.rb', line 202

def spent_on=(date)
  super
  self.tyear = spent_on ? spent_on.year : nil
  self.tmonth = spent_on ? spent_on.month : nil
  self.tweek = spent_on ? Date.civil(spent_on.year, spent_on.month, spent_on.day).cweek : nil
end

#validate_time_entryObject



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'app/models/time_entry.rb', line 159

def validate_time_entry
  if hours
    errors.add :hours, :invalid if hours < 0
    errors.add :hours, :invalid if hours == 0.0 && hours_changed? && !Setting.timelog_accept_0_hours?

    max_hours = Setting.timelog_max_hours_per_day.to_f
    if hours_changed? && max_hours > 0.0
      logged_hours = other_hours_with_same_user_and_day
      if logged_hours + hours > max_hours
        errors.add(
          :base,
          I18n.t(:error_exceeds_maximum_hours_per_day,
                 :logged_hours => format_hours(logged_hours),
                 :max_hours => format_hours(max_hours)))
      end
    end
  end
  errors.add :project_id, :invalid if project.nil?
  if @invalid_user_id || (user_id_changed? && user_id != author_id && !self.assignable_users.map(&:id).include?(user_id))
    errors.add :user_id, :invalid
  end
  errors.add :issue_id, :invalid if (issue_id && !issue) || (issue && project!=issue.project) || @invalid_issue_id
  errors.add :activity_id, :inclusion if activity_id_changed? && project && !project.activities.include?(activity)
  if spent_on && spent_on_changed? && user
    errors.add :base, I18n.t(:error_spent_on_future_date) if !Setting.timelog_accept_future_dates? && (spent_on > user.today)
  end
end

#visible?(user = nil) ⇒ Boolean

Returns true if user or current user is allowed to view the time entry

Returns:

  • (Boolean)


95
96
97
98
99
100
101
102
103
104
105
# File 'app/models/time_entry.rb', line 95

def visible?(user=nil)
  (user || User.current).allowed_to?(:view_time_entries, self.project) do |role, user|
    if role.time_entries_visibility == 'all'
      true
    elsif role.time_entries_visibility == 'own'
      self.user == user
    else
      false
    end
  end
end

#visible_custom_field_values(user = nil) ⇒ Object



226
227
228
229
230
231
# File 'app/models/time_entry.rb', line 226

def visible_custom_field_values(user = nil)
  user ||= User.current
  custom_field_values.select do |value|
    value.custom_field.visible_by?(project, user)
  end
end