Commit 820f43b4 authored by jplang's avatar jplang

Adds settings for time entry hours validation (#24005).

git-svn-id: https://svn.redmine.org/redmine/trunk@16832 e93f8b46-1217-0410-a6f0-8f06a7374b81
parent b836bf52
......@@ -122,7 +122,19 @@ class TimeEntry < ActiveRecord::Base
end
def validate_time_entry
errors.add :hours, :invalid if hours && (hours < 0 || hours >= 1000)
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?
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)
......@@ -166,4 +178,18 @@ class TimeEntry < ActiveRecord::Base
def editable_custom_fields(user=nil)
editable_custom_field_values(user).map(&:custom_field).uniq
end
private
# Returns the hours that were logged in other time entries for the same user and the same day
def other_hours_with_same_user_and_day
if user_id && spent_on
TimeEntry.
where(:user_id => user_id, :spent_on => spent_on).
where.not(:id => id).
sum(:hours).to_f
else
0.0
end
end
end
......@@ -4,6 +4,9 @@
<p><%= setting_multiselect(:timelog_required_fields,
[[l(:field_issue), 'issue_id'], [l(:field_comments), 'comments'] ]) %></p>
<p><%= setting_text_field :timelog_max_hours_per_day, :size => 6 %></p>
<p><%= setting_check_box :timelog_accept_0_hours %></p>
</div>
<fieldset class="box">
......
......@@ -220,6 +220,7 @@ en:
error_move_of_child_not_possible: "Subtask %{child} could not be moved to the new project: %{errors}"
error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: "Spent time cannot be reassigned to an issue that is about to be deleted"
warning_fields_cleared_on_bulk_edit: "Changes will result in the automatic deletion of values from one or more fields on the selected objects"
error_exceeds_maximum_hours_per_day: "Cannot log more than %{max_hours} hours on the same day (%{logged_hours} hours have already been logged)"
mail_subject_lost_password: "Your %{value} password"
mail_body_lost_password: 'To change your password, click on the following link:'
......@@ -464,6 +465,8 @@ en:
setting_timelog_required_fields: Required fields for time logs
setting_close_duplicate_issues: Close duplicate issues automatically
setting_time_entry_list_defaults: Timelog list defaults
setting_timelog_accept_0_hours: Accept time logs with 0 hours
setting_timelog_max_hours_per_day: Maximum hours that can be logged per day and user
permission_add_project: Create project
permission_add_subprojects: Create subprojects
......
......@@ -240,6 +240,7 @@ fr:
error_move_of_child_not_possible: "La sous-tâche %{child} n'a pas pu être déplacée dans le nouveau projet : %{errors}"
error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: "Le temps passé ne peut pas être réaffecté à une demande qui va être supprimée"
warning_fields_cleared_on_bulk_edit: "Les changements apportés entraîneront la suppression automatique des valeurs d'un ou plusieurs champs sur les objets sélectionnés"
error_exceeds_maximum_hours_per_day: "Impossible de saisir plus de %{max_hours} heures pour le même jour (%{logged_hours} heures ont déjà été saisies)"
mail_subject_lost_password: "Votre mot de passe %{value}"
mail_body_lost_password: 'Pour changer votre mot de passe, cliquez sur le lien suivant :'
......@@ -476,7 +477,8 @@ fr:
setting_timelog_required_fields: Champs obligatoire pour les temps passés
setting_close_duplicate_issues: Fermer les doublons automatiquement
setting_time_entry_list_defaults: Affichage par défaut de la liste des temps passés
setting_timelog_accept_0_hours: Autoriser la saisie de temps avec 0 heure
setting_timelog_max_hours_per_day: Maximum d'heures pouvant être saisies par un utilisateur sur un jour
permission_add_project: Créer un projet
permission_add_subprojects: Créer des sous-projets
......
......@@ -302,3 +302,8 @@ new_item_menu_tab:
timelog_required_fields:
serialized: true
default: []
timelog_accept_0_hours:
default: 1
timelog_max_hours_per_day:
format: int
default: 999
......@@ -139,7 +139,7 @@ module ObjectHelpers
version
end
def TimeEntry.generate!(attributes={})
def TimeEntry.generate(attributes={})
entry = TimeEntry.new(attributes)
entry.user ||= User.find(2)
entry.issue ||= Issue.find(1) unless entry.project
......@@ -147,6 +147,11 @@ module ObjectHelpers
entry.activity ||= TimeEntryActivity.first
entry.spent_on ||= Date.today
entry.hours ||= 1.0
entry
end
def TimeEntry.generate!(attributes={}, &block)
entry = TimeEntry.generate(attributes, &block)
entry.save!
entry
end
......
......@@ -91,6 +91,34 @@ class TimeEntryTest < ActiveSupport::TestCase
assert_nil TimeEntry.new.hours
end
def test_should_accept_0_hours
entry = TimeEntry.generate
entry.hours = 0
assert entry.save
end
def test_should_not_accept_0_hours_if_disabled
with_settings :timelog_accept_0_hours => '0' do
entry = TimeEntry.generate
entry.hours = 0
assert !entry.save
assert entry.errors[:hours].present?
end
end
def test_should_not_accept_more_than_maximum_hours_per_day_and_user
with_settings :timelog_max_hours_per_day => '8' do
entry = TimeEntry.generate(:spent_on => '2017-07-16', :hours => 6.0, :user_id => 2)
assert entry.save
entry = TimeEntry.generate(:spent_on => '2017-07-16', :hours => 1.5, :user_id => 2)
assert entry.save
entry = TimeEntry.generate(:spent_on => '2017-07-16', :hours => 3.0, :user_id => 2)
assert !entry.save
end
end
def test_spent_on_with_blank
c = TimeEntry.new
c.spent_on = ''
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment