backlog 0.31.1 → 0.32.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +24 -0
- data/Rakefile +2 -0
- data/app/controllers/user_controller.rb +1 -0
- data/app/controllers/work_accounts_controller.rb +0 -15
- data/app/controllers/works_controller.rb +22 -8
- data/app/helpers/application_helper.rb +4 -4
- data/app/models/absence.rb +2 -2
- data/app/models/report_filter.rb +3 -2
- data/app/models/user.rb +1 -0
- data/app/models/work.rb +2 -2
- data/app/models/works_report_filter.rb +5 -0
- data/app/views/periods/_show_active.rhtml +5 -1
- data/app/views/tasks/_task.rhtml +2 -3
- data/app/views/user/edit.rhtml +36 -4
- data/app/views/work_accounts/_form.rhtml +3 -2
- data/app/views/work_accounts/edit.rhtml +1 -1
- data/app/views/work_accounts/list.rhtml +1 -1
- data/app/views/work_accounts/new.rhtml +3 -1
- data/app/views/works/_new_row.rhtml +1 -1
- data/app/views/works/daily_work_sheet.rhtml +4 -3
- data/app/views/works/list.rhtml +23 -12
- data/app/views/works/list_excel.rhtml +29 -22
- data/app/views/works/weekly_work_sheet.rhtml +4 -1
- data/config/database.yml +1 -1
- data/config/environment.rb +2 -2
- data/test/fixtures/works.yml +8 -0
- data/test/functional/work_accounts_controller_test.rb +0 -20
- data/test/functional/works_controller_test.rb +29 -0
- data/test/unit/work_test.rb +4 -0
- metadata +2 -2
data/History.txt
CHANGED
@@ -1,3 +1,27 @@
|
|
1
|
+
== 0.32.0 2008-05-06
|
2
|
+
|
3
|
+
== Features
|
4
|
+
|
5
|
+
* Added filtering on work account to work search view.
|
6
|
+
* Added "Sick child" as absence reason in the daily work sheet.
|
7
|
+
* Added navigating to work records for a user from the subscription list in the user view.
|
8
|
+
|
9
|
+
=== Fixes
|
10
|
+
|
11
|
+
* Fixed wrong parsing of one-digit minutes for work duration.
|
12
|
+
* Reformatted the Excel export of work records for a work account.
|
13
|
+
Also added sorting by user, task, start and stop times.
|
14
|
+
Should work with MS Excel now.
|
15
|
+
* Set default search dates for work to current month.
|
16
|
+
* Keep user_id when following links from the weekly work sheet to the daily work sheet.
|
17
|
+
This enables subscribers of time sheets to get details about the time sheets they are watching.
|
18
|
+
* Added a "Back" link from weekly work sheet.
|
19
|
+
* Indent subtasks in the sprint view.
|
20
|
+
* Fixed bug in work lock subscription invitation.
|
21
|
+
* Fixed so that partially filled in work records are retained after failed creation.
|
22
|
+
Also error messages for failed work record creations are displayed.
|
23
|
+
* Fixed design of the "New Work Account" view, and set focus on the "Name" field when it is first loaded.
|
24
|
+
|
1
25
|
== 0.31.1 2008-04-24
|
2
26
|
|
3
27
|
=== Fixes
|
data/Rakefile
CHANGED
@@ -14,6 +14,8 @@ require 'tasks/rails'
|
|
14
14
|
require 'hoe'
|
15
15
|
require 'version_from_history'
|
16
16
|
|
17
|
+
ENV['SKIP_AR_JDBC_RAKE_REDEFINES'] = '1'
|
18
|
+
|
17
19
|
Hoe.new("backlog", APP::VERSION) do |p|
|
18
20
|
p.rubyforge_name = "backlog"
|
19
21
|
p.summary = "Application to aid collecting, processing, organizing, reviewing and doing tasks."
|
@@ -217,6 +217,7 @@ class UserController < ApplicationController
|
|
217
217
|
@groups = Group.find(:all, :order => 'name')
|
218
218
|
@periods = @user.periods
|
219
219
|
@associates = User.find(:all) - @user.work_lock_subscribers - [current_user]
|
220
|
+
@potential_subscribees = User.find(:all) - @user.work_lock_subscriptions - [current_user]
|
220
221
|
absences = Absence.find(:all, :conditions => ['user_id = ? AND "on" BETWEEN ? AND ?', current_user.id, Date.new(Date.today.year, 1, 1), Date.new(Date.today.year, 12, 31)])
|
221
222
|
@holidays = absences.select {|a| a.reason == 'HOLIDAY'}
|
222
223
|
@holidays.map! {|a| a.on}
|
@@ -55,19 +55,4 @@ class WorkAccountsController < ApplicationController
|
|
55
55
|
redirect_to :action => 'list'
|
56
56
|
end
|
57
57
|
|
58
|
-
def works
|
59
|
-
work_account = WorkAccount.find(params[:id])
|
60
|
-
@report_filter = WorksReportFilter.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.invoice.nil? ? '' : 'AND invoice = ?'} #{@report_filter.user_id.nil? ? '' : 'AND user_id = ?'}", @report_filter.start_on, @report_filter.end_on + 1, work_account.id, @report_filter.invoice, @report_filter.user_id].compact,
|
63
|
-
:page => params[:page], :per_page => @report_filter.page_size,
|
64
|
-
:order => 'started_on, start_time, completed_at'
|
65
|
-
@users = User.find(:all)
|
66
|
-
if params[:export] == 'excel'
|
67
|
-
render :template => '/works/list_excel', :layout => false
|
68
|
-
else
|
69
|
-
render :template => '/works/list'
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
58
|
end
|
@@ -8,7 +8,6 @@ class WorksController < ApplicationController
|
|
8
8
|
|
9
9
|
def index
|
10
10
|
list
|
11
|
-
render :action => 'list'
|
12
11
|
end
|
13
12
|
|
14
13
|
# GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
|
@@ -16,8 +15,21 @@ class WorksController < ApplicationController
|
|
16
15
|
:redirect_to => { :action => :list }
|
17
16
|
|
18
17
|
def list
|
18
|
+
work_account_name = (params[:work_account_id] && WorkAccount.find(params[:work_account_id]).name) || 'all accoutns'
|
19
|
+
@report_filter = WorksReportFilter.new(params[:report_filter])
|
20
|
+
@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')}"
|
19
21
|
@period = params[:id] && Period.find(params[:id])
|
20
|
-
@works = Work.paginate :
|
22
|
+
@works = Work.paginate :conditions => ["completed_at BETWEEN ? AND ? #{'AND work_account_id = ?' if @report_filter.work_account_id} #{@report_filter.invoice.nil? ? '' : 'AND invoice = ?'} #{@report_filter.user_id.nil? ? '' : 'AND user_id = ?'}", @report_filter.start_on, @report_filter.end_on + 1, @report_filter.work_account_id, @report_filter.invoice, @report_filter.user_id].compact,
|
23
|
+
:page => params[:page], :per_page => @report_filter.page_size,
|
24
|
+
:order => 'started_on, start_time, completed_at'
|
25
|
+
@work_accounts = WorkAccount.find(:all, :order => :name)
|
26
|
+
@users = User.find(:all, :order => 'first_name, last_name')
|
27
|
+
if params[:export] == 'excel'
|
28
|
+
@works = @works.sort_by {|w| [w.user_id || 0, w.task_id || 999999, w.started_on]}
|
29
|
+
render :template => '/works/list_excel', :layout => false
|
30
|
+
else
|
31
|
+
render :template => '/works/list'
|
32
|
+
end
|
21
33
|
end
|
22
34
|
|
23
35
|
def show
|
@@ -63,6 +75,7 @@ class WorksController < ApplicationController
|
|
63
75
|
else
|
64
76
|
if params[:detour]
|
65
77
|
flash[:notice] = 'Error creating new work record.'
|
78
|
+
flash[:work] = @work
|
66
79
|
back
|
67
80
|
else
|
68
81
|
new
|
@@ -180,13 +193,14 @@ class WorksController < ApplicationController
|
|
180
193
|
@date = (params[:id] && Date.parse(params[:id])) || Date.today
|
181
194
|
@year = @date.year
|
182
195
|
@week = @date.cweek
|
196
|
+
@user = (params[:user_id] && User.find(params[:user_id])) || current_user
|
183
197
|
@periods = []
|
184
|
-
@works = Work.find_work_for_day @
|
198
|
+
@works = Work.find_work_for_day(@date, @user)
|
185
199
|
@customers = Customer.find(:all, :order => :name)
|
186
200
|
@started_works = Task.find_started
|
187
|
-
@
|
201
|
+
@new_work = flash[:work] || Work.new(:started_at => Time.now)
|
188
202
|
@work_accounts = WorkAccount.find(:all, :order => :name)
|
189
|
-
@absence = Absence.find(:first, :conditions => {:on => @date, :user_id =>
|
203
|
+
@absence = Absence.find(:first, :conditions => {:on => @date, :user_id => @user.id})
|
190
204
|
@public_holiday = PublicHoliday.find(:first, :conditions => {:on => @date})
|
191
205
|
render :layout => 'wide'
|
192
206
|
end
|
@@ -209,7 +223,7 @@ class WorksController < ApplicationController
|
|
209
223
|
@first_date = Date.commercial(@year, @week, 1)
|
210
224
|
@last_date = @first_date + 6
|
211
225
|
@lock = WorkLock.find_by_week(@year, @week)
|
212
|
-
@absences = Absence.find_by_week(@year, @week)
|
226
|
+
@absences = Absence.find_by_week(@year, @week, @user)
|
213
227
|
@public_holidays = PublicHoliday.find_by_week(@year, @week)
|
214
228
|
render :layout => 'wide'
|
215
229
|
end
|
@@ -302,8 +316,8 @@ class WorksController < ApplicationController
|
|
302
316
|
end
|
303
317
|
if params[:work][:hours_time] =~ /^(\d*):(\d{2})?$/
|
304
318
|
new_hours = $1
|
305
|
-
new_minutes = $2
|
306
|
-
new_value_str =
|
319
|
+
new_minutes = BigDecimal($2)
|
320
|
+
new_value_str = '%02d.%03d' % [new_hours, (new_minutes / BigDecimal('0.06')).round]
|
307
321
|
new_value = BigDecimal(new_value_str)
|
308
322
|
params[:work][:hours] = new_value
|
309
323
|
elsif params[:work][:hours_time] =~ /^(\d*)[.,]?(\d*)$/
|
@@ -6,10 +6,10 @@ module ApplicationHelper
|
|
6
6
|
include UserSystem
|
7
7
|
include UrlForFix
|
8
8
|
|
9
|
-
def image_button_to(image_source, title, options)
|
10
|
-
image_submit_tag image_source, :class => 'image-submit', :alt => title, :title => title,
|
11
|
-
|
12
|
-
|
9
|
+
def image_button_to(image_source, title, options, html_options = {})
|
10
|
+
image_submit_tag image_source, {:class => 'image-submit', :alt => title, :title => title,
|
11
|
+
:id => "#{title}_#{options[:id]}", :name => title,
|
12
|
+
:onclick => "form.action='#{url_for(options)}'"}.update(html_options)
|
13
13
|
end
|
14
14
|
|
15
15
|
def detour_to(title, options, html_options = nil)
|
data/app/models/absence.rb
CHANGED
@@ -17,10 +17,10 @@ class Absence < ActiveRecord::Base
|
|
17
17
|
end
|
18
18
|
|
19
19
|
# Return an array of either an absence or nil for each day of the given week.
|
20
|
-
def self.find_by_week(year, week)
|
20
|
+
def self.find_by_week(year, week, user = current_user)
|
21
21
|
first_date = Date.commercial(year, week, 1)
|
22
22
|
last_date = first_date + 6
|
23
|
-
results = self.find(:all, :conditions => ['"on" BETWEEN ? AND ? AND user_id = ?', first_date, last_date,
|
23
|
+
results = self.find(:all, :conditions => ['"on" BETWEEN ? AND ? AND user_id = ?', first_date, last_date, user && user.id], :order => '"on"')
|
24
24
|
(0..6).each do |day|
|
25
25
|
results.insert(day, nil) if results[day] && results[day].on > first_date + day
|
26
26
|
end
|
data/app/models/report_filter.rb
CHANGED
@@ -5,8 +5,9 @@ class ReportFilter
|
|
5
5
|
attr_reader :page_size
|
6
6
|
|
7
7
|
def initialize(attributes)
|
8
|
-
|
9
|
-
@
|
8
|
+
date = Date.today - 5
|
9
|
+
@start_on = Date.civil(date.year, date.month, 01)
|
10
|
+
@end_on = Date.civil(date.year, date.month, -1)
|
10
11
|
@page_size = 1000
|
11
12
|
|
12
13
|
if attributes
|
data/app/models/user.rb
CHANGED
@@ -9,6 +9,7 @@ class User < Party
|
|
9
9
|
# even if they follow the defaults since ClassTableInheritanceInRails breaks it.
|
10
10
|
has_and_belongs_to_many :groups, :join_table => "groups_users", :foreign_key => "user_id", :association_foreign_key => 'group_id'
|
11
11
|
has_and_belongs_to_many :work_lock_subscribers, :class_name => 'User', :join_table => "user_work_lock_subscriptions", :foreign_key => "user_id", :association_foreign_key => 'subscriber_user_id'
|
12
|
+
has_and_belongs_to_many :work_lock_subscriptions, :class_name => 'User', :join_table => "user_work_lock_subscriptions", :association_foreign_key => "user_id", :foreign_key => 'subscriber_user_id'
|
12
13
|
has_many :works, :foreign_key => :user_id, :order => :completed_at
|
13
14
|
has_many :work_locks, :foreign_key => :user_id, :order => :end_on
|
14
15
|
|
data/app/models/work.rb
CHANGED
@@ -103,8 +103,8 @@ class Work < ActiveRecord::Base
|
|
103
103
|
totals_per_work_account
|
104
104
|
end
|
105
105
|
|
106
|
-
def self.find_work_for_day
|
107
|
-
Work.find(:all, :conditions => "started_on = '#{date}' AND user_id = #{
|
106
|
+
def self.find_work_for_day(date, user = current_user)
|
107
|
+
Work.find(:all, :conditions => "started_on = '#{date}' AND user_id = #{user.id}",
|
108
108
|
:order => 'start_time, completed_at')
|
109
109
|
end
|
110
110
|
|
@@ -1,14 +1,19 @@
|
|
1
1
|
class WorksReportFilter < ReportFilter
|
2
|
+
attr_reader :work_account_id
|
2
3
|
attr_reader :invoice
|
3
4
|
attr_reader :user_id
|
4
5
|
|
5
6
|
def initialize(attributes)
|
7
|
+
@work_account_id = nil
|
6
8
|
@invoice = nil
|
7
9
|
@user_id = nil
|
8
10
|
|
9
11
|
if attributes
|
10
12
|
attributes = attributes.clone
|
11
13
|
|
14
|
+
work_account_id_param = attributes.delete(:work_account_id)
|
15
|
+
@work_account_id = work_account_id_param.to_i if work_account_id_param && work_account_id_param.size > 0
|
16
|
+
|
12
17
|
invoice_param = attributes.delete(:invoice)
|
13
18
|
@invoice = invoice_param == 'true' if invoice_param && invoice_param.size > 0
|
14
19
|
|
@@ -35,9 +35,13 @@ function handleEvent(field, event, id) {
|
|
35
35
|
<div id="active_tasks"<%=' style="display: none;"' unless @tasks and not @tasks.empty?%>>
|
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
|
+
<% max_depth = @tasks.map{|t|t.depth}.max %>
|
38
39
|
<% for task in @tasks -%>
|
39
40
|
<% next if @show_only_grabbed_tasks && !(task.users.empty? || task.users.include?(current_user))%>
|
40
|
-
<%=render :partial => '/tasks/task', :locals => { :task => task, :i => i,
|
41
|
+
<%=render :partial => '/tasks/task', :locals => { :task => task, :i => i,
|
42
|
+
:active => true, :highlight_task => (task == @selected_task),
|
43
|
+
:update => :spotlight, :show_backlog => true, :hidden => false,
|
44
|
+
:max_depth => max_depth} %>
|
41
45
|
<% i += 1 %>
|
42
46
|
<% end -%>
|
43
47
|
</ul>
|
data/app/views/tasks/_task.rhtml
CHANGED
@@ -3,14 +3,13 @@
|
|
3
3
|
<div style="float: left" style="border: 0px">
|
4
4
|
<% if @task.enable_subtasks? && active && (@task.period.nil? || @task.period.active_or_future?) %>
|
5
5
|
<% form_tag({:controller => 'tasks', :action => :specify, :id => @task}) do %>
|
6
|
-
<%=
|
6
|
+
<%=image_button_to('add.png', l(:specify), {:controller => 'tasks', :action => :specify, :id => @task.id},
|
7
|
+
:style => "margin-left: #{@task.depth * 2}em") %>
|
7
8
|
<% end %>
|
8
9
|
<% end %>
|
9
10
|
</div>
|
10
11
|
<div style="float: left" style="border: 3px solid red">
|
11
|
-
<%=(" " * @task.depth * 4) if @task.depth > 0 %>
|
12
12
|
<%=resolution_image(@task.resolution) if @task.finished_at %>
|
13
|
-
<%="-" if @task.children.size > 0 %>
|
14
13
|
</div>
|
15
14
|
<div id="task_<%=@task.id%>_id" class="task_id">
|
16
15
|
<%=detour_to "##{@task.id}", :controller => 'tasks', :action => :edit, :id => @task.id, :style => 'border: 0px; margin: 0px; padding: 0px' if @task.position || @task.depth == 0 %>
|
data/app/views/user/edit.rhtml
CHANGED
@@ -53,6 +53,7 @@
|
|
53
53
|
<div id="lfeature">
|
54
54
|
<div class="btitle">
|
55
55
|
<h4><%=l :work_lock_subscriptions %></h4>
|
56
|
+
<h3>Users watching <%=@user.name%></h4>
|
56
57
|
</div>
|
57
58
|
|
58
59
|
<% if @user == current_user %>
|
@@ -67,10 +68,11 @@
|
|
67
68
|
<% end %>
|
68
69
|
<% unless @associates.empty? %>
|
69
70
|
<tr>
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
71
|
+
<td align="right">
|
72
|
+
<% remote_form_for :user, :html => {:id => 'invitation_form'}, :url => {:action => :invite_work_lock_subscriber} do |f| %>
|
73
|
+
<%=select :id, nil, @associates.map{|u| [u.name, u.id]}, {}, :name => 'id'%></td>
|
74
|
+
<% end %>
|
75
|
+
<td align="left"><%=button_to_function l(:invite), "new Ajax.Request('/user/invite_work_lock_subscriber', {asynchronous:true, evalScripts:true, parameters:Form.serialize($('invitation_form'))});" %></td>
|
74
76
|
</tr>
|
75
77
|
<% end %>
|
76
78
|
</table>
|
@@ -80,6 +82,36 @@
|
|
80
82
|
<% end %>
|
81
83
|
</div>
|
82
84
|
|
85
|
+
<div id="rfeature">
|
86
|
+
<div class="btitle">
|
87
|
+
<h4><%=l :work_lock_subscriptions %></h4>
|
88
|
+
<h3><%=@user.name%> is monitoring</h4>
|
89
|
+
</div>
|
90
|
+
|
91
|
+
<% if @user == current_user %>
|
92
|
+
<table align="right">
|
93
|
+
<% unless @user.work_lock_subscriptions.empty? %>
|
94
|
+
<% for subscribee in @user.work_lock_subscriptions %>
|
95
|
+
<tr>
|
96
|
+
<td align="right"><%=detour_to subscribee.name, :action => :edit, :id => subscribee.id %></td>
|
97
|
+
<td align="left"><%=image_link_to 'work_account.png', "#{l :work}", {:controller => 'works', :action => :weekly_work_sheet, :user_id => subscribee.id} %></td>
|
98
|
+
<td align="left"><%=image_link_to_remote 'email.png', "#{l :stop} #{l :monitoring}", {:action => :toggle_work_lock_monitoring, :subscriber_id => current_user.id, :id => subscribee.id}, :id => "work_lock_monitor_icon_#{subscribee.id}"%></td>
|
99
|
+
</tr>
|
100
|
+
<% end %>
|
101
|
+
<% end %>
|
102
|
+
<% unless @potential_subscribees.empty? %>
|
103
|
+
<tr>
|
104
|
+
<td align="right">
|
105
|
+
<% remote_form_for :user, :html => {:id => 'monitoring_form'}, :url => {:action => :invite_work_lock_subscriber} do |f| %>
|
106
|
+
<%=select :id, nil, @potential_subscribees.map{|u| [u.name, u.id]}, {}, :name => 'id'%></td>
|
107
|
+
<% end %>
|
108
|
+
<td align="left"><%=button_to_function l(:monitor), "new Ajax.Request('/user/toggle_work_lock_monitoring', {asynchronous:true, evalScripts:true, parameters:Form.serialize($('monitoring_form'))});" %></td>
|
109
|
+
</tr>
|
110
|
+
<% end %>
|
111
|
+
</table>
|
112
|
+
<% end %>
|
113
|
+
</div>
|
114
|
+
|
83
115
|
<div id="lfeature">
|
84
116
|
<div class="btitle">
|
85
117
|
<h4><%=l :holidays_used %></h4>
|
@@ -1,6 +1,5 @@
|
|
1
1
|
<%= error_messages_for 'work_account' %>
|
2
2
|
|
3
|
-
<!--[form:work_account]-->
|
4
3
|
<p><label for="work_account_name">Name</label><br/>
|
5
4
|
<%= text_field 'work_account', 'name' %></p>
|
6
5
|
|
@@ -11,5 +10,7 @@
|
|
11
10
|
|
12
11
|
<p><label for="work_account_invoice_code">Invoice code</label><br/>
|
13
12
|
<%= text_field 'work_account', 'invoice_code' %></p>
|
14
|
-
<!--[eoform:work_account]-->
|
15
13
|
|
14
|
+
<%=javascript_tag update_page { |page|
|
15
|
+
page[:work_account_name].focus
|
16
|
+
} %>
|
@@ -6,7 +6,7 @@
|
|
6
6
|
<% form_tag :action => 'update', :id => @work_account do %>
|
7
7
|
<%=render :partial => 'form' %>
|
8
8
|
<%=submit_tag l(:save) %>
|
9
|
-
<%=back_or_link_to l(:back), :action =>
|
9
|
+
<%=back_or_link_to l(:back), :action => :list %>
|
10
10
|
<% end %>
|
11
11
|
|
12
12
|
</div>
|
@@ -15,7 +15,7 @@
|
|
15
15
|
|
16
16
|
<% for work_account in @work_accounts %>
|
17
17
|
<tr>
|
18
|
-
<td><%=link_to h(work_account.name), :
|
18
|
+
<td><%=link_to h(work_account.name), :controller => 'works', :action => :list, :report_filter => {:work_account_id => work_account.id} %></td>
|
19
19
|
<td align="right"><%=work_account.track_times? %></td>
|
20
20
|
<td><%=h(work_account.invoice_code) %></td>
|
21
21
|
<td><%= link_to 'Edit', :action => 'edit', :id => work_account %></td>
|
@@ -1,8 +1,10 @@
|
|
1
|
-
|
1
|
+
<% @page_title = "#{l :new_work_account}" %>
|
2
2
|
|
3
|
+
<div id="spotlight">
|
3
4
|
<% form_tag :action => 'create' do %>
|
4
5
|
<%= render :partial => 'form' %>
|
5
6
|
<%= submit_tag "Create" %>
|
6
7
|
<% end %>
|
7
8
|
|
8
9
|
<%= link_to 'Back', :action => 'list' %>
|
10
|
+
</div>
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<% @page_title = "#{l :daily_work_sheet} on #{@date}" + (" for #{user.
|
1
|
+
<% @page_title = "#{l :daily_work_sheet} on #{@date}" + (" for #{@user.name}" if user?) %>
|
2
2
|
|
3
3
|
<div id="spotlight">
|
4
4
|
|
@@ -21,6 +21,7 @@
|
|
21
21
|
<%=f.radio_button :reason, '', :disabled => work_disabled, :checked => @absence.nil? && (!@works.empty? || @public_holiday.nil?), :onchange => 'form.submit()' %><label for="absence_reason_">Work day</label>
|
22
22
|
<%=f.radio_button :reason, 'HOLIDAY', :disabled => absence_disabled, :onchange => 'form.submit()' %><label for="absence_reason_holiday"><%=l :holiday%></label>
|
23
23
|
<%=f.radio_button :reason, 'SICK', :disabled => absence_disabled, :onchange => 'form.submit()' %><label for="absence_reason_sick">Sick day</label>
|
24
|
+
<%=f.radio_button :reason, 'SICK_CHILD', :disabled => absence_disabled, :onchange => 'form.submit()' %><label for="absence_reason_sick_child">Sick child</label>
|
24
25
|
<%=f.radio_button :reason, 'SICK_WITH_DOCTOR', :disabled => absence_disabled, :onchange => 'form.submit()' %><label for="absence_reason_sick_with_doctor">Sick day with doctor's note</label>
|
25
26
|
<% end %>
|
26
27
|
</div>
|
@@ -64,7 +65,7 @@
|
|
64
65
|
<% last_work = @work %>
|
65
66
|
|
66
67
|
<% unless @absence %>
|
67
|
-
<% @work =
|
68
|
+
<% @work = @new_work %>
|
68
69
|
<%=render :partial => 'new_row', :locals => {:last_work => last_work} %>
|
69
70
|
<% end %>
|
70
71
|
|
@@ -97,7 +98,7 @@
|
|
97
98
|
<script type="text/JavaScript">
|
98
99
|
//<!--
|
99
100
|
var start_field;
|
100
|
-
<% if last_work.completed_at %>
|
101
|
+
<% if last_work.nil? || last_work.completed_at %>
|
101
102
|
start_field = $('work_work_account_id');
|
102
103
|
<% else %>
|
103
104
|
start_field = $('work_<%=last_work.id%>_completed_at_time');
|
data/app/views/works/list.rhtml
CHANGED
@@ -7,6 +7,14 @@
|
|
7
7
|
<% if @report_filter %>
|
8
8
|
<% form_for :report_filter, :html => {:method => :get} do |f| %>
|
9
9
|
<table>
|
10
|
+
<tr>
|
11
|
+
<td coslpan="3">
|
12
|
+
<label for="report_filter_work_account_id"><%=l :work_account%></label>
|
13
|
+
</td>
|
14
|
+
<td>
|
15
|
+
<%=f.select :work_account_id, [[l(:all), nil]] + @work_accounts.map{|wa|[wa.name, wa.id]}, {}, {:onchange => 'form.submit()'} %>
|
16
|
+
</td>
|
17
|
+
</tr>
|
10
18
|
<tr>
|
11
19
|
<td>
|
12
20
|
<label for="report_filter_start_on"><%=l :start_on%></label>
|
@@ -91,9 +99,9 @@
|
|
91
99
|
|
92
100
|
<table>
|
93
101
|
<tr>
|
94
|
-
<th><%=l :task %></th>
|
95
102
|
<th><%=l :user %></th>
|
96
103
|
<th><%=l :done %></th>
|
104
|
+
<th><%=l :description %></th>
|
97
105
|
<th><%=l :invoice %></th>
|
98
106
|
<% if @period && @period.track_times? %>
|
99
107
|
<th><%=l :started_at %></th>
|
@@ -103,23 +111,26 @@
|
|
103
111
|
|
104
112
|
<% for work in @works %>
|
105
113
|
<tr>
|
106
|
-
<td><%=work.
|
107
|
-
<td><%=work.
|
108
|
-
|
114
|
+
<td valign="top"><%=work.user && work.user.login %></td>
|
115
|
+
<td valign="top"><%=work.hours %></td>
|
116
|
+
<%if work.task
|
117
|
+
link = "Task: #{work.task.description} <br/>"
|
118
|
+
else
|
119
|
+
link = ""
|
120
|
+
end
|
121
|
+
link += h(work.description)
|
122
|
+
%>
|
123
|
+
<td><%= link_to link, :controller => 'works', :action => 'daily_work_sheet', :id => work.started_on.strftime("%Y-%m-%d") %></td>
|
109
124
|
<td><%=work.invoice ? l(:yes) : '' %></td>
|
110
125
|
<% if @period && @period.track_times? %>
|
111
126
|
<td><%=work.started_at && work.started_at.strftime('%Y-%m-%d %H:%M:%S') %></td>
|
112
127
|
<% end %>
|
113
|
-
<td><%=work.completed_at && work.completed_at.strftime('%Y-%m-%d %H:%M:%S') %></td>
|
114
|
-
<
|
115
|
-
<td><%= link_to 'Edit', :controller => 'works', :action => 'edit', :id => work %></td>
|
116
|
-
<td><%= link_to 'Destroy', { :controller => 'works', :action => 'destroy', :id => work }, :confirm => 'Are you sure?', :method => :post %></td>
|
117
|
-
</tr>
|
128
|
+
<td valign="top"><%=work.completed_at && work.completed_at.strftime('%Y-%m-%d %H:%M:%S') %></td>
|
129
|
+
<tr>
|
118
130
|
<% end %>
|
119
131
|
<tr>
|
120
132
|
<th><%=l :total%>:</th>
|
121
|
-
<td
|
122
|
-
<th><%=@works.inject(BigDecimal('0')){|total, work| total += work.hours}%> <%=l(:hours).downcase%></td>
|
133
|
+
<th colspan="2"><%=@works.inject(BigDecimal('0')){|total, work| total += work.hours}%> <%=l(:hours).downcase%></td>
|
123
134
|
</tr>
|
124
135
|
</table>
|
125
136
|
|
@@ -136,4 +147,4 @@
|
|
136
147
|
|
137
148
|
<% if @period %>
|
138
149
|
<%=render :partial => '/periods/burn_down_chart' %>
|
139
|
-
<% end %>
|
150
|
+
<% end %>
|
@@ -29,11 +29,6 @@
|
|
29
29
|
<Styles>
|
30
30
|
<Style ss:ID="Default" ss:Name="Normal">
|
31
31
|
<Alignment ss:Vertical="Top"/>
|
32
|
-
<Borders/>
|
33
|
-
<Font/>
|
34
|
-
<Interior/>
|
35
|
-
<NumberFormat/>
|
36
|
-
<Protection/>
|
37
32
|
</Style>
|
38
33
|
<Style ss:ID="title">
|
39
34
|
<Font x:Family="Swiss" ss:Size="24" ss:Bold="1"/>
|
@@ -41,37 +36,49 @@
|
|
41
36
|
<Style ss:ID="header">
|
42
37
|
<Font x:Family="Swiss" ss:Size="14" ss:Bold="1"/>
|
43
38
|
</Style>
|
39
|
+
<Style ss:ID="DateTime">
|
40
|
+
<NumberFormat ss:Format="Number" />
|
41
|
+
</Style>
|
44
42
|
</Styles>
|
45
43
|
<Worksheet ss:Name="<%=l(:done)%>">
|
46
|
-
<Table ss:ExpandedColumnCount="256"
|
44
|
+
<Table ss:ExpandedColumnCount="256" x:FullColumns="1"
|
47
45
|
x:FullRows="1">
|
48
|
-
<Column ss:AutoFitWidth="1" ss:Width="
|
49
|
-
<Column ss:AutoFitWidth="
|
50
|
-
<Column ss:AutoFitWidth="
|
51
|
-
<Column ss:AutoFitWidth="
|
52
|
-
<Column ss:AutoFitWidth="
|
46
|
+
<Column ss:AutoFitWidth="1" ss:Width="100"/>
|
47
|
+
<Column ss:AutoFitWidth="1" ss:Width="90"/>
|
48
|
+
<Column ss:AutoFitWidth="1" ss:Width="120"/>
|
49
|
+
<Column ss:AutoFitWidth="1" ss:Width="200"/>
|
50
|
+
<Column ss:AutoFitWidth="1" ss:Width="60"/>
|
51
|
+
<Column ss:AutoFitWidth="1" ss:Width="45"/>
|
52
|
+
<Column ss:AutoFitWidth="1" ss:Width="90"/>
|
53
|
+
<Column ss:AutoFitWidth="1" ss:Width="90"/>
|
53
54
|
<Row>
|
54
|
-
<Cell ss:StyleID="title" ss:MergeAcross="
|
55
|
+
<Cell ss:StyleID="title" ss:MergeAcross="6"><Data ss:Type="String"><%=@report_filter.title%></Data></Cell>
|
55
56
|
</Row>
|
56
57
|
<Row ss:AutoFitHeight="0" ss:Height="6.5625">
|
57
|
-
<Cell ss:MergeAcross="
|
58
|
+
<Cell ss:MergeAcross="6"><Data ss:Type="String"></Data></Cell>
|
58
59
|
</Row>
|
59
60
|
|
60
61
|
<Row ss:AutoFitHeight="0" ss:Height="19.875">
|
61
|
-
<Cell ss:StyleID="header"><Data ss:Type="String"
|
62
|
-
<Cell ss:StyleID="header"><Data ss:Type="String"
|
63
|
-
<Cell ss:StyleID="header"><Data ss:Type="String"
|
64
|
-
<Cell ss:StyleID="header"><Data ss:Type="String"
|
65
|
-
<Cell ss:StyleID="header"><Data ss:Type="String"
|
62
|
+
<Cell ss:StyleID="header"><Data ss:Type="String"><%=l :work_account%></Data></Cell>
|
63
|
+
<Cell ss:StyleID="header"><Data ss:Type="String"><%=l :user%></Data></Cell>
|
64
|
+
<Cell ss:StyleID="header"><Data ss:Type="String"><%=l :task%></Data></Cell>
|
65
|
+
<Cell ss:StyleID="header"><Data ss:Type="String"><%=l :description%></Data></Cell>
|
66
|
+
<Cell ss:StyleID="header"><Data ss:Type="String"><%=l :done%></Data></Cell>
|
67
|
+
<Cell ss:StyleID="header"><Data ss:Type="String"><%=l :invoice_short%></Data></Cell>
|
68
|
+
<Cell ss:StyleID="header"><Data ss:Type="String"><%=l :start%></Data></Cell>
|
69
|
+
<Cell ss:StyleID="header"><Data ss:Type="String"><%=l :stop%></Data></Cell>
|
66
70
|
</Row>
|
67
71
|
|
68
72
|
<% for work in @works %>
|
69
73
|
<Row>
|
70
|
-
<Cell><Data ss:Type="String"><%=
|
74
|
+
<Cell><Data ss:Type="String"><%=work.work_account.name %></Data></Cell>
|
71
75
|
<Cell><Data ss:Type="String"><%=work.user && work.user.name %></Data></Cell>
|
72
|
-
<Cell><Data ss:Type="
|
73
|
-
<Cell><Data ss:Type="
|
74
|
-
<Cell><Data ss:Type="Number"><%=work.hours
|
76
|
+
<Cell><Data ss:Type="String"><%=work.task && work.task.description %></Data></Cell>
|
77
|
+
<Cell><Data ss:Type="String"><%=work.description %></Data></Cell>
|
78
|
+
<Cell><Data ss:Type="Number"><%=work.hours %></Data></Cell>
|
79
|
+
<Cell><Data ss:Type="String"><%=work.invoice? ? 'Ja' : 'Nei' %></Data></Cell>
|
80
|
+
<Cell ss:StyleID="DateTime"><Data ss:Type="Number"><%=BigDecimal((work.started_on - Date.new(1899, 12, 31)).to_s) + (work.start_time ? (BigDecimal(work.start_time.hour.to_s) + BigDecimal(work.start_time.minute.to_s)/60) / 24 : 0) %></Data></Cell>
|
81
|
+
<Cell ss:StyleID="DateTime"><Data ss:Type="Number"><%=work.completed_at && (BigDecimal((work.completed_at.to_datetime - DateTime.parse('1899-12-31T00:00:00+02:00')).to_f.to_s))%></Data></Cell>
|
75
82
|
</Row>
|
76
83
|
<% end %>
|
77
84
|
|
@@ -9,7 +9,7 @@
|
|
9
9
|
<tr>
|
10
10
|
<th><%=l :work_account %></th>
|
11
11
|
<% [:monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday].each_with_index do |day, i| %>
|
12
|
-
<th align="center"><%=detour_to "#{l(day)} #{week_date(i+1)}", :action => :daily_work_sheet, :id => (@first_date + i).strftime('%Y-%m-%d') %></th>
|
12
|
+
<th align="center"><%=detour_to "#{l(day)} #{week_date(i+1)}", :action => :daily_work_sheet, :id => (@first_date + i).strftime('%Y-%m-%d'), :user_id => (@user != current_user ? @user : nil) %></th>
|
13
13
|
<% end %>
|
14
14
|
<th align="center" nowrap="true"><%=l :week %> <%=@week%></th>
|
15
15
|
</tr>
|
@@ -43,4 +43,7 @@
|
|
43
43
|
<th class="hours"><%=t(week_total)%></th>
|
44
44
|
</tr>
|
45
45
|
</table>
|
46
|
+
|
47
|
+
<%=back_or_link_to l(:back), '' %>
|
48
|
+
|
46
49
|
</div>
|
data/config/database.yml
CHANGED
data/config/environment.rb
CHANGED
@@ -110,10 +110,10 @@ require 'version_from_history'
|
|
110
110
|
require 'user_system'
|
111
111
|
require 'url_for_fix'
|
112
112
|
|
113
|
-
if RUBY_PLATFORM !~ /i386-mswin32/
|
113
|
+
if RUBY_PLATFORM !~ /(i386-mswin32|java)/
|
114
114
|
gem 'slave'
|
115
115
|
require 'slave'
|
116
116
|
work_lock_nagger_thread = Slave.object(:async=>true) {WorkLockNagger.new.nag}
|
117
117
|
else
|
118
|
-
puts
|
118
|
+
puts "Not spawning worklog nagger on #$1 platform."
|
119
119
|
end
|
data/test/fixtures/works.yml
CHANGED
@@ -91,24 +91,4 @@ class WorkAccountsControllerTest < Test::Unit::TestCase
|
|
91
91
|
}
|
92
92
|
end
|
93
93
|
|
94
|
-
def test_works
|
95
|
-
get :works, :id => 1, :report_filter => {:start_on => '2007-04-01', :end_on => '2007-06-13'}
|
96
|
-
|
97
|
-
assert_response :success
|
98
|
-
assert_template 'list'
|
99
|
-
|
100
|
-
assert_not_nil assigns(:works)
|
101
|
-
assert_equal 4, assigns(:works).size
|
102
|
-
end
|
103
|
-
|
104
|
-
def test_works_excel
|
105
|
-
get :works, :id => 1, :report_filter => {:start_on => '2007-04-01', :end_on => '2007-06-13'}, :export => 'excel'
|
106
|
-
|
107
|
-
assert_response :success
|
108
|
-
assert_template 'list_excel'
|
109
|
-
|
110
|
-
assert_not_nil assigns(:works)
|
111
|
-
assert_equal 4, assigns(:works).size
|
112
|
-
end
|
113
|
-
|
114
94
|
end
|
@@ -36,6 +36,26 @@ class WorksControllerTest < Test::Unit::TestCase
|
|
36
36
|
assert_not_nil assigns(:works)
|
37
37
|
end
|
38
38
|
|
39
|
+
def test_list_with_filter
|
40
|
+
get :list, :id => 1, :report_filter => {:start_on => '2007-04-01', :end_on => '2007-06-13'}
|
41
|
+
|
42
|
+
assert_response :success
|
43
|
+
assert_template 'list'
|
44
|
+
|
45
|
+
assert_not_nil assigns(:works)
|
46
|
+
assert_equal 4, assigns(:works).size
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_list_excel
|
50
|
+
get :list, :id => 1, :report_filter => {:start_on => '2007-04-01', :end_on => '2007-06-13'}, :export => 'excel'
|
51
|
+
|
52
|
+
assert_response :success
|
53
|
+
assert_template 'list_excel'
|
54
|
+
|
55
|
+
assert_not_nil assigns(:works)
|
56
|
+
assert_equal 4, assigns(:works).size
|
57
|
+
end
|
58
|
+
|
39
59
|
def test_show
|
40
60
|
get :show, :id => 1
|
41
61
|
|
@@ -118,6 +138,15 @@ class WorksControllerTest < Test::Unit::TestCase
|
|
118
138
|
assert_equal expected_time, assigns(:work).start_time
|
119
139
|
end
|
120
140
|
|
141
|
+
def test_update_time_with_five_minutes
|
142
|
+
before = works(:first)
|
143
|
+
|
144
|
+
post :update_time, :id => 1, :work => {"hours_time"=>"0:05"}
|
145
|
+
|
146
|
+
assert_response :success
|
147
|
+
assert_equal BigDecimal('0.083'), assigns(:work).hours
|
148
|
+
end
|
149
|
+
|
121
150
|
def test_update_row_with_empty_start_time
|
122
151
|
before = works(:first)
|
123
152
|
old_start_date = before.started_on
|
data/test/unit/work_test.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: backlog
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.32.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Uwe Kubosch
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-05-08 00:00:00 +02:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|