backlog 0.22.1 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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>