">
<%= format_date(entry.spent_on) %> |
+<%= format_time(entry.start_time) %> |
+<%= format_time(entry.end_time) %> |
+<%= hours(entry) %> |
<%=h entry.user %> |
<%=h entry.activity %> |
<%=h entry.project %> |
@@ -24,7 +29,6 @@
<% end -%>
-<%= html_hours("%.2f" % entry.hours) %> |
<% if entry.editable_by?(User.current) -%>
<%= link_to image_tag('edit.png'), {:controller => 'timelog', :action => 'edit', :id => entry},
Index: app/views/timelog/edit.rhtml
===================================================================
--- app/views/timelog/edit.rhtml (revision 1669)
+++ app/views/timelog/edit.rhtml (working copy)
@@ -3,11 +3,24 @@
<% labelled_tabular_form_for :time_entry, @time_entry, :url => {:action => 'edit', :project_id => @time_entry.project} do |f| %>
<%= error_messages_for 'time_entry' %>
<%= back_url_hidden_field_tag %>
+<% content_for :header_tags do %>
+ <%= javascript_include_tag 'time' %>
+<% end %>
+<% content_for :header_tags do %>
+
+<% end %>
+
<%= f.text_field :issue_id, :size => 6 %> <%= h("#{@time_entry.issue.tracker.name} ##{@time_entry.issue.id}: #{@time_entry.issue.subject}") if @time_entry.issue %>
<%= f.text_field :spent_on, :size => 10, :required => true %><%= calendar_for('time_entry_spent_on') %>
- <%= f.text_field :hours, :size => 6, :required => true %>
+ <%= f.time_field :start_time %>
+ <%= f.time_field :end_time %>
+ <%= f.text_field :hours, :size => 6 %> <%= l(:text_clear_to_recalculate_time_by_range) %>
<%= f.text_field :comments, :size => 100 %>
<%= f.select :activity_id, activity_collection_for_select_options, :required => true %>
Index: lib/tabular_form_builder.rb
===================================================================
--- lib/tabular_form_builder.rb (revision 1669)
+++ lib/tabular_form_builder.rb (working copy)
@@ -40,6 +40,17 @@
END_SRC
class_eval src, __FILE__, __LINE__
end
+
+ def time_field(field)
+ value = nil
+ value = @object.send(field) if @object
+ value = value.strftime('%Y-%m-%d %H:%M') if value
+ result = text_field field, :size => 15, :value => value
+ result += (' ') % "#{@object_name}_#{field}"
+ result
+ end
def select(field, choices, options = {}, html_options = {})
label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
Index: app/controllers/application.rb
===================================================================
--- app/controllers/application.rb (revision 1669)
+++ app/controllers/application.rb (working copy)
@@ -219,4 +219,9 @@
def filename_for_content_disposition(name)
request.env['HTTP_USER_AGENT'] =~ %r{MSIE} ? ERB::Util.url_encode(name) : name
end
+
+ def url_path(url_parameters)
+ rs = ::ActionController::Routing::Routes
+ rs.generate url_parameters
+ end
end
Index: app/controllers/issues_controller.rb
===================================================================
--- app/controllers/issues_controller.rb (revision 1669)
+++ app/controllers/issues_controller.rb (working copy)
@@ -101,7 +101,7 @@
@allowed_statuses = @issue.new_statuses_allowed_to(User.current)
@edit_allowed = User.current.allowed_to?(:edit_issues, @project)
@priorities = Enumeration::get_values('IPRI')
- @time_entry = TimeEntry.new
+ @time_entry = @issue.time_entry_in_progress(User.current) || TimeEntry.new
respond_to do |format|
format.html { render :template => 'issues/show.rhtml' }
format.atom { render :action => 'changes', :layout => false, :content_type => 'application/atom+xml' }
@@ -172,7 +172,10 @@
end
if request.post?
- @time_entry = TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => Date.today)
+ @time_entry = TimeEntry.find_by_id(params[:time_entry][:id]) if params[:time_entry]
+ @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue,
+ :user => User.current, :spent_on => Date.today)
+
@time_entry.attributes = params[:time_entry]
attachments = attach_files(@issue, params[:attachments])
attachments.each {|a| journal.details << JournalDetail.new(:property => 'attachment', :prop_key => a.id, :value => a.filename)}
Index: app/controllers/timelog_controller.rb
===================================================================
--- app/controllers/timelog_controller.rb (revision 1669)
+++ app/controllers/timelog_controller.rb (working copy)
@@ -175,11 +175,39 @@
end
def edit
+ return if redirect_if_in_progress
+
render_403 and return if @time_entry && !@time_entry.editable_by?(User.current)
@time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => Date.today)
@time_entry.attributes = params[:time_entry]
+ if !@time_entry.hours
+ if !@time_entry.start_time
+ @activate_field = 'time_entry_start_time'
+ else
+ @activate_field = 'time_entry_end_time'
+ end
+ end
+
+ url_writer = lambda do |entry|
+ " :timelog, :action => :edit,
+ :id => entry.id)}\">##{entry.issue_id}-#{entry.id}"
+ end
+
if request.post? and @time_entry.save
- flash[:notice] = l(:notice_successful_update)
+ intersecting = @time_entry.find_intersecting_entries
+ logger.debug "intersecting = #{intersecting.inspect}"
+ msg = l(:notice_successful_update)
+ if !intersecting.empty?
+
+ list = lwr(:text_time_entry_intersecting_notice_entry,
+ intersecting.size) + ' ' + intersecting.
+ map { |entry| url_writer.call(entry) }.
+ to_sentence(:skip_last_comma => true, :connector => l(:text_and))
+
+ msg += ' ' + l(:text_time_entry_intersecting_notice,
+ url_writer.call(@time_entry), list)
+ end
+ flash[:notice] = msg
redirect_to(params[:back_url].blank? ? {:action => 'details', :project_id => @time_entry.project} : params[:back_url])
return
end
@@ -258,4 +286,17 @@
@from ||= (TimeEntry.minimum(:spent_on, :include => :project, :conditions => @project.project_condition(Setting.display_subprojects_issues?)) || Date.today) - 1
@to ||= (TimeEntry.maximum(:spent_on, :include => :project, :conditions => @project.project_condition(Setting.display_subprojects_issues?)) || Date.today)
end
+
+ def redirect_if_in_progress
+ if !@time_entry && @issue
+ in_progress_entry = @issue.time_entry_in_progress(User.current)
+ if in_progress_entry
+ #in order to avoid :id form parameter and not complicate :find_project filter
+ redirect_to(:controller => 'timelog', :action => 'edit',
+ :id => in_progress_entry)
+ return true
+ end
+ end
+ false
+ end
end
Index: app/helpers/application_helper.rb
===================================================================
--- app/helpers/application_helper.rb (revision 1669)
+++ app/helpers/application_helper.rb (working copy)
@@ -70,7 +70,7 @@
@date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
date.strftime(@date_format)
end
-
+
def format_time(time, include_date = true)
return nil unless time
time = time.to_time if time.is_a?(String)
@@ -78,7 +78,8 @@
local = zone ? time.in_time_zone(zone) : (time.utc? ? time.utc_to_local : time)
@date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
@time_format ||= (Setting.time_format.blank? ? l(:general_fmt_time) : Setting.time_format)
- include_date ? local.strftime("#{@date_format} #{@time_format}") : local.strftime(@time_format)
+ format = include_date ? "#{@date_format} #{@time_format}" : @time_format
+ local.strftime(format)
end
# Truncates and returns the string as a single line
Index: app/helpers/issues_helper.rb
===================================================================
--- app/helpers/issues_helper.rb (revision 1669)
+++ app/helpers/issues_helper.rb (working copy)
@@ -181,4 +181,11 @@
export.rewind
export
end
+
+ def spent_hours(issue)
+ return '-' if issue.time_entries.empty?
+ spent_hours = lwr(:label_f_hour, issue.spent_hours)
+ spent_hours += " (#{l(:text_in_progress)})" if issue.time_entry_in_progress
+ link_to spent_hours, {:controller => 'timelog', :action => 'details', :project_id => issue.project, :issue_id => issue}, :class => 'icon icon-time'
+ end
end
Index: app/helpers/timelog_helper.rb
===================================================================
--- app/helpers/timelog_helper.rb (revision 1669)
+++ app/helpers/timelog_helper.rb (working copy)
@@ -36,6 +36,11 @@
sum
end
+ def hours(entry)
+ return '[%s]' % l(:text_in_progress) if !entry.hours
+ html_hours("%.2f" % entry.hours)
+ end
+
def options_for_period_select(value)
options_for_select([[l(:label_all_time), 'all'],
[l(:label_today), 'today'],
Index: app/models/issue.rb
===================================================================
--- app/models/issue.rb (revision 1669)
+++ app/models/issue.rb (working copy)
@@ -28,6 +28,7 @@
has_many :journals, :as => :journalized, :dependent => :destroy
has_many :attachments, :as => :container, :dependent => :destroy
has_many :time_entries, :dependent => :delete_all
+
has_and_belongs_to_many :changesets, :order => "#{Changeset.table_name}.committed_on ASC, #{Changeset.table_name}.id ASC"
has_many :relations_from, :class_name => 'IssueRelation', :foreign_key => 'issue_from_id', :dependent => :delete_all
@@ -251,4 +252,9 @@
def to_s
"#{tracker} ##{id}: #{subject}"
end
+
+ def time_entry_in_progress(user = nil)
+ TimeEntry.find_by_issue_id(self.id,
+ :conditions => 'start_time IS NOT NULL and hours IS NULL' + (user ? " and user_id = #{user.id}" : ''))
+ end
end
Index: app/models/time_entry.rb
===================================================================
--- app/models/time_entry.rb (revision 1669)
+++ app/models/time_entry.rb (working copy)
@@ -25,12 +25,17 @@
attr_protected :project_id, :user_id, :tyear, :tmonth, :tweek
- acts_as_event :title => Proc.new {|o| "#{o.user}: #{lwr(:label_f_hour, o.hours)} (#{(o.issue || o.project).event_title})"},
- :url => Proc.new {|o| {:controller => 'timelog', :action => 'details', :project_id => o.project}},
+ title_proc = Proc.new do |entry|
+ hours = entry.hours ? lwr(:label_f_hour, entry.hours) : "[#{l(:text_in_progress)}]"
+ "#{entry.user}: #{hours} (#{(entry.issue || entry.project).event_title})"
+ end
+
+ acts_as_event :title => title_proc,
+ :url => Proc.new {|entry| {:controller => 'timelog', :action => 'details', :project_id => entry.project}},
:author => :user,
:description => :comments
- validates_presence_of :user_id, :activity_id, :project_id, :hours, :spent_on
+ validates_presence_of :user_id, :activity_id, :project_id, :spent_on
validates_numericality_of :hours, :allow_nil => true
validates_length_of :comments, :maximum => 255, :allow_nil => true
@@ -48,6 +53,18 @@
def validate
errors.add :hours, :activerecord_error_invalid if hours && (hours < 0 || hours >= 1000)
+
+ if !start_time && !hours
+ #rather verbose, but l() always translate to English here for some reason
+ errors.add :hours, ll(User.current.language,
+ :activerecord_error_field_must_be_set_if_other_is_not,
+ ll(User.current.language, :field_start_time))
+
+ errors.add :start_time, ll(User.current.language,
+ :activerecord_error_field_must_be_set_if_other_is_not,
+ ll(User.current.language, :field_hours))
+ end
+
errors.add :project_id, :activerecord_error_invalid if project.nil?
errors.add :issue_id, :activerecord_error_invalid if (issue_id && !issue) || (issue && project!=issue.project)
end
@@ -75,4 +92,25 @@
yield
end
end
+
+ def before_save
+ if !hours && start_time && end_time
+ self.hours = (end_time - start_time) / 3600
+ end
+ end
+
+ def find_intersecting_entries
+ params = {:start_time => start_time, :end_time => end_time, :id => id}
+
+ self.class.find_all_by_user_id(user_id, :conditions =>
+ ["(" +
+ #this entry's start time or end time is between other's start_time and end_time
+ "start_time < :start_time and :start_time < end_time OR " +
+ "start_time < :end_time and :end_time < end_time OR " +
+ #other's entry's start time or end time is between this entry's start_time and end_time
+ "start_time > :start_time and start_time < :end_time OR " +
+ "end_time > :start_time and end_time < :end_time" +
+ ")" +
+ "and id <> :id", params])
+ end
end
Index: lang/bg.yml
===================================================================
--- lang/bg.yml (revision 1669)
+++ lang/bg.yml (working copy)
@@ -632,3 +632,13 @@
setting_mail_handler_api_enabled: Enable WS for incoming emails
setting_mail_handler_api_key: API key
text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
+field_start_time: Start Time
+field_end_time: End Time
+text_in_progress: in progress
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
+text_and: and
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
+text_time_entry_intersecting_notice_entry: entry
+text_time_entry_intersecting_notice_entry_plural: entries
+button_now: Now
Index: lang/cs.yml
===================================================================
--- lang/cs.yml (revision 1669)
+++ lang/cs.yml (working copy)
@@ -637,3 +637,13 @@
setting_mail_handler_api_enabled: Enable WS for incoming emails
setting_mail_handler_api_key: API key
text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
+field_start_time: Start Time
+field_end_time: End Time
+text_in_progress: in progress
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
+text_and: and
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
+text_time_entry_intersecting_notice_entry: entry
+text_time_entry_intersecting_notice_entry_plural: entries
+button_now: Now
Index: lang/da.yml
===================================================================
--- lang/da.yml (revision 1669)
+++ lang/da.yml (working copy)
@@ -634,3 +634,13 @@
setting_mail_handler_api_enabled: Enable WS for incoming emails
setting_mail_handler_api_key: API key
text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
+field_start_time: Start Time
+field_end_time: End Time
+text_in_progress: in progress
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
+text_and: and
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
+text_time_entry_intersecting_notice_entry: entry
+text_time_entry_intersecting_notice_entry_plural: entries
+button_now: Now
Index: lang/de.yml
===================================================================
--- lang/de.yml (revision 1669)
+++ lang/de.yml (working copy)
@@ -633,3 +633,13 @@
setting_mail_handler_api_enabled: Enable WS for incoming emails
setting_mail_handler_api_key: API key
text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
+field_start_time: Start Time
+field_end_time: End Time
+text_in_progress: in progress
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
+text_and: and
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
+text_time_entry_intersecting_notice_entry: entry
+text_time_entry_intersecting_notice_entry_plural: entries
+button_now: Now
Index: lang/en.yml
===================================================================
--- lang/en.yml (revision 1669)
+++ lang/en.yml (working copy)
@@ -170,6 +170,8 @@
field_hours: Hours
field_activity: Activity
field_spent_on: Date
+field_start_time: Start Time
+field_end_time: End Time
field_identifier: Identifier
field_is_filter: Used as a filter
field_issue_to_id: Related issue
@@ -558,6 +560,7 @@
button_annotate: Annotate
button_update: Update
button_configure: Configure
+button_now: Now
status_active: active
status_registered: registered
@@ -633,3 +636,10 @@
enumeration_issue_priorities: Issue priorities
enumeration_doc_categories: Document categories
enumeration_activities: Activities (time tracking)
+text_in_progress: in progress
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
+text_and: and
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
+text_time_entry_intersecting_notice_entry: entry
+text_time_entry_intersecting_notice_entry_plural: entries
Index: lang/es.yml
===================================================================
--- lang/es.yml (revision 1669)
+++ lang/es.yml (working copy)
@@ -635,3 +635,13 @@
setting_mail_handler_api_enabled: Enable WS for incoming emails
setting_mail_handler_api_key: API key
text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
+field_start_time: Start Time
+field_end_time: End Time
+text_in_progress: in progress
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
+text_and: and
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
+text_time_entry_intersecting_notice_entry: entry
+text_time_entry_intersecting_notice_entry_plural: entries
+button_now: Now
Index: lang/fi.yml
===================================================================
--- lang/fi.yml (revision 1669)
+++ lang/fi.yml (working copy)
@@ -632,3 +632,13 @@
setting_mail_handler_api_enabled: Enable WS for incoming emails
setting_mail_handler_api_key: API key
text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
+field_start_time: Start Time
+field_end_time: End Time
+text_in_progress: in progress
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
+text_and: and
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
+text_time_entry_intersecting_notice_entry: entry
+text_time_entry_intersecting_notice_entry_plural: entries
+button_now: Now
Index: lang/fr.yml
===================================================================
--- lang/fr.yml (revision 1669)
+++ lang/fr.yml (working copy)
@@ -633,3 +633,13 @@
enumeration_issue_priorities: Priorités des demandes
enumeration_doc_categories: Catégories des documents
enumeration_activities: Activités (suivi du temps)
+field_start_time: Start Time
+field_end_time: End Time
+text_in_progress: in progress
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
+text_and: and
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
+text_time_entry_intersecting_notice_entry: entry
+text_time_entry_intersecting_notice_entry_plural: entries
+button_now: Now
Index: lang/he.yml
===================================================================
--- lang/he.yml (revision 1669)
+++ lang/he.yml (working copy)
@@ -632,3 +632,13 @@
setting_mail_handler_api_enabled: Enable WS for incoming emails
setting_mail_handler_api_key: API key
text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
+field_start_time: Start Time
+field_end_time: End Time
+text_in_progress: in progress
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
+text_and: and
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
+text_time_entry_intersecting_notice_entry: entry
+text_time_entry_intersecting_notice_entry_plural: entries
+button_now: Now
Index: lang/hu.yml
===================================================================
--- lang/hu.yml (revision 1669)
+++ lang/hu.yml (working copy)
@@ -633,3 +633,13 @@
setting_mail_handler_api_enabled: Web Service engedélyezése a beérkezett levelekhez
setting_mail_handler_api_key: API kulcs
text_email_delivery_not_configured: "Az E-mail küldés nincs konfigurálva, és az értesítések ki vannak kapcsolva.\nÁllítsd be az SMTP szervert a config/email.yml fájlban és indítsd újra az alkalmazást, hogy érvénybe lépjen."
+field_start_time: Start Time
+field_end_time: End Time
+text_in_progress: in progress
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
+text_and: and
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
+text_time_entry_intersecting_notice_entry: entry
+text_time_entry_intersecting_notice_entry_plural: entries
+button_now: Now
Index: lang/it.yml
===================================================================
--- lang/it.yml (revision 1669)
+++ lang/it.yml (working copy)
@@ -632,3 +632,13 @@
setting_mail_handler_api_enabled: Abilita WS per le e-mail in arrivo
setting_mail_handler_api_key: chiave API
text_email_delivery_not_configured: "La consegna via e-mail non è configurata e le notifiche sono disabilitate.\nConfigura il tuo server SMTP in config/email.yml e riavvia l'applicazione per abilitarle."
+field_start_time: Start Time
+field_end_time: End Time
+text_in_progress: in progress
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
+text_and: and
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
+text_time_entry_intersecting_notice_entry: entry
+text_time_entry_intersecting_notice_entry_plural: entries
+button_now: Now
Index: lang/ja.yml
===================================================================
--- lang/ja.yml (revision 1669)
+++ lang/ja.yml (working copy)
@@ -633,3 +633,13 @@
setting_mail_handler_api_enabled: Enable WS for incoming emails
setting_mail_handler_api_key: API key
text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
+field_start_time: Start Time
+field_end_time: End Time
+text_in_progress: in progress
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
+text_and: and
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
+text_time_entry_intersecting_notice_entry: entry
+text_time_entry_intersecting_notice_entry_plural: entries
+button_now: Now
Index: lang/ko.yml
===================================================================
--- lang/ko.yml (revision 1669)
+++ lang/ko.yml (working copy)
@@ -632,3 +632,13 @@
setting_mail_handler_api_enabled: Enable WS for incoming emails
setting_mail_handler_api_key: API key
text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
+field_start_time: Start Time
+field_end_time: End Time
+text_in_progress: in progress
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
+text_and: and
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
+text_time_entry_intersecting_notice_entry: entry
+text_time_entry_intersecting_notice_entry_plural: entries
+button_now: Now
Index: lang/lt.yml
===================================================================
--- lang/lt.yml (revision 1669)
+++ lang/lt.yml (working copy)
@@ -635,3 +635,13 @@
setting_mail_handler_api_key: API raktas
text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
+field_start_time: Start Time
+field_end_time: End Time
+text_in_progress: in progress
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
+text_and: and
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
+text_time_entry_intersecting_notice_entry: entry
+text_time_entry_intersecting_notice_entry_plural: entries
+button_now: Now
Index: lang/nl.yml
===================================================================
--- lang/nl.yml (revision 1669)
+++ lang/nl.yml (working copy)
@@ -633,3 +633,13 @@
setting_mail_handler_api_enabled: Enable WS for incoming emails
setting_mail_handler_api_key: API key
text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
+field_start_time: Start Time
+field_end_time: End Time
+text_in_progress: in progress
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
+text_and: and
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
+text_time_entry_intersecting_notice_entry: entry
+text_time_entry_intersecting_notice_entry_plural: entries
+button_now: Now
Index: lang/no.yml
===================================================================
--- lang/no.yml (revision 1669)
+++ lang/no.yml (working copy)
@@ -633,3 +633,13 @@
setting_mail_handler_api_enabled: Enable WS for incoming emails
setting_mail_handler_api_key: API key
text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
+field_start_time: Start Time
+field_end_time: End Time
+text_in_progress: in progress
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
+text_and: and
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
+text_time_entry_intersecting_notice_entry: entry
+text_time_entry_intersecting_notice_entry_plural: entries
+button_now: Now
Index: lang/pl.yml
===================================================================
--- lang/pl.yml (revision 1669)
+++ lang/pl.yml (working copy)
@@ -632,3 +632,13 @@
setting_mail_handler_api_enabled: Enable WS for incoming emails
setting_mail_handler_api_key: API key
text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
+field_start_time: Start Time
+field_end_time: End Time
+text_in_progress: in progress
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
+text_and: and
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
+text_time_entry_intersecting_notice_entry: entry
+text_time_entry_intersecting_notice_entry_plural: entries
+button_now: Now
Index: lang/pt-br.yml
===================================================================
--- lang/pt-br.yml (revision 1669)
+++ lang/pt-br.yml (working copy)
@@ -632,3 +632,13 @@
setting_mail_handler_api_enabled: Enable WS for incoming emails
setting_mail_handler_api_key: API key
text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
+field_start_time: Start Time
+field_end_time: End Time
+text_in_progress: in progress
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
+text_and: and
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
+text_time_entry_intersecting_notice_entry: entry
+text_time_entry_intersecting_notice_entry_plural: entries
+button_now: Now
Index: lang/pt.yml
===================================================================
--- lang/pt.yml (revision 1669)
+++ lang/pt.yml (working copy)
@@ -632,3 +632,13 @@
setting_mail_handler_api_enabled: Enable WS for incoming emails
setting_mail_handler_api_key: API key
text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
+field_start_time: Start Time
+field_end_time: End Time
+text_in_progress: in progress
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
+text_and: and
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
+text_time_entry_intersecting_notice_entry: entry
+text_time_entry_intersecting_notice_entry_plural: entries
+button_now: Now
Index: lang/ro.yml
===================================================================
--- lang/ro.yml (revision 1669)
+++ lang/ro.yml (working copy)
@@ -632,3 +632,13 @@
setting_mail_handler_api_enabled: Enable WS for incoming emails
setting_mail_handler_api_key: API key
text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
+field_start_time: Start Time
+field_end_time: End Time
+text_in_progress: in progress
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
+text_and: and
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
+text_time_entry_intersecting_notice_entry: entry
+text_time_entry_intersecting_notice_entry_plural: entries
+button_now: Now
Index: lang/ru.yml
===================================================================
--- lang/ru.yml (revision 1669)
+++ lang/ru.yml (working copy)
@@ -164,9 +164,11 @@
field_url: URL
field_start_page: Стартовая страница
field_subproject: Подпроект
-field_hours: Час(а,ов)
+field_hours: Часов
field_activity: Деятельность
field_spent_on: Дата
+field_start_time: Время начала
+field_end_time: Время окончания
field_identifier: Ун. идентификатор
field_is_filter: Используется в качестве фильтра
field_issue_to_id: Связанные задачи
@@ -395,7 +397,7 @@
label_issue_tracking: Ситуация по задачам
label_spent_time: Затраченное время
label_f_hour: %.2f час
-label_f_hour_plural: %.2f часов(а)
+label_f_hour_plural: %.2f часов
label_time_tracking: Учет времени
label_change_plural: Правки
label_statistics: Статистика
@@ -508,6 +510,7 @@
button_copy: Копировать
button_annotate: Авторство
button_update: Обновить
+button_now: Сейчас
status_active: Активен
status_registered: Зарегистрирован
@@ -636,3 +639,10 @@
setting_mail_handler_api_enabled: Включить веб-сервис для входящих сообщений
setting_mail_handler_api_key: API ключ
text_email_delivery_not_configured: "Параметры работы с почтовым сервером не настроены и функция уведомления по email не активна.\nНастроить параметры для вашего SMTP сервера вы можете в файле config/email.yml. Для применения изменений перезапустите приложение."
+text_in_progress: в процессе
+activerecord_error_field_must_be_set_if_other_is_not: необходимо задать, если значение поля '%s' не задано
+text_clear_to_recalculate_time_by_range: Очистите, чтобы пересчитать по времени окончания
+text_and: и
+text_time_entry_intersecting_notice: Обратите внимание, что эта запись о потраченном времени (%s) пересекается с %s.
+text_time_entry_intersecting_notice_entry: записью
+text_time_entry_intersecting_notice_entry_plural: записями
Index: lang/sr.yml
===================================================================
--- lang/sr.yml (revision 1669)
+++ lang/sr.yml (working copy)
@@ -633,3 +633,13 @@
setting_mail_handler_api_enabled: Enable WS for incoming emails
setting_mail_handler_api_key: API key
text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
+field_start_time: Start Time
+field_end_time: End Time
+text_in_progress: in progress
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
+text_and: and
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
+text_time_entry_intersecting_notice_entry: entry
+text_time_entry_intersecting_notice_entry_plural: entries
+button_now: Now
Index: lang/sv.yml
===================================================================
--- lang/sv.yml (revision 1669)
+++ lang/sv.yml (working copy)
@@ -633,3 +633,13 @@
setting_mail_handler_api_enabled: Enable WS for incoming emails
setting_mail_handler_api_key: API key
text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
+field_start_time: Start Time
+field_end_time: End Time
+text_in_progress: in progress
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
+text_and: and
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
+text_time_entry_intersecting_notice_entry: entry
+text_time_entry_intersecting_notice_entry_plural: entries
+button_now: Now
Index: lang/th.yml
===================================================================
--- lang/th.yml (revision 1669)
+++ lang/th.yml (working copy)
@@ -635,3 +635,13 @@
setting_mail_handler_api_enabled: Enable WS for incoming emails
setting_mail_handler_api_key: API key
text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
+field_start_time: Start Time
+field_end_time: End Time
+text_in_progress: in progress
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
+text_and: and
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
+text_time_entry_intersecting_notice_entry: entry
+text_time_entry_intersecting_notice_entry_plural: entries
+button_now: Now
Index: lang/uk.yml
===================================================================
--- lang/uk.yml (revision 1669)
+++ lang/uk.yml (working copy)
@@ -634,3 +634,13 @@
setting_mail_handler_api_enabled: Enable WS for incoming emails
setting_mail_handler_api_key: API key
text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
+field_start_time: Start Time
+field_end_time: End Time
+text_in_progress: in progress
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
+text_and: and
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
+text_time_entry_intersecting_notice_entry: entry
+text_time_entry_intersecting_notice_entry_plural: entries
+button_now: Now
Index: lang/zh-tw.yml
===================================================================
--- lang/zh-tw.yml (revision 1669)
+++ lang/zh-tw.yml (working copy)
@@ -633,3 +633,14 @@
enumeration_issue_priorities: 項目優先權
enumeration_doc_categories: 文件分類
enumeration_activities: 活動 (時間追蹤)
+text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
+field_start_time: Start Time
+field_end_time: End Time
+text_in_progress: in progress
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
+text_and: and
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
+text_time_entry_intersecting_notice_entry: entry
+text_time_entry_intersecting_notice_entry_plural: entries
+button_now: Now
Index: lang/zh.yml
===================================================================
--- lang/zh.yml (revision 1669)
+++ lang/zh.yml (working copy)
@@ -633,3 +633,13 @@
enumeration_doc_categories: 文档类别
enumeration_activities: 活动(时间跟踪)
text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
+field_start_time: Start Time
+field_end_time: End Time
+text_in_progress: in progress
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
+text_and: and
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
+text_time_entry_intersecting_notice_entry: entry
+text_time_entry_intersecting_notice_entry_plural: entries
+button_now: Now
Index: public/javascripts/time.js
===================================================================
--- public/javascripts/time.js (revision 0)
+++ public/javascripts/time.js (revision 0)
@@ -0,0 +1,68 @@
+//taken from XPlanner
+var dateFormatChars = "dMyhHmsa";
+
+function formatDate(date, format) {
+ return formatDate2(date, format, 0);
+}
+
+function formatDate2(date, format, offset) {
+ if (offset >= format.length) {
+ return "";
+ } else if (dateFormatChars.indexOf(format.charAt(offset)) != -1) {
+ return formatDateElement(date, format, offset);
+ } else {
+ return formatDateLiteral(date, format, offset);
+ }
+}
+
+function formatDateElement(date, format, offset) {
+ var end = offset;
+ var ch = format.charAt(offset);
+ while (++end < format.length && format.charAt(end) == ch);
+ var count = end - offset;
+ var value;
+ if (ch == 'd') {
+ value = padValue(count, date.getDate());
+ }
+ else if (ch == 'M') {
+ value = padValue(count, date.getMonth()+1);
+ }
+ else if (ch == 'y') {
+ value = padValue(count, date.getFullYear());
+ }
+ else if (ch == 'H') {
+ value = padValue(count, date.getHours());
+ }
+ else if (ch == 'h') {
+ value = padValue(count, date.getHours() % 12);
+ }
+ else if (ch == 'm') {
+ value = padValue(count, date.getMinutes());
+ }
+ else if (ch == 's') {
+ value = padValue(count, date.getSeconds());
+ }
+ else if (ch == 'a') {
+ value = date.getHours() > 12 ? 'PM' : 'AM';
+ }
+ return value + formatDate2(date, format, end);
+}
+
+function padValue(count, value) {
+ for (i = value.toString().length; i < count; i++) {
+ value = '0'+value;
+ }
+ return value;
+}
+
+function formatDateLiteral(date, format, offset) {
+ end = offset;
+ while (++end < format.length && dateFormatChars.indexOf(format.charAt(end)) == -1);
+ return format.substr(offset, end - offset) + formatDate2(date, format, end);
+}
+
+function maybeSetTimeNow(field) {
+ if (field.value == "") {
+ field.value = formatDate(new Date(), "yyyy-MM-dd HH:mm");
+ }
+}
Index: test/fixtures/time_entries.yml
===================================================================
--- test/fixtures/time_entries.yml (revision 1669)
+++ test/fixtures/time_entries.yml (working copy)
@@ -55,4 +55,55 @@
hours: 7.65
user_id: 1
tyear: 2007
-
\ No newline at end of file
+
+#this is to run existing controller tests with Start/End time improvement - specific entries
+time_entry_in_progress:
+ created_on: 2007-04-22 12:20:48 +02:00
+ tweek: 16
+ tmonth: 4
+ project_id: 1
+ comments: Time spent on a subproject (in progress)
+ updated_on: 2007-04-22 12:20:48 +02:00
+ activity_id: 10
+ spent_on: 2007-04-22
+ issue_id: 1
+ id: 5
+ user_id: 1
+ tyear: 2007
+ start_time: 2007-07-11 16:20
+
+#same user but another issue and project, and intersects with time_entry_in_progress
+intersecting_time_entry:
+ created_on: 2007-04-22 12:20:48 +02:00
+ tweek: 16
+ tmonth: 4
+ project_id: 2
+ comments: Time entry intersecting with time_entry_in_progress
+ updated_on: 2007-04-22 12:20:48 +02:00
+ activity_id: 10
+ spent_on: 2007-04-22
+ issue_id: 4
+ id: 6
+ user_id: 1
+ tyear: 2007
+ start_time: 2007-07-11 15:20
+ end_time: 2007-07-11 17:20
+ hours: 0 #to not break time calculation tests
+
+#this spans several years - should intersect with time_entry_in_progress as well
+big_intersecting_time_entry:
+ created_on: 2007-04-22 12:20:48 +02:00
+ tweek: 16
+ tmonth: 4
+ project_id: 1
+ comments: Time entry intersecting with time_entry_in_progress
+ updated_on: 2007-04-22 12:20:48 +02:00
+ activity_id: 10
+ spent_on: 2007-04-22
+ issue_id: 1
+ id: 7
+ user_id: 1
+ tyear: 2007
+ start_time: 2005-07-11 16:30
+ end_time: 2007-07-11 17:20
+ hours: 0 #to not break time calculation tests
Index: test/functional/issues_controller_test.rb
===================================================================
--- test/functional/issues_controller_test.rb (revision 1669)
+++ test/functional/issues_controller_test.rb (working copy)
@@ -164,6 +164,19 @@
:content => /Notes/ } }
end
+ def test_show_in_progress
+ @request.session[:user_id] = 1
+ get :show, :id => 1
+ assert_equal time_entries(:time_entry_in_progress), assigns(:time_entry),
+ 'there should be an in-progress time entry for this user'
+ end
+
+ def test_show_not_in_progress
+ @request.session[:user_id] = 2
+ get :show, :id => 1
+ assert_nil assigns(:time_entry).id, 'time entry for this user should be new'
+ end
+
def test_get_new
@request.session[:user_id] = 2
get :new, :project_id => 1, :tracker_id => 1
@@ -381,7 +394,7 @@
issue = Issue.find(1)
assert_equal 1, issue.status_id
@request.session[:user_id] = 2
- assert_difference('TimeEntry.count', 0) do
+ assert_difference('TimeEntry.count', 0) do #time entries count should not change because given entry is empty (hence invalid)
post :edit,
:id => 1,
:issue => { :status_id => 2, :assigned_to_id => 3 },
@@ -399,6 +412,21 @@
assert mail.body.include?("Status changed from New to Assigned")
end
+ def test_post_edit_with_task_in_progress
+ user_id = 1
+ @request.session[:user_id] = user_id
+ issue = Issue.find(1)
+ time_entry = issue.time_entry_in_progress(User.find(user_id))
+ assert_not_nil time_entry, 'there should be time entry in progress'
+ assert_difference('TimeEntry.count', 0) do #time entries count should not change because entry in progress should be updated
+ post :edit,
+ :id => 1,
+ :issue => { },
+ :notes => 'Assigned to dlopper',
+ :time_entry => { :id => time_entry.id, :comments => 'xyz' }
+ end
+ end
+
def test_post_edit_with_note_only
notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
# anonymous user
@@ -428,11 +456,13 @@
issue = Issue.find(1)
- j = issue.journals.find(:first, :order => 'id DESC')
- assert_equal '2.5 hours added', j.notes
- assert_equal 0, j.details.size
+ #:first doesn't work sometimes here with MySQL 5.0.51b
+ e = issue.journals.find(:all, :order => 'id DESC')[0]
+ assert_equal '2.5 hours added', e.notes
+ assert_equal 0, e.details.size
- t = issue.time_entries.find(:first, :order => 'id DESC')
+ #:first doesn't work sometimes here with MySQL 5.0.51b
+ t = issue.time_entries.find(:all, :order => 'id DESC')[0]
assert_not_nil t
assert_equal 2.5, t.hours
assert_equal spent_hours_before + 2.5, issue.spent_hours
Index: test/functional/timelog_controller_test.rb
===================================================================
--- test/functional/timelog_controller_test.rb (revision 1669)
+++ test/functional/timelog_controller_test.rb (working copy)
@@ -40,6 +40,28 @@
:content => 'Development'
end
+ def test_get_edit_in_progress_by_entry_id
+ @request.session[:user_id] = 1 #to avoid 'no permission' error
+ get :edit, :id => time_entries(:time_entry_in_progress)
+ assert_response :success
+ assert_equal 'time_entry_end_time', assigns(:activate_field)
+ end
+
+ def test_get_edit_not_in_progress
+ @request.session[:user_id] = 2 #there are no in-progress entries of this user for the issue
+ get :edit, :issue_id => 1
+ assert_response :success
+ assert_equal 'time_entry_start_time', assigns(:activate_field)
+ end
+
+ def test_get_edit_in_progress_by_issue_id
+ user_id = 1
+ @request.session[:user_id] = user_id #there is in-progress entry of this user for the issue
+ issue_id = 1
+ get :edit, :issue_id => issue_id
+ assert_redirected_to :action => "edit", :id => Issue.find(issue_id).time_entry_in_progress(User.find(user_id)).id
+ end
+
def test_post_edit
@request.session[:user_id] = 3
post :edit, :project_id => 1,
@@ -70,6 +92,7 @@
post :edit, :id => 1,
:time_entry => {:issue_id => '2',
:hours => '8'}
+ assert_no_errors(assigns(:time_entry))
assert_redirected_to 'projects/ecookbook/timelog/details'
entry.reload
@@ -165,7 +188,7 @@
assert_response :success
assert_template 'details'
assert_not_nil assigns(:entries)
- assert_equal 4, assigns(:entries).size
+ assert_equal 6, assigns(:entries).size
# project and subproject
assert_equal [1, 3], assigns(:entries).collect(&:project_id).uniq.sort
assert_not_nil assigns(:total_hours)
@@ -180,7 +203,7 @@
assert_response :success
assert_template 'details'
assert_not_nil assigns(:entries)
- assert_equal 3, assigns(:entries).size
+ assert_equal 5, assigns(:entries).size
assert_not_nil assigns(:total_hours)
assert_equal "12.90", "%.2f" % assigns(:total_hours)
assert_equal '2007-03-20'.to_date, assigns(:from)
@@ -202,7 +225,7 @@
assert_response :success
assert_template 'details'
assert_not_nil assigns(:entries)
- assert_equal 2, assigns(:entries).size
+ assert_equal 4, assigns(:entries).size
assert_not_nil assigns(:total_hours)
assert_equal 154.25, assigns(:total_hours)
# display all time by default
Index: test/test_helper.rb
===================================================================
--- test/test_helper.rb (revision 1669)
+++ test/test_helper.rb (working copy)
@@ -64,4 +64,19 @@
Dir.mkdir "#{RAILS_ROOT}/tmp/test/attachments" unless File.directory?("#{RAILS_ROOT}/tmp/test/attachments")
Attachment.storage_path = "#{RAILS_ROOT}/tmp/test/attachments"
end
+
+ def assert_error_on(object, field)
+ object.valid?
+ assert object.errors.on(field), "expected error on #{field} attribute"
+ end
+
+ def assert_no_error_on(object, field)
+ object.valid?
+ assert !object.errors.on(field), "expected no error on #{field} attribute"
+ end
+
+ def assert_no_errors(object, options = {})
+ object.valid? if options[:validate]
+ assert_equal [], object.errors.full_messages
+ end
end
Index: test/unit/issue_test.rb
===================================================================
--- test/unit/issue_test.rb (revision 1669)
+++ test/unit/issue_test.rb (working copy)
@@ -181,4 +181,13 @@
assert_nil Issue.find_by_id(1)
assert_nil TimeEntry.find_by_issue_id(1)
end
+
+ def test_find_in_progress_success
+ assert_equal time_entries(:time_entry_in_progress),
+ issues(:issues_001).time_entry_in_progress(users(:users_001))
+ end
+
+ def test_find_in_progress_failure
+ assert_nil issues(:issues_001).time_entry_in_progress(users(:users_002))
+ end
end
Index: test/unit/time_entry_test.rb
===================================================================
--- test/unit/time_entry_test.rb (revision 1669)
+++ test/unit/time_entry_test.rb (working copy)
@@ -20,6 +20,10 @@
class TimeEntryTest < Test::Unit::TestCase
fixtures :issues, :projects, :users, :time_entries
+ def setup
+ User.current.language = 'en'
+ end
+
def test_hours_format
assertions = { "2" => 2.0,
"21.1" => 21.1,
@@ -43,4 +47,80 @@
assert_equal v, t.hours
end
end
+
+ def test_start_time_must_be_set_if_hours_are_not_and_reverse
+ entry = TimeEntry.new
+ assert_error_on(entry, :start_time)
+ assert_error_on(entry, :hours)
+ end
+
+ def test_start_time_can_be_absent_if_hours_are_set_and_reverse
+ entry = TimeEntry.new :hours => 1
+ entry.valid?
+ assert_no_error_on(entry, :start_time)
+ entry = TimeEntry.new :start_time => Time.now
+ entry.valid?
+ assert_no_error_on(entry, :hours)
+ end
+
+ def successful_params
+ {:spent_on => '2008-07-13', :issue_id => 1, :user => users(:users_004),
+ :activity_id => Enumeration.get_values('ACTI').first}
+ end
+
+ def test_hours_not_calculated_if_set_explicitly
+ #I worked on this time to time during the day, and it was 1 hour in sum
+ entry = TimeEntry.new successful_params.merge(:hours => 1,
+ :start_time => '2008-07-13 10:56', :end_time => '2008-07-14 10:56')
+
+ entry.save!
+ assert_equal 1, entry.hours
+ end
+
+ {['10:56', '11:56'] => 1, ['10:56', '11:26'] => 0.5,
+ ['10:56', '10:57'] => 0.0167,
+ ['2008-07-13 23:50', '2008-07-14 00:20'] => 0.5}.each do |range, hours|
+
+ define_method "test_hours_calculated_#{range[0]}_to_#{range[1]}" do
+
+ #add default day if not specified
+ range = range.map {|time| time['-'] ? time : '2008-07-13 ' + time}
+
+ entry = TimeEntry.new successful_params.merge(:hours => nil,
+ :start_time => range[0], :end_time => range[1])
+ entry.save!
+ assert_in_delta hours, entry.hours, 0.0001
+ end
+ end
+
+ def assert_intersects(source, dest)
+ intersecting = time_entries(source).find_intersecting_entries
+
+ assert !intersecting.empty?,
+ "there should be intersecting entries for #{source.inspect}"
+
+ assert intersecting.map {|e| e.id}.
+ include?(time_entries(dest).id),
+ "#{source.inspect} should intersect with #{dest.inspect}"
+
+ intersecting
+ end
+
+ def test_find_intersecting_entries_for_incomplete
+ assert_intersects(:time_entry_in_progress, :intersecting_time_entry)
+ end
+
+ def test_find_intersecting_entries_for_complete_doesnt_find_itself
+ intersecting = assert_intersects(:intersecting_time_entry,
+ :time_entry_in_progress)
+
+ assert !intersecting.map {|e| e.id}.include?(
+ time_entries(:intersecting_time_entry)), 'time entry\'s ' +
+ 'intersecting entries shouldn\'t include itself'
+ end
+
+ def test_find_intersecting_entries_for_big
+ assert_intersects(:big_intersecting_time_entry, :time_entry_in_progress)
+ assert_intersects(:big_intersecting_time_entry, :intersecting_time_entry)
+ end
end
Index: vendor/plugins/test_utils/MIT-LICENSE
===================================================================
--- vendor/plugins/test_utils/MIT-LICENSE (revision 0)
+++ vendor/plugins/test_utils/MIT-LICENSE (revision 0)
@@ -0,0 +1,21 @@
+Copyright (C) 2008 Texuna Technologies
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
Index: vendor/plugins/test_utils/tasks/test_utils.rake
===================================================================
--- vendor/plugins/test_utils/tasks/test_utils.rake (revision 0)
+++ vendor/plugins/test_utils/tasks/test_utils.rake (revision 0)
@@ -0,0 +1,66 @@
+##
+# based on http://nubyonrails.com/articles/foscon-and-living-dangerously-with-rake
+# Run a single test (or group of tests started with given string) in Rails.
+#
+# rake blogs-list (or f_blogs-list)
+# => Runs test_list for BlogsController (functional test; use f_blogs-list to force it if unit test found)
+#
+# rake blog-create (or u_blog-create)
+# => Runs test_create for BlogTest (unit test; use u_blog-create to force it if functional test found))
+
+#test file will be matched in the order of this array items
+TEST_TYPES = [
+ ['u_', "unit/[file_name]_test.rb"],
+ ['f_', "functional/[file_name]_controller_test.rb"],
+ ['i_', "integration/[file_name]_test.rb"],
+ ['l_', "long/[file_name]_test.rb"],
+]
+
+rule "" do |t|
+ all_flags = TEST_TYPES.map { |item| item[0] }
+ if Regexp.new("(#{all_flags.join '|'}|)(.*)\\-([^.]+)$").match(t.name)
+ flag = $1
+ file_name = $2
+ test_name = $3
+
+ path_getter = lambda { |type_info| type_info[1].gsub '[file_name]', file_name}
+ file_path = nil
+ TEST_TYPES.each do |type_info|
+ my_file_path = path_getter.call(type_info)
+ if flag == type_info[0]
+ type_info[1].match /((.+)\/)/
+ type = $2
+ puts "forced #{type} test"
+ file_path = my_file_path
+ break
+ end
+ end
+
+ if file_path && !File.exist?("test/#{file_path}")
+ raise "No file found for #{file_path}"
+ end
+
+ if !file_path
+ TEST_TYPES.each do |type_info|
+ my_file_path = path_getter.call(type_info)
+ if File.exist? "test/#{my_file_path}"
+ puts "found #{my_file_path}"
+ file_path = my_file_path
+ break
+ end
+ end
+ end
+
+ if !file_path
+ raise "No file found for #{file_name}"
+ end
+
+ begin
+ sh "ruby -Ilib:test test/#{file_path} -n /^test_#{test_name}/"
+ rescue Exception => e
+ #no logger here, oops!
+ #log.debug "error executing tests: #{e.inspect}"
+ puts "error executing tests: #{e.inspect}"
+ end
+ end
+end
|