backlog 0.29.0 → 0.30.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.
- data/History.txt +25 -0
- data/app/controllers/application.rb +2 -0
- data/app/controllers/work_locks_controller.rb +1 -1
- data/app/controllers/works_controller.rb +6 -8
- data/app/helpers/application_helper.rb +2 -0
- data/app/models/absence.rb +13 -0
- data/app/models/public_holiday.rb +11 -0
- data/app/models/work.rb +4 -5
- data/app/models/work_lock.rb +8 -0
- data/app/models/work_lock_nagger.rb +2 -2
- data/app/views/layouts/_headers.rhtml +1 -1
- data/app/views/layouts/_shortcuts.rhtml +16 -0
- data/app/views/layouts/_shortcuts_js.rhtml +19 -0
- data/app/views/layouts/mwrt002.rhtml +2 -31
- data/app/views/layouts/wide.rhtml +5 -1
- data/app/views/works/_buttons.rhtml +2 -2
- data/app/views/works/_row.rhtml +2 -2
- data/app/views/works/_weekly_work_sheet_buttons.rhtml +3 -3
- data/app/views/works/daily_work_sheet.rhtml +2 -2
- data/app/views/works/update_new_row.rjs +2 -3
- data/app/views/works/update_row.rjs +1 -1
- data/app/views/works/weekly_work_sheet.rhtml +25 -81
- data/app/views/works/weekly_work_sheet_details.rhtml +102 -0
- data/db/migrate/031_expand_work_done_precision.rb +9 -0
- data/db/schema.rb +2 -2
- data/test/functional/works_controller_test.rb +1 -1
- data/vendor/plugins/rails_time/lib/time_of_day.rb +1 -1
- metadata +6 -4
- data/app/views/layouts/absences.rhtml +0 -17
- data/app/views/works/weekly_work_sheet_by_work_account.rhtml +0 -46
data/History.txt
CHANGED
@@ -1,3 +1,28 @@
|
|
1
|
+
== 0.30.0 2008-04-08
|
2
|
+
|
3
|
+
=== Features
|
4
|
+
|
5
|
+
* Added two new shortcuts: ALT-SHIFT-D to go to the Daily Work Sheet
|
6
|
+
and ALT-SHIFT-W to go to the Weekly Work Sheet.
|
7
|
+
|
8
|
+
=== Fixes
|
9
|
+
|
10
|
+
* Add 8 hours of work time for public holidays.
|
11
|
+
* Fixed bad rounding of work record durations.
|
12
|
+
* Fixed so that the "task" field gets focus after the "done" field has changed.
|
13
|
+
* Fixed wrong label for "Sick with doctor's note" radio button in the Daily Work Sheet.
|
14
|
+
It would select the "Sick day" button instead.
|
15
|
+
* Fixed parsing error for time of day: 930, 815 etc.
|
16
|
+
|
17
|
+
=== Internal
|
18
|
+
|
19
|
+
* Renamed the weekly work sheet view from weekly_work_sheet_by_work_account to weekly_work_sheet for shorter URLs.
|
20
|
+
* Moved search call from works_controller to Work model.
|
21
|
+
* Removed non-use of client side JRuby applet.
|
22
|
+
* Extracted shortcut code into partials for use in all layouts.
|
23
|
+
* Removed unused absences layout.
|
24
|
+
* Removed duplicate code for parsing time of day.
|
25
|
+
|
1
26
|
== 0.29.0 2008-04-07
|
2
27
|
|
3
28
|
=== Features
|
@@ -148,6 +148,8 @@ class ApplicationController < ActionController::Base
|
|
148
148
|
{:key => 'Alt-Shift-N', :function => :new_period, :options => {:controller => 'periods', :action => 'new'}},
|
149
149
|
{:key => 'Alt-Ctrl-N', :function => :new_backlog, :options => {:controller => 'backlogs', :action => 'new'}},
|
150
150
|
{:key => 'Alt-Ctrl-G', :function => :new_group, :options => {:controller => 'groups', :action => :new}},
|
151
|
+
{:key => 'Alt-Shift-D', :function => :daily_work_sheet, :options => {:controller => 'works', :action => :daily_work_sheet}},
|
152
|
+
{:key => 'Alt-Shift-W', :function => :weekly_work_sheet, :options => {:controller => 'works', :action => :weekly_work_sheet}},
|
151
153
|
]
|
152
154
|
end
|
153
155
|
|
@@ -61,7 +61,7 @@ class WorkLocksController < ApplicationController
|
|
61
61
|
user_subscribers = current_user.work_lock_subscribers
|
62
62
|
notify_users = (work_account_subscribers + user_subscribers).uniq
|
63
63
|
notify_users.each do |user|
|
64
|
-
week_url = url_for :controller => 'works', :action => :
|
64
|
+
week_url = url_for :controller => 'works', :action => :weekly_work_sheet, :year => @year, :week => @week, :user_id => current_user.id
|
65
65
|
spreadsheet_url = url_for :controller => 'works', :action => :timeliste, :year => @year, :week => @week, :user_id => current_user.id
|
66
66
|
WorkLockNotify.deliver_lock(user, current_user, @week, week_url, spreadsheet_url)
|
67
67
|
end
|
@@ -191,7 +191,7 @@ class WorksController < ApplicationController
|
|
191
191
|
render :layout => 'wide'
|
192
192
|
end
|
193
193
|
|
194
|
-
def
|
194
|
+
def weekly_work_sheet_details
|
195
195
|
@year = (params[:year] && params[:year].to_i) || Date.today.year
|
196
196
|
@week = (params[:week] && params[:week].to_i) || Date.today.cweek
|
197
197
|
@rows = Work.works_for_week(@year, @week)
|
@@ -201,18 +201,16 @@ class WorksController < ApplicationController
|
|
201
201
|
render :layout => 'wide'
|
202
202
|
end
|
203
203
|
|
204
|
-
def
|
204
|
+
def weekly_work_sheet
|
205
205
|
@year = (params[:year] && params[:year].to_i) || Date.today.year
|
206
206
|
@week = (params[:week] && params[:week].to_i) || Date.today.cweek
|
207
207
|
@user = params[:user_id] ? User.find(params[:user_id]) : current_user
|
208
208
|
@work_accounts = Work.works_for_week_by_work_account(@year, @week, @user)
|
209
209
|
@first_date = Date.commercial(@year, @week, 1)
|
210
210
|
@last_date = @first_date + 6
|
211
|
-
@lock = WorkLock.
|
212
|
-
@absences = Absence.
|
213
|
-
(
|
214
|
-
@absences.insert(day, nil) if @absences[day] && @absences[day].on > @first_date + day
|
215
|
-
end
|
211
|
+
@lock = WorkLock.find_by_week(@year, @week)
|
212
|
+
@absences = Absence.find_by_week(@year, @week)
|
213
|
+
@public_holidays = PublicHoliday.find_by_week(@year, @week)
|
216
214
|
render :layout => 'wide'
|
217
215
|
end
|
218
216
|
|
@@ -286,7 +284,7 @@ class WorksController < ApplicationController
|
|
286
284
|
raise "Unknown time format: #{params[:hours_time]}" unless convert_hours_param
|
287
285
|
@work = Work.new(params[:work])
|
288
286
|
@updated_fields = [@field]
|
289
|
-
if @work.hours == 0
|
287
|
+
if @work.hours == 0 && @work.started_on && @work.start_time && @work.completed_at
|
290
288
|
@work.calculate_hours!
|
291
289
|
@updated_fields << :hours_time
|
292
290
|
end
|
@@ -59,6 +59,8 @@ module ApplicationHelper
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def t(time_as_float)
|
62
|
+
return '' unless time_as_float
|
63
|
+
time_as_float = BigDecimal(time_as_float.to_s) unless time_as_float.is_a? BigDecimal
|
62
64
|
"#{time_as_float.to_i}:#{'%02d' % (time_as_float.frac * 60).round}"
|
63
65
|
end
|
64
66
|
|
data/app/models/absence.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
class Absence < ActiveRecord::Base
|
2
|
+
extend UserSystem
|
3
|
+
|
2
4
|
validates_presence_of :user_id
|
3
5
|
validates_presence_of :on
|
4
6
|
validates_presence_of :reason
|
@@ -14,4 +16,15 @@ class Absence < ActiveRecord::Base
|
|
14
16
|
end
|
15
17
|
end
|
16
18
|
|
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)
|
21
|
+
first_date = Date.commercial(year, week, 1)
|
22
|
+
last_date = first_date + 6
|
23
|
+
results = self.find(:all, :conditions => ['"on" BETWEEN ? AND ? AND user_id = ?', first_date, last_date, current_user && current_user.id], :order => '"on"')
|
24
|
+
(0..6).each do |day|
|
25
|
+
results.insert(day, nil) if results[day] && results[day].on > first_date + day
|
26
|
+
end
|
27
|
+
results
|
28
|
+
end
|
29
|
+
|
17
30
|
end
|
@@ -1,2 +1,13 @@
|
|
1
1
|
class PublicHoliday < ActiveRecord::Base
|
2
|
+
# Return an array of either a public holiday or nil for each day of the given week.
|
3
|
+
def self.find_by_week(year, week)
|
4
|
+
first_date = Date.commercial(year, week, 1)
|
5
|
+
last_date = first_date + 6
|
6
|
+
results = self.find(:all, :conditions => ['"on" BETWEEN ? AND ?', first_date, last_date], :order => '"on"')
|
7
|
+
(0..6).each do |day|
|
8
|
+
results.insert(day, nil) if results[day] && results[day].on > first_date + day
|
9
|
+
end
|
10
|
+
results
|
11
|
+
end
|
12
|
+
|
2
13
|
end
|
data/app/models/work.rb
CHANGED
@@ -139,10 +139,9 @@ class Work < ActiveRecord::Base
|
|
139
139
|
self.completed_at = nil
|
140
140
|
return
|
141
141
|
end
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
self.completed_at = Time.local(t.year, t.month, t.day, new_hour, new_minutes)
|
142
|
+
time_of_day = TimeOfDay.parse(new_value)
|
143
|
+
date = started_on || completed_at.to_date || Date.today
|
144
|
+
self.completed_at = date.at time_of_day
|
146
145
|
end
|
147
146
|
|
148
147
|
def calculate_hours!
|
@@ -151,7 +150,7 @@ class Work < ActiveRecord::Base
|
|
151
150
|
end
|
152
151
|
|
153
152
|
def calculate_hours
|
154
|
-
BigDecimal(((completed_at - started_at) / 3600).to_s).round(
|
153
|
+
BigDecimal(((completed_at - started_at) / 3600).to_s).round(3)
|
155
154
|
end
|
156
155
|
|
157
156
|
end
|
data/app/models/work_lock.rb
CHANGED
@@ -1,3 +1,11 @@
|
|
1
1
|
class WorkLock < ActiveRecord::Base
|
2
|
+
extend UserSystem
|
3
|
+
|
2
4
|
belongs_to :user
|
5
|
+
|
6
|
+
def self.find_by_week(year, week)
|
7
|
+
first_date = Date.commercial(year, week, 1)
|
8
|
+
last_date = first_date + 6
|
9
|
+
result = self.find(:first, :conditions => ['user_id = ? AND start_on <= ? AND end_on >= ?', current_user.id, first_date, last_date])
|
10
|
+
end
|
3
11
|
end
|
@@ -13,11 +13,11 @@ class WorkLockNagger
|
|
13
13
|
app_url = config[:app_url]
|
14
14
|
end
|
15
15
|
if app_url
|
16
|
-
url = app_url + 'works/
|
16
|
+
url = app_url + 'works/weekly_work_sheet'
|
17
17
|
else
|
18
18
|
host = Socket::gethostname
|
19
19
|
port = config[:port] || 3000
|
20
|
-
url = url_for(:host => host, :port => port, :controller => 'works', :action => :
|
20
|
+
url = url_for(:host => host, :port => port, :controller => 'works', :action => :weekly_work_sheet)
|
21
21
|
end
|
22
22
|
rescue Exception => e
|
23
23
|
puts "Work Lock Nagger Exception"
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<% if @shortcuts %>
|
2
|
+
<div class="rblock">
|
3
|
+
<div class="btitle">
|
4
|
+
<h4><%=l :shortcuts %></h4>
|
5
|
+
</div>
|
6
|
+
<table>
|
7
|
+
<% @shortcuts.each_with_index do |shortcut, index| %>
|
8
|
+
<% if shortcut[:options] %>
|
9
|
+
<tr><td valign="top"><%= shortcut[:key]%></td><td><%=detour_to l(shortcut[:function]), shortcut[:options], :id => shortcut[:function], :tabindex => index+100 %></td></tr>
|
10
|
+
<% else %>
|
11
|
+
<tr><td valign="top"><%= shortcut[:key]%></td><td><%=l shortcut[:function] %></td></tr>
|
12
|
+
<% end %>
|
13
|
+
<% end %>
|
14
|
+
</table>
|
15
|
+
</div>
|
16
|
+
<% end %>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<script type="text/javascript">
|
2
|
+
function handlePageEvent(event) {
|
3
|
+
if (event.altKey && event.shiftKey && event.charCode == 110) {
|
4
|
+
window.location = '<%=url_for(:controller => 'periods', :action => :new)%>';
|
5
|
+
} else if (event.altKey && event.ctrlKey && event.charCode == 110) {
|
6
|
+
window.location = '<%=url_for(:controller => 'backlogs', :action => :new ) %>';
|
7
|
+
} else if (event.altKey && event.charCode == 110) {
|
8
|
+
window.location = '<%=url_for(:controller => 'tasks', :action => :new, :task => {:backlog_id => (@backlog ? @backlog.id : nil), :period_id => (@period ? @period.id : nil)} ) %>';
|
9
|
+
} else if (event.altKey && event.ctrlKey && event.charCode == 103) {
|
10
|
+
window.location = '<%=url_for(:controller => 'groups', :action => :new ) %>';
|
11
|
+
} else if (event.shiftKey && event.altKey && event.charCode == 100) {
|
12
|
+
window.location = '<%=url_for(:controller => 'works', :action => :daily_work_sheet ) %>';
|
13
|
+
} else if (event.shiftKey && event.altKey && event.charCode == 119) {
|
14
|
+
window.location = '<%=url_for(:controller => 'works', :action => :weekly_work_sheet ) %>';
|
15
|
+
} else {
|
16
|
+
//alert("shift: " + event.shiftKey + ", alt: " + event.altKey + ", ctrl: " + event.ctrlKey + ", " + event.charCode + ", " + event.keyCode);
|
17
|
+
}
|
18
|
+
}
|
19
|
+
</script>
|
@@ -6,21 +6,7 @@
|
|
6
6
|
<%=render :partial => '/layouts/headers' %>
|
7
7
|
</head>
|
8
8
|
|
9
|
-
|
10
|
-
function handlePageEvent(event) {
|
11
|
-
if (event.altKey && event.shiftKey && event.charCode == 110) {
|
12
|
-
window.location = '<%=url_for(:controller => 'periods', :action => :new)%>';
|
13
|
-
} else if (event.altKey && event.ctrlKey && event.charCode == 110) {
|
14
|
-
window.location = '<%=url_for(:controller => 'backlogs', :action => :new ) %>';
|
15
|
-
} else if (event.altKey && event.charCode == 110) {
|
16
|
-
window.location = '<%=url_for(:controller => 'tasks', :action => :new, :task => {:backlog_id => (@backlog ? @backlog.id : nil), :period_id => (@period ? @period.id : nil)} ) %>';
|
17
|
-
} else if (event.altKey && event.ctrlKey && event.charCode == 103) {
|
18
|
-
window.location = '<%=url_for(:controller => 'groups', :action => :new ) %>';
|
19
|
-
} else {
|
20
|
-
// alert("shift: " + event.shiftKey + ", alt: " + event.altKey + ", ctrl: " + event.ctrlKey + ", " + event.charCode + ", " + event.keyCode);
|
21
|
-
}
|
22
|
-
}
|
23
|
-
</script>
|
9
|
+
<%=render :partial => '/layouts/shortcuts_js'%>
|
24
10
|
|
25
11
|
<body onkeypress="handlePageEvent(event);">
|
26
12
|
<div id="wrap">
|
@@ -31,22 +17,7 @@ function handlePageEvent(event) {
|
|
31
17
|
</div>
|
32
18
|
</div>
|
33
19
|
<div id="rightcol">
|
34
|
-
|
35
|
-
<div class="rblock">
|
36
|
-
<div class="btitle">
|
37
|
-
<h4><%=l :shortcuts %></h4>
|
38
|
-
</div>
|
39
|
-
<table>
|
40
|
-
<% @shortcuts.each_with_index do |shortcut, index| %>
|
41
|
-
<% if shortcut[:options] %>
|
42
|
-
<tr><td valign="top"><%= shortcut[:key]%></td><td><%=detour_to l(shortcut[:function]), shortcut[:options], :id => shortcut[:function], :tabindex => index+100 %></td></tr>
|
43
|
-
<% else %>
|
44
|
-
<tr><td valign="top"><%= shortcut[:key]%></td><td><%=l shortcut[:function] %></td></tr>
|
45
|
-
<% end %>
|
46
|
-
<% end %>
|
47
|
-
</table>
|
48
|
-
</div>
|
49
|
-
<% end %>
|
20
|
+
<%=render :partial => '/layouts/shortcuts'%>
|
50
21
|
|
51
22
|
<% if @sidebars %>
|
52
23
|
<% @sidebars.each do |sidebar| %>
|
@@ -8,10 +8,14 @@
|
|
8
8
|
<head>
|
9
9
|
<%=render :partial => '/layouts/headers' %>
|
10
10
|
</head>
|
11
|
-
|
11
|
+
|
12
|
+
<%=render :partial => '/layouts/shortcuts_js'%>
|
13
|
+
|
14
|
+
<body onkeypress="handlePageEvent(event);">
|
12
15
|
|
13
16
|
<div width="793" align="center">
|
14
17
|
<%=render :partial => '/layouts/left_top'%>
|
18
|
+
<%=render :partial => '/layouts/shortcuts'%>
|
15
19
|
<%= yield %>
|
16
20
|
</div>
|
17
21
|
|
@@ -1,6 +1,6 @@
|
|
1
|
-
<%=image_detour_to('hammer.png', l(:weekly_work_sheet), :controller => 'works', :action => :weekly_work_sheet_by_work_account)%>
|
2
|
-
<!--
|
3
1
|
<%=image_detour_to('hammer.png', l(:weekly_work_sheet), :controller => 'works', :action => :weekly_work_sheet)%>
|
2
|
+
<!--
|
3
|
+
<%=image_detour_to('hammer.png', l(:weekly_work_sheet), :controller => 'works', :action => :weekly_work_sheet_details)%>
|
4
4
|
-->
|
5
5
|
<%=link_to(image_tag(url_for("hammer.png"), :alt => l(:daily_work_sheet), :title => l(:daily_work_sheet), :class => 'image-submit'), :controller => 'works', :action => :daily_work_sheet)%>
|
6
6
|
<!--
|
data/app/views/works/_row.rhtml
CHANGED
@@ -67,7 +67,7 @@
|
|
67
67
|
:next_field => 'hours_time', :next_row_id => next_row_id, :previous_row_id => previous_row_id} %>
|
68
68
|
</td>
|
69
69
|
<td align="right">
|
70
|
-
<% remote_form_for :work, :url => {:action => :update_row, :id => @work.id, :next_field => :
|
70
|
+
<% remote_form_for :work, :url => {:action => :update_row, :id => @work.id, :next_field => :task_id} do |f|%>
|
71
71
|
<%=f.text_field :hours_time, :value => (@work.hours && @work.hours > 0 ? t(@work.hours) : ''), :id => "work_#{@work.id}_hours_time", :class => 'task_hours',
|
72
72
|
:onkeypress => "
|
73
73
|
if(event.keyCode == 40) {
|
@@ -79,7 +79,7 @@
|
|
79
79
|
e.focus();
|
80
80
|
e.select();
|
81
81
|
}",
|
82
|
-
:onchange => "new Ajax.Request('/works/update_row/#{@work.id}?next_field=
|
82
|
+
:onchange => "new Ajax.Request('/works/update_row/#{@work.id}?next_field=task_id', {asynchronous:true, evalScripts:true, parameters:Form.serialize(form)})"%>
|
83
83
|
<% end %>
|
84
84
|
</td>
|
85
85
|
<td align="right">
|
@@ -5,10 +5,10 @@
|
|
5
5
|
<% lock_action = @lock ? :unlock : :lock %>
|
6
6
|
[<%=link_to l(lock_action), with_detour(:controller => 'work_locks', :action => lock_action, :year => @year, :week => @week) %>]
|
7
7
|
<%unless controller.action_name == 'weekly_work_sheet'%>
|
8
|
-
[<%=link_to l(:
|
8
|
+
[<%=link_to l(:totals), :action => :weekly_work_sheet, :year => @year, :week => @week %>]
|
9
9
|
<% end %>
|
10
|
-
<%unless controller.action_name == '
|
11
|
-
[<%=link_to l(:
|
10
|
+
<%unless controller.action_name == 'weekly_work_sheet_details'%>
|
11
|
+
[<%=link_to l(:details), :action => :weekly_work_sheet_details, :year => @year, :week => @week %>]
|
12
12
|
<% end %>
|
13
13
|
<% if @lock %>
|
14
14
|
[<%=link_to l(:spreadsheet), :action => :timeliste, :year => @year, :week => @week %>]
|
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
<div style="float: left"><%=image_link_to('arrow_left.png', l(:previous_week), {:id => @date - 7}, :class => nil)%></div>
|
6
6
|
<div style="float: left"><%=image_link_to('arrow_left.png', l(:previous_day), {:id => @date-1}, :class => nil)%></div>
|
7
|
-
<%=link_to l(:weekly_work_sheet), :action => :
|
7
|
+
<%=link_to l(:weekly_work_sheet), :action => :weekly_work_sheet, :year => @year, :week => @week%>
|
8
8
|
<div style="float: right"><%=image_link_to('arrow_right.png', l(:next_week), {:id => @date + 7}, :class => nil)%></div>
|
9
9
|
<div style="float: right"><%=image_link_to('arrow_right.png', l(:next_day), {:id => @date+1}, :class => nil)%></div>
|
10
10
|
|
@@ -21,7 +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_WITH_DOCTOR', :disabled => absence_disabled, :onchange => 'form.submit()' %><label for="
|
24
|
+
<%=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
25
|
<% end %>
|
26
26
|
</div>
|
27
27
|
</td>
|
@@ -16,9 +16,8 @@ if @work && @work.errors.empty?
|
|
16
16
|
end
|
17
17
|
page["work_" + field_name.to_s].value = value
|
18
18
|
end
|
19
|
-
#page
|
20
|
-
#page["work_#{@
|
21
|
-
#page["work_#{@work.id}_#{@next_field}"].focus
|
19
|
+
#page["work_#{@next_field}"].focus
|
20
|
+
#page["work_#{@next_field}"].select
|
22
21
|
else
|
23
22
|
@work.errors.each do |error|
|
24
23
|
page["work_#{@work.id}_#{error[0]}"].addClassName('fieldWithErrors')
|
@@ -1,102 +1,46 @@
|
|
1
|
-
<% @page_title = l(:weekly_work_sheet) +
|
1
|
+
<% @page_title = l(:weekly_work_sheet) + " " + @week.to_s + (@user ? " for #{@user.name}" : '')%>
|
2
2
|
|
3
3
|
<div id="spotlight">
|
4
|
-
<%
|
5
|
-
<% invoicing = @rows.find {|r| r.find {|w| w && w.work_account.enable_invoicing?}} %>
|
6
|
-
<% columns = 2 + (track_times ? 1 : 0) + (invoicing ? 1 : 0) %>
|
7
|
-
|
8
|
-
<h1>Work for week <%=@week%><%=user? ? " for #{user.login}" : ''%></h1>
|
4
|
+
<% columns = 2 %>
|
9
5
|
|
10
6
|
<%=render :partial => 'weekly_work_sheet_buttons'%>
|
11
7
|
|
12
8
|
<table border="1">
|
13
9
|
<tr>
|
10
|
+
<th><%=l :work_account %></th>
|
14
11
|
<% [:monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday].each_with_index do |day, i| %>
|
15
|
-
<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') %></th>
|
16
13
|
<% end %>
|
17
14
|
<th align="center" nowrap="true"><%=l :week %> <%=@week%></th>
|
18
15
|
</tr>
|
19
|
-
<tr>
|
20
|
-
<% (1..7).each do %>
|
21
|
-
<th><%=l :work_account %></th>
|
22
|
-
<% if invoicing %>
|
23
|
-
<th><%=l :invoice_short %></th>
|
24
|
-
<% end %>
|
25
|
-
<% if track_times %>
|
26
|
-
<th><%=l(:start) if track_times %> - <%=l(:stop) if track_times %></th>
|
27
|
-
<% end %>
|
28
|
-
<th><%=l :done %></th>
|
29
|
-
<% end %>
|
30
|
-
<th><%=l :totals %></th>
|
31
|
-
</tr>
|
32
16
|
|
33
|
-
<%
|
17
|
+
<% week_totals = Hash.new(0) %>
|
34
18
|
<% day_totals = Array.new(7, 0) %>
|
35
|
-
<% for
|
19
|
+
<% for work_account, totals in @work_accounts %>
|
36
20
|
<tr>
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
<%=text_field(:work, :description, :id => field_id, :name => "work_#{@work.id}_description", :class => :task_description, :style => 'width: 8em', :disabled => @lock,
|
48
|
-
:onchange => "new Ajax.Updater('#{field_id}', '#{url_for(:action => :update, :id => @work.id)}' + '?work[description]=' + this.value);") %>
|
49
|
-
<% end %>
|
50
|
-
<br/>
|
51
|
-
<!--
|
52
|
-
<%=detour_to(h(@work.task.period.name), :controller => 'periods', :action => :show, :id => @work.task.period) if @work.task && @work.task.period %>
|
53
|
-
-->
|
54
|
-
<%=detour_to(h(@work.work_account.name), :controller => 'work_accounts', :action => :show, :id => @work.work_account_id) %>
|
55
|
-
</td>
|
56
|
-
<% if invoicing %>
|
57
|
-
<td id="invoice_<%=@work.id%>" valign="top">
|
58
|
-
<% form_id = "work_#{@work.id}_invoice_form" %>
|
59
|
-
<% field_id = "work_#{@work.id}_invoice" %>
|
60
|
-
<%=check_box(:work, :invoice, :id => field_id, :name => field_id, :onchange => "new Ajax.Updater('invoice_#{@work}', '#{url_for(:action => :set_work_invoice, :id => @work)}' + '?value=' + this.checked);", :disabled => @lock) if @work.work_account.enable_invoicing? %>
|
61
|
-
</td>
|
62
|
-
<% end %>
|
63
|
-
<% if track_times %>
|
64
|
-
<td align="left" valign="top">
|
65
|
-
<% form_id = "work_#{@work.id}_time_form" %>
|
66
|
-
<% field_id = "work_#{@work.id}_start_time" %>
|
67
|
-
<form id="<%=form_id%>">
|
68
|
-
<%=text_field(:work, :start_time, :class => :task_time, :disabled => @lock,
|
69
|
-
:onchange => "new Ajax.Updater('#{field_id}', '#{url_for(:action => :update, :id => @work.id)}' + '?work[start_time]=' + this.value);") %> -
|
70
|
-
<%=text_field(:work, :completed_at_time, :class => :task_time, :disabled => @lock,
|
71
|
-
:onchange => "new Ajax.Updater('work_#{@work.id}_completed_at_time', '#{url_for(:action => :update, :id => @work.id)}' + '?work[completed_at_time]=' + this.value);") %>
|
72
|
-
</form>
|
73
|
-
</td>
|
74
|
-
<% end %>
|
75
|
-
<td align="right" valign="top">
|
76
|
-
<% form_id = "work_#{@work.id}_hours_form" %>
|
77
|
-
<% field_id = "work_#{@work.id}_hours_time" %>
|
78
|
-
<form id="<%=form_id%>">
|
79
|
-
<%=text_field(:work, :hours_time, :value => '%d:%02d' % [@work.hours , 60 * @work.hours % 60], :class => 'task_hours', :disabled => @lock,
|
80
|
-
:onchange => "new Ajax.Updater('#{field_id}', '#{url_for(:action => :update, :id => @work.id)}' + '?work[hours_time]=' + this.value);") %>
|
81
|
-
</form>
|
82
|
-
</td>
|
83
|
-
<% else %>
|
84
|
-
<td/>
|
85
|
-
<% if invoicing %>
|
86
|
-
<td/>
|
87
|
-
<% end %>
|
88
|
-
<% if track_times %>
|
89
|
-
<td/>
|
90
|
-
<% end %>
|
91
|
-
<td/>
|
21
|
+
<td valign="top">
|
22
|
+
<%=detour_to(h(work_account.name), :controller => 'work_accounts', :action => :show, :id => work_account.id) %>
|
23
|
+
</td>
|
24
|
+
<% totals.each_with_index do |total, day| %>
|
25
|
+
<% if total %>
|
26
|
+
<% day_totals[day] += total %>
|
27
|
+
<% week_totals[work_account] += total %>
|
28
|
+
<td align="right" valign="top"><%=t(total)%></td>
|
29
|
+
<% else %>
|
30
|
+
<td/>
|
92
31
|
<% end %>
|
93
32
|
<% end %>
|
33
|
+
<td align="right"><%=t(week_totals[work_account])%></td>
|
94
34
|
</tr>
|
95
35
|
<% end %>
|
96
36
|
<tr>
|
97
|
-
|
98
|
-
|
99
|
-
<%
|
100
|
-
<th class="hours"><%=
|
37
|
+
<th><%=l :totals%></th>
|
38
|
+
<% (0..6).each do |day| %>
|
39
|
+
<% day_totals[day] += BigDecimal('8') if @absences[day] || @public_holidays[day] %>
|
40
|
+
<th class="hours"><%=t(day_totals[day]) if day_totals[day] > 0%></th>
|
41
|
+
<% end %>
|
42
|
+
<% week_total = day_totals.inject(BigDecimal('0')) {|total, day_total| total + day_total} %>
|
43
|
+
<th class="hours"><%=t(week_total)%></th>
|
101
44
|
</tr>
|
102
45
|
</table>
|
46
|
+
</div>
|
@@ -0,0 +1,102 @@
|
|
1
|
+
<% @page_title = l(:weekly_work_sheet) + (@period ? " for #{@period}" : '') + (user? ? " for #{user.login}" : '')%>
|
2
|
+
|
3
|
+
<div id="spotlight">
|
4
|
+
<% track_times = @rows.find {|r| r.find {|w| w && w.work_account.track_times?}} %>
|
5
|
+
<% invoicing = @rows.find {|r| r.find {|w| w && w.work_account.enable_invoicing?}} %>
|
6
|
+
<% columns = 2 + (track_times ? 1 : 0) + (invoicing ? 1 : 0) %>
|
7
|
+
|
8
|
+
<h1>Work for week <%=@week%><%=user? ? " for #{user.login}" : ''%></h1>
|
9
|
+
|
10
|
+
<%=render :partial => 'weekly_work_sheet_buttons'%>
|
11
|
+
|
12
|
+
<table border="1">
|
13
|
+
<tr>
|
14
|
+
<% [:monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday].each_with_index do |day, i| %>
|
15
|
+
<th colspan="<%=columns%>" align="center"><%=detour_to "#{l(day)} #{week_date(i+1)}", :action => :daily_work_sheet, :id => (@first_date + i).strftime('%Y-%m-%d') %></th>
|
16
|
+
<% end %>
|
17
|
+
<th align="center" nowrap="true"><%=l :week %> <%=@week%></th>
|
18
|
+
</tr>
|
19
|
+
<tr>
|
20
|
+
<% (1..7).each do %>
|
21
|
+
<th><%=l :work_account %></th>
|
22
|
+
<% if invoicing %>
|
23
|
+
<th><%=l :invoice_short %></th>
|
24
|
+
<% end %>
|
25
|
+
<% if track_times %>
|
26
|
+
<th><%=l(:start) if track_times %> - <%=l(:stop) if track_times %></th>
|
27
|
+
<% end %>
|
28
|
+
<th><%=l :done %></th>
|
29
|
+
<% end %>
|
30
|
+
<th><%=l :totals %></th>
|
31
|
+
</tr>
|
32
|
+
|
33
|
+
<% week_total = 0 %>
|
34
|
+
<% day_totals = Array.new(7, 0) %>
|
35
|
+
<% for row in @rows %>
|
36
|
+
<tr>
|
37
|
+
<% row.each_with_index do |@work, day| %>
|
38
|
+
<% if @work %>
|
39
|
+
<% day_totals[day] += @work.hours %>
|
40
|
+
<% week_total += @work.hours %>
|
41
|
+
<td valign="top">
|
42
|
+
<%=image_detour_to('hammer.png', l(:edit), {:controller => 'works', :action => :edit, :id => @work.id}, {:style => "float: left"}) %>
|
43
|
+
<% if @work.task_id %>
|
44
|
+
<%=detour_to(h(@work.task.description), :controller => 'tasks', :action => :edit, :id => @work.task_id)%>
|
45
|
+
<% else %>
|
46
|
+
<% field_id = "work_#{@work.id}_description" %>
|
47
|
+
<%=text_field(:work, :description, :id => field_id, :name => "work_#{@work.id}_description", :class => :task_description, :style => 'width: 8em', :disabled => @lock,
|
48
|
+
:onchange => "new Ajax.Updater('#{field_id}', '#{url_for(:action => :update, :id => @work.id)}' + '?work[description]=' + this.value);") %>
|
49
|
+
<% end %>
|
50
|
+
<br/>
|
51
|
+
<!--
|
52
|
+
<%=detour_to(h(@work.task.period.name), :controller => 'periods', :action => :show, :id => @work.task.period) if @work.task && @work.task.period %>
|
53
|
+
-->
|
54
|
+
<%=detour_to(h(@work.work_account.name), :controller => 'work_accounts', :action => :show, :id => @work.work_account_id) %>
|
55
|
+
</td>
|
56
|
+
<% if invoicing %>
|
57
|
+
<td id="invoice_<%=@work.id%>" valign="top">
|
58
|
+
<% form_id = "work_#{@work.id}_invoice_form" %>
|
59
|
+
<% field_id = "work_#{@work.id}_invoice" %>
|
60
|
+
<%=check_box(:work, :invoice, :id => field_id, :name => field_id, :onchange => "new Ajax.Updater('invoice_#{@work}', '#{url_for(:action => :set_work_invoice, :id => @work)}' + '?value=' + this.checked);", :disabled => @lock) if @work.work_account.enable_invoicing? %>
|
61
|
+
</td>
|
62
|
+
<% end %>
|
63
|
+
<% if track_times %>
|
64
|
+
<td align="left" valign="top">
|
65
|
+
<% form_id = "work_#{@work.id}_time_form" %>
|
66
|
+
<% field_id = "work_#{@work.id}_start_time" %>
|
67
|
+
<form id="<%=form_id%>">
|
68
|
+
<%=text_field(:work, :start_time, :class => :task_time, :disabled => @lock,
|
69
|
+
:onchange => "new Ajax.Updater('#{field_id}', '#{url_for(:action => :update, :id => @work.id)}' + '?work[start_time]=' + this.value);") %> -
|
70
|
+
<%=text_field(:work, :completed_at_time, :class => :task_time, :disabled => @lock,
|
71
|
+
:onchange => "new Ajax.Updater('work_#{@work.id}_completed_at_time', '#{url_for(:action => :update, :id => @work.id)}' + '?work[completed_at_time]=' + this.value);") %>
|
72
|
+
</form>
|
73
|
+
</td>
|
74
|
+
<% end %>
|
75
|
+
<td align="right" valign="top">
|
76
|
+
<% form_id = "work_#{@work.id}_hours_form" %>
|
77
|
+
<% field_id = "work_#{@work.id}_hours_time" %>
|
78
|
+
<form id="<%=form_id%>">
|
79
|
+
<%=text_field(:work, :hours_time, :value => '%d:%02d' % [@work.hours , 60 * @work.hours % 60], :class => 'task_hours', :disabled => @lock,
|
80
|
+
:onchange => "new Ajax.Updater('#{field_id}', '#{url_for(:action => :update, :id => @work.id)}' + '?work[hours_time]=' + this.value);") %>
|
81
|
+
</form>
|
82
|
+
</td>
|
83
|
+
<% else %>
|
84
|
+
<td/>
|
85
|
+
<% if invoicing %>
|
86
|
+
<td/>
|
87
|
+
<% end %>
|
88
|
+
<% if track_times %>
|
89
|
+
<td/>
|
90
|
+
<% end %>
|
91
|
+
<td/>
|
92
|
+
<% end %>
|
93
|
+
<% end %>
|
94
|
+
</tr>
|
95
|
+
<% end %>
|
96
|
+
<tr>
|
97
|
+
<% (0..6).each do |day| %>
|
98
|
+
<th class="hours" colspan="<%=columns%>"><%='%.2f' % day_totals[day] if day_totals[day] > 0%></th>
|
99
|
+
<% end %>
|
100
|
+
<th class="hours"><%= '%.2f' % week_total %></th>
|
101
|
+
</tr>
|
102
|
+
</table>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class ExpandWorkDonePrecision < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
change_column :works, :hours, :decimal, :precision => 6, :scale => 3, :null => false, :default => 0
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.down
|
7
|
+
change_column :works, :hours, :decimal, :precision => 6, :scale => 2, :null => false, :default => 0
|
8
|
+
end
|
9
|
+
end
|
data/db/schema.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
# migrations feature of ActiveRecord to incrementally modify your database, and
|
3
3
|
# then regenerate this schema definition.
|
4
4
|
|
5
|
-
ActiveRecord::Schema.define(:version =>
|
5
|
+
ActiveRecord::Schema.define(:version => 31) do
|
6
6
|
|
7
7
|
create_table "absences", :force => true do |t|
|
8
8
|
t.column "user_id", :integer, :null => false
|
@@ -135,7 +135,7 @@ ActiveRecord::Schema.define(:version => 30) do
|
|
135
135
|
|
136
136
|
create_table "works", :force => true do |t|
|
137
137
|
t.column "task_id", :integer
|
138
|
-
t.column "hours", :decimal, :precision => 6, :scale =>
|
138
|
+
t.column "hours", :decimal, :precision => 6, :scale => 3, :default => 0.0, :null => false
|
139
139
|
t.column "completed_at", :datetime
|
140
140
|
t.column "user_id", :integer
|
141
141
|
t.column "invoice", :boolean
|
@@ -87,7 +87,7 @@ class WorksControllerTest < Test::Unit::TestCase
|
|
87
87
|
assert_redirected_to :controller => 'periods', :action => 'show', :id => periods(:past).id, :task_id => 1
|
88
88
|
|
89
89
|
assert_equal num_works + 1, Work.count
|
90
|
-
assert_equal BigDecimal('0.
|
90
|
+
assert_equal BigDecimal('0.333'), assigns(:work).hours
|
91
91
|
end
|
92
92
|
|
93
93
|
def test_edit
|
@@ -21,7 +21,7 @@ class TimeOfDay
|
|
21
21
|
def self.parse(string)
|
22
22
|
string.strip!
|
23
23
|
return nil if string.empty?
|
24
|
-
raise "Illegal time format: '#{string}'" unless string =~
|
24
|
+
raise "Illegal time format: '#{string}'" unless string =~ /^(\d{1,2}):?(\d{2})?(?::(\d{1,2}))?$/
|
25
25
|
self.new($1.to_i, $2.to_i, $3.to_i)
|
26
26
|
end
|
27
27
|
|
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.30.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-04-
|
12
|
+
date: 2008-04-09 00:00:00 +02:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -171,11 +171,12 @@ files:
|
|
171
171
|
- app/views/works/_form.rhtml
|
172
172
|
- app/views/works/weekly_work_sheet.rhtml
|
173
173
|
- app/views/works/_weekly_work_sheet_buttons.rhtml
|
174
|
+
- app/views/works/weekly_work_sheet_details.rhtml
|
174
175
|
- app/views/works/show.rhtml
|
175
|
-
- app/views/works/weekly_work_sheet_by_work_account.rhtml
|
176
176
|
- app/views/layouts
|
177
|
+
- app/views/layouts/_shortcuts_js.rhtml
|
177
178
|
- app/views/layouts/wide.rhtml
|
178
|
-
- app/views/layouts/
|
179
|
+
- app/views/layouts/_shortcuts.rhtml
|
179
180
|
- app/views/layouts/mwrt002.rhtml
|
180
181
|
- app/views/layouts/_headers.rhtml
|
181
182
|
- app/views/layouts/_left_top.rhtml
|
@@ -709,6 +710,7 @@ files:
|
|
709
710
|
- db/migrate/018_create_groups.rb
|
710
711
|
- db/migrate/003_add_estimation_points.rb
|
711
712
|
- db/migrate/010_add_work_start.rb
|
713
|
+
- db/migrate/031_expand_work_done_precision.rb
|
712
714
|
- db/migrate/007_add_task_created_at.rb
|
713
715
|
- db/migrate/016_add_invoicable_flag.rb
|
714
716
|
- db/migrate/009_add_subtasks.rb
|
@@ -1,17 +0,0 @@
|
|
1
|
-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
2
|
-
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
3
|
-
|
4
|
-
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
5
|
-
<head>
|
6
|
-
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
|
7
|
-
<title>Absences: <%= controller.action_name %></title>
|
8
|
-
<%= stylesheet_link_tag 'scaffold' %>
|
9
|
-
</head>
|
10
|
-
<body>
|
11
|
-
|
12
|
-
<p style="color: green"><%= flash[:notice] %></p>
|
13
|
-
|
14
|
-
<%= yield %>
|
15
|
-
|
16
|
-
</body>
|
17
|
-
</html>
|
@@ -1,46 +0,0 @@
|
|
1
|
-
<% @page_title = l(:weekly_work_sheet) + " " + @week.to_s + (@user ? " for #{@user.name}" : '')%>
|
2
|
-
|
3
|
-
<div id="spotlight">
|
4
|
-
<% columns = 2 %>
|
5
|
-
|
6
|
-
<%=render :partial => 'weekly_work_sheet_buttons'%>
|
7
|
-
|
8
|
-
<table border="1">
|
9
|
-
<tr>
|
10
|
-
<th><%=l :work_account %></th>
|
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>
|
13
|
-
<% end %>
|
14
|
-
<th align="center" nowrap="true"><%=l :week %> <%=@week%></th>
|
15
|
-
</tr>
|
16
|
-
|
17
|
-
<% week_totals = Hash.new(0) %>
|
18
|
-
<% day_totals = Array.new(7, 0) %>
|
19
|
-
<% for work_account, totals in @work_accounts %>
|
20
|
-
<tr>
|
21
|
-
<td valign="top">
|
22
|
-
<%=detour_to(h(work_account.name), :controller => 'work_accounts', :action => :show, :id => work_account.id) %>
|
23
|
-
</td>
|
24
|
-
<% totals.each_with_index do |total, day| %>
|
25
|
-
<% if total %>
|
26
|
-
<% day_totals[day] += total %>
|
27
|
-
<% week_totals[work_account] += total %>
|
28
|
-
<td align="right" valign="top"><%='%d:%02d' % [total, 60 * total % 60]%></td>
|
29
|
-
<% else %>
|
30
|
-
<td/>
|
31
|
-
<% end %>
|
32
|
-
<% end %>
|
33
|
-
<td align="right"><%='%d:%02d' % [week_totals[work_account], 60 * week_totals[work_account] % 60]%></td>
|
34
|
-
</tr>
|
35
|
-
<% end %>
|
36
|
-
<tr>
|
37
|
-
<th><%=l :totals%></th>
|
38
|
-
<% (0..6).each do |day| %>
|
39
|
-
<% day_totals[day] += 8 if @absences[day] %>
|
40
|
-
<th class="hours"><%='%d:%02d' % [day_totals[day], 60 * day_totals[day] % 60] if day_totals[day] > 0%></th>
|
41
|
-
<% end %>
|
42
|
-
<% week_total = day_totals.inject(0) {|total, day_total| total + day_total} %>
|
43
|
-
<th class="hours"><%= '%d:%02d' % [week_total, 60 * week_total % 60] %></th>
|
44
|
-
</tr>
|
45
|
-
</table>
|
46
|
-
</div>
|