backlog 0.25.0 → 0.26.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,23 @@
1
+ == 0.26.0 2008-03-28
2
+
3
+ === Features
4
+
5
+ * Upgraded JRuby in the WAR distribution to JRuby 1.1RC3
6
+ * Added "Invite" function to Task view to invite another user to grab a task.
7
+ * Added "Total" for holidays and sick days in the Edit User view.
8
+
9
+ === Fixes
10
+
11
+ * Fixed layout in the Daily Work Sheet
12
+ * Fixed time calculation in the daily work sheet for new rows.
13
+ * Changed input field type for Work Account for new rows in the Daily Work Sheet from text to drop-down.
14
+ * Fixed rounding error for displayed durations.
15
+ * Handled illegal time format when creating new work record in the Daily Work Sheet.
16
+ * Fixed deleting registered hours in Daily Work Sheet.
17
+ * Removed the "Save" button from the Daily Work Sheet since it didn't have a clear function. use "RETURN" or "ENTER" instead.
18
+ * Fixed crossover of absences between users in Daily Work Sheet.
19
+ * Fixed so that the link from the Daily Work Sheet to the Weekly Work Sheet goes to the correct week.
20
+
1
21
  == 0.25.0 2008-03-27
2
22
 
3
23
  === Features
data/Rakefile CHANGED
@@ -46,7 +46,7 @@ task :release_all do
46
46
  Rake::Task[:post_news].invoke
47
47
  end
48
48
 
49
- # Remember to set timeout in /usr/lib/ruby/gems/1.8/gems/rubyforge-0.4.4/lib/http-access2.rb
49
+ # Remember to set @send_timeout in /usr/lib/ruby/gems/1.8/gems/rubyforge-0.4.4/lib/http-access2.rb
50
50
  # @send_timeout = 3600
51
51
  desc 'Release the application as a Java EE WAR file to RubyForge'
52
52
  task :release_war do
@@ -1,7 +1,7 @@
1
1
  class TasksController < ApplicationController
2
2
  skip_before_filter :populate_layout, :except => [:edit, :grab, :list_started, :move_down, :move_to_bottom, :move_to_top, :move_up, :new, :specify]
3
3
 
4
- verify :method => :post, :except => [ :new, :show, :edit, :grab, :list_started, :move_to_next_period, :notes],
4
+ verify :method => :post, :except => [ :invite, :new, :show, :edit, :grab, :list_started, :move_to_next_period, :notes],
5
5
  :redirect_to => { :controller => 'backlogs' }
6
6
 
7
7
  def list_started
@@ -201,7 +201,18 @@ class TasksController < ApplicationController
201
201
  @task.grab
202
202
  detour = pop_detour
203
203
  params.update(detour) if detour
204
- render :template => '/tasks/_update.rjs'
204
+ render :template => '/tasks/_update.rjs' if request.xhr?
205
+ end
206
+
207
+ def invite
208
+ @task = Task.find(params[:id])
209
+ @invitee = User.find(params[:user_id])
210
+ task_url = url_for(:action => 'edit', :id => @task.id)
211
+ grab_url = url_for(:action => 'grab', :id => @task.id)
212
+ TaskNotify.deliver_invite(current_user, @invitee, @task, task_url, grab_url)
213
+ detour = pop_detour
214
+ params.update(detour) if detour
215
+ render :template => '/tasks/_invite.rjs' if request.xhr?
205
216
  end
206
217
 
207
218
  def release
@@ -46,7 +46,11 @@ class WorksController < ApplicationController
46
46
  end
47
47
  convert_work_account_param
48
48
  convert_customer_param
49
- convert_hours_param
49
+ unless convert_hours_param
50
+ flash[:notice] = "Illegal time format"
51
+ #@work.errors.add :hours_time, "Illegal time format."
52
+ params[:work].delete(:hours_time)
53
+ end
50
54
  @work = Work.new(params[:work])
51
55
  @work.started_on ||= Date.today
52
56
  @work.completed_at = Time.now unless @work.start_time || @work.completed_at
@@ -168,13 +172,15 @@ class WorksController < ApplicationController
168
172
 
169
173
  def daily_work_sheet
170
174
  @date = (params[:id] && Date.parse(params[:id])) || Date.today
175
+ @year = @date.year
176
+ @week = @date.cweek
171
177
  @periods = []
172
178
  @works = Work.find_work_for_day @date
173
179
  @customers = Customer.find(:all, :order => :name)
174
180
  @started_works = Task.find_started
175
181
  @work = Work.new(:started_at => Time.now, :completed_at => Time.now)
176
182
  @work_accounts = WorkAccount.find(:all, :order => :name)
177
- @absence = Absence.find(:first, :conditions => {:on => @date})
183
+ @absence = Absence.find(:first, :conditions => {:on => @date, :user_id => current_user.id})
178
184
  render :layout => 'wide'
179
185
  end
180
186
 
@@ -269,7 +275,7 @@ class WorksController < ApplicationController
269
275
  raise "Unknown time format: #{params[:hours_time]}" unless convert_hours_param
270
276
  @work = Work.new(params[:work])
271
277
  @updated_fields = [@field]
272
- if @work.hours == 0 && @work.start_time && @work.completed_at
278
+ if @work.hours == 0 && @work.started_on && @work.start_time && @work.completed_at
273
279
  @work.calculate_hours!
274
280
  @updated_fields << :hours_time
275
281
  end
@@ -282,12 +288,13 @@ class WorksController < ApplicationController
282
288
  params[:work][:hours_time].strip!
283
289
  if params[:work][:hours_time].empty?
284
290
  params[:work].delete(:hours_time)
291
+ params[:work][:hours] = 0
285
292
  return true
286
293
  end
287
294
  if params[:work][:hours_time] =~ /^(\d*):(\d{2})?$/
288
295
  new_hours = $1
289
296
  new_minutes = $2.to_i
290
- new_value_str = "#{new_hours}.#{(new_minutes / 0.6).ceil}"
297
+ new_value_str = "#{new_hours}.#{(new_minutes / 0.6).round}"
291
298
  new_value = BigDecimal(new_value_str)
292
299
  params[:work][:hours] = new_value
293
300
  elsif params[:work][:hours_time] =~ /^(\d*)[.,]?(\d*)$/
@@ -59,7 +59,7 @@ module ApplicationHelper
59
59
  end
60
60
 
61
61
  def t(time_as_float)
62
- "#{time_as_float.to_i}:#{'%02d' % (time_as_float.frac * 60).ceil}"
62
+ "#{time_as_float.to_i}:#{'%02d' % (time_as_float.frac * 60).round}"
63
63
  end
64
64
 
65
65
  def h(object)
@@ -0,0 +1,27 @@
1
+ class TaskNotify < ActionMailer::Base
2
+ def invite(inviter, invitee, task, task_url, grab_url)
3
+ setup_email(invitee)
4
+
5
+ # Email header info
6
+ @subject += "#{inviter.name} has invited you to grab task: #{task.id}."
7
+
8
+ # Email body substitutions
9
+ @body["app_name"] = UserSystem::CONFIG[:app_name].to_s
10
+ @body["app_url"] = UserSystem::CONFIG[:app_url].to_s
11
+ @body["task"] = task
12
+ @body["task_url"] = task_url
13
+ @body["grab_url"] = grab_url
14
+ @body["inviter"] = inviter
15
+ @body["invitee"] = invitee
16
+ end
17
+
18
+ private
19
+
20
+ def setup_email(user)
21
+ @recipients = "#{user.email}"
22
+ @from = UserSystem::CONFIG[:email_from].to_s
23
+ @subject = "[#{UserSystem::CONFIG[:app_name]}] "
24
+ @sent_on = Time.now
25
+ @headers['Content-Type'] = "text/plain; charset=#{UserSystem::CONFIG[:mail_charset]}; format=flowed"
26
+ end
27
+ end
@@ -105,6 +105,10 @@ class User < Party
105
105
  return user == self
106
106
  end
107
107
 
108
+ def users
109
+ [self]
110
+ end
111
+
108
112
  protected
109
113
 
110
114
  attr_accessor :password, :password_confirmation
@@ -140,13 +140,17 @@ class Work < ActiveRecord::Base
140
140
  end
141
141
  raise "invalid time format: #{new_value}" unless new_value =~ /^(\d{0,2}):?(\d{2})?$/
142
142
  new_hour, new_minutes = $1, $2
143
- t = completed_at || Time.now
143
+ t = started_on || completed_at || Time.now
144
144
  self.completed_at = Time.local(t.year, t.month, t.day, new_hour, new_minutes)
145
145
  end
146
146
 
147
147
  def calculate_hours!
148
148
  return unless started_at && completed_at
149
- self.hours = (completed_at - started_at) / 3600
149
+ self.hours = calculate_hours
150
+ end
151
+
152
+ def calculate_hours
153
+ BigDecimal(((completed_at - started_at) / 3600).to_s).round(2)
150
154
  end
151
155
 
152
156
  end
@@ -0,0 +1,13 @@
1
+ <%=@invitee.name %>
2
+
3
+ <%=@inviter.name%> has invited you to grab task
4
+
5
+ <%=@task.id%> "<%=@task.description%>"
6
+
7
+ Click on the following link to view the task:
8
+
9
+ <%=@task_url%>
10
+
11
+ or click on the link below to grab the task directly:
12
+
13
+ <%=@grab_url%>
@@ -0,0 +1,13 @@
1
+ <%=@invitee.name %>
2
+
3
+ <%=@inviter.name%> har invitert deg til å ta oppgave
4
+
5
+ <%=@task.id%> "<%=@task.description%>"
6
+
7
+ Klick på følgende link for å se på oppgaven:
8
+
9
+ <%=@task_url%>
10
+
11
+ eller klick på linken under for å ta oppgaven direkte:
12
+
13
+ <%=@grab_url%>
@@ -83,7 +83,13 @@
83
83
  l(:grabbed_by_you)
84
84
  end
85
85
  %>
86
- <%=link_to_remote l(:invite), :action => :invite, :id => @task.id %>
86
+ <br/>
87
+ <% if @task.period %>
88
+ <%=l :invite%>:
89
+ <% @task.period.party.users.each do |user| %>
90
+ <%=link_to "#{user.name}", :action => :invite, :id => @task.id, :user_id => user.id unless @task.users.include? user%>
91
+ <% end %>
92
+ <% end %>
87
93
  </p>
88
94
  <% end %>
89
95
  <% end %>
@@ -0,0 +1 @@
1
+ display_notice(page)
@@ -0,0 +1,9 @@
1
+ <% @page_title = "#{l :grab_task}" -%>
2
+
3
+ <div id="spotlight">
4
+ <div class="btitle">
5
+ <h4><%=l :grab_task %> #<%=@task.id%></h4>
6
+ </div>
7
+
8
+ Thank you for grabbing the task.
9
+ </div>
@@ -0,0 +1,9 @@
1
+ <% @page_title = "#{l :invite}" -%>
2
+
3
+ <div id="spotlight">
4
+ <div class="btitle">
5
+ <h4><%=l :invite %> #<%=@task.id%></h4>
6
+ </div>
7
+
8
+ <%=@invitee.name%> has been invited to grab task <%=@task.id%>.
9
+ </div>
@@ -94,6 +94,9 @@
94
94
  <td align="center"><%=link_to date, :controller => 'works', :action => :daily_work_sheet, :id => date %></td>
95
95
  </tr>
96
96
  <% end %>
97
+ <tr>
98
+ <td align="right">Total: <%=@holidays.size%></td>
99
+ </tr>
97
100
  </table>
98
101
  </div>
99
102
 
@@ -111,6 +114,9 @@
111
114
  <td align="center"><%=link_to date, :controller => 'works', :action => :daily_work_sheet, :id => date %></td>
112
115
  </tr>
113
116
  <% end %>
117
+ <tr>
118
+ <td align="right">Total: <%=@sick_days.size%></td>
119
+ </tr>
114
120
  </table>
115
121
  </div>
116
122
 
@@ -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%>
7
+ <%=link_to l(:weekly_work_sheet), :action => :weekly_work_sheet_by_work_account, :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
 
@@ -43,7 +43,6 @@
43
43
  <%=render :partial => 'row', :locals => {:next_row_id => @works[i+1] && @works[i+1].id,
44
44
  :previous_row_id => @works[i-1] && @works[i-1].id} %>
45
45
  <% end %>
46
- </table>
47
46
 
48
47
  <% last_work = @work %>
49
48
 
@@ -54,10 +53,10 @@
54
53
  <%=submit_tag('checkmark', :value => l(:save), :style => 'display: none')%>
55
54
  <%=hidden_field :work, :started_on, :value => @date %>
56
55
 
57
- <table>
58
56
  <tr>
59
57
  <td id="account_cell" valign="bottom">
60
- <%=text_field_with_auto_complete :work, :work_account_name, {:value => '', :size => 16, :class => :task_description}, {:delay => "0.01"} %>
58
+ <%#=text_field_with_auto_complete :work, :work_account_name, {:value => '', :size => 16, :class => :task_description}, {:delay => "0.01"} %>
59
+ <%=select :work, :work_account_id, @work_accounts.map {|wa| [wa.name, wa.id]}, :value => '' %>
61
60
  </td>
62
61
  <td valign="bottom">
63
62
  <%=text_field_with_auto_complete :work, :description, :class => :task_description,
@@ -77,7 +76,17 @@
77
76
  %>
78
77
  </td>
79
78
  <td id="work_start_time_cell" align="right" valign="bottom">
79
+ <% field = 'start_time'; next_field = 'completed_at_time' %>
80
80
  <%=text_field :work, :start_time, :value => last_work && last_work.completed_at && last_work.completed_at.strftime('%H:%M'), :class => 'task_time',
81
+ :onchange => remote_function(:url => {:action => :update_new_row, :field => field,
82
+ :next_field => next_field},
83
+ :with => "
84
+ 'work[started_on]=' + work_started_on.value
85
+ + '&work[start_time]=' + work_start_time.value
86
+ + '&work[completed_at_time]=' + work_completed_at_time.value
87
+ + '&work[hours_time]=' + work_hours_time.value
88
+ "
89
+ ),
81
90
  :onkeypress => "
82
91
  if(event.keyCode == 38) {
83
92
  e = $('work#{"_#{last_work.id}" if last_work}_start_time');
@@ -97,7 +106,14 @@
97
106
  <% field = 'completed_at_time'; next_field = 'hours_time' %>
98
107
  <%=text_field :work, :completed_at_time, :class => 'task_time',
99
108
  :onchange => remote_function(:url => {:action => :update_new_row, :field => field,
100
- :next_field => next_field}, :with => "Form.serialize(form)"),
109
+ :next_field => next_field},
110
+ :with => "
111
+ 'work[started_on]=' + work_started_on.value
112
+ + '&work[start_time]=' + work_start_time.value
113
+ + '&work[completed_at_time]=' + work_completed_at_time.value
114
+ + '&work[hours_time]=' + work_hours_time.value
115
+ "
116
+ ),
101
117
  :onkeypress => "
102
118
  if(event.keyCode == 38) {
103
119
  e = $('work#{"_#{last_work.id}" if last_work}_completed_at_time');
@@ -133,7 +149,6 @@
133
149
  </td>
134
150
  <td valign="bottom">
135
151
  <%=text_field_with_auto_complete :work, :task_id, {:value => '', :size => 16, :class => :task_id} %>
136
- <%=image_tag 'task.png'%>
137
152
  </td>
138
153
  <td id="work_invoice_cell" align="left" valign="bottom">
139
154
  <%=check_box :work, :invoice %>
@@ -156,7 +171,6 @@
156
171
  <th/>
157
172
  </tr>
158
173
  </table>
159
- <%= submit_tag l(:save) %>
160
174
  <%= back_or_link_to l(:back), '' %>
161
175
  <% end %>
162
176
 
@@ -173,15 +187,16 @@
173
187
  <script type="text/JavaScript">
174
188
  //<!--
175
189
  <% if last_work %>
176
- $('work_work_account_name').style.width = $('work_<%=last_work.id%>_work_account_id').clientWidth - 6 + 'px';
177
- $('work_description').style.width = $('work_<%=last_work.id%>_description').clientWidth - 2 + 'px';
178
- $('work_start_time_cell').style.width = $('start_time_header').clientWidth - 2 + 'px';
179
- $('work_invoice_cell').style.width = $('invoice_header').clientWidth - 2 + 'px';
180
- $('work_customer_name').style.width = $('work_<%=last_work.id%>_customer_id').clientWidth + 10 + 'px';
181
- $('work_icons_cell').style.width = $('icons_header').clientWidth - 2 + 'px';
190
+ // $('work_work_account_name').style.width = $('work_<%=last_work.id%>_work_account_id').clientWidth - 6 + 'px';
191
+ // $('work_description').style.width = $('work_<%=last_work.id%>_description').clientWidth - 2 + 'px';
192
+ // $('work_start_time_cell').style.width = $('start_time_header').clientWidth - 2 + 'px';
193
+ // $('work_invoice_cell').style.width = $('invoice_header').clientWidth - 2 + 'px';
194
+ // $('work_customer_name').style.width = $('work_<%=last_work.id%>_customer_id').clientWidth + 10 + 'px';
195
+ // $('work_icons_cell').style.width = $('icons_header').clientWidth - 2 + 'px';
182
196
  <% end %>
197
+ var start_field;
183
198
  <% if last_work.completed_at %>
184
- start_field = $('work_work_account_name');
199
+ start_field = $('work_work_account_id');
185
200
  <% else %>
186
201
  start_field = $('work_<%=last_work.id%>_completed_at_time');
187
202
  <% end %>
@@ -3,9 +3,9 @@ if @work && @work.errors.empty?
3
3
  @updated_fields.each do |field_name|
4
4
  if field_name.to_s =~ /(.*)_time$/
5
5
  attribute_name = $1
6
- value = @work[attribute_name]
6
+ value = @work[attribute_name] || @work[field_name]
7
7
  if value.is_a? BigDecimal
8
- value = '%d:%02d' % [value.to_i, (value.frac * 60).to_i]
8
+ value = t(value)
9
9
  else
10
10
  value = value.strftime('%H:%M')
11
11
  end
@@ -1,7 +1,7 @@
1
1
  # Goldspike configuration
2
2
 
3
3
  # Set the version of JRuby and GoldSpike to use:
4
- maven_library 'org.jruby', 'jruby-complete', '1.1RC2'
4
+ maven_library 'org.jruby', 'jruby-complete', '1.1RC3'
5
5
  maven_library 'backport-util-concurrent', 'backport-util-concurrent', '3.0'
6
6
  #maven_library 'org.jruby.extras', 'goldspike', '1.3-SNAPSHOT'
7
7
 
@@ -77,6 +77,19 @@ class WorksControllerTest < Test::Unit::TestCase
77
77
  assert_equal num_works + 1, Work.count
78
78
  end
79
79
 
80
+ def test_create_with_old_start_date
81
+ num_works = Work.count
82
+
83
+ post :create, :work => {:task_id => 1, :started_on => '2007-07-31', :work_account_id => '1',
84
+ :start_time => '12:00', :completed_at => '2007-07-31 12:20'}
85
+
86
+ assert_response :redirect
87
+ assert_redirected_to :controller => 'periods', :action => 'show', :id => periods(:past).id, :task_id => 1
88
+
89
+ assert_equal num_works + 1, Work.count
90
+ assert_equal BigDecimal('0.33'), assigns(:work).hours
91
+ end
92
+
80
93
  def test_edit
81
94
  get :edit, :id => 1
82
95
 
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.25.0
4
+ version: 0.26.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Uwe Kubosch
@@ -97,6 +97,7 @@ files:
97
97
  - app/views/tasks
98
98
  - app/views/tasks/new.rhtml
99
99
  - app/views/tasks/list.rhtml
100
+ - app/views/tasks/_invite.rjs
100
101
  - app/views/tasks/start_work.rjs
101
102
  - app/views/tasks/_fields_header.rhtml
102
103
  - app/views/tasks/_update.rjs
@@ -108,7 +109,9 @@ files:
108
109
  - app/views/tasks/_backlog_header.rhtml
109
110
  - app/views/tasks/_form.rhtml
110
111
  - app/views/tasks/update_estimate.rjs
112
+ - app/views/tasks/invite.rhtml
111
113
  - app/views/tasks/list_started.rhtml
114
+ - app/views/tasks/grab.rhtml
112
115
  - app/views/work_locks
113
116
  - app/views/work_locks/new.rhtml
114
117
  - app/views/work_locks/list.rhtml
@@ -138,6 +141,9 @@ files:
138
141
  - app/views/groups/edit.rhtml
139
142
  - app/views/groups/_form.rhtml
140
143
  - app/views/display_notice.rjs
144
+ - app/views/task_notify
145
+ - app/views/task_notify/invite_en.rhtml
146
+ - app/views/task_notify/invite_no.rhtml
141
147
  - app/views/backlogs
142
148
  - app/views/backlogs/new.rhtml
143
149
  - app/views/backlogs/list.rhtml
@@ -265,6 +271,7 @@ files:
265
271
  - app/models/group.rb
266
272
  - app/models/work_lock_subscription.rb
267
273
  - app/models/backlog.rb
274
+ - app/models/task_notify.rb
268
275
  - app/models/party.rb
269
276
  - app/models/task_file.rb
270
277
  - app/models/work_lock.rb