backlog 0.29.0 → 0.30.0
Sign up to get free protection for your applications and to get access to all the features.
- 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>
|