Project

General

Profile

Patch #2268 » total_estimated_hour_2_6_4_edited.diff

Muhammad Azmi Farih, 2015-05-04 09:58

View differences:

app/controllers/issues_controller.rb (working copy)
81 81
                              :order => sort_clause,
82 82
                              :offset => @offset,
83 83
                              :limit => @limit)
84

  
85
      @all_issues = @query.issues(:include => [:status, :project, :assigned_to, :tracker, :priority, :category, :fixed_version])
86
      
84 87
      @issue_count_by_group = @query.issue_count_by_group
88
      @issue_sum_by_group = @query.issue_sum_by_group
89
      @issue_progress_by_group = @query.issue_progress_by_group
85 90

  
86 91
      respond_to do |format|
87 92
        format.html { render :template => 'issues/index', :layout => !request.xhr? }
......
148 153
    call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue })
149 154
    @issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads]))
150 155
    if @issue.save
156
      @issue.update_estimated_done
151 157
      call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
152 158
      respond_to do |format|
153 159
        format.html {
......
195 201
    end
196 202

  
197 203
    if saved
204
      @issue.update_estimated_done
198 205
      render_attachment_warning_if_needed(@issue)
199 206
      flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
200 207

  
app/helpers/issues_helper.rb (working copy)
427 427
      end
428 428
    end
429 429
  end
430

  
431
  def estimated_done(issues)
432
    issues.map(&:estimated_done).reject{|x|x.nil?}.sum.round(2)
433
  end
434

  
435
  def estimated_hours(issues)
436
    issues.map(&:estimated_hours).reject{|x| x.nil?}.sum
437
  end
438

  
439
  def estimated_done_percentage(issues)
440
    (100 * estimated_done(issues) / estimated_hours(issues)).round(2)
441
  end
430 442
end
app/helpers/queries_helper.rb (working copy)
103 103
      issue.description? ? content_tag('div', textilizable(issue, :description), :class => "wiki") : ''
104 104
    when :done_ratio
105 105
      progress_bar(value, :width => '80px')
106
    when :estimated_done
107
      sprintf "%.2f", value
106 108
    when :relations
107 109
      other = value.other_issue(issue)
108 110
      content_tag('span',
app/models/issue.rb (working copy)
356 356
    write_attribute :estimated_hours, (h.is_a?(String) ? h.to_hours : h)
357 357
  end
358 358

  
359
  def estimated_done=(h)
360
    write_attribute :estimated_done, (h.is_a?(String) ? h.to_hours : h)
361
  end
362
  
359 363
  safe_attributes 'project_id',
360 364
    :if => lambda {|issue, user|
361 365
      if issue.new_record?
......
569 573
  private :workflow_rule_by_attribute
570 574

  
571 575
  def done_ratio
572
    if Issue.use_status_for_done_ratio? && status && status.default_done_ratio
576
    if Issue.use_status_for_done_ratio? && status && status.default_done_ratio && self.leaves && self.leaves.count == 0
573 577
      status.default_done_ratio
574 578
    else
575 579
      read_attribute(:done_ratio)
......
651 655
  # Set the done_ratio using the status if that setting is set.  This will keep the done_ratios
652 656
  # even if the user turns off the setting later
653 657
  def update_done_ratio_from_issue_status
654
    if Issue.use_status_for_done_ratio? && status && status.default_done_ratio
658
    if Issue.use_status_for_done_ratio? && status && status.default_done_ratio && self.leaves && self.leaves.count == 0
655 659
      self.done_ratio = status.default_done_ratio
656 660
    end
657 661
  end
......
770 774
    !relations_to.detect {|ir| ir.relation_type == 'blocks' && !ir.issue_from.closed?}.nil?
771 775
  end
772 776

  
777
  def update_estimated_done
778
     if children.count < 1
779
       x1 = Issue.find_by_id(id).done_ratio.to_f
780
       x2 = Issue.find_by_id(id).estimated_hours.to_f
781
       r = ((x1 * x2)/100).round(2)
782
       Issue.update(id, :estimated_done => r)
783
     end
784
  end
785
 
773 786
  # Returns an array of statuses that user is able to apply
774 787
  def new_statuses_allowed_to(user=User.current, include_default=false)
775 788
    if new_record? && @copied_from
......
1406 1419
          if average == 0
1407 1420
            average = 1
1408 1421
          end
1409
          done = p.leaves.joins(:status).
1410
            sum("COALESCE(CASE WHEN estimated_hours > 0 THEN estimated_hours ELSE NULL END, #{average}) " +
1411
                "* (CASE WHEN is_closed = #{connection.quoted_true} THEN 100 ELSE COALESCE(done_ratio, 0) END)").to_f
1412
          progress = done / (average * leaves_count)
1413
          p.done_ratio = progress.round
1414 1422
        end
1415 1423
      end
1416 1424

  
1417 1425
      # estimate = sum of leaves estimates
1418 1426
      p.estimated_hours = p.leaves.sum(:estimated_hours).to_f
1427
      p.estimated_done = p.leaves.sum(:estimated_done).to_f
1428
	  
1429
      if (p.estimated_hours > 0)
1430
        p.done_ratio = (100 * p.estimated_done / p.estimated_hours).to_f.round(2)
1431
      end
1432
	  
1419 1433
      p.estimated_hours = nil if p.estimated_hours == 0.0
1434
      p.estimated_done = nil if p.estimated_done == 0.0
1420 1435

  
1421 1436
      # ancestors will be recursively updated
1422 1437
      p.save(:validate => false)
......
1611 1626
                                              group by s.id, s.is_closed, j.id")
1612 1627
  end
1613 1628
end
1629

  
1630

  
app/models/issue_query.rb (working copy)
38 38
    QueryColumn.new(:done_ratio, :sortable => "#{Issue.table_name}.done_ratio", :groupable => true),
39 39
    QueryColumn.new(:created_on, :sortable => "#{Issue.table_name}.created_on", :default_order => 'desc'),
40 40
    QueryColumn.new(:closed_on, :sortable => "#{Issue.table_name}.closed_on", :default_order => 'desc'),
41
    QueryColumn.new(:estimated_done, :sortable => "#{Issue.table_name}.estimated_done", :caption => :field_estimated_done),
41 42
    QueryColumn.new(:relations, :caption => :label_related_issues),
42 43
    QueryColumn.new(:description, :inline => false)
43 44
  ]
......
214 215
    add_available_filter "due_date", :type => :date
215 216
    add_available_filter "estimated_hours", :type => :float
216 217
    add_available_filter "done_ratio", :type => :integer
218
    add_available_filter "estimated_done", :type => :float
217 219

  
218 220
    if User.current.allowed_to?(:set_issues_private, nil, :global => true) ||
219 221
      User.current.allowed_to?(:set_own_issues_private, nil, :global => true)
......
293 295
  rescue ::ActiveRecord::StatementInvalid => e
294 296
    raise StatementInvalid.new(e.message)
295 297
  end
298
  
299
  # Returns sum of all the issue's estimated_hours 
300
  def issue_sum
301
    Issue.visible.sum(:estimated_hours, :include => [:status, :project], :conditions => statement)
302
  rescue ::ActiveRecord::StatementInvalid => e
303
    raise StatementInvalid.new(e.message)  
304
  end  
296 305

  
306
  # Returns sum of all the issue's estimated_done 
307
  def issue_sum_in_progress
308
    Issue.visible.sum(:estimated_done, :include => [:status, :project], :conditions => statement)
309
  rescue ::ActiveRecord::StatementInvalid => e
310
    raise StatementInvalid.new(e.message)  
311
  end
312

  
297 313
  # Returns the issue count by group or nil if query is not grouped
298 314
  def issue_count_by_group
299 315
    r = nil
......
319 335
    raise StatementInvalid.new(e.message)
320 336
  end
321 337

  
338
  # Returns  sum of the issue's estimated_hours by group or nil if query is not grouped
339
  def issue_sum_by_group
340
    r = nil
341
    if grouped?
342
      begin
343
        r = Issue.visible.sum(:estimated_hours, :joins => joins_for_order_statement(group_by_statement), :group => group_by_statement, :include => [:status, :project], :conditions => statement)
344
      rescue ActiveRecord::RecordNotFound
345
        r= {r => issue_sum}
346
      end
347
       
348
      c = group_by_column
349
      if c.is_a?(QueryCustomFieldColumn)
350
        r = r.keys.inject({}) {|h,k| h[c.custom_field.cast_value(k)] = r[k]; h}
351
      end
352
    end
353
    r
354
  rescue ::ActiveRecord::StatementInvalid => e
355
    raise StatementInvalid.new(e.message)
356
  end
357
  
358
  # Returns  sum of the issue's estimated_done by group or nil if query is not grouped
359
  def issue_progress_by_group
360
    r = nil
361
    if grouped?
362
      begin
363
        r = Issue.visible.sum(:estimated_done, :joins => joins_for_order_statement(group_by_statement), :group => group_by_statement, :include => [:status, :project], :conditions => statement)
364
      rescue ActiveRecord::RecordNotFound
365
        r= {r => issue_sum_by_group}
366
      end
367
       
368
      c = group_by_column
369
      if c.is_a?(QueryCustomFieldColumn)
370
        r = r.keys.inject({}) {|h, k| h[c.custom_field.cast_value(k)] = r[k]; h}
371
      end
372
    end
373
    r
374
  rescue ::ActiveRecord::StatementInvalid => e
375
    raise StatementInvalid.new(e.message)
376
  end
377
   
322 378
  # Returns the issues
323 379
  # Valid options are :order, :offset, :limit, :include, :conditions
324 380
  def issues(options={})
app/views/issues/_list.html.erb (working copy)
14 14
      <% end %>
15 15
    </tr>
16 16
  </thead>
17
  <% previous_group, first = false, true %>
17
  <% previous_group = false %>
18 18
  <tbody>
19 19
  <% issue_list(issues) do |issue, level| -%>
20
  <% if @query.grouped? && ((group = @query.group_by_column.value(issue)) != previous_group || first) %>
20
  <% if @query.grouped? && (group = @query.group_by_column.value(issue)) != previous_group %>
21 21
    <% reset_cycle %>
22 22
    <tr class="group open">
23 23
      <td colspan="<%= query.inline_columns.size + 2 %>">
24 24
        <span class="expander" onclick="toggleRowGroup(this);">&nbsp;</span>
25
        <%= (group.blank? && group != false) ? l(:label_none) : column_content(@query.group_by_column, issue) %> <span class="count"><%= @issue_count_by_group[group] %></span>
26
        <%= link_to_function("#{l(:button_collapse_all)}/#{l(:button_expand_all)}",
27
                             "toggleAllRowGroups(this)", :class => 'toggle-all') %>
25
        <%= group.blank? ? l(:label_none) : column_content(@query.group_by_column, issue) %> <span class="count"><%= @issue_count_by_group[group] %>, Est Done:  <%= (@issue_progress_by_group[group] * 100).round / 100.0 %>
26
        <% if @issue_sum_by_group[group] > 0 %>
27
        (<%= (100 * @issue_progress_by_group[group] / @issue_sum_by_group[group]).round(2) %>%),
28
        <% else %>
29
        (0.0%),
30
        <% end %>
31
        <%= l(:label_total) %>: <%= @issue_sum_by_group[group] %>)</span>
32
        <%= link_to_function("#{l(:button_collapse_all)}/#{l(:button_expand_all)}", "toggleAllRowGroups(this)", :class => 'toggle-all') %>
28 33
      </td>
29 34
    </tr>
30
    <% previous_group, first = group, false %>
35
    <% previous_group = group %>
31 36
  <% end %>
32 37
  <tr id="issue-<%= issue.id %>" class="hascontextmenu <%= cycle('odd', 'even') %> <%= issue.css_classes %> <%= level > 0 ? "idnt idnt-#{level}" : nil %>">
33 38
    <td class="checkbox hide-when-print"><%= check_box_tag("ids[]", issue.id, false, :id => nil) %></td>
......
45 50
</table>
46 51
</div>
47 52
<% end -%>
53
 <p align="right">
54
     Current page: <b><%=estimated_hours(issues) %></b>
55
     Est Done: <b><%= estimated_done(@all_issues) %> <% if estimated_hours(@all_issues) > 0 %>
56
	 	 (<%= "#{estimated_done_percentage(@all_issues)}%" %>)
57
		 <% else %>(0.0%)
58
		 <% end %></b>
59
     <%= l(:label_total) %>: <b><%=@query.issue_sum %></b>
60
 </p>
app/views/issues/show.html.erb (working copy)
60 60
      rows.right l(:field_estimated_hours), l_hours(@issue.estimated_hours), :class => 'estimated-hours'
61 61
    end
62 62
  end
63
  unless @issue.disabled_core_fields.include?('estimated_hours')
64
     rows.right "Estimated done", l_hours(@issue.estimated_done), :class => 'estimated-hours'
65
  end
63 66
  if User.current.allowed_to?(:view_time_entries, @project)
64 67
    rows.right l(:label_spent_time), (@issue.total_spent_hours > 0 ? link_to(l_hours(@issue.total_spent_hours), issue_time_entries_path(@issue)) : "-"), :class => 'spent-time'
65 68
  end
config/locales/en.yml (working copy)
300 300
  field_assignable: Issues can be assigned to this role
301 301
  field_redirect_existing_links: Redirect existing links
302 302
  field_estimated_hours: Estimated time
303
  field_estimated_done: Estimated done
303 304
  field_column_names: Columns
304 305
  field_time_entries: Log time
305 306
  field_time_zone: Time zone
(5-5/5)