backlog 0.18.0 → 0.19.0

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