backlog 0.25.0 → 0.26.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,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