mirror of
https://github.com/meineerde/redmine.git
synced 2026-02-06 09:03:25 +00:00
Adds settings for time entry hours validation (#24005).
git-svn-id: http://svn.redmine.org/redmine/trunk@16832 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
parent
a9d7f32da3
commit
7bd8280a83
@ -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 = ''
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user