backlog 0.22.1 → 0.23.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/History.txt +28 -0
  2. data/README.txt +23 -2
  3. data/app/controllers/application.rb +15 -2
  4. data/app/controllers/periods_controller.rb +3 -0
  5. data/app/controllers/tasks_controller.rb +2 -2
  6. data/app/controllers/user_controller.rb +2 -2
  7. data/app/controllers/work_accounts_controller.rb +14 -0
  8. data/app/controllers/works_controller.rb +23 -3
  9. data/app/models/report_filter.rb +7 -3
  10. data/app/models/task.rb +11 -10
  11. data/app/models/work.rb +3 -2
  12. data/app/views/customers/_name_list.rhtml +5 -0
  13. data/app/views/periods/_show_active.rhtml +1 -0
  14. data/app/views/periods/_title.rhtml +8 -1
  15. data/app/views/periods/edit.rhtml +1 -1
  16. data/app/views/periods/order.rjs +0 -1
  17. data/app/views/tasks/_task.rhtml +6 -5
  18. data/app/views/user/login.rhtml +1 -1
  19. data/app/views/work_accounts/_title.rhtml +4 -0
  20. data/app/views/work_accounts/edit.rhtml +1 -3
  21. data/app/views/work_accounts/list.rhtml +9 -3
  22. data/app/views/work_accounts/show.rhtml +47 -30
  23. data/app/views/works/_form.rhtml +6 -0
  24. data/app/views/works/_row.rhtml +16 -9
  25. data/app/views/works/_row_field.rhtml +2 -2
  26. data/app/views/works/daily_work_sheet.rhtml +37 -7
  27. data/app/views/works/daily_work_sheet_old.rhtml +93 -0
  28. data/app/views/works/list.rhtml +11 -5
  29. data/app/views/works/update_row.rjs +5 -2
  30. data/config/environment.rb +19 -3
  31. data/lang/en.yaml +5 -0
  32. data/lang/no.yaml +5 -0
  33. data/lib/class_table_inheritance.rb +10 -3
  34. data/no_test.rb~ +6 -0
  35. data/test/client/login.rb +25 -0
  36. data/test/client/login.rb~ +33 -0
  37. data/test/client/login_test.rb +64 -0
  38. data/test/client/setup.rb +24 -0
  39. data/test/functional/works_controller_test.rb +3 -2
  40. data/test/unit/party_test.rb +10 -0
  41. data/test/unit/user_test.rb +15 -17
  42. metadata +12 -2
@@ -1,3 +1,31 @@
1
+ == 0.23.0 2008-02-25
2
+
3
+ === Features
4
+
5
+ * Improved input for daily_work_sheet
6
+ * Allowed override for development and test database with new config file
7
+ * Listed details for works without backlog in "Show Work Acoount" view.
8
+ * Added "List Works" view for Work Account.
9
+ * Added filtering of tasks grabbed by other users in the sprint view.
10
+
11
+ === Fixes
12
+
13
+ * Improved work flow ("Back" links) several places.
14
+ * Fixed bug in layout when you had started tasks without a sprint.
15
+ * Some speedup of grabbing tasks.
16
+ * Fixed database corruption when trying to register users with duplicate login or email.
17
+ * Fixed display of welcome message after following link from "Forgot password" email.
18
+ * Fixed bug in daily work sheet when start time was not set for a work record.
19
+ * Starting work on a task grabs it.
20
+ * Changed to show "complete" check marks in sprint view for tasks that track time, but are not started.
21
+ * Display blank field for hours in daily work sheet if it has not been filled in yet.
22
+ * Always display row for new work record entry in daily work sheet.
23
+ Earlier, a row for new input was only shown if the previous row was ended.
24
+ * Removed message about missing database settings in main config file on startup since it is optional and deprecated.
25
+ * Fixed error in class table inheritance library. Validations for the subclass were not called.
26
+ * Added some Firewatir tests. EXPERIMENTAL!
27
+ * Added explenation on how to set database connection parameters.
28
+
1
29
  == 0.22.1 2008-02-15
2
30
 
3
31
  === Features
data/README.txt CHANGED
@@ -57,19 +57,40 @@ If you have concrete needs, and are willing to beta test the integration, please
57
57
 
58
58
  === Configuration
59
59
 
60
- You can set configuration parameters for backlog using the /etc/backlog.conf file on unix and "c:\documents and settings\all\backlog.conf" on windows.
60
+ ==== Setting port
61
+
62
+ You can set configuration parameters for backlog using the /etc/backlog.conf file on unix
63
+ and "c:\documents and settings\all\backlog.conf" on windows.
61
64
  The format is YAML.
62
65
 
63
66
  Example:
64
67
 
65
68
  port: 3000
66
69
 
67
- database:
70
+
71
+ ==== Setting database connection parameters
72
+
73
+ You can set database connnection parameters using the /etc/backlog/config/database.yml file on unix
74
+ and "c:\documents and settings\all\backlog\config\database.yml" on windows.
75
+ The format is YAML.
76
+
77
+ Example:
78
+
79
+ development:
80
+ adapter: postgresql
81
+ database: backlog_development
82
+ username: root
83
+ password: verySecret42
84
+ host: localhost
85
+
86
+ production:
68
87
  adapter: postgresql
69
88
  database: backlog_production
70
89
  username: root
71
90
  password: verySecret42
72
91
  host: localhost
92
+
93
+ See the Rails documentation (http://rubyonrails.org) for more documentation on the databse connection seetings.
73
94
 
74
95
  === Charts
75
96
 
@@ -12,6 +12,7 @@ class ApplicationController < ActionController::Base
12
12
  helper :user
13
13
  before_filter :store_detour_from_params
14
14
  before_filter :authenticate_user
15
+ before_filter :store_cookies_from_params
15
16
  before_filter :populate_layout
16
17
 
17
18
  def initialize
@@ -124,7 +125,20 @@ class ApplicationController < ActionController::Base
124
125
  end
125
126
  detour
126
127
  end
127
- private :pop_detour
128
+
129
+
130
+
131
+ def store_cookies_from_params
132
+ if params[:cookies]
133
+ params[:cookies].each_pair do |key, value|
134
+ logger.info "Storing cookie #{key.inspect}=#{value.inspect}"
135
+ #cookies[key] = {:value => value, :expires => 1.year.from_now}
136
+ session[key] = value
137
+ logger.info "Stored cookie #{key.inspect}=#{session[key].inspect}"
138
+ end
139
+ end
140
+ end
141
+
128
142
 
129
143
  def populate_shortcuts
130
144
  @shortcuts = [
@@ -182,7 +196,6 @@ class ApplicationController < ActionController::Base
182
196
  end
183
197
  end
184
198
 
185
- private
186
199
 
187
200
  def user_id
188
201
  session[:user_id]
@@ -36,6 +36,9 @@ class PeriodsController < ApplicationController
36
36
  if @selected_task.nil?
37
37
  @selected_task = @period.open_tasks.first
38
38
  end
39
+ # TODO (uwe): Switch to use cookie since the same behavior should work on next session.
40
+ # TODO (uwe): Problem: Cannot read cookie in the same request it was written...
41
+ @show_only_grabbed_tasks = session['show_only_grabbed_tasks'] == 'true'
39
42
  load_tasks(@period)
40
43
  end
41
44
 
@@ -1,7 +1,7 @@
1
1
  class TasksController < ApplicationController
2
- skip_before_filter :populate_layout, :except => [:edit, :list_started, :move_down, :move_to_bottom, :move_to_top, :move_up, :new, :specify]
2
+ skip_before_filter :populate_layout, :except => [:edit, :grab, :list_started, :move_down, :move_to_bottom, :move_to_top, :move_up, :new, :specify]
3
3
 
4
- verify :method => :post, :except => [ :new, :show, :edit, :list_started, :move_to_next_period, :notes],
4
+ verify :method => :post, :except => [ :new, :show, :edit, :grab, :list_started, :move_to_next_period, :notes],
5
5
  :redirect_to => { :controller => 'backlogs' }
6
6
 
7
7
  def list_started
@@ -24,9 +24,9 @@ class UserController < ApplicationController
24
24
  def signup
25
25
  return if generate_blank_form
26
26
  params['user'].delete('form')
27
- @user = User.new(params['user'])
28
27
  begin
29
28
  User.transaction do
29
+ @user = User.new(params['user'])
30
30
  @user.password_needs_confirmation = true
31
31
  if @user.save
32
32
  key = @user.generate_security_token
@@ -88,7 +88,7 @@ class UserController < ApplicationController
88
88
  url = url_for(:action => 'change_password')
89
89
  url += "?user[id]=#{user.id}&key=#{key}"
90
90
  UserNotify.deliver_forgot_password(user, url)
91
- flash['notice'] = "Instructions on resetting your password have been emailed to #{CGI.escapeHTML(params['user']['email'])}."
91
+ flash[:notice] = "Instructions on resetting your password have been emailed to #{CGI.escapeHTML(params['user']['email'])}."
92
92
  unless authenticated_user?
93
93
  redirect_to :action => 'login'
94
94
  return
@@ -19,6 +19,7 @@ class WorkAccountsController < ApplicationController
19
19
  @task_totals = works.to_summarized_hash {|work| [work.task, work.hours]}
20
20
  @task_totals_per_backlog = @task_totals.to_a.to_grouped_hash {|task, hours| [task && task.backlog, [task, hours]]}
21
21
  @total_hours = works.inject(BigDecimal('0')) {|total, work| total += work.hours}
22
+ @works_without_backlog = works.select {|w| w.task_id.nil?}
22
23
  end
23
24
 
24
25
  def new
@@ -53,4 +54,17 @@ class WorkAccountsController < ApplicationController
53
54
  WorkAccount.find(params[:id]).destroy
54
55
  redirect_to :action => 'list'
55
56
  end
57
+
58
+ def works
59
+ work_account = WorkAccount.find(params[:id])
60
+ @report_filter = ReportFilter.new(params[:report_filter])
61
+ @report_filter.title = "#{l :hours} for #{work_account.name} #{@report_filter.start_on && @report_filter.start_on.strftime('%Y-%m-%d - ')}#{@report_filter.end_on && @report_filter.end_on.strftime('%Y-%m-%d')}"
62
+ @works = Work.paginate :conditions => ["completed_at BETWEEN ? AND ? AND work_account_id = ?", @report_filter.start_on, @report_filter.end_on, work_account.id], :page => params[:page], :per_page => @report_filter.page_size
63
+ if params[:export] == 'excel'
64
+ render :template => '/works/list_excel', :layout => false
65
+ else
66
+ render :template => '/works/list'
67
+ end
68
+ end
69
+
56
70
  end
@@ -5,7 +5,7 @@ class WorksController < ApplicationController
5
5
  in_place_edit_for :work, :completed_at_time
6
6
  skip_before_filter :populate_layout, :except => [:create, :destroy, :edit, :index, :list, :new, :show, :update, :daily_work_sheet, :weekly_work_sheet_by_work_account]
7
7
  auto_complete_for :work, :description
8
-
8
+
9
9
  def index
10
10
  list
11
11
  render :action => 'list'
@@ -49,6 +49,14 @@ class WorksController < ApplicationController
49
49
  account = WorkAccount.find_by_name(account_name)
50
50
  params[:work][:work_account_id] = account.id
51
51
  end
52
+ customer_name = params[:work].delete(:customer_name)
53
+ if customer_name && customer_name.size > 0
54
+ customer = Customer.find_by_name(customer_name)
55
+ if (customer.nil? )
56
+ customer = Customer.create!(:name => customer_name)
57
+ end
58
+ params[:work][:customer_id] = customer.id
59
+ end
52
60
  end
53
61
  convert_hours_param
54
62
  convert_start_time_param
@@ -95,8 +103,11 @@ class WorksController < ApplicationController
95
103
  def update_row
96
104
  update_work
97
105
  flash.discard
98
- @field = params[:field] || 'started_at_time'
106
+ @next_field = params[:next_field] || 'work_account_name'
99
107
  @work_accounts = WorkAccount.find(:all, :order => :name)
108
+ @customers = Customer.find(:all, :order => :name)
109
+ works = Work.find_work_for_day((@work.started_at || @work.completed_at).to_date)
110
+ @day_total = works.inject(BigDecimal('0')){|total,work|total+=work.hours}
100
111
  end
101
112
 
102
113
  def update_time
@@ -107,7 +118,6 @@ class WorksController < ApplicationController
107
118
 
108
119
  def update_work
109
120
  @work = Work.find(params[:id])
110
- convert_start_time_param
111
121
  convert_hours_param
112
122
  if @work.update_attributes(params[:work])
113
123
  flash[:notice] = 'Work was successfully updated.'
@@ -137,6 +147,7 @@ class WorksController < ApplicationController
137
147
  @date = (params[:id] && Date.parse(params[:id])) || Date.today
138
148
  @periods = []
139
149
  @works = Work.find_work_for_day @date
150
+ @customers = Customer.find(:all, :order => :name)
140
151
  @started_works = Task.find_started
141
152
  @work = Work.new(:started_at => Time.now, :completed_at => Time.now)
142
153
  @work_accounts = WorkAccount.find(:all, :order => :name)
@@ -182,6 +193,15 @@ class WorksController < ApplicationController
182
193
  render :partial => '/work_accounts/name_list'
183
194
  end
184
195
 
196
+ def auto_complete_for_work_customer_name
197
+ @customers = Customer.find(:all,
198
+ :conditions => [ 'LOWER(name) LIKE ?',
199
+ '%' + params[:work][:customer_name].downcase + '%' ],
200
+ :order => 'name ASC',
201
+ :limit => 16)
202
+ render :partial => '/customers/name_list'
203
+ end
204
+
185
205
  def auto_complete_for_work_backlog_name
186
206
  @backlogs = Backlog.find(:all,
187
207
  :conditions => [ 'LOWER(name) LIKE ?',
@@ -5,17 +5,21 @@ class ReportFilter
5
5
  attr_reader :page_size
6
6
 
7
7
  def initialize(attributes)
8
+ @start_on = Date.civil(2007, 01, 01)
9
+ @end_on = Date.today
10
+ @page_size = 1000
11
+
8
12
  if attributes
9
13
  attributes = attributes.clone
10
14
 
11
15
  start_on_param = attributes.delete(:start_on)
12
- @start_on = start_on_param && start_on_param.size > 0 ? Date.parse(start_on_param) : Date.civil(2007, 01, 01)
16
+ @start_on = Date.parse(start_on_param) if start_on_param && start_on_param.size > 0
13
17
 
14
18
  end_on_param = attributes.delete(:end_on)
15
- @end_on = end_on_param && end_on_param.size > 0 ? Date.parse(end_on_param) : Date.today
19
+ @end_on = Date.parse(end_on_param) if end_on_param && end_on_param.size > 0
16
20
 
17
21
  page_size_param = attributes.delete(:page_size)
18
- @page_size = page_size_param.to_i > 0 ? page_size_param.to_i : 1000
22
+ @page_size = page_size_param.to_i if page_size_param.to_i > 0
19
23
 
20
24
  raise "Unknown parameters: #{attributes.inspect}" unless attributes.empty?
21
25
  end
@@ -57,7 +57,7 @@ class Task < ActiveRecord::Base
57
57
  end
58
58
  conditions = "completed_at IS NULL AND (user_id IS NULL#{user_clause})"
59
59
  Work.find(:all, :conditions => conditions).map {|work| work.task}.compact.sort_by do |t|
60
- [t.root_task.backlog.name, t.root_task.period.end_on, t.position || 0]
60
+ [t.root_task.backlog.name, t.root_task.period ? t.root_task.period.end_on : 0, t.position || 0]
61
61
  end
62
62
  end
63
63
 
@@ -344,21 +344,22 @@ class Task < ActiveRecord::Base
344
344
  def start_work
345
345
  return if work_started?
346
346
  open
347
+ grab if current_user
347
348
  new_work = works.new
348
349
 
349
350
  # TODO (uwe): Only needed for rails 1.2.x branch. Remove when moving to 1.3 or 2.0.
350
351
  new_work.task = self
351
352
 
352
353
  new_work.started_at = Time.previous_quarter
353
- if current_user
354
- last_work = current_user.works.select {|w| w.completed_at}.last
355
- else
356
- last_work = Work.find(:first, :conditions => 'completed_at IS NOT NULL', :order => 'completed_at DESC')
357
- end
358
- if last_work && last_work.completed_at > new_work.started_at
359
- new_work.started_at = last_work.completed_at
360
- end
361
-
354
+ if current_user
355
+ last_work = current_user.works.select {|w| w.completed_at}.last
356
+ else
357
+ last_work = Work.find(:first, :conditions => 'completed_at IS NOT NULL', :order => 'completed_at DESC')
358
+ end
359
+ if last_work && last_work.completed_at > new_work.started_at
360
+ new_work.started_at = last_work.completed_at
361
+ end
362
+
362
363
  new_work.user = current_user
363
364
  new_work.work_account_id = work_account.id
364
365
  new_work.save!
@@ -110,8 +110,9 @@ class Work < ActiveRecord::Base
110
110
  end
111
111
 
112
112
  def started_at_time=(new_value)
113
- raise "invalid time format: #{new_value}" unless new_value =~ /(\d{2}):(\d{2})/
114
- new_hour, new_minutes = $1, $2
113
+ new_value = '0:00' if new_value == ''
114
+ raise "invalid time format: #{new_value}" unless new_value =~ /(\d{0,2}):(\d{2})/
115
+ new_hour, new_minutes = $1.to_i, $2.to_i
115
116
  t = started_at || Time.now
116
117
  self.started_at = Time.local(t.year, t.month, t.day, new_hour, new_minutes)
117
118
  end
@@ -0,0 +1,5 @@
1
+ <ul class="backlogs">
2
+ <% for customer in @customers do -%>
3
+ <li class="task"><div class="name"><%=h customer.name %></div></li>
4
+ <% end -%>
5
+ </ul>
@@ -36,6 +36,7 @@ function handleEvent(field, event, id) {
36
36
  <%=render :partial => '/tasks/fields_header', :locals => { :backlog => nil, :active => true, :track_todo => @tasks.find {|t|t.period && t.period.active?}, :track_times => @tasks.find {|t|t.period && t.period.active?}, :track_done => @tasks.find {|t|t.period && t.period.active? }, :work_done => @tasks.find {|t| t.total_done > 0} } %>
37
37
  <ul id="active_tasks_<%=@period.id%>" class="task_list">
38
38
  <% for task in @tasks -%>
39
+ <% next if @show_only_grabbed_tasks && !(task.users.empty? || task.users.include?(current_user))%>
39
40
  <%=render :partial => '/tasks/task', :locals => { :task => task, :i => i, :active => true, :highlight_task => task == @selected_task, :update => :spotlight, :show_backlog => true, :hidden => false } %>
40
41
  <% i += 1 %>
41
42
  <% end -%>
@@ -7,5 +7,12 @@
7
7
 
8
8
  <%=if @period.higher_item then link_to(image_tag(url_for("arrow_left.png"), :alt => "#{l :previous} #{l :period}", :title => "#{l :previous} #{l :period}", :class => 'image-submit'), :controller => 'periods', :action => :show, :id => @period.higher_item) end%>
9
9
  <%=unless @period.passed? then detour_to(image_tag(url_for("add.png"), :alt => l(:add_task), :title => l(:add_task), :class => 'image-submit'), :controller => 'tasks', :action => 'new', :task => {:period_id => (@period ? @period.id : (@backlog && @backlog.periods.first ? @backlog.periods.first.id : nil))} ) end %>
10
- <h4><%=h @period.name %> (<%=@period.start_on%> - <%=@period.end_on%>)</h4>
10
+ <h4><%=h @period.name %> (<%=@period.start_on%> - <%=@period.end_on%>)
11
+ <% filter_style = {:style => 'float: none; vertical-align: bottom'} %>
12
+ <% if @show_only_grabbed_tasks %>
13
+ <%=image_link_to('grab.png', l(:release_task), {'cookies[show_only_grabbed_tasks]' => 'false'}, filter_style, false)%>
14
+ <% else %>
15
+ <%=image_link_to('grab_gray.png', l(:grab_task), {'cookies[show_only_grabbed_tasks]' => 'true'}, filter_style, false)%>
16
+ <% end %>
17
+ </h4>
11
18
  </div>
@@ -4,7 +4,7 @@
4
4
  <% form_tag :action => 'update', :id => @period do %>
5
5
  <%= render :partial => 'form' %>
6
6
  <%= submit_tag l(:save) %>
7
- <%= link_to l(:back), :action => 'show', :id => @period %>
7
+ <%=back_or_link_to l(:back), :action => 'show', :id => @period %>
8
8
  <% end %>
9
9
 
10
10
  </div>
@@ -1 +0,0 @@
1
- record page, 'alert("hi");'
@@ -75,9 +75,10 @@
75
75
  <% else -%>
76
76
  <%=@task.todo %>
77
77
  <% end -%>
78
- <% end -%>
79
- <% if (not @task.track_times?) && !@task.work_started? && (@task.period.nil? || @task.period.active?) %>
80
- <%=image_link_to_remote('checkmark.png', l(:complete), {:action => :finish_task, :id => @task}, nil, true)%>
78
+ <% else %>
79
+ <% if !@task.work_started? && (@task.period.nil? || @task.period.active?) %>
80
+ <%=image_link_to_remote('checkmark.png', l(:complete), {:action => :finish_task, :id => @task}, nil, true)%>
81
+ <% end -%>
81
82
  <% end -%>
82
83
  <% end -%>
83
84
  </div>
@@ -87,9 +88,9 @@
87
88
  <% unless @task.work_started? -%>
88
89
  <% if @task.backlog.enable_users? %>
89
90
  <% if @task.users.include?(current_user) %>
90
- <%=image_link_to_remote('grab.png', l(:grab_task), with_detour({:controller => 'tasks', :action => :release, :id => @task}), nil, true)%>
91
+ <%=image_link_to_remote('grab.png', l(:release_task), with_detour({:controller => 'tasks', :action => :release, :id => @task}), nil, true)%>
91
92
  <% else %>
92
- <%=image_link_to_remote('grab_gray.png', l(:grab_task), with_detour({:controller => 'tasks', :action => :grab, :id => @task}), nil, true)%>
93
+ <%=image_link_to_remote('grab_gray.png', "#{l:grab_task} #{" (#{l :not_grabbed})" if @task.users.empty?}", with_detour({:controller => 'tasks', :action => :grab, :id => @task}), nil, true)%>
93
94
  <% end %>
94
95
  <% end %>
95
96
  <%=image_link_to_remote('arrow_right.png', l(:move_to_next_period), {:action => :move_task_to_next_period, :id => @task}, nil, true) if @task.backlog.enable_periods? || @task.period_id%>
@@ -2,7 +2,7 @@
2
2
 
3
3
  <div title="<%= title_helper %>" class="form">
4
4
  <div class="form-padding">
5
- <% form_for :user do |f| %>
5
+ <% form_for :user, :html => {:id => 'loginForm'} do |f| %>
6
6
  <table>
7
7
  <tr class="two_columns">
8
8
  <td class="prompt"><label>Login:</label></td>
@@ -0,0 +1,4 @@
1
+ <div class="btitle">
2
+ <%=image_detour_to('hammer.png', l(:work), :action => :works, :id => @work_account) %>
3
+ <h4><%=@work_account.name %></h4>
4
+ </div>
@@ -1,9 +1,7 @@
1
1
  <% @page_title = "#{l(:editing)} #{l(:work_account)}" %>
2
2
 
3
3
  <div id="spotlight">
4
- <div class="btitle">
5
- <h4><%=@work_account.name%></h4>
6
- </div>
4
+ <%=render :partial => 'title'%>
7
5
 
8
6
  <% form_tag :action => 'update', :id => @work_account do %>
9
7
  <%=render :partial => 'form' %>
@@ -1,4 +1,10 @@
1
- <h1>Listing work_accounts</h1>
1
+ <% @page_title = "#{l :work_accounts}"%>
2
+
3
+ <div id="spotlight">
4
+ <div class="btitle">
5
+ <%=image_detour_to 'add.png', l(:new_work_account), :action => 'new' %>
6
+ <h4><%=@page_title%></h4>
7
+ </div>
2
8
 
3
9
  <table>
4
10
  <tr>
@@ -21,6 +27,6 @@
21
27
 
22
28
  <%=will_paginate @work_accounts %>
23
29
 
24
- <br />
30
+ <%=back_or_link_to l(:back), :controller => 'welcome', :action => 'index' %>
25
31
 
26
- <%= link_to 'New work_account', :action => 'new' %>
32
+ </div>
@@ -1,33 +1,50 @@
1
1
  <% @page_title = "#{l(:work_account)} #{l(:work_account)}" %>
2
-
3
2
  <div id="spotlight">
4
- <div class="btitle">
5
- <h4><%=@work_account.name%></h4>
6
- </div>
7
-
8
- <table>
9
- <% for backlog in @backlog_totals.keys %>
10
- <tr>
11
- <th><%=backlog ? backlog.name : l(:no_backlog)%></th>
12
- <th><%=@backlog_totals[backlog]%></th>
13
- </tr>
14
- <% for task, hours in @task_totals_per_backlog[backlog] %>
15
- <tr>
16
- <td>
17
- <% if task %>
18
- <%=link_to task.description, :controller => 'tasks', :action => :edit, :id => task.id %>
19
- <% end %>
20
- </td>
21
- <td align="right" valign="top"><%='%.2f' % hours%></td>
22
- </tr>
23
- <% end %>
24
- <% end %>
25
- <tr>
26
- <th>Total hours</th>
27
- <th><%=@total_hours%></th>
28
- </tr>
29
- </table>
30
-
31
- <%= link_to 'Edit', :action => 'edit', :id => @work_account %> |
32
- <%= link_to 'Back', :action => 'list' %>
3
+ <%=render :partial => 'title'%>
4
+ <table>
5
+ <% for backlog in @backlog_totals.keys %>
6
+ <tr>
7
+ <th>
8
+ <%=backlog ? backlog.name : l(:no_backlog) %>
9
+ </th>
10
+ <th>
11
+ <%=@backlog_totals[backlog] %>
12
+ </th>
13
+ </tr>
14
+ <% if backlog %>
15
+ <% for task, hours in @task_totals_per_backlog[backlog] %>
16
+ <tr>
17
+ <td>
18
+ <% if task %>
19
+ <%=link_to task.description, :controller => 'tasks', :action => :edit, :id => task.id %>
20
+ <% end %>
21
+ </td>
22
+ <td align="right" valign="top">
23
+ <%='%.2f' % hours %>
24
+ </td>
25
+ </tr>
26
+ <% end %>
27
+ <% else %>
28
+ <% for work in @works_without_backlog %>
29
+ <tr>
30
+ <td>
31
+ <%=work.description %>
32
+ </td>
33
+ <td align="right" valign="top">
34
+ <%='%.2f' % work.hours %>
35
+ </td>
36
+ </tr>
37
+ <% end %>
38
+ <% end %>
39
+ <% end %>
40
+ <tr>
41
+ <th>
42
+ Total hours
43
+ </th>
44
+ <th>
45
+ <%=@total_hours %>
46
+ </th>
47
+ </tr>
48
+ </table>
49
+ <%=detour_to l(:edit), :action => 'edit', :id => @work_account %>|<%=back_or_link_to l(:back), :action => 'list' %>
33
50
  </div>