backlog 0.31.1 → 0.32.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.
- data/History.txt +24 -0
- data/Rakefile +2 -0
- data/app/controllers/user_controller.rb +1 -0
- data/app/controllers/work_accounts_controller.rb +0 -15
- data/app/controllers/works_controller.rb +22 -8
- data/app/helpers/application_helper.rb +4 -4
- data/app/models/absence.rb +2 -2
- data/app/models/report_filter.rb +3 -2
- data/app/models/user.rb +1 -0
- data/app/models/work.rb +2 -2
- data/app/models/works_report_filter.rb +5 -0
- data/app/views/periods/_show_active.rhtml +5 -1
- data/app/views/tasks/_task.rhtml +2 -3
- data/app/views/user/edit.rhtml +36 -4
- data/app/views/work_accounts/_form.rhtml +3 -2
- data/app/views/work_accounts/edit.rhtml +1 -1
- data/app/views/work_accounts/list.rhtml +1 -1
- data/app/views/work_accounts/new.rhtml +3 -1
- data/app/views/works/_new_row.rhtml +1 -1
- data/app/views/works/daily_work_sheet.rhtml +4 -3
- data/app/views/works/list.rhtml +23 -12
- data/app/views/works/list_excel.rhtml +29 -22
- data/app/views/works/weekly_work_sheet.rhtml +4 -1
- data/config/database.yml +1 -1
- data/config/environment.rb +2 -2
- data/test/fixtures/works.yml +8 -0
- data/test/functional/work_accounts_controller_test.rb +0 -20
- data/test/functional/works_controller_test.rb +29 -0
- data/test/unit/work_test.rb +4 -0
- metadata +2 -2
data/History.txt
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
== 0.32.0 2008-05-06
|
|
2
|
+
|
|
3
|
+
== Features
|
|
4
|
+
|
|
5
|
+
* Added filtering on work account to work search view.
|
|
6
|
+
* Added "Sick child" as absence reason in the daily work sheet.
|
|
7
|
+
* Added navigating to work records for a user from the subscription list in the user view.
|
|
8
|
+
|
|
9
|
+
=== Fixes
|
|
10
|
+
|
|
11
|
+
* Fixed wrong parsing of one-digit minutes for work duration.
|
|
12
|
+
* Reformatted the Excel export of work records for a work account.
|
|
13
|
+
Also added sorting by user, task, start and stop times.
|
|
14
|
+
Should work with MS Excel now.
|
|
15
|
+
* Set default search dates for work to current month.
|
|
16
|
+
* Keep user_id when following links from the weekly work sheet to the daily work sheet.
|
|
17
|
+
This enables subscribers of time sheets to get details about the time sheets they are watching.
|
|
18
|
+
* Added a "Back" link from weekly work sheet.
|
|
19
|
+
* Indent subtasks in the sprint view.
|
|
20
|
+
* Fixed bug in work lock subscription invitation.
|
|
21
|
+
* Fixed so that partially filled in work records are retained after failed creation.
|
|
22
|
+
Also error messages for failed work record creations are displayed.
|
|
23
|
+
* Fixed design of the "New Work Account" view, and set focus on the "Name" field when it is first loaded.
|
|
24
|
+
|
|
1
25
|
== 0.31.1 2008-04-24
|
|
2
26
|
|
|
3
27
|
=== Fixes
|
data/Rakefile
CHANGED
|
@@ -14,6 +14,8 @@ require 'tasks/rails'
|
|
|
14
14
|
require 'hoe'
|
|
15
15
|
require 'version_from_history'
|
|
16
16
|
|
|
17
|
+
ENV['SKIP_AR_JDBC_RAKE_REDEFINES'] = '1'
|
|
18
|
+
|
|
17
19
|
Hoe.new("backlog", APP::VERSION) do |p|
|
|
18
20
|
p.rubyforge_name = "backlog"
|
|
19
21
|
p.summary = "Application to aid collecting, processing, organizing, reviewing and doing tasks."
|
|
@@ -217,6 +217,7 @@ class UserController < ApplicationController
|
|
|
217
217
|
@groups = Group.find(:all, :order => 'name')
|
|
218
218
|
@periods = @user.periods
|
|
219
219
|
@associates = User.find(:all) - @user.work_lock_subscribers - [current_user]
|
|
220
|
+
@potential_subscribees = User.find(:all) - @user.work_lock_subscriptions - [current_user]
|
|
220
221
|
absences = Absence.find(:all, :conditions => ['user_id = ? AND "on" BETWEEN ? AND ?', current_user.id, Date.new(Date.today.year, 1, 1), Date.new(Date.today.year, 12, 31)])
|
|
221
222
|
@holidays = absences.select {|a| a.reason == 'HOLIDAY'}
|
|
222
223
|
@holidays.map! {|a| a.on}
|
|
@@ -55,19 +55,4 @@ class WorkAccountsController < ApplicationController
|
|
|
55
55
|
redirect_to :action => 'list'
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
-
def works
|
|
59
|
-
work_account = WorkAccount.find(params[:id])
|
|
60
|
-
@report_filter = WorksReportFilter.new(params[:report_filter])
|
|
61
|
-
@report_filter.title = "#{l :hours} for #{work_account.name} #{@report_filter.start_on && @report_filter.start_on.strftime('%Y-%m-%d - ')}#{@report_filter.end_on && @report_filter.end_on.strftime('%Y-%m-%d')}"
|
|
62
|
-
@works = Work.paginate :conditions => ["completed_at BETWEEN ? AND ? AND work_account_id = ? #{@report_filter.invoice.nil? ? '' : 'AND invoice = ?'} #{@report_filter.user_id.nil? ? '' : 'AND user_id = ?'}", @report_filter.start_on, @report_filter.end_on + 1, work_account.id, @report_filter.invoice, @report_filter.user_id].compact,
|
|
63
|
-
:page => params[:page], :per_page => @report_filter.page_size,
|
|
64
|
-
:order => 'started_on, start_time, completed_at'
|
|
65
|
-
@users = User.find(:all)
|
|
66
|
-
if params[:export] == 'excel'
|
|
67
|
-
render :template => '/works/list_excel', :layout => false
|
|
68
|
-
else
|
|
69
|
-
render :template => '/works/list'
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
|
|
73
58
|
end
|
|
@@ -8,7 +8,6 @@ class WorksController < ApplicationController
|
|
|
8
8
|
|
|
9
9
|
def index
|
|
10
10
|
list
|
|
11
|
-
render :action => 'list'
|
|
12
11
|
end
|
|
13
12
|
|
|
14
13
|
# GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
|
|
@@ -16,8 +15,21 @@ class WorksController < ApplicationController
|
|
|
16
15
|
:redirect_to => { :action => :list }
|
|
17
16
|
|
|
18
17
|
def list
|
|
18
|
+
work_account_name = (params[:work_account_id] && WorkAccount.find(params[:work_account_id]).name) || 'all accoutns'
|
|
19
|
+
@report_filter = WorksReportFilter.new(params[:report_filter])
|
|
20
|
+
@report_filter.title = "#{l :hours} for #{work_account_name} #{@report_filter.start_on && @report_filter.start_on.strftime('%Y-%m-%d - ')}#{@report_filter.end_on && @report_filter.end_on.strftime('%Y-%m-%d')}"
|
|
19
21
|
@period = params[:id] && Period.find(params[:id])
|
|
20
|
-
@works = Work.paginate :
|
|
22
|
+
@works = Work.paginate :conditions => ["completed_at BETWEEN ? AND ? #{'AND work_account_id = ?' if @report_filter.work_account_id} #{@report_filter.invoice.nil? ? '' : 'AND invoice = ?'} #{@report_filter.user_id.nil? ? '' : 'AND user_id = ?'}", @report_filter.start_on, @report_filter.end_on + 1, @report_filter.work_account_id, @report_filter.invoice, @report_filter.user_id].compact,
|
|
23
|
+
:page => params[:page], :per_page => @report_filter.page_size,
|
|
24
|
+
:order => 'started_on, start_time, completed_at'
|
|
25
|
+
@work_accounts = WorkAccount.find(:all, :order => :name)
|
|
26
|
+
@users = User.find(:all, :order => 'first_name, last_name')
|
|
27
|
+
if params[:export] == 'excel'
|
|
28
|
+
@works = @works.sort_by {|w| [w.user_id || 0, w.task_id || 999999, w.started_on]}
|
|
29
|
+
render :template => '/works/list_excel', :layout => false
|
|
30
|
+
else
|
|
31
|
+
render :template => '/works/list'
|
|
32
|
+
end
|
|
21
33
|
end
|
|
22
34
|
|
|
23
35
|
def show
|
|
@@ -63,6 +75,7 @@ class WorksController < ApplicationController
|
|
|
63
75
|
else
|
|
64
76
|
if params[:detour]
|
|
65
77
|
flash[:notice] = 'Error creating new work record.'
|
|
78
|
+
flash[:work] = @work
|
|
66
79
|
back
|
|
67
80
|
else
|
|
68
81
|
new
|
|
@@ -180,13 +193,14 @@ class WorksController < ApplicationController
|
|
|
180
193
|
@date = (params[:id] && Date.parse(params[:id])) || Date.today
|
|
181
194
|
@year = @date.year
|
|
182
195
|
@week = @date.cweek
|
|
196
|
+
@user = (params[:user_id] && User.find(params[:user_id])) || current_user
|
|
183
197
|
@periods = []
|
|
184
|
-
@works = Work.find_work_for_day @
|
|
198
|
+
@works = Work.find_work_for_day(@date, @user)
|
|
185
199
|
@customers = Customer.find(:all, :order => :name)
|
|
186
200
|
@started_works = Task.find_started
|
|
187
|
-
@
|
|
201
|
+
@new_work = flash[:work] || Work.new(:started_at => Time.now)
|
|
188
202
|
@work_accounts = WorkAccount.find(:all, :order => :name)
|
|
189
|
-
@absence = Absence.find(:first, :conditions => {:on => @date, :user_id =>
|
|
203
|
+
@absence = Absence.find(:first, :conditions => {:on => @date, :user_id => @user.id})
|
|
190
204
|
@public_holiday = PublicHoliday.find(:first, :conditions => {:on => @date})
|
|
191
205
|
render :layout => 'wide'
|
|
192
206
|
end
|
|
@@ -209,7 +223,7 @@ class WorksController < ApplicationController
|
|
|
209
223
|
@first_date = Date.commercial(@year, @week, 1)
|
|
210
224
|
@last_date = @first_date + 6
|
|
211
225
|
@lock = WorkLock.find_by_week(@year, @week)
|
|
212
|
-
@absences = Absence.find_by_week(@year, @week)
|
|
226
|
+
@absences = Absence.find_by_week(@year, @week, @user)
|
|
213
227
|
@public_holidays = PublicHoliday.find_by_week(@year, @week)
|
|
214
228
|
render :layout => 'wide'
|
|
215
229
|
end
|
|
@@ -302,8 +316,8 @@ class WorksController < ApplicationController
|
|
|
302
316
|
end
|
|
303
317
|
if params[:work][:hours_time] =~ /^(\d*):(\d{2})?$/
|
|
304
318
|
new_hours = $1
|
|
305
|
-
new_minutes = $2
|
|
306
|
-
new_value_str =
|
|
319
|
+
new_minutes = BigDecimal($2)
|
|
320
|
+
new_value_str = '%02d.%03d' % [new_hours, (new_minutes / BigDecimal('0.06')).round]
|
|
307
321
|
new_value = BigDecimal(new_value_str)
|
|
308
322
|
params[:work][:hours] = new_value
|
|
309
323
|
elsif params[:work][:hours_time] =~ /^(\d*)[.,]?(\d*)$/
|
|
@@ -6,10 +6,10 @@ module ApplicationHelper
|
|
|
6
6
|
include UserSystem
|
|
7
7
|
include UrlForFix
|
|
8
8
|
|
|
9
|
-
def image_button_to(image_source, title, options)
|
|
10
|
-
image_submit_tag image_source, :class => 'image-submit', :alt => title, :title => title,
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
def image_button_to(image_source, title, options, html_options = {})
|
|
10
|
+
image_submit_tag image_source, {:class => 'image-submit', :alt => title, :title => title,
|
|
11
|
+
:id => "#{title}_#{options[:id]}", :name => title,
|
|
12
|
+
:onclick => "form.action='#{url_for(options)}'"}.update(html_options)
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def detour_to(title, options, html_options = nil)
|
data/app/models/absence.rb
CHANGED
|
@@ -17,10 +17,10 @@ class Absence < ActiveRecord::Base
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
# Return an array of either an absence or nil for each day of the given week.
|
|
20
|
-
def self.find_by_week(year, week)
|
|
20
|
+
def self.find_by_week(year, week, user = current_user)
|
|
21
21
|
first_date = Date.commercial(year, week, 1)
|
|
22
22
|
last_date = first_date + 6
|
|
23
|
-
results = self.find(:all, :conditions => ['"on" BETWEEN ? AND ? AND user_id = ?', first_date, last_date,
|
|
23
|
+
results = self.find(:all, :conditions => ['"on" BETWEEN ? AND ? AND user_id = ?', first_date, last_date, user && user.id], :order => '"on"')
|
|
24
24
|
(0..6).each do |day|
|
|
25
25
|
results.insert(day, nil) if results[day] && results[day].on > first_date + day
|
|
26
26
|
end
|
data/app/models/report_filter.rb
CHANGED
|
@@ -5,8 +5,9 @@ class ReportFilter
|
|
|
5
5
|
attr_reader :page_size
|
|
6
6
|
|
|
7
7
|
def initialize(attributes)
|
|
8
|
-
|
|
9
|
-
@
|
|
8
|
+
date = Date.today - 5
|
|
9
|
+
@start_on = Date.civil(date.year, date.month, 01)
|
|
10
|
+
@end_on = Date.civil(date.year, date.month, -1)
|
|
10
11
|
@page_size = 1000
|
|
11
12
|
|
|
12
13
|
if attributes
|
data/app/models/user.rb
CHANGED
|
@@ -9,6 +9,7 @@ class User < Party
|
|
|
9
9
|
# even if they follow the defaults since ClassTableInheritanceInRails breaks it.
|
|
10
10
|
has_and_belongs_to_many :groups, :join_table => "groups_users", :foreign_key => "user_id", :association_foreign_key => 'group_id'
|
|
11
11
|
has_and_belongs_to_many :work_lock_subscribers, :class_name => 'User', :join_table => "user_work_lock_subscriptions", :foreign_key => "user_id", :association_foreign_key => 'subscriber_user_id'
|
|
12
|
+
has_and_belongs_to_many :work_lock_subscriptions, :class_name => 'User', :join_table => "user_work_lock_subscriptions", :association_foreign_key => "user_id", :foreign_key => 'subscriber_user_id'
|
|
12
13
|
has_many :works, :foreign_key => :user_id, :order => :completed_at
|
|
13
14
|
has_many :work_locks, :foreign_key => :user_id, :order => :end_on
|
|
14
15
|
|
data/app/models/work.rb
CHANGED
|
@@ -103,8 +103,8 @@ class Work < ActiveRecord::Base
|
|
|
103
103
|
totals_per_work_account
|
|
104
104
|
end
|
|
105
105
|
|
|
106
|
-
def self.find_work_for_day
|
|
107
|
-
Work.find(:all, :conditions => "started_on = '#{date}' AND user_id = #{
|
|
106
|
+
def self.find_work_for_day(date, user = current_user)
|
|
107
|
+
Work.find(:all, :conditions => "started_on = '#{date}' AND user_id = #{user.id}",
|
|
108
108
|
:order => 'start_time, completed_at')
|
|
109
109
|
end
|
|
110
110
|
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
class WorksReportFilter < ReportFilter
|
|
2
|
+
attr_reader :work_account_id
|
|
2
3
|
attr_reader :invoice
|
|
3
4
|
attr_reader :user_id
|
|
4
5
|
|
|
5
6
|
def initialize(attributes)
|
|
7
|
+
@work_account_id = nil
|
|
6
8
|
@invoice = nil
|
|
7
9
|
@user_id = nil
|
|
8
10
|
|
|
9
11
|
if attributes
|
|
10
12
|
attributes = attributes.clone
|
|
11
13
|
|
|
14
|
+
work_account_id_param = attributes.delete(:work_account_id)
|
|
15
|
+
@work_account_id = work_account_id_param.to_i if work_account_id_param && work_account_id_param.size > 0
|
|
16
|
+
|
|
12
17
|
invoice_param = attributes.delete(:invoice)
|
|
13
18
|
@invoice = invoice_param == 'true' if invoice_param && invoice_param.size > 0
|
|
14
19
|
|
|
@@ -35,9 +35,13 @@ function handleEvent(field, event, id) {
|
|
|
35
35
|
<div id="active_tasks"<%=' style="display: none;"' unless @tasks and not @tasks.empty?%>>
|
|
36
36
|
<%=render :partial => '/tasks/fields_header', :locals => { :backlog => nil, :active => true, :track_todo => @tasks.find {|t|t.period && t.period.active?}, :track_times => @tasks.find {|t|t.period && t.period.active?}, :track_done => @tasks.find {|t|t.period && t.period.active? }, :work_done => @tasks.find {|t| t.total_done > 0} } %>
|
|
37
37
|
<ul id="active_tasks_<%=@period.id%>" class="task_list">
|
|
38
|
+
<% max_depth = @tasks.map{|t|t.depth}.max %>
|
|
38
39
|
<% for task in @tasks -%>
|
|
39
40
|
<% next if @show_only_grabbed_tasks && !(task.users.empty? || task.users.include?(current_user))%>
|
|
40
|
-
<%=render :partial => '/tasks/task', :locals => { :task => task, :i => i,
|
|
41
|
+
<%=render :partial => '/tasks/task', :locals => { :task => task, :i => i,
|
|
42
|
+
:active => true, :highlight_task => (task == @selected_task),
|
|
43
|
+
:update => :spotlight, :show_backlog => true, :hidden => false,
|
|
44
|
+
:max_depth => max_depth} %>
|
|
41
45
|
<% i += 1 %>
|
|
42
46
|
<% end -%>
|
|
43
47
|
</ul>
|
data/app/views/tasks/_task.rhtml
CHANGED
|
@@ -3,14 +3,13 @@
|
|
|
3
3
|
<div style="float: left" style="border: 0px">
|
|
4
4
|
<% if @task.enable_subtasks? && active && (@task.period.nil? || @task.period.active_or_future?) %>
|
|
5
5
|
<% form_tag({:controller => 'tasks', :action => :specify, :id => @task}) do %>
|
|
6
|
-
<%=
|
|
6
|
+
<%=image_button_to('add.png', l(:specify), {:controller => 'tasks', :action => :specify, :id => @task.id},
|
|
7
|
+
:style => "margin-left: #{@task.depth * 2}em") %>
|
|
7
8
|
<% end %>
|
|
8
9
|
<% end %>
|
|
9
10
|
</div>
|
|
10
11
|
<div style="float: left" style="border: 3px solid red">
|
|
11
|
-
<%=(" " * @task.depth * 4) if @task.depth > 0 %>
|
|
12
12
|
<%=resolution_image(@task.resolution) if @task.finished_at %>
|
|
13
|
-
<%="-" if @task.children.size > 0 %>
|
|
14
13
|
</div>
|
|
15
14
|
<div id="task_<%=@task.id%>_id" class="task_id">
|
|
16
15
|
<%=detour_to "##{@task.id}", :controller => 'tasks', :action => :edit, :id => @task.id, :style => 'border: 0px; margin: 0px; padding: 0px' if @task.position || @task.depth == 0 %>
|
data/app/views/user/edit.rhtml
CHANGED
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
<div id="lfeature">
|
|
54
54
|
<div class="btitle">
|
|
55
55
|
<h4><%=l :work_lock_subscriptions %></h4>
|
|
56
|
+
<h3>Users watching <%=@user.name%></h4>
|
|
56
57
|
</div>
|
|
57
58
|
|
|
58
59
|
<% if @user == current_user %>
|
|
@@ -67,10 +68,11 @@
|
|
|
67
68
|
<% end %>
|
|
68
69
|
<% unless @associates.empty? %>
|
|
69
70
|
<tr>
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
<td align="right">
|
|
72
|
+
<% remote_form_for :user, :html => {:id => 'invitation_form'}, :url => {:action => :invite_work_lock_subscriber} do |f| %>
|
|
73
|
+
<%=select :id, nil, @associates.map{|u| [u.name, u.id]}, {}, :name => 'id'%></td>
|
|
74
|
+
<% end %>
|
|
75
|
+
<td align="left"><%=button_to_function l(:invite), "new Ajax.Request('/user/invite_work_lock_subscriber', {asynchronous:true, evalScripts:true, parameters:Form.serialize($('invitation_form'))});" %></td>
|
|
74
76
|
</tr>
|
|
75
77
|
<% end %>
|
|
76
78
|
</table>
|
|
@@ -80,6 +82,36 @@
|
|
|
80
82
|
<% end %>
|
|
81
83
|
</div>
|
|
82
84
|
|
|
85
|
+
<div id="rfeature">
|
|
86
|
+
<div class="btitle">
|
|
87
|
+
<h4><%=l :work_lock_subscriptions %></h4>
|
|
88
|
+
<h3><%=@user.name%> is monitoring</h4>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<% if @user == current_user %>
|
|
92
|
+
<table align="right">
|
|
93
|
+
<% unless @user.work_lock_subscriptions.empty? %>
|
|
94
|
+
<% for subscribee in @user.work_lock_subscriptions %>
|
|
95
|
+
<tr>
|
|
96
|
+
<td align="right"><%=detour_to subscribee.name, :action => :edit, :id => subscribee.id %></td>
|
|
97
|
+
<td align="left"><%=image_link_to 'work_account.png', "#{l :work}", {:controller => 'works', :action => :weekly_work_sheet, :user_id => subscribee.id} %></td>
|
|
98
|
+
<td align="left"><%=image_link_to_remote 'email.png', "#{l :stop} #{l :monitoring}", {:action => :toggle_work_lock_monitoring, :subscriber_id => current_user.id, :id => subscribee.id}, :id => "work_lock_monitor_icon_#{subscribee.id}"%></td>
|
|
99
|
+
</tr>
|
|
100
|
+
<% end %>
|
|
101
|
+
<% end %>
|
|
102
|
+
<% unless @potential_subscribees.empty? %>
|
|
103
|
+
<tr>
|
|
104
|
+
<td align="right">
|
|
105
|
+
<% remote_form_for :user, :html => {:id => 'monitoring_form'}, :url => {:action => :invite_work_lock_subscriber} do |f| %>
|
|
106
|
+
<%=select :id, nil, @potential_subscribees.map{|u| [u.name, u.id]}, {}, :name => 'id'%></td>
|
|
107
|
+
<% end %>
|
|
108
|
+
<td align="left"><%=button_to_function l(:monitor), "new Ajax.Request('/user/toggle_work_lock_monitoring', {asynchronous:true, evalScripts:true, parameters:Form.serialize($('monitoring_form'))});" %></td>
|
|
109
|
+
</tr>
|
|
110
|
+
<% end %>
|
|
111
|
+
</table>
|
|
112
|
+
<% end %>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
83
115
|
<div id="lfeature">
|
|
84
116
|
<div class="btitle">
|
|
85
117
|
<h4><%=l :holidays_used %></h4>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<%= error_messages_for 'work_account' %>
|
|
2
2
|
|
|
3
|
-
<!--[form:work_account]-->
|
|
4
3
|
<p><label for="work_account_name">Name</label><br/>
|
|
5
4
|
<%= text_field 'work_account', 'name' %></p>
|
|
6
5
|
|
|
@@ -11,5 +10,7 @@
|
|
|
11
10
|
|
|
12
11
|
<p><label for="work_account_invoice_code">Invoice code</label><br/>
|
|
13
12
|
<%= text_field 'work_account', 'invoice_code' %></p>
|
|
14
|
-
<!--[eoform:work_account]-->
|
|
15
13
|
|
|
14
|
+
<%=javascript_tag update_page { |page|
|
|
15
|
+
page[:work_account_name].focus
|
|
16
|
+
} %>
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<% form_tag :action => 'update', :id => @work_account do %>
|
|
7
7
|
<%=render :partial => 'form' %>
|
|
8
8
|
<%=submit_tag l(:save) %>
|
|
9
|
-
<%=back_or_link_to l(:back), :action =>
|
|
9
|
+
<%=back_or_link_to l(:back), :action => :list %>
|
|
10
10
|
<% end %>
|
|
11
11
|
|
|
12
12
|
</div>
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
<% for work_account in @work_accounts %>
|
|
17
17
|
<tr>
|
|
18
|
-
<td><%=link_to h(work_account.name), :
|
|
18
|
+
<td><%=link_to h(work_account.name), :controller => 'works', :action => :list, :report_filter => {:work_account_id => work_account.id} %></td>
|
|
19
19
|
<td align="right"><%=work_account.track_times? %></td>
|
|
20
20
|
<td><%=h(work_account.invoice_code) %></td>
|
|
21
21
|
<td><%= link_to 'Edit', :action => 'edit', :id => work_account %></td>
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
<% @page_title = "#{l :new_work_account}" %>
|
|
2
2
|
|
|
3
|
+
<div id="spotlight">
|
|
3
4
|
<% form_tag :action => 'create' do %>
|
|
4
5
|
<%= render :partial => 'form' %>
|
|
5
6
|
<%= submit_tag "Create" %>
|
|
6
7
|
<% end %>
|
|
7
8
|
|
|
8
9
|
<%= link_to 'Back', :action => 'list' %>
|
|
10
|
+
</div>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<% @page_title = "#{l :daily_work_sheet} on #{@date}" + (" for #{user.
|
|
1
|
+
<% @page_title = "#{l :daily_work_sheet} on #{@date}" + (" for #{@user.name}" if user?) %>
|
|
2
2
|
|
|
3
3
|
<div id="spotlight">
|
|
4
4
|
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
<%=f.radio_button :reason, '', :disabled => work_disabled, :checked => @absence.nil? && (!@works.empty? || @public_holiday.nil?), :onchange => 'form.submit()' %><label for="absence_reason_">Work day</label>
|
|
22
22
|
<%=f.radio_button :reason, 'HOLIDAY', :disabled => absence_disabled, :onchange => 'form.submit()' %><label for="absence_reason_holiday"><%=l :holiday%></label>
|
|
23
23
|
<%=f.radio_button :reason, 'SICK', :disabled => absence_disabled, :onchange => 'form.submit()' %><label for="absence_reason_sick">Sick day</label>
|
|
24
|
+
<%=f.radio_button :reason, 'SICK_CHILD', :disabled => absence_disabled, :onchange => 'form.submit()' %><label for="absence_reason_sick_child">Sick child</label>
|
|
24
25
|
<%=f.radio_button :reason, 'SICK_WITH_DOCTOR', :disabled => absence_disabled, :onchange => 'form.submit()' %><label for="absence_reason_sick_with_doctor">Sick day with doctor's note</label>
|
|
25
26
|
<% end %>
|
|
26
27
|
</div>
|
|
@@ -64,7 +65,7 @@
|
|
|
64
65
|
<% last_work = @work %>
|
|
65
66
|
|
|
66
67
|
<% unless @absence %>
|
|
67
|
-
<% @work =
|
|
68
|
+
<% @work = @new_work %>
|
|
68
69
|
<%=render :partial => 'new_row', :locals => {:last_work => last_work} %>
|
|
69
70
|
<% end %>
|
|
70
71
|
|
|
@@ -97,7 +98,7 @@
|
|
|
97
98
|
<script type="text/JavaScript">
|
|
98
99
|
//<!--
|
|
99
100
|
var start_field;
|
|
100
|
-
<% if last_work.completed_at %>
|
|
101
|
+
<% if last_work.nil? || last_work.completed_at %>
|
|
101
102
|
start_field = $('work_work_account_id');
|
|
102
103
|
<% else %>
|
|
103
104
|
start_field = $('work_<%=last_work.id%>_completed_at_time');
|
data/app/views/works/list.rhtml
CHANGED
|
@@ -7,6 +7,14 @@
|
|
|
7
7
|
<% if @report_filter %>
|
|
8
8
|
<% form_for :report_filter, :html => {:method => :get} do |f| %>
|
|
9
9
|
<table>
|
|
10
|
+
<tr>
|
|
11
|
+
<td coslpan="3">
|
|
12
|
+
<label for="report_filter_work_account_id"><%=l :work_account%></label>
|
|
13
|
+
</td>
|
|
14
|
+
<td>
|
|
15
|
+
<%=f.select :work_account_id, [[l(:all), nil]] + @work_accounts.map{|wa|[wa.name, wa.id]}, {}, {:onchange => 'form.submit()'} %>
|
|
16
|
+
</td>
|
|
17
|
+
</tr>
|
|
10
18
|
<tr>
|
|
11
19
|
<td>
|
|
12
20
|
<label for="report_filter_start_on"><%=l :start_on%></label>
|
|
@@ -91,9 +99,9 @@
|
|
|
91
99
|
|
|
92
100
|
<table>
|
|
93
101
|
<tr>
|
|
94
|
-
<th><%=l :task %></th>
|
|
95
102
|
<th><%=l :user %></th>
|
|
96
103
|
<th><%=l :done %></th>
|
|
104
|
+
<th><%=l :description %></th>
|
|
97
105
|
<th><%=l :invoice %></th>
|
|
98
106
|
<% if @period && @period.track_times? %>
|
|
99
107
|
<th><%=l :started_at %></th>
|
|
@@ -103,23 +111,26 @@
|
|
|
103
111
|
|
|
104
112
|
<% for work in @works %>
|
|
105
113
|
<tr>
|
|
106
|
-
<td><%=work.
|
|
107
|
-
<td><%=work.
|
|
108
|
-
|
|
114
|
+
<td valign="top"><%=work.user && work.user.login %></td>
|
|
115
|
+
<td valign="top"><%=work.hours %></td>
|
|
116
|
+
<%if work.task
|
|
117
|
+
link = "Task: #{work.task.description} <br/>"
|
|
118
|
+
else
|
|
119
|
+
link = ""
|
|
120
|
+
end
|
|
121
|
+
link += h(work.description)
|
|
122
|
+
%>
|
|
123
|
+
<td><%= link_to link, :controller => 'works', :action => 'daily_work_sheet', :id => work.started_on.strftime("%Y-%m-%d") %></td>
|
|
109
124
|
<td><%=work.invoice ? l(:yes) : '' %></td>
|
|
110
125
|
<% if @period && @period.track_times? %>
|
|
111
126
|
<td><%=work.started_at && work.started_at.strftime('%Y-%m-%d %H:%M:%S') %></td>
|
|
112
127
|
<% end %>
|
|
113
|
-
<td><%=work.completed_at && work.completed_at.strftime('%Y-%m-%d %H:%M:%S') %></td>
|
|
114
|
-
<
|
|
115
|
-
<td><%= link_to 'Edit', :controller => 'works', :action => 'edit', :id => work %></td>
|
|
116
|
-
<td><%= link_to 'Destroy', { :controller => 'works', :action => 'destroy', :id => work }, :confirm => 'Are you sure?', :method => :post %></td>
|
|
117
|
-
</tr>
|
|
128
|
+
<td valign="top"><%=work.completed_at && work.completed_at.strftime('%Y-%m-%d %H:%M:%S') %></td>
|
|
129
|
+
<tr>
|
|
118
130
|
<% end %>
|
|
119
131
|
<tr>
|
|
120
132
|
<th><%=l :total%>:</th>
|
|
121
|
-
<td
|
|
122
|
-
<th><%=@works.inject(BigDecimal('0')){|total, work| total += work.hours}%> <%=l(:hours).downcase%></td>
|
|
133
|
+
<th colspan="2"><%=@works.inject(BigDecimal('0')){|total, work| total += work.hours}%> <%=l(:hours).downcase%></td>
|
|
123
134
|
</tr>
|
|
124
135
|
</table>
|
|
125
136
|
|
|
@@ -136,4 +147,4 @@
|
|
|
136
147
|
|
|
137
148
|
<% if @period %>
|
|
138
149
|
<%=render :partial => '/periods/burn_down_chart' %>
|
|
139
|
-
<% end %>
|
|
150
|
+
<% end %>
|
|
@@ -29,11 +29,6 @@
|
|
|
29
29
|
<Styles>
|
|
30
30
|
<Style ss:ID="Default" ss:Name="Normal">
|
|
31
31
|
<Alignment ss:Vertical="Top"/>
|
|
32
|
-
<Borders/>
|
|
33
|
-
<Font/>
|
|
34
|
-
<Interior/>
|
|
35
|
-
<NumberFormat/>
|
|
36
|
-
<Protection/>
|
|
37
32
|
</Style>
|
|
38
33
|
<Style ss:ID="title">
|
|
39
34
|
<Font x:Family="Swiss" ss:Size="24" ss:Bold="1"/>
|
|
@@ -41,37 +36,49 @@
|
|
|
41
36
|
<Style ss:ID="header">
|
|
42
37
|
<Font x:Family="Swiss" ss:Size="14" ss:Bold="1"/>
|
|
43
38
|
</Style>
|
|
39
|
+
<Style ss:ID="DateTime">
|
|
40
|
+
<NumberFormat ss:Format="Number" />
|
|
41
|
+
</Style>
|
|
44
42
|
</Styles>
|
|
45
43
|
<Worksheet ss:Name="<%=l(:done)%>">
|
|
46
|
-
<Table ss:ExpandedColumnCount="256"
|
|
44
|
+
<Table ss:ExpandedColumnCount="256" x:FullColumns="1"
|
|
47
45
|
x:FullRows="1">
|
|
48
|
-
<Column ss:AutoFitWidth="1" ss:Width="
|
|
49
|
-
<Column ss:AutoFitWidth="
|
|
50
|
-
<Column ss:AutoFitWidth="
|
|
51
|
-
<Column ss:AutoFitWidth="
|
|
52
|
-
<Column ss:AutoFitWidth="
|
|
46
|
+
<Column ss:AutoFitWidth="1" ss:Width="100"/>
|
|
47
|
+
<Column ss:AutoFitWidth="1" ss:Width="90"/>
|
|
48
|
+
<Column ss:AutoFitWidth="1" ss:Width="120"/>
|
|
49
|
+
<Column ss:AutoFitWidth="1" ss:Width="200"/>
|
|
50
|
+
<Column ss:AutoFitWidth="1" ss:Width="60"/>
|
|
51
|
+
<Column ss:AutoFitWidth="1" ss:Width="45"/>
|
|
52
|
+
<Column ss:AutoFitWidth="1" ss:Width="90"/>
|
|
53
|
+
<Column ss:AutoFitWidth="1" ss:Width="90"/>
|
|
53
54
|
<Row>
|
|
54
|
-
<Cell ss:StyleID="title" ss:MergeAcross="
|
|
55
|
+
<Cell ss:StyleID="title" ss:MergeAcross="6"><Data ss:Type="String"><%=@report_filter.title%></Data></Cell>
|
|
55
56
|
</Row>
|
|
56
57
|
<Row ss:AutoFitHeight="0" ss:Height="6.5625">
|
|
57
|
-
<Cell ss:MergeAcross="
|
|
58
|
+
<Cell ss:MergeAcross="6"><Data ss:Type="String"></Data></Cell>
|
|
58
59
|
</Row>
|
|
59
60
|
|
|
60
61
|
<Row ss:AutoFitHeight="0" ss:Height="19.875">
|
|
61
|
-
<Cell ss:StyleID="header"><Data ss:Type="String"
|
|
62
|
-
<Cell ss:StyleID="header"><Data ss:Type="String"
|
|
63
|
-
<Cell ss:StyleID="header"><Data ss:Type="String"
|
|
64
|
-
<Cell ss:StyleID="header"><Data ss:Type="String"
|
|
65
|
-
<Cell ss:StyleID="header"><Data ss:Type="String"
|
|
62
|
+
<Cell ss:StyleID="header"><Data ss:Type="String"><%=l :work_account%></Data></Cell>
|
|
63
|
+
<Cell ss:StyleID="header"><Data ss:Type="String"><%=l :user%></Data></Cell>
|
|
64
|
+
<Cell ss:StyleID="header"><Data ss:Type="String"><%=l :task%></Data></Cell>
|
|
65
|
+
<Cell ss:StyleID="header"><Data ss:Type="String"><%=l :description%></Data></Cell>
|
|
66
|
+
<Cell ss:StyleID="header"><Data ss:Type="String"><%=l :done%></Data></Cell>
|
|
67
|
+
<Cell ss:StyleID="header"><Data ss:Type="String"><%=l :invoice_short%></Data></Cell>
|
|
68
|
+
<Cell ss:StyleID="header"><Data ss:Type="String"><%=l :start%></Data></Cell>
|
|
69
|
+
<Cell ss:StyleID="header"><Data ss:Type="String"><%=l :stop%></Data></Cell>
|
|
66
70
|
</Row>
|
|
67
71
|
|
|
68
72
|
<% for work in @works %>
|
|
69
73
|
<Row>
|
|
70
|
-
<Cell><Data ss:Type="String"><%=
|
|
74
|
+
<Cell><Data ss:Type="String"><%=work.work_account.name %></Data></Cell>
|
|
71
75
|
<Cell><Data ss:Type="String"><%=work.user && work.user.name %></Data></Cell>
|
|
72
|
-
<Cell><Data ss:Type="
|
|
73
|
-
<Cell><Data ss:Type="
|
|
74
|
-
<Cell><Data ss:Type="Number"><%=work.hours
|
|
76
|
+
<Cell><Data ss:Type="String"><%=work.task && work.task.description %></Data></Cell>
|
|
77
|
+
<Cell><Data ss:Type="String"><%=work.description %></Data></Cell>
|
|
78
|
+
<Cell><Data ss:Type="Number"><%=work.hours %></Data></Cell>
|
|
79
|
+
<Cell><Data ss:Type="String"><%=work.invoice? ? 'Ja' : 'Nei' %></Data></Cell>
|
|
80
|
+
<Cell ss:StyleID="DateTime"><Data ss:Type="Number"><%=BigDecimal((work.started_on - Date.new(1899, 12, 31)).to_s) + (work.start_time ? (BigDecimal(work.start_time.hour.to_s) + BigDecimal(work.start_time.minute.to_s)/60) / 24 : 0) %></Data></Cell>
|
|
81
|
+
<Cell ss:StyleID="DateTime"><Data ss:Type="Number"><%=work.completed_at && (BigDecimal((work.completed_at.to_datetime - DateTime.parse('1899-12-31T00:00:00+02:00')).to_f.to_s))%></Data></Cell>
|
|
75
82
|
</Row>
|
|
76
83
|
<% end %>
|
|
77
84
|
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
<tr>
|
|
10
10
|
<th><%=l :work_account %></th>
|
|
11
11
|
<% [:monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday].each_with_index do |day, i| %>
|
|
12
|
-
<th align="center"><%=detour_to "#{l(day)} #{week_date(i+1)}", :action => :daily_work_sheet, :id => (@first_date + i).strftime('%Y-%m-%d') %></th>
|
|
12
|
+
<th align="center"><%=detour_to "#{l(day)} #{week_date(i+1)}", :action => :daily_work_sheet, :id => (@first_date + i).strftime('%Y-%m-%d'), :user_id => (@user != current_user ? @user : nil) %></th>
|
|
13
13
|
<% end %>
|
|
14
14
|
<th align="center" nowrap="true"><%=l :week %> <%=@week%></th>
|
|
15
15
|
</tr>
|
|
@@ -43,4 +43,7 @@
|
|
|
43
43
|
<th class="hours"><%=t(week_total)%></th>
|
|
44
44
|
</tr>
|
|
45
45
|
</table>
|
|
46
|
+
|
|
47
|
+
<%=back_or_link_to l(:back), '' %>
|
|
48
|
+
|
|
46
49
|
</div>
|
data/config/database.yml
CHANGED
data/config/environment.rb
CHANGED
|
@@ -110,10 +110,10 @@ require 'version_from_history'
|
|
|
110
110
|
require 'user_system'
|
|
111
111
|
require 'url_for_fix'
|
|
112
112
|
|
|
113
|
-
if RUBY_PLATFORM !~ /i386-mswin32/
|
|
113
|
+
if RUBY_PLATFORM !~ /(i386-mswin32|java)/
|
|
114
114
|
gem 'slave'
|
|
115
115
|
require 'slave'
|
|
116
116
|
work_lock_nagger_thread = Slave.object(:async=>true) {WorkLockNagger.new.nag}
|
|
117
117
|
else
|
|
118
|
-
puts
|
|
118
|
+
puts "Not spawning worklog nagger on #$1 platform."
|
|
119
119
|
end
|
data/test/fixtures/works.yml
CHANGED
|
@@ -91,24 +91,4 @@ class WorkAccountsControllerTest < Test::Unit::TestCase
|
|
|
91
91
|
}
|
|
92
92
|
end
|
|
93
93
|
|
|
94
|
-
def test_works
|
|
95
|
-
get :works, :id => 1, :report_filter => {:start_on => '2007-04-01', :end_on => '2007-06-13'}
|
|
96
|
-
|
|
97
|
-
assert_response :success
|
|
98
|
-
assert_template 'list'
|
|
99
|
-
|
|
100
|
-
assert_not_nil assigns(:works)
|
|
101
|
-
assert_equal 4, assigns(:works).size
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def test_works_excel
|
|
105
|
-
get :works, :id => 1, :report_filter => {:start_on => '2007-04-01', :end_on => '2007-06-13'}, :export => 'excel'
|
|
106
|
-
|
|
107
|
-
assert_response :success
|
|
108
|
-
assert_template 'list_excel'
|
|
109
|
-
|
|
110
|
-
assert_not_nil assigns(:works)
|
|
111
|
-
assert_equal 4, assigns(:works).size
|
|
112
|
-
end
|
|
113
|
-
|
|
114
94
|
end
|
|
@@ -36,6 +36,26 @@ class WorksControllerTest < Test::Unit::TestCase
|
|
|
36
36
|
assert_not_nil assigns(:works)
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
+
def test_list_with_filter
|
|
40
|
+
get :list, :id => 1, :report_filter => {:start_on => '2007-04-01', :end_on => '2007-06-13'}
|
|
41
|
+
|
|
42
|
+
assert_response :success
|
|
43
|
+
assert_template 'list'
|
|
44
|
+
|
|
45
|
+
assert_not_nil assigns(:works)
|
|
46
|
+
assert_equal 4, assigns(:works).size
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def test_list_excel
|
|
50
|
+
get :list, :id => 1, :report_filter => {:start_on => '2007-04-01', :end_on => '2007-06-13'}, :export => 'excel'
|
|
51
|
+
|
|
52
|
+
assert_response :success
|
|
53
|
+
assert_template 'list_excel'
|
|
54
|
+
|
|
55
|
+
assert_not_nil assigns(:works)
|
|
56
|
+
assert_equal 4, assigns(:works).size
|
|
57
|
+
end
|
|
58
|
+
|
|
39
59
|
def test_show
|
|
40
60
|
get :show, :id => 1
|
|
41
61
|
|
|
@@ -118,6 +138,15 @@ class WorksControllerTest < Test::Unit::TestCase
|
|
|
118
138
|
assert_equal expected_time, assigns(:work).start_time
|
|
119
139
|
end
|
|
120
140
|
|
|
141
|
+
def test_update_time_with_five_minutes
|
|
142
|
+
before = works(:first)
|
|
143
|
+
|
|
144
|
+
post :update_time, :id => 1, :work => {"hours_time"=>"0:05"}
|
|
145
|
+
|
|
146
|
+
assert_response :success
|
|
147
|
+
assert_equal BigDecimal('0.083'), assigns(:work).hours
|
|
148
|
+
end
|
|
149
|
+
|
|
121
150
|
def test_update_row_with_empty_start_time
|
|
122
151
|
before = works(:first)
|
|
123
152
|
old_start_date = before.started_on
|
data/test/unit/work_test.rb
CHANGED
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.
|
|
4
|
+
version: 0.32.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Uwe Kubosch
|
|
@@ -9,7 +9,7 @@ autorequire:
|
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
11
|
|
|
12
|
-
date: 2008-
|
|
12
|
+
date: 2008-05-08 00:00:00 +02:00
|
|
13
13
|
default_executable:
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|