Index: app/helpers/versions_helper.rb =================================================================== --- app/helpers/versions_helper.rb (revision 1900) +++ app/helpers/versions_helper.rb (working copy) @@ -26,12 +26,21 @@ h = Hash.new {|k,v| k[v] = [0, 0]} begin # Total issue count - Issue.count(:group => criteria, - :conditions => ["#{Issue.table_name}.fixed_version_id = ?", version.id]).each {|c,s| h[c][0] = s} - # Open issues count - Issue.count(:group => criteria, - :include => :status, - :conditions => ["#{Issue.table_name}.fixed_version_id = ? AND #{IssueStatus.table_name}.is_closed = ?", version.id, false]).each {|c,s| h[c][1] = s} + if version.children.count > 0 + Issue.count(:group => criteria, + :conditions => ["#{Issue.table_name}.fixed_version_id = ? OR #{Issue.table_name}.fixed_version_id IN (#{version.children.collect{|p| p.id}.join(',')})", version.id]).each {|c,s| h[c][0] = s} + # Open issues count + Issue.count(:group => criteria, + :include => :status, + :conditions => ["(#{Issue.table_name}.fixed_version_id = ? OR #{Issue.table_name}.fixed_version_id IN (#{version.children.collect{|p| p.id}.join(',')})) AND #{IssueStatus.table_name}.is_closed = ?", version.id, false]).each {|c,s| h[c][1] = s} + else + Issue.count(:group => criteria, + :conditions => ["#{Issue.table_name}.fixed_version_id = ?", version.id]).each {|c,s| h[c][0] = s} + # Open issues count + Issue.count(:group => criteria, + :include => :status, + :conditions => ["#{Issue.table_name}.fixed_version_id = ? AND #{IssueStatus.table_name}.is_closed = ?", version.id, false]).each {|c,s| h[c][1] = s} + end rescue ActiveRecord::RecordNotFound # When grouping by an association, Rails throws this exception if there's no result (bug) end Index: app/models/version.rb =================================================================== --- app/models/version.rb (revision 1900) +++ app/models/version.rb (working copy) @@ -18,7 +18,9 @@ class Version < ActiveRecord::Base before_destroy :check_integrity belongs_to :project - has_many :fixed_issues, :class_name => 'Issue', :foreign_key => 'fixed_version_id' + belongs_to :parent, :class_name => "Version", :foreign_key => "parent_version_id" + has_many :children, :class_name => "Version", :foreign_key => "parent_version_id" + has_many :fixed_issues, :class_name => 'Issue', :finder_sql => 'select distinct i.* from issues i,versions v WHERE i.fixed_version_id = #{id} OR (i.fixed_version_id = v.id AND v.parent_version_id = #{id})', :counter_sql => 'select count(distinct i.id) from issues i,versions v WHERE i.fixed_version_id = #{id} OR (i.fixed_version_id = v.id AND v.parent_version_id = #{id})' has_many :attachments, :as => :container, :dependent => :destroy validates_presence_of :name @@ -36,12 +38,18 @@ # Returns the total estimated time for this version def estimated_hours - @estimated_hours ||= fixed_issues.sum(:estimated_hours).to_f + hours = 0 + fixed_issues.each {|i| hours += i.estimated_hours.to_f} + @estimated_hours ||= hours end # Returns the total reported time for this version def spent_hours - @spent_hours ||= TimeEntry.sum(:hours, :include => :issue, :conditions => ["#{Issue.table_name}.fixed_version_id = ?", id]).to_f + if self.children.count > 0 + @spent_hours ||= TimeEntry.sum(:hours, :include => :issue, :conditions => ["#{Issue.table_name}.fixed_version_id = ? OR #{Issue.table_name}.fixed_version_id IN (#{self.children.collect{|p| p.id}.join(',')})", id]).to_f + else + @spent_hours ||= TimeEntry.sum(:hours, :include => :issue, :conditions => ["#{Issue.table_name}.fixed_version_id = ?", id]).to_f + end end # Returns true if the version is completed: due date reached and no open issues @@ -54,8 +62,10 @@ 0 elsif open_issues_count == 0 100 - else - (closed_issues_count * 100 + Issue.sum('done_ratio', :include => 'status', :conditions => ["fixed_version_id = ? AND is_closed = ?", id, false]).to_f) / fixed_issues.count + elsif self.children.count > 0 + (closed_issues_count * 100 + Issue.sum('done_ratio', :include => 'status', :conditions => ["(fixed_version_id = ? OR fixed_version_id IN (#{self.children.collect{|p| p.id}.join(',')})) AND is_closed = ?", id, false]).to_f) / fixed_issues.count + else + (closed_issues_count * 100 + Issue.sum('done_ratio', :include => 'status', :conditions => ["(fixed_version_id = ?) AND is_closed = ?", id, false]).to_f) / fixed_issues.count end end @@ -73,11 +83,19 @@ end def open_issues_count - @open_issues_count ||= Issue.count(:all, :conditions => ["fixed_version_id = ? AND is_closed = ?", self.id, false], :include => :status) + if self.children.count > 0 + @open_issues_count ||= Issue.count(:all, :conditions => ["(fixed_version_id = ? OR fixed_version_id IN (#{self.children.collect{|p| p.id}.join(',')})) AND is_closed = ?", self.id, false], :include => :status) + else + @open_issues_count ||= Issue.count(:all, :conditions => ["(fixed_version_id = ?) AND is_closed = ?", self.id, false], :include => :status) + end end def closed_issues_count - @closed_issues_count ||= Issue.count(:all, :conditions => ["fixed_version_id = ? AND is_closed = ?", self.id, true], :include => :status) + if self.children.count > 0 + @closed_issues_count ||= Issue.count(:all, :conditions => ["(fixed_version_id = ? OR fixed_version_id IN (#{self.children.collect{|p| p.id}.join(',')})) AND is_closed = ?", self.id, true], :include => :status) + else + @closed_issues_count ||= Issue.count(:all, :conditions => ["(fixed_version_id = ?) AND is_closed = ?", self.id, true], :include => :status) + end end def wiki_page Index: app/controllers/versions_controller.rb =================================================================== --- app/controllers/versions_controller.rb (revision 1900) +++ app/controllers/versions_controller.rb (working copy) @@ -23,6 +23,7 @@ end def edit + @versions = Version.find(:all, :conditions => ["id <> ? AND parent_version_id IS NULL", params[:id]]).collect{|p| [ p.name, p.id ] } if request.post? and @version.update_attributes(params[:version]) flash[:notice] = l(:notice_successful_update) redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project Index: app/controllers/projects_controller.rb =================================================================== --- app/controllers/projects_controller.rb (revision 1900) +++ app/controllers/projects_controller.rb (working copy) @@ -180,6 +180,7 @@ # Add a new version to @project def add_version @version = @project.versions.build(params[:version]) + @versions = Version.find(:all, :conditions => ["parent_version_id IS NULL"]).collect{|p| [ p.name, p.id ] } if request.post? and @version.save flash[:notice] = l(:notice_successful_create) redirect_to :action => 'settings', :tab => 'versions', :id => @project @@ -213,7 +214,7 @@ def roadmap @trackers = @project.trackers.find(:all, :conditions => ["is_in_roadmap=?", true]) retrieve_selected_tracker_ids(@trackers) - @versions = @project.versions.sort + @versions = (@project.versions.delete_if {|x| x.parent_version_id }).sort @versions = @versions.select {|v| !v.completed? } unless params[:completed] end Index: app/views/versions/show.rhtml =================================================================== --- app/views/versions/show.rhtml (revision 1900) +++ app/views/versions/show.rhtml (working copy) @@ -2,7 +2,9 @@ <%= link_to_if_authorized l(:button_edit), {:controller => 'versions', :action => 'edit', :id => @version}, :class => 'icon icon-edit' %> -

<%= h(@version.name) %>

+

<%= h(@version.name) %> <% if @version.parent_version_id %> +(<%= l(:label_child_of)%> <%= link_to @version.parent.name, :controller => 'versions', :action => 'show', :id => @version.parent.id %>) +<% end %>

<% if @version.estimated_hours > 0 || User.current.allowed_to?(:view_time_entries, @project) %> @@ -25,8 +27,22 @@
<%= render_issue_status_by(@version, params[:status_by]) if @version.fixed_issues.count > 0 %>
+ +<% if @version.children.count > 0 %> +
+
+ <%= l(:label_child_versions)%> +
    + <% @version.children.each do |v| %> +
  • <%= link_to v.name, :controller => 'versions', :action => 'show', :id => v.id %>
  • + <% end %> +
+
+<% end %> +
+
<%= render :partial => 'versions/overview', :locals => {:version => @version} %> <%= render(:partial => "wiki/content", :locals => {:content => @version.wiki_page.content}) if @version.wiki_page %> @@ -34,7 +50,7 @@ <% issues = @version.fixed_issues.find(:all, :include => [:status, :tracker], :order => "#{Tracker.table_name}.position, #{Issue.table_name}.id") %> -<% if issues.size > 0 %> +<% if issues && issues.size > 0 %>
Index: app/views/versions/_issue_counts.rhtml =================================================================== --- app/views/versions/_issue_counts.rhtml (revision 1900) +++ app/views/versions/_issue_counts.rhtml (working copy) @@ -15,12 +15,28 @@ <% counts.each do |count| %> - <%= link_to count[:group], {:controller => 'issues', - :action => 'index', - :project_id => version.project, - :set_filter => 1, - :fixed_version_id => version, - "#{criteria}_id" => count[:group]} %> + <% puts "BOO " + (version.fixed_issues.collect {|p| p.fixed_version_id} ).uniq.to_s %> + <%= link_to count[:group], {:controller => 'issues', + :action => 'index', + :project_id => version.project, + :set_filter => 1, + :fields => ["#{criteria}_id", "fixed_version_id"], + :operators => { :start_date => " ">t-", + :estimated_hours => "=", + :updated_on => ">t-", + :priority_id => "=", + :subject => "~", + :fixed_version_id => "=", + :category_id =>"=", + :tracker_id => "=", + :done_ratio =>"=", + :due_date => " "=", + :author_id => "=", + :status_id => "o" }, + :values => {:fixed_version_id => (version.fixed_issues.collect {|p| p.fixed_version_id} ).uniq, + "#{criteria}_id".to_sym => [count[:group]]} } %> <%= progress_bar((count[:closed].to_f / count[:total])*100, Index: app/views/versions/_overview.rhtml =================================================================== --- app/views/versions/_overview.rhtml (revision 1900) +++ app/views/versions/_overview.rhtml (working copy) @@ -9,11 +9,11 @@ <% if version.fixed_issues.count > 0 %> <%= progress_bar([version.closed_pourcent, version.completed_pourcent], :width => '40em', :legend => ('%0.0f%' % version.completed_pourcent)) %>

- <%= link_to(version.closed_issues_count, :controller => 'issues', :action => 'index', :project_id => version.project, :status_id => 'c', :fixed_version_id => version, :set_filter => 1) %> + <%= link_to(version.closed_issues_count, :controller => 'issues', :action => 'index', :project_id => version.project, :set_filter => 1, :fields => ["status_id", "fixed_version_id"], :operators => {:fixed_version_id => "=",:status_id => "c"}, :values => {:fixed_version_id => (version.fixed_issues.collect {|p| p.fixed_version_id} ).uniq, :status_id =>["1"]}) %> <%= lwr(:label_closed_issues, version.closed_issues_count) %> (<%= '%0.0f' % (version.closed_issues_count.to_f / version.fixed_issues.count * 100) %>%)   - <%= link_to(version.open_issues_count, :controller => 'issues', :action => 'index', :project_id => version.project, :status_id => 'o', :fixed_version_id => version, :set_filter => 1) %> + <%= link_to(version.open_issues_count, :controller => 'issues', :action => 'index', :project_id => version.project, :set_filter => 1, :fields => ["status_id", "fixed_version_id"], :operators => {:fixed_version_id => "=",:status_id => "o"}, :values => {:fixed_version_id => (version.fixed_issues.collect {|p| p.fixed_version_id} ).uniq, :status_id =>["1"]}) %> <%= lwr(:label_open_issues, version.open_issues_count)%> (<%= '%0.0f' % (version.open_issues_count.to_f / version.fixed_issues.count * 100) %>%)

Index: app/views/projects/roadmap.rhtml =================================================================== --- app/views/projects/roadmap.rhtml (revision 1900) +++ app/views/projects/roadmap.rhtml (working copy) @@ -5,17 +5,15 @@ <% else %>
<% @versions.each do |version| %> +
<%= tag 'a', :name => version.name %>

<%= link_to h(version.name), :controller => 'versions', :action => 'show', :id => version %>

<%= render :partial => 'versions/overview', :locals => {:version => version} %> <%= render(:partial => "wiki/content", :locals => {:content => version.wiki_page.content}) if version.wiki_page %> - <% issues = version.fixed_issues.find(:all, - :include => [:status, :tracker], - :conditions => ["tracker_id in (#{@selected_tracker_ids.join(',')})"], - :order => "#{Tracker.table_name}.position, #{Issue.table_name}.id") unless @selected_tracker_ids.empty? - issues ||= [] - %> + <% + issues = version.fixed_issues.delete_if {|i| !@selected_tracker_ids.include? i.tracker_id.to_s } + %> <% if issues.size > 0 %> <% end %> + <% if version.children.count > 0 %> +
+

<%= l(:label_child_versions) %>

+ <% version.children.each do |v| %> +
+ <%= tag 'a', :name => v.name %> +

<%= link_to h(v.name), :controller => 'versions', :action => 'show', :id => v %>

+ <%= render :partial => 'versions/overview', :locals => {:version => v} %> + <% + issues = v.fixed_issues.delete_if {|i| !@selected_tracker_ids.include? i.tracker_id.to_s } + if issues.size > 0 %> + + <% end %> +
+ <% end %> + +
+ <% end %> +
<% end %>
<% end %> Index: lang/en.yml =================================================================== --- lang/en.yml (revision 1900) +++ lang/en.yml (working copy) @@ -184,6 +184,7 @@ field_default_value: Default value field_comments_sorting: Display comments field_parent_title: Parent page +field_parent_version: Parent version setting_app_title: Application title setting_app_subtitle: Application subtitle @@ -429,6 +430,7 @@ label_wiki_edit_plural: Wiki edits label_wiki_page: Wiki page label_wiki_page_plural: Wiki pages +label_parent_version: Parent version label_index_by_title: Index by title label_index_by_date: Index by date label_current_version: Current version @@ -527,6 +529,8 @@ label_incoming_emails: Incoming emails label_generate_key: Generate a key label_issue_watchers: Watchers +label_child_versions: Child Versions +label_child_of: child of button_login: Login button_submit: Submit Index: db/migrate/099_add_version_dependency.rb =================================================================== --- db/migrate/099_add_version_dependency.rb (revision 0) +++ db/migrate/099_add_version_dependency.rb (revision 0) @@ -0,0 +1,9 @@ +class AddVersionDependency < ActiveRecord::Migration + def self.up + add_column :versions, :parent_version_id, :integer + end + + def self.down + remove_column :versions, :parent_version_id + end +end Index: public/stylesheets/application.css =================================================================== --- public/stylesheets/application.css (revision 1900) +++ public/stylesheets/application.css (working copy) @@ -209,6 +209,8 @@ div#roadmap .wiki h1:first-child { display: none; } div#roadmap .wiki h1 { font-size: 120%; } div#roadmap .wiki h2 { font-size: 110%; } +div#roadmap .top-level-version { padding: 8px; margin-bottom: 10px; border: 4px solid #e0e0e0;} +div#roadmap .child-version { margin-left: 20px;} div#version-summary { float:right; width:380px; margin-left: 16px; margin-bottom: 16px; background-color: #fff; } div#version-summary fieldset { margin-bottom: 1em; }