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.
@@ -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 => :weekly_work_sheet_by_work_account, :year => @year, :week => @week, :user_id => current_user.id
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 weekly_work_sheet
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 weekly_work_sheet_by_work_account
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.find(:first, :conditions => ['user_id = ? AND start_on <= ? AND end_on >= ?', current_user.id, @first_date, @last_date])
212
- @absences = Absence.find(:all, :conditions => ['"on" BETWEEN ? AND ?', @first_date, @last_date], :order => '"on"')
213
- (0..6).each do |day|
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 && @work.started_on && @work.start_time && @work.completed_at
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
 
@@ -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
@@ -139,10 +139,9 @@ class Work < ActiveRecord::Base
139
139
  self.completed_at = nil
140
140
  return
141
141
  end
142
- raise "invalid time format: #{new_value}" unless new_value =~ /^(\d{0,2}):?(\d{2})?$/
143
- new_hour, new_minutes = $1, $2
144
- t = started_on || completed_at || Time.now
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(2)
153
+ BigDecimal(((completed_at - started_at) / 3600).to_s).round(3)
155
154
  end
156
155
 
157
156
  end
@@ -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/weekly_work_sheet_by_work_account'
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 => :weekly_work_sheet_by_work_account)
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"
@@ -20,4 +20,4 @@
20
20
  <%= javascript_include_tag "effects" %>
21
21
  <%= javascript_include_tag "controls" %>
22
22
  <%= javascript_include_tag "dragdrop" %>
23
- <%= javascript_include_tag "ruby" %>
23
+ <%#= javascript_include_tag "ruby" %>
@@ -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
- <script type="text/javascript">
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
- <% if @shortcuts %>
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
- <body>
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
  <!--
@@ -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 => :invoice} do |f|%>
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=invoice', {asynchronous:true, evalScripts:true, parameters:Form.serialize(form)})"%>
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(:details), :action => :weekly_work_sheet, :year => @year, :week => @week %>]
8
+ [<%=link_to l(:totals), :action => :weekly_work_sheet, :year => @year, :week => @week %>]
9
9
  <% end %>
10
- <%unless controller.action_name == 'weekly_work_sheet_by_work_account'%>
11
- [<%=link_to l(:totals), :action => :weekly_work_sheet_by_work_account, :year => @year, :week => @week %>]
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 => :weekly_work_sheet_by_work_account, :year => @year, :week => @week%>
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="absence_reason_sick">Sick day with doctor's note</label>
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.replace "work_#{@work.id}", :partial => 'row', :object => @work
20
- #page["work_#{@work.id}_#{@next_field}"].select
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')
@@ -9,5 +9,5 @@ else
9
9
  end
10
10
  end
11
11
  if @day_total
12
- page.replace_html "hours_total", "#{'%d:%02d' % [@day_total.to_i, 60 * (@day_total % 1)]}"
12
+ page.replace_html "hours_total", "#{t @day_total}"
13
13
  end
@@ -1,102 +1,46 @@
1
- <% @page_title = l(:weekly_work_sheet) + (@period ? " for #{@period}" : '') + (user? ? " for #{user.login}" : '')%>
1
+ <% @page_title = l(:weekly_work_sheet) + " " + @week.to_s + (@user ? " for #{@user.name}" : '')%>
2
2
 
3
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>
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 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>
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
- <% week_total = 0 %>
17
+ <% week_totals = Hash.new(0) %>
34
18
  <% day_totals = Array.new(7, 0) %>
35
- <% for row in @rows %>
19
+ <% for work_account, totals in @work_accounts %>
36
20
  <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/>
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
- <% (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>
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
@@ -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 => 30) do
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 => 2, :default => 0.0, :null => false
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.33'), assigns(:work).hours
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 =~ /(\d{1,2}):?(\d{2})?(?::(\d{1,2}))?/
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.29.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-07 00:00:00 +02:00
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/absences.rhtml
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>