backlog 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. data/History.txt +19 -0
  2. data/app/controllers/periods_controller.rb +0 -14
  3. data/app/controllers/tasks_controller.rb +15 -4
  4. data/app/controllers/user_controller.rb +15 -1
  5. data/app/controllers/work_accounts_controller.rb +1 -1
  6. data/app/controllers/work_locks_controller.rb +88 -0
  7. data/app/controllers/works_controller.rb +29 -5
  8. data/app/helpers/work_locks_helper.rb +2 -0
  9. data/app/models/task.rb +1 -1
  10. data/app/models/user.rb +1 -0
  11. data/app/models/user_notify.rb +15 -0
  12. data/app/models/work.rb +28 -6
  13. data/app/models/work_account.rb +2 -0
  14. data/app/models/work_lock.rb +3 -0
  15. data/app/models/work_lock_notify.rb +27 -0
  16. data/app/models/work_lock_subscription.rb +3 -0
  17. data/app/views/display_notice.rjs +1 -0
  18. data/app/views/layouts/_left_top.rhtml +1 -0
  19. data/app/views/tasks/_task.rhtml +2 -2
  20. data/app/views/user/edit.rhtml +15 -2
  21. data/app/views/user/toggle_work_lock_monitoring.rjs +2 -0
  22. data/app/views/user_notify/monitoring_en.rhtml +3 -0
  23. data/app/views/user_notify/monitoring_no.rhtml +3 -0
  24. data/app/views/work_lock_notify/lock_en.rhtml +7 -0
  25. data/app/views/work_lock_notify/lock_no.rhtml +7 -0
  26. data/app/views/work_locks/_form.rhtml +10 -0
  27. data/app/views/work_locks/edit.rhtml +9 -0
  28. data/app/views/work_locks/list.rhtml +27 -0
  29. data/app/views/work_locks/new.rhtml +8 -0
  30. data/app/views/work_locks/show.rhtml +8 -0
  31. data/app/views/works/_buttons.rhtml +5 -0
  32. data/app/views/works/_form.rhtml +4 -1
  33. data/app/views/works/_row.rhtml +14 -17
  34. data/app/views/works/_row_field.rhtml +1 -1
  35. data/app/views/works/daily_work_sheet.rhtml +16 -25
  36. data/app/views/works/timeliste.rhtml +12 -12
  37. data/app/views/works/update_row.rjs +2 -4
  38. data/app/views/works/weekly_work_sheet.rhtml +17 -17
  39. data/app/views/works/weekly_work_sheet_by_work_account.rhtml +57 -0
  40. data/db/migrate/027_create_work_locks.rb +25 -0
  41. data/db/schema.rb +25 -1
  42. data/public/images/delete.png +0 -0
  43. data/public/images/email.png +0 -0
  44. data/public/images/email_grey.png +0 -0
  45. data/public/images/refresh.png +0 -0
  46. data/test/fixtures/work_lock_subscriptions.yml +7 -0
  47. data/test/fixtures/work_locks.yml +11 -0
  48. data/test/functional/estimates_controller_test.rb +1 -1
  49. data/test/functional/groups_controller_test.rb +1 -1
  50. data/test/functional/user_controller_test.rb +2 -2
  51. data/test/functional/work_locks_controller_test.rb +93 -0
  52. data/test/integration/user_system_test.rb +1 -1
  53. data/test/test_helper.rb +1 -1
  54. data/test/unit/estimate_test.rb +1 -1
  55. data/test/unit/group_test.rb +1 -1
  56. data/test/unit/period_test.rb +1 -1
  57. data/test/unit/task_test.rb +1 -1
  58. data/test/unit/user_test.rb +1 -1
  59. data/test/unit/work_account_test.rb +1 -1
  60. data/test/unit/work_lock_subscription_test.rb +10 -0
  61. data/test/unit/work_lock_test.rb +10 -0
  62. data/test/unit/work_test.rb +1 -1
  63. metadata +31 -2
@@ -1,19 +1,15 @@
1
- <tr id="work_<%=@work.id%>">
1
+ <tr id="work_<%=@work.id%>" style="border: 1px solid black">
2
2
  <td>
3
- <%=image_detour_to('period.png', l(:period), :controller => 'periods', :action => :show, :id => @work.task.period) if @work.task %>
4
- </td>
5
- <td>
6
- <% if @work.task %>
7
- <%=link_to(@work.task.root_task.backlog.name, :controller => 'backlogs', :action => :show)%> /
8
- <%=link_to(@work.task.description, :controller => 'tasks', :action => :edit)%>
9
- <% end %>
10
- </td>
11
- <td>
12
- <%=link_to(@work.work_account.name, :controller => 'work_accounts', :action => :show, :id => @work.work_account.id) if @work.work_account%>
3
+ <% remote_form_for :work, :url => {:action => :update_row, :id => @work.id, :field => :description}, :html => {:id => "work_#{@work.id}_form"} do |f|%>
4
+ <%=f.select :work_account_id, @work_accounts.map {|wa| [wa.name, wa.id]}, {}, :onchange => "new Ajax.Request('/works/update_row/#{@work.id}?field=description', {asynchronous:true, evalScripts:true, parameters:Form.serialize(form)})"%>
5
+ <% end %>
13
6
  </td>
14
7
  <td>
15
- <%=in_place_editor_field :work, :description, :id => "work_#{@work.id}_description", :size => 16, :class => :task_description%>
16
- </td>
8
+ <!--
9
+ <%=text_field :work, :description, :id => "work_#{@work.id}_description", :size => 16, :class => :task_description%>
10
+ -->
11
+ <%=render :partial => 'row_field', :locals => {:field => 'description'} %>
12
+ </span>
17
13
  <td align="right">
18
14
  <%=render :partial => 'row_field', :locals => {:field => 'started_at_time'} %>
19
15
  </td>
@@ -25,9 +21,10 @@
25
21
  <%=f.text_field :hours_time, :value => t(@work.hours), :class => 'task_hours'%>
26
22
  <% end %>
27
23
  </td>
28
- <td align="right">
29
- <form>
30
- <%=image_detour_to('ernes_stop.png', l(:delete), {:controller => 'works', :action => :destroy, :id => @work, :confirm => true}, nil, true)%>
31
- </form>
24
+ <td>
25
+ <%=image_detour_to('delete.png', l(:delete), {:controller => 'works', :action => :destroy, :id => @work, :confirm => true}, nil, true)%>
26
+ <% if @work.task %>
27
+ <%=image_detour_to('task.png', "#{l :task}: #{@work.task.description}", {:controller => 'tasks', :action => :edit, :id => @work.task.id})%>
28
+ <% end %>
32
29
  </td>
33
30
  </tr>
@@ -1,3 +1,3 @@
1
1
  <% remote_form_for :work, :url => {:action => :update_row, :id => @work.id, :field => field} do |f|%>
2
- <%=text_field :work, field, :class => ('task_time' if field=~/_at_time$/) %>
2
+ <%=text_field :work, field, :id => "work_#{@work.id}_#{field}", :class => ('task_time' if field=~/_at_time$/) %>
3
3
  <% end %>
@@ -2,17 +2,15 @@
2
2
 
3
3
  <div id="spotlight">
4
4
 
5
- <div style="float: left"><%=link_to(image_tag('arrow_left.png'), :id => (@date - 7))%></div>
6
- <div style="float: left"><%=link_to(image_tag('arrow_left.png'), :id => @date-1)%></div>
7
- <div style="float: right"><%=link_to(image_tag('arrow_right.png'), :id => (@date + 7))%></div>
8
- <div style="float: right"><%=link_to(image_tag('arrow_right.png'), :id => @date+1)%></div>
5
+ <div style="float: left"><%=image_link_to('arrow_left.png', l(:previous_week), {:id => @date - 7}, :class => nil)%></div>
6
+ <div style="float: left"><%=image_link_to('arrow_left.png', l(:previous_day), {:id => @date-1}, :class => nil)%></div>
7
+ <div style="float: right"><%=image_link_to('arrow_right.png', l(:next_week), {:id => @date + 7}, :class => nil)%></div>
8
+ <div style="float: right"><%=image_link_to('arrow_right.png', l(:next_day), {:id => @date+1}, :class => nil)%></div>
9
9
 
10
10
  <br clear="all" />
11
11
 
12
12
  <table sstyle="width: 100%" border="0">
13
13
  <tr>
14
- <td/>
15
- <th><%=l :backlog %> / <%=l :task %></th>
16
14
  <th><%=l :account %></th>
17
15
  <th><%=l :description %></th>
18
16
  <th><%=l :started_at %></th>
@@ -26,24 +24,15 @@
26
24
  <% day_total += @work.hours if @work %>
27
25
  <%=render :partial => 'row'%>
28
26
  <% end %>
27
+ <% last_work = @work %>
28
+ <% @work = nil %>
29
29
 
30
-
30
+ <% if last_work.completed_at %>
31
31
  <% form_tag with_detour(:controller => 'works', :action => 'create') do %>
32
32
  <%=submit_tag('checkmark', :value => l(:save), :style => 'display: none')%>
33
- <%=hidden_field :work, :completed_at, :value => Time.now %>
33
+ <%=hidden_field :work, :completed_at, :value => @date %>
34
34
 
35
35
  <tr>
36
- <td width="22"/>
37
- <td>
38
- <script type="text/javascript">
39
- function set_backlog_for_task() {
40
- work_task_description_auto_completer.url = '<%=url_for(:action => :auto_complete_for_work_task_description)%>?backlog_name=' + $('work_backlog_name').value;
41
- }
42
-
43
- </script>
44
- <%=text_field_with_auto_complete :work, :backlog_name, {:value => '', :size => 16}, {:after_update_element => "function(element, value) {set_backlog_for_task()}"} %>
45
- <%=text_field_with_auto_complete(:work, :task_description, {:value => '', :size => 16, :class => :task_description}, :url => url_for({:action => :auto_complete_for_work_task_description, :backlog_name => "' + $('work_backlog_name').value + '", :escape => false}), :after_update_element => "function(element,value) {if ($('work_backlog_name').value == '') { $('work_backlog_name').value = element.value.split(/: /)[0];element.value = element.value.split(/: /)[1]};set_backlog_for_task();return true; }") %>
46
- </td>
47
36
  <td valign="bottom">
48
37
  <%=text_field_with_auto_complete :work, :work_account_name, {:value => '', :size => 16, :class => :task_description} %>
49
38
  </td>
@@ -51,16 +40,15 @@ function set_backlog_for_task() {
51
40
  <%=text_field_with_auto_complete :work, :description, {:size => 16, :class => :task_description} %>
52
41
  </td>
53
42
  <td align="right" valign="bottom">
54
- <%=text_field :work, :started_at_time, :value => @work.started_at.strftime('%H:%M'), :class => 'task_time' %>
43
+ <%=text_field :work, :started_at_time, :value => last_work && last_work.completed_at.strftime('%H:%M'), :class => 'task_time' %>
55
44
  </td>
56
45
  <td align="right" valign="bottom">
57
- <%=text_field :work, :completed_at_time, :class => 'task_time', :value => @work.completed_at.strftime('%H:%M') %>
46
+ <%=text_field :work, :completed_at_time, :class => 'task_time', :value => @work && @work.completed_at.strftime('%H:%M') %>
58
47
  </td>
59
48
  <td align="right" valign="bottom">
60
49
  <%=text_field :work, :hours_time, :value => '', :class => 'task_hours' %>
61
50
  </td>
62
51
  <td valign="bottom">
63
- <%=image_button_to('arrow_left.png', l(:calculate), :controller => 'backlogs', :action => :show) %>
64
52
  </td>
65
53
  </tr>
66
54
  <tr>
@@ -68,10 +56,9 @@ function set_backlog_for_task() {
68
56
  <th><%=l :totals %></th>
69
57
  <th/>
70
58
  <th/>
71
- <th/>
72
- <th/>
73
59
  <th class="hours"><%='%.2f' % day_total %></th>
74
60
  </tr>
61
+ <% end %>
75
62
  </table>
76
63
  <%= submit_tag l(:save) %>
77
64
  <%= back_or_link_to l(:back), '' %>
@@ -89,7 +76,11 @@ function set_backlog_for_task() {
89
76
 
90
77
  <script type="text/JavaScript">
91
78
  //<!--
92
- start_field = $('work_description')
79
+ <% if last_work.completed_at %>
80
+ start_field = $('work_work_account_name')
81
+ <% else %>
82
+ start_field = $('work_<%=last_work.id%>_completed_at_time')
83
+ <% end %>
93
84
  start_field.focus();
94
85
  start_field.select();
95
86
  //-->
@@ -449,11 +449,11 @@
449
449
  <Cell ss:StyleID="s37"><Data ss:Type="String">Merknad</Data></Cell>
450
450
  </Row>
451
451
 
452
- <% for backlog_id in @backlogs.keys %>
453
- <% invoice_works_per_day = @backlogs[backlog_id][0]%>
452
+ <% for work_account_id in @work_totals_per_work_account.keys %>
453
+ <% invoice_works_per_day = @work_totals_per_work_account[work_account_id][0]%>
454
454
  <% if invoice_works_per_day.find{|total| total > 0} %>
455
455
  <Row>
456
- <Cell ss:StyleID="s24"><Data ss:Type="Number"><%=Backlog.find(backlog_id).invoice_code %></Data></Cell>
456
+ <Cell ss:StyleID="s24"><Data ss:Type="Number"><%=Backlog.find(work_account_id).invoice_code %></Data></Cell>
457
457
  <Cell ss:StyleID="s25"/>
458
458
  <Cell ss:StyleID="s25"><Data ss:Type="String"> X</Data></Cell>
459
459
  <Cell ss:StyleID="s25"/>
@@ -468,10 +468,10 @@
468
468
  </Row>
469
469
  <% end %>
470
470
 
471
- <% internal_works_per_day = @backlogs[backlog_id][1]%>
471
+ <% internal_works_per_day = @work_totals_per_work_account[work_account_id][1]%>
472
472
  <% if internal_works_per_day.find{|total| total > 0} %>
473
473
  <Row>
474
- <Cell ss:StyleID="s24"><Data ss:Type="String"><%=Backlog.find(backlog_id).name %></Data></Cell>
474
+ <Cell ss:StyleID="s24"><Data ss:Type="String"><%=WorkAccount.find(work_account_id).name %></Data></Cell>
475
475
  <Cell ss:StyleID="s25"><Data ss:Type="String">Mesta2</Data></Cell>
476
476
  <Cell ss:StyleID="s25"/>
477
477
  <Cell ss:StyleID="s25"/>
@@ -527,25 +527,25 @@
527
527
  <Cell ss:StyleID="s46"/>
528
528
  <Cell ss:StyleID="s46"/>
529
529
  <Cell ss:StyleID="s46"
530
- ss:Formula="=IF(SUM(R[-<%=@backlogs.size*2+2%>]C:R[-1]C),SUM(R[-<%=@backlogs.size*2+2%>]C:R[-1]C),&quot;&quot;)"><Data
530
+ ss:Formula="=IF(SUM(R[-<%=@work_totals_per_work_account.size*2+2%>]C:R[-1]C),SUM(R[-<%=@work_totals_per_work_account.size*2+2%>]C:R[-1]C),&quot;&quot;)"><Data
531
531
  ss:Type="String"></Data></Cell>
532
532
  <Cell ss:StyleID="s46"
533
- ss:Formula="=IF(SUM(R[-<%=@backlogs.size*2+2%>]C:R[-1]C),SUM(R[-<%=@backlogs.size*2+2%>]C:R[-1]C),&quot;&quot;)"><Data
533
+ ss:Formula="=IF(SUM(R[-<%=@work_totals_per_work_account.size*2+2%>]C:R[-1]C),SUM(R[-<%=@work_totals_per_work_account.size*2+2%>]C:R[-1]C),&quot;&quot;)"><Data
534
534
  ss:Type="String"></Data></Cell>
535
535
  <Cell ss:StyleID="s46"
536
- ss:Formula="=IF(SUM(R[-<%=@backlogs.size*2+2%>]C:R[-1]C),SUM(R[-<%=@backlogs.size*2+2%>]C:R[-1]C),&quot;&quot;)"><Data
536
+ ss:Formula="=IF(SUM(R[-<%=@work_totals_per_work_account.size*2+2%>]C:R[-1]C),SUM(R[-<%=@work_totals_per_work_account.size*2+2%>]C:R[-1]C),&quot;&quot;)"><Data
537
537
  ss:Type="String"></Data></Cell>
538
538
  <Cell ss:StyleID="s46"
539
- ss:Formula="=IF(SUM(R[-<%=@backlogs.size*2+2%>]C:R[-1]C),SUM(R[-<%=@backlogs.size*2+2%>]C:R[-1]C),&quot;&quot;)"><Data
539
+ ss:Formula="=IF(SUM(R[-<%=@work_totals_per_work_account.size*2+2%>]C:R[-1]C),SUM(R[-<%=@work_totals_per_work_account.size*2+2%>]C:R[-1]C),&quot;&quot;)"><Data
540
540
  ss:Type="String"></Data></Cell>
541
541
  <Cell ss:StyleID="s46"
542
- ss:Formula="=IF(SUM(R[-<%=@backlogs.size*2+2%>]C:R[-1]C),SUM(R[-<%=@backlogs.size*2+2%>]C:R[-1]C),&quot;&quot;)"><Data
542
+ ss:Formula="=IF(SUM(R[-<%=@work_totals_per_work_account.size*2+2%>]C:R[-1]C),SUM(R[-<%=@work_totals_per_work_account.size*2+2%>]C:R[-1]C),&quot;&quot;)"><Data
543
543
  ss:Type="String"></Data></Cell>
544
544
  <Cell ss:StyleID="s46"
545
- ss:Formula="=IF(SUM(R[-<%=@backlogs.size*2+2%>]C:R[-1]C),SUM(R[-<%=@backlogs.size*2+2%>]C:R[-1]C),&quot;&quot;)"><Data
545
+ ss:Formula="=IF(SUM(R[-<%=@work_totals_per_work_account.size*2+2%>]C:R[-1]C),SUM(R[-<%=@work_totals_per_work_account.size*2+2%>]C:R[-1]C),&quot;&quot;)"><Data
546
546
  ss:Type="String"></Data></Cell>
547
547
  <Cell ss:StyleID="s46"
548
- ss:Formula="=IF(SUM(R[-<%=@backlogs.size*2+2%>]C:R[-1]C),SUM(R[-<%=@backlogs.size*2+2%>]C:R[-1]C),&quot;&quot;)"><Data
548
+ ss:Formula="=IF(SUM(R[-<%=@work_totals_per_work_account.size*2+2%>]C:R[-1]C),SUM(R[-<%=@work_totals_per_work_account.size*2+2%>]C:R[-1]C),&quot;&quot;)"><Data
549
549
  ss:Type="String"></Data></Cell>
550
550
  <Cell ss:StyleID="s47"/>
551
551
  </Row>
@@ -1,6 +1,4 @@
1
1
  page.replace "work_#{@work.id}", :partial => 'row', :object => @work
2
2
  page.replace "notice", :partial => '/layouts/notice'
3
- page.select("tr#work_#{@work.id} td input#work_#{@field}").each do |elem|
4
- elem.select
5
- # elem.focus
6
- end
3
+ page["work_#{@work.id}_#{@field}"].select
4
+ page["work_#{@work.id}_#{@field}"].focus
@@ -3,7 +3,7 @@
3
3
  <div id="spotlight">
4
4
  <% track_times = @rows.find {|r| r.find {|w| w && w.work_account.track_times?}} %>
5
5
  <% invoicing = @rows.find {|r| r.find {|w| w && w.work_account.enable_invoicing?}} %>
6
- <% columns = 2 + (track_times ? 2 : 0) + (invoicing ? 1 : 0) %>
6
+ <% columns = 2 + (track_times ? 1 : 0) + (invoicing ? 1 : 0) %>
7
7
 
8
8
  <div style="float: left"><%=link_to(image_tag('arrow_left.png'), :year => (@week > 1 ? @year : @year - 1), :week => (@week > 1 ? @week - 1 : 52))%></div>
9
9
  <div style="float: right"><%=link_to(image_tag('arrow_right.png'), :year => (@week < 52 ? @year : @year + 1), :week => (@week < 52 ? @week + 1 : 1))%></div>
@@ -11,6 +11,8 @@
11
11
  <h1>Work for week <%=@week%><%=user? ? " for #{user.login}" : ''%></h1>
12
12
 
13
13
  <div align="right">
14
+ <% lock_action = @lock ? :unlock : :lock %>
15
+ [<%=link_to l(lock_action), :controller => 'work_locks', :action => lock_action %>]
14
16
  [<%=detour_to l(:new_work), :controller => 'works', :action => :new %>]
15
17
  [<%=link_to 'Timeliste', :action => :timeliste, :id => @week %>]
16
18
  [<%= if params[:with_empty]
@@ -22,24 +24,19 @@ end %>]
22
24
 
23
25
  <table border="1">
24
26
  <tr>
25
- <th colspan="<%=columns%>" align="center"><%=l :monday %> <%=week_date(1)%></th>
26
- <th colspan="<%=columns%>" align="center"><%=l :tuesday %> <%=week_date(2)%></th>
27
- <th colspan="<%=columns%>" align="center"><%=l :wednesday %> <%=week_date(3)%></th>
28
- <th colspan="<%=columns%>" align="center"><%=l :thursday %> <%=week_date(4)%></th>
29
- <th colspan="<%=columns%>" align="center"><%=l :friday %> <%=week_date(5)%></th>
30
- <th colspan="<%=columns%>" align="center"><%=l :saturday %> <%=week_date(6)%></th>
31
- <th colspan="<%=columns%>" align="center"><%=l :sunday %> <%=week_date(7)%></th>
27
+ <% [:monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday].each_with_index do |day, i| %>
28
+ <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>
29
+ <% end %>
32
30
  <th align="center" nowrap="true"><%=l :week %> <%=@week%></th>
33
31
  </tr>
34
32
  <tr>
35
33
  <% (1..7).each do %>
36
- <th><%=l :task %></th>
34
+ <th><%=l :work_account %></th>
37
35
  <% if invoicing %>
38
36
  <th><%=l :invoice_short %></th>
39
37
  <% end %>
40
38
  <% if track_times %>
41
- <th><%=l(:start) if track_times %></th>
42
- <th><%=l(:stop) if track_times %></th>
39
+ <th><%=l(:start) if track_times %> - <%=l(:stop) if track_times %></th>
43
40
  <% end %>
44
41
  <th><%=l :done %></th>
45
42
  <% end %>
@@ -55,28 +52,31 @@ end %>]
55
52
  <% day_totals[day] += @work.hours %>
56
53
  <% week_total += @work.hours %>
57
54
  <td valign="top">
58
- <%=image_detour_to('hammer.png', l(:edit), {:controller => 'works', :action => :edit, :id => @work.id}, {:style => "float: right;"}) %>
55
+ <%=image_detour_to('hammer.png', l(:edit), {:controller => 'works', :action => :edit, :id => @work.id}, {:style => "float: left; border: 1px solid black"}) %>
56
+ <!--
59
57
  <%=detour_to(h(@work.task.period.name), :controller => 'periods', :action => :show, :id => @work.task.period) if @work.task && @work.task.period %>
58
+ -->
60
59
  <%=detour_to(h(@work.work_account.name), :controller => 'work_accounts', :action => :show, :id => @work.work_account_id) %>:
60
+ <!--
61
61
  <%=detour_to(h(@work.task.description), :controller => 'tasks', :action => :edit, :id => @work.task_id) if @work.task_id%>
62
+ -->
62
63
  </td>
63
64
  <% if invoicing %>
64
65
  <td id="invoice_<%=@work.id%>" valign="top">
65
- <%=check_box(:work, :invoice, :onchange => "new Ajax.Updater('invoice_#{@work}', '#{url_for(:action => :set_work_invoice, :id => @work)}' + '?value=' + this.checked);") if @work.work_account.enable_invoicing? %>
66
+ <%=check_box(:work, :invoice, :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? %>
66
67
  </td>
67
68
  <% end %>
68
69
  <% if track_times %>
69
- <td align="right" valign="top"><%=in_place_editor_field(:work, :started_at_time, :class => :task_time) %></td>
70
- <td align="right" valign="top"><%=in_place_editor_field(:work, :completed_at_time, :class => :task_time) %></td>
70
+ <td align="left" valign="top"><%=text_field(:work, :started_at_time, :class => :task_time, :disabled => @lock) %> -
71
+ <%=text_field(:work, :completed_at_time, :class => :task_time, :disabled => @lock) %></td>
71
72
  <% end %>
72
- <td align="right" valign="top"><%=in_place_editor_field(:work, :hours, {}, :value => '%.2f' % @work.hours) %></td>
73
+ <td align="right" valign="top"><%=text_field(:work, :hours, :value => '%.2f' % @work.hours, :class => 'task_hours') %></td>
73
74
  <% else %>
74
75
  <td/>
75
76
  <% if invoicing %>
76
77
  <td/>
77
78
  <% end %>
78
79
  <% if track_times %>
79
- <td/>
80
80
  <td/>
81
81
  <% end %>
82
82
  <td/>
@@ -0,0 +1,57 @@
1
+ <% @page_title = l(:weekly_work_sheet) + " " + @week.to_s + (user? ? " for #{user.login}" : '')%>
2
+
3
+ <div id="spotlight">
4
+ <% columns = 2 %>
5
+
6
+ <div style="float: left"><%=link_to(image_tag('arrow_left.png'), :year => (@week > 1 ? @year : @year - 1), :week => (@week > 1 ? @week - 1 : 52))%></div>
7
+ <div style="float: right"><%=link_to(image_tag('arrow_right.png'), :year => (@week < 52 ? @year : @year + 1), :week => (@week < 52 ? @week + 1 : 1))%></div>
8
+
9
+ <div align="right">
10
+ <% lock_action = @lock ? :unlock : :lock %>
11
+ [<%=link_to l(lock_action), with_detour(:controller => 'work_locks', :action => lock_action, :year => @year, :week => @week) %>]
12
+ <% if @lock %>
13
+ [<%=link_to l(:spreadsheet), :action => :timeliste, :year => @year, :week => @week %>]
14
+ <% else %>
15
+ [<%=detour_to l(:new_work), :controller => 'works', :action => :new %>]
16
+ <% end %>
17
+ </div>
18
+
19
+ <br clear="all" />
20
+
21
+ <table border="1">
22
+ <tr>
23
+ <th><%=l :work_account %></th>
24
+ <% [:monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday].each_with_index do |day, i| %>
25
+ <th align="center"><%=detour_to "#{l(day)} #{week_date(i+1)}", :action => :daily_work_sheet, :id => (@first_date + i).strftime('%Y-%m-%d') %></th>
26
+ <% end %>
27
+ <th align="center" nowrap="true"><%=l :week %> <%=@week%></th>
28
+ </tr>
29
+
30
+ <% week_totals = Hash.new(0) %>
31
+ <% day_totals = Array.new(7, 0) %>
32
+ <% for work_account, totals in @work_accounts %>
33
+ <tr>
34
+ <td valign="top">
35
+ <%=detour_to(h(work_account.name), :controller => 'work_accounts', :action => :show, :id => work_account.id) %>
36
+ </td>
37
+ <% totals.each_with_index do |total, day| %>
38
+ <% if total %>
39
+ <% day_totals[day] += total %>
40
+ <% week_totals[work_account] += total %>
41
+ <td align="right" valign="top"><%='%.2f' % total%></td>
42
+ <% else %>
43
+ <td/>
44
+ <% end %>
45
+ <% end %>
46
+ <td align="right"><%=week_totals[work_account]%></td>
47
+ </tr>
48
+ <% end %>
49
+ <tr>
50
+ <th><%=l :totals%></th>
51
+ <% (0..6).each do |day| %>
52
+ <th class="hours"><%='%.2f' % day_totals[day] if day_totals[day] > 0%></th>
53
+ <% end %>
54
+ <th class="hours"><%= '%.2f' % week_totals.values.inject(0) {|total, work_account_total| total + work_account_total} %></th>
55
+ </tr>
56
+ </table>
57
+ </div>
@@ -0,0 +1,25 @@
1
+ class CreateWorkLocks < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :work_locks do |t|
4
+ t.column :user_id, :integer, :null => false, :references => [:users, :party_id]
5
+ t.column :start_on, :date, :null => false
6
+ t.column :end_on, :date, :null => false
7
+ end
8
+
9
+ create_table :work_lock_subscriptions, :id => false do |t|
10
+ t.column :subscriber_user_id, :integer, :null => false, :references => [:users, :party_id]
11
+ t.column :work_account_id, :integer, :null => false
12
+ end
13
+
14
+ create_table :user_work_lock_subscriptions, :id => false do |t|
15
+ t.column :subscriber_user_id, :integer, :null => false, :references => [:users, :party_id]
16
+ t.column :user_id, :integer, :null => false, :references => [:users, :party_id]
17
+ end
18
+ end
19
+
20
+ def self.down
21
+ drop_table :user_work_lock_subscriptions
22
+ drop_table :work_lock_subscriptions
23
+ drop_table :work_locks
24
+ end
25
+ 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 => 26) do
5
+ ActiveRecord::Schema.define(:version => 27) do
6
6
 
7
7
  create_table "backlogs", :force => true do |t|
8
8
  t.column "name", :string, :limit => 64, :null => false
@@ -87,6 +87,11 @@ ActiveRecord::Schema.define(:version => 26) do
87
87
  t.column "user_id", :integer, :null => false
88
88
  end
89
89
 
90
+ create_table "user_work_lock_subscriptions", :id => false, :force => true do |t|
91
+ t.column "subscriber_user_id", :integer, :null => false
92
+ t.column "user_id", :integer, :null => false
93
+ end
94
+
90
95
  create_table "users", :primary_key => "party_id", :force => true do |t|
91
96
  t.column "login", :string, :limit => 80, :null => false
92
97
  t.column "salted_password", :string, :limit => 40, :null => false
@@ -107,6 +112,17 @@ ActiveRecord::Schema.define(:version => 26) do
107
112
  t.column "invoice_code", :string
108
113
  end
109
114
 
115
+ create_table "work_lock_subscriptions", :id => false, :force => true do |t|
116
+ t.column "subscriber_user_id", :integer, :null => false
117
+ t.column "work_account_id", :integer, :null => false
118
+ end
119
+
120
+ create_table "work_locks", :force => true do |t|
121
+ t.column "user_id", :integer, :null => false
122
+ t.column "start_on", :date, :null => false
123
+ t.column "end_on", :date, :null => false
124
+ end
125
+
110
126
  create_table "works", :force => true do |t|
111
127
  t.column "task_id", :integer
112
128
  t.column "hours", :decimal, :precision => 6, :scale => 2, :default => 0.0, :null => false
@@ -148,8 +164,16 @@ ActiveRecord::Schema.define(:version => 26) do
148
164
  add_foreign_key "tasks_users", ["task_id"], "tasks", ["id"], :name => "tasks_users_task_id_fkey"
149
165
  add_foreign_key "tasks_users", ["user_id"], "users", ["party_id"], :name => "tasks_users_user_id_fkey"
150
166
 
167
+ add_foreign_key "user_work_lock_subscriptions", ["subscriber_user_id"], "users", ["party_id"], :name => "user_work_lock_subscriptions_subscriber_user_id_fkey"
168
+ add_foreign_key "user_work_lock_subscriptions", ["user_id"], "users", ["party_id"], :name => "user_work_lock_subscriptions_user_id_fkey"
169
+
151
170
  add_foreign_key "users", ["party_id"], "parties", ["id"], :name => "users_party_id_fkey"
152
171
 
172
+ add_foreign_key "work_lock_subscriptions", ["subscriber_user_id"], "users", ["party_id"], :name => "work_lock_subscriptions_subscriber_user_id_fkey"
173
+ add_foreign_key "work_lock_subscriptions", ["work_account_id"], "work_accounts", ["id"], :name => "work_lock_subscriptions_work_account_id_fkey"
174
+
175
+ add_foreign_key "work_locks", ["user_id"], "users", ["party_id"], :name => "work_locks_user_id_fkey"
176
+
153
177
  add_foreign_key "works", ["work_account_id"], "work_accounts", ["id"], :name => "works_work_account_id_fkey"
154
178
  add_foreign_key "works", ["customer_id"], "customers", ["id"], :name => "works_customer_id_fkey"
155
179
  add_foreign_key "works", ["user_id"], "users", ["party_id"], :name => "works_user_id_fkey"
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,7 @@
1
+ # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
2
+ one:
3
+ subscriber_user_id: 1000001
4
+ work_account_id: 1
5
+ two:
6
+ subscriber_user_id: 1000001
7
+ work_account_id: 2
@@ -0,0 +1,11 @@
1
+ # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
2
+ one:
3
+ id: 1
4
+ user_id: 1000001
5
+ start_on: 2008-01-28
6
+ end_on: 2008-01-28
7
+ two:
8
+ id: 2
9
+ user_id: 1000001
10
+ start_on: 2008-01-29
11
+ end_on: 2008-01-29
@@ -4,7 +4,7 @@ require 'estimates_controller'
4
4
  class EstimatesController; def rescue_action(e) raise e end; end
5
5
 
6
6
  class EstimatesControllerTest < Test::Unit::TestCase
7
- fixtures :parties, :users, :groups, :groups_users, :backlogs, :periods, :tasks, :task_files, :works, :estimates
7
+ main_scenario
8
8
 
9
9
  def setup
10
10
  @controller = EstimatesController.new
@@ -4,7 +4,7 @@ require 'groups_controller'
4
4
  class GroupsController; def rescue_action(e) raise e end; end
5
5
 
6
6
  class GroupsControllerTest < Test::Unit::TestCase
7
- fixtures :parties, :users, :groups, :groups_users, :periods, :tasks, :task_files, :estimates, :works
7
+ main_scenario
8
8
 
9
9
  def setup
10
10
  @controller = GroupsController.new
@@ -33,7 +33,7 @@ class UserControllerTest < Test::Unit::TestCase
33
33
  post :login, :user => { :login => "tesla", :password => "atest" }
34
34
  assert_logged_in users(:tesla)
35
35
  assert_response :redirect
36
- assert_equal @controller.url_for(:controller => 'backlogs', :action => :index), @response.redirect_url
36
+ assert_equal @controller.url_for(:controller => 'welcome', :action => :index), @response.redirect_url
37
37
  end
38
38
 
39
39
  def test_login__wrong_password
@@ -219,7 +219,7 @@ class UserControllerTest < Test::Unit::TestCase
219
219
 
220
220
  assert_logged_in users(:tesla)
221
221
  assert_response :redirect
222
- assert_equal @controller.url_for(:controller => 'backlogs', :action => :index), @response.redirect_url
222
+ assert_equal @controller.url_for(:controller => 'welcome', :action => :index), @response.redirect_url
223
223
  assert_cookie :autologin, :value => '1000001'
224
224
  assert_cookie :token
225
225
  assert_not_equal 'random_token_string', cookies['token'].value
@@ -0,0 +1,93 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+ require 'work_locks_controller'
3
+
4
+ # Re-raise errors caught by the controller.
5
+ class WorkLocksController; def rescue_action(e) raise e end; end
6
+
7
+ class WorkLocksControllerTest < Test::Unit::TestCase
8
+ fixtures :work_locks
9
+
10
+ def setup
11
+ @controller = WorkLocksController.new
12
+ @request = ActionController::TestRequest.new
13
+ @response = ActionController::TestResponse.new
14
+ @request.session[:user_id] = 1000001
15
+
16
+ @first_id = work_locks(:one).id
17
+ end
18
+
19
+ def test_index
20
+ get :index
21
+ assert_response :success
22
+ assert_template 'list'
23
+ end
24
+
25
+ def test_list
26
+ get :list
27
+
28
+ assert_response :success
29
+ assert_template 'list'
30
+
31
+ assert_not_nil assigns(:work_locks)
32
+ end
33
+
34
+ def test_show
35
+ get :show, :id => @first_id
36
+
37
+ assert_response :success
38
+ assert_template 'show'
39
+
40
+ assert_not_nil assigns(:work_lock)
41
+ assert assigns(:work_lock).valid?
42
+ end
43
+
44
+ def test_new
45
+ get :new
46
+
47
+ assert_response :success
48
+ assert_template 'new'
49
+
50
+ assert_not_nil assigns(:work_lock)
51
+ end
52
+
53
+ def test_create
54
+ num_work_locks = WorkLock.count
55
+
56
+ post :create, :work_lock => {:user_id => '1000001', :start_on => '2008-01-28', :end_on => '2008-01-28'}
57
+
58
+ assert_response :redirect
59
+ assert_redirected_to :action => 'list'
60
+
61
+ assert_equal num_work_locks + 1, WorkLock.count
62
+ end
63
+
64
+ def test_edit
65
+ get :edit, :id => @first_id
66
+
67
+ assert_response :success
68
+ assert_template 'edit'
69
+
70
+ assert_not_nil assigns(:work_lock)
71
+ assert assigns(:work_lock).valid?
72
+ end
73
+
74
+ def test_update
75
+ post :update, :id => @first_id
76
+ assert_response :redirect
77
+ assert_redirected_to :action => 'show', :id => @first_id
78
+ end
79
+
80
+ def test_destroy
81
+ assert_nothing_raised {
82
+ WorkLock.find(@first_id)
83
+ }
84
+
85
+ post :destroy, :id => @first_id
86
+ assert_response :redirect
87
+ assert_redirected_to :action => 'list'
88
+
89
+ assert_raise(ActiveRecord::RecordNotFound) {
90
+ WorkLock.find(@first_id)
91
+ }
92
+ end
93
+ end
@@ -4,7 +4,7 @@ require 'user_notify'
4
4
 
5
5
  class UserSystemTest < ActionController::IntegrationTest
6
6
  self.use_transactional_fixtures = false
7
- fixtures :parties, :users, :groups, :groups_users, :work_accounts, :backlogs, :periods, :tasks, :estimates, :works, :task_files
7
+ main_scenario
8
8
 
9
9
  def setup
10
10
  ActionMailer::Base.inject_one_error = false
data/test/test_helper.rb CHANGED
@@ -5,7 +5,7 @@ require 'user_notify'
5
5
 
6
6
  class Test::Unit::TestCase
7
7
  def self.main_scenario
8
- fixtures :parties, :users, :groups, :groups_users, :work_accounts, :backlogs, :periods, :tasks, :task_files, :works, :estimates
8
+ fixtures :parties, :users, :groups, :groups_users, :work_accounts, :backlogs, :periods, :tasks, :task_files, :works, :estimates, :work_lock_subscriptions, :work_locks
9
9
  end
10
10
 
11
11
  # Transactional fixtures accelerate your tests by wrapping each test method
@@ -1,7 +1,7 @@
1
1
  require File.dirname(__FILE__) + '/../test_helper'
2
2
 
3
3
  class EstimateTest < Test::Unit::TestCase
4
- fixtures :parties, :users, :groups, :groups_users, :work_accounts, :backlogs, :periods, :tasks, :task_files, :works, :estimates
4
+ main_scenario
5
5
 
6
6
  # Replace this with your real tests.
7
7
  def test_truth