backlog 0.18.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +19 -0
- data/app/controllers/periods_controller.rb +0 -14
- data/app/controllers/tasks_controller.rb +15 -4
- data/app/controllers/user_controller.rb +15 -1
- data/app/controllers/work_accounts_controller.rb +1 -1
- data/app/controllers/work_locks_controller.rb +88 -0
- data/app/controllers/works_controller.rb +29 -5
- data/app/helpers/work_locks_helper.rb +2 -0
- data/app/models/task.rb +1 -1
- data/app/models/user.rb +1 -0
- data/app/models/user_notify.rb +15 -0
- data/app/models/work.rb +28 -6
- data/app/models/work_account.rb +2 -0
- data/app/models/work_lock.rb +3 -0
- data/app/models/work_lock_notify.rb +27 -0
- data/app/models/work_lock_subscription.rb +3 -0
- data/app/views/display_notice.rjs +1 -0
- data/app/views/layouts/_left_top.rhtml +1 -0
- data/app/views/tasks/_task.rhtml +2 -2
- data/app/views/user/edit.rhtml +15 -2
- data/app/views/user/toggle_work_lock_monitoring.rjs +2 -0
- data/app/views/user_notify/monitoring_en.rhtml +3 -0
- data/app/views/user_notify/monitoring_no.rhtml +3 -0
- data/app/views/work_lock_notify/lock_en.rhtml +7 -0
- data/app/views/work_lock_notify/lock_no.rhtml +7 -0
- data/app/views/work_locks/_form.rhtml +10 -0
- data/app/views/work_locks/edit.rhtml +9 -0
- data/app/views/work_locks/list.rhtml +27 -0
- data/app/views/work_locks/new.rhtml +8 -0
- data/app/views/work_locks/show.rhtml +8 -0
- data/app/views/works/_buttons.rhtml +5 -0
- data/app/views/works/_form.rhtml +4 -1
- data/app/views/works/_row.rhtml +14 -17
- data/app/views/works/_row_field.rhtml +1 -1
- data/app/views/works/daily_work_sheet.rhtml +16 -25
- data/app/views/works/timeliste.rhtml +12 -12
- data/app/views/works/update_row.rjs +2 -4
- data/app/views/works/weekly_work_sheet.rhtml +17 -17
- data/app/views/works/weekly_work_sheet_by_work_account.rhtml +57 -0
- data/db/migrate/027_create_work_locks.rb +25 -0
- data/db/schema.rb +25 -1
- data/public/images/delete.png +0 -0
- data/public/images/email.png +0 -0
- data/public/images/email_grey.png +0 -0
- data/public/images/refresh.png +0 -0
- data/test/fixtures/work_lock_subscriptions.yml +7 -0
- data/test/fixtures/work_locks.yml +11 -0
- data/test/functional/estimates_controller_test.rb +1 -1
- data/test/functional/groups_controller_test.rb +1 -1
- data/test/functional/user_controller_test.rb +2 -2
- data/test/functional/work_locks_controller_test.rb +93 -0
- data/test/integration/user_system_test.rb +1 -1
- data/test/test_helper.rb +1 -1
- data/test/unit/estimate_test.rb +1 -1
- data/test/unit/group_test.rb +1 -1
- data/test/unit/period_test.rb +1 -1
- data/test/unit/task_test.rb +1 -1
- data/test/unit/user_test.rb +1 -1
- data/test/unit/work_account_test.rb +1 -1
- data/test/unit/work_lock_subscription_test.rb +10 -0
- data/test/unit/work_lock_test.rb +10 -0
- data/test/unit/work_test.rb +1 -1
- metadata +31 -2
data/History.txt
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
== 0.19.0 2008-01-23
|
2
|
+
|
3
|
+
=== Features
|
4
|
+
|
5
|
+
* Added button to recalculate the hours when changing start/stop times in the create/edit work record view.
|
6
|
+
* Added locking of work records, and notification.
|
7
|
+
* Added work accounts list link to navigation top.
|
8
|
+
* Changed weekly work sheet to show totals instead of individual work records.
|
9
|
+
Added links in the weekly work sheet to each day for details.
|
10
|
+
* Improved daily work sheet. Maybe usable now.
|
11
|
+
|
12
|
+
=== Fixes
|
13
|
+
|
14
|
+
* Fixed bug when grabbing/releasing a task and then starting and stopping work.
|
15
|
+
This would render javascript in cleartext.
|
16
|
+
* Changed redirect after login from displaying backlog to welcome view.
|
17
|
+
* Fixed Excel export.
|
18
|
+
|
1
19
|
== 0.18.0 2008-01-21
|
2
20
|
|
3
21
|
=== Features
|
@@ -11,6 +29,7 @@
|
|
11
29
|
=== Fixes
|
12
30
|
|
13
31
|
* Updated graphs in README.txt
|
32
|
+
* Sorted work account list by name.
|
14
33
|
|
15
34
|
== 0.17.6 2008-01-19
|
16
35
|
|
@@ -147,20 +147,6 @@ class PeriodsController < ApplicationController
|
|
147
147
|
move_task_to_period
|
148
148
|
end
|
149
149
|
|
150
|
-
def grab_task
|
151
|
-
@task = Task.find(params[:id])
|
152
|
-
@task.grab
|
153
|
-
load_tasks(@task.period)
|
154
|
-
render :template => '/tasks/_update.rjs'
|
155
|
-
end
|
156
|
-
|
157
|
-
def release_task
|
158
|
-
@task = Task.find(params[:id])
|
159
|
-
@task.release
|
160
|
-
load_tasks(@task.period)
|
161
|
-
render :template => '/tasks/_update.rjs'
|
162
|
-
end
|
163
|
-
|
164
150
|
def finish_task
|
165
151
|
@task = Task.find(params[:id])
|
166
152
|
@task.finish(Task::COMPLETED, true)
|
@@ -196,10 +196,21 @@ class TasksController < ApplicationController
|
|
196
196
|
redirect_to :controller => 'periods', :action => :show_no_layout, :id => task.period_id, :task => task.id
|
197
197
|
end
|
198
198
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
199
|
+
def grab
|
200
|
+
@task = Task.find(params[:id])
|
201
|
+
@task.grab
|
202
|
+
detour = pop_detour
|
203
|
+
params.update(detour) if detour
|
204
|
+
render :template => '/tasks/_update.rjs'
|
205
|
+
end
|
206
|
+
|
207
|
+
def release
|
208
|
+
@task = Task.find(params[:id])
|
209
|
+
@task.release
|
210
|
+
detour = pop_detour
|
211
|
+
params.update(detour) if detour
|
212
|
+
render :template => '/tasks/_update.rjs'
|
213
|
+
end
|
203
214
|
|
204
215
|
def start_work
|
205
216
|
@task = Task.find(params[:id])
|
@@ -14,7 +14,7 @@ class UserController < ApplicationController
|
|
14
14
|
cookies[:autologin] = {:value => user.id.to_s, :expires =>90.days.from_now}
|
15
15
|
cookies[:token] = {:value => user.security_token, :expires =>90.days.from_now}
|
16
16
|
end
|
17
|
-
back_or_redirect_to :controller => '
|
17
|
+
back_or_redirect_to :controller => 'welcome', :action => :index
|
18
18
|
else
|
19
19
|
@login = params['user']['login']
|
20
20
|
flash[:notice] = 'Login failed'
|
@@ -155,6 +155,20 @@ class UserController < ApplicationController
|
|
155
155
|
@users = User.find(:all)
|
156
156
|
end
|
157
157
|
|
158
|
+
def toggle_work_lock_monitoring
|
159
|
+
@user = User.find(params[:id])
|
160
|
+
already_monitoring = @user.work_lock_subscribers.include? current_user
|
161
|
+
if already_monitoring
|
162
|
+
@user.work_lock_subscribers.delete current_user
|
163
|
+
action = 'stopped'
|
164
|
+
else
|
165
|
+
@user.work_lock_subscribers << current_user
|
166
|
+
action = 'started'
|
167
|
+
end
|
168
|
+
flash[:notice] = "Monitoring #{action}"
|
169
|
+
UserNotify.deliver_monitoring(@user, current_user, action)
|
170
|
+
end
|
171
|
+
|
158
172
|
protected
|
159
173
|
|
160
174
|
def protect?(action)
|
@@ -9,7 +9,7 @@ class WorkAccountsController < ApplicationController
|
|
9
9
|
:redirect_to => { :action => :list }
|
10
10
|
|
11
11
|
def list
|
12
|
-
@work_accounts = WorkAccount.paginate :page => params[:page]
|
12
|
+
@work_accounts = WorkAccount.paginate :page => params[:page], :order => 'name'
|
13
13
|
end
|
14
14
|
|
15
15
|
def show
|
@@ -0,0 +1,88 @@
|
|
1
|
+
class WorkLocksController < ApplicationController
|
2
|
+
def index
|
3
|
+
list
|
4
|
+
render :action => 'list'
|
5
|
+
end
|
6
|
+
|
7
|
+
# GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
|
8
|
+
verify :method => :post, :only => [ :destroy, :create, :update ],
|
9
|
+
:redirect_to => { :action => :list }
|
10
|
+
|
11
|
+
def list
|
12
|
+
@work_lock_pages, @work_locks = paginate :work_locks, :per_page => 10
|
13
|
+
end
|
14
|
+
|
15
|
+
def show
|
16
|
+
@work_lock = WorkLock.find(params[:id])
|
17
|
+
end
|
18
|
+
|
19
|
+
def new
|
20
|
+
@work_lock = WorkLock.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def create
|
24
|
+
@work_lock = WorkLock.new(params[:work_lock])
|
25
|
+
if @work_lock.save
|
26
|
+
flash[:notice] = 'WorkLock was successfully created.'
|
27
|
+
redirect_to :action => 'list'
|
28
|
+
else
|
29
|
+
render :action => 'new'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def edit
|
34
|
+
@work_lock = WorkLock.find(params[:id])
|
35
|
+
end
|
36
|
+
|
37
|
+
def update
|
38
|
+
@work_lock = WorkLock.find(params[:id])
|
39
|
+
if @work_lock.update_attributes(params[:work_lock])
|
40
|
+
flash[:notice] = 'WorkLock was successfully updated.'
|
41
|
+
redirect_to :action => 'show', :id => @work_lock
|
42
|
+
else
|
43
|
+
render :action => 'edit'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def destroy
|
48
|
+
WorkLock.find(params[:id]).destroy
|
49
|
+
redirect_to :action => 'list'
|
50
|
+
end
|
51
|
+
|
52
|
+
def lock
|
53
|
+
@year = (params[:year] && params[:year].to_i) || Date.today.year
|
54
|
+
@week = (params[:week] && params[:week].to_i) || Date.today.cweek
|
55
|
+
first_date = Date.commercial(@year, @week, 1)
|
56
|
+
last_date = first_date + 6
|
57
|
+
@lock = WorkLock.find(:first, :conditions => ['user_id = ? AND start_on <= ? AND end_on >= ?', current_user.id, first_date, last_date])
|
58
|
+
raise "Already locked" if @lock
|
59
|
+
WorkLock.create! :user_id => current_user.id, :start_on => first_date, :end_on => last_date
|
60
|
+
work_account_subscribers = Work.works_for_week(@year, @week).flatten.compact.map {|w| w.work_account}.uniq.map {|wa| wa.work_lock_subscribers}.flatten.uniq
|
61
|
+
user_subscribers = current_user.work_lock_subscribers
|
62
|
+
notify_users = (work_account_subscribers + user_subscribers).uniq
|
63
|
+
notify_users.each do |user|
|
64
|
+
week_url = url_for :controller => 'works', :action => :weekly_work_sheet_by_work_account, :year => @year, :week => @week
|
65
|
+
spreadsheet_url = url_for :controller => 'works', :action => :timeliste, :year => @year, :week => @week
|
66
|
+
WorkLockNotify.deliver_lock(user, current_user, @week, week_url, spreadsheet_url)
|
67
|
+
end
|
68
|
+
back_or_redirect_to :controller => 'works', :action => :weekly_work_sheet, :year => @year, :week => @week
|
69
|
+
end
|
70
|
+
|
71
|
+
def unlock
|
72
|
+
get_lock_from_params
|
73
|
+
raise "Not locked" unless @lock
|
74
|
+
raise "Unable to destroy" unless @lock.destroy
|
75
|
+
back_or_redirect_to :controller => 'works', :action => :weekly_work_sheet, :year => @year, :week => @week
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def get_lock_from_params
|
81
|
+
@year = (params[:year] && params[:year].to_i) || Date.today.year
|
82
|
+
@week = (params[:week] && params[:week].to_i) || Date.today.cweek
|
83
|
+
@first_date = Date.commercial(@year, @week, 1)
|
84
|
+
@last_date = @first_date + 6
|
85
|
+
@lock = WorkLock.find(:first, :conditions => ['user_id = ? AND start_on <= ? AND end_on >= ?', current_user.id, @first_date, @last_date])
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
@@ -3,7 +3,7 @@ class WorksController < ApplicationController
|
|
3
3
|
in_place_edit_for :work, :invoice
|
4
4
|
in_place_edit_for :work, :started_at_time
|
5
5
|
in_place_edit_for :work, :completed_at_time
|
6
|
-
skip_before_filter :populate_layout, :except => [:create, :destroy, :edit, :index, :list, :new, :show, :update]
|
6
|
+
skip_before_filter :populate_layout, :except => [:create, :destroy, :edit, :index, :list, :new, :show, :update, :daily_work_sheet, :weekly_work_sheet_by_work_account]
|
7
7
|
auto_complete_for :work, :description
|
8
8
|
|
9
9
|
def index
|
@@ -73,7 +73,7 @@ class WorksController < ApplicationController
|
|
73
73
|
@work.attributes = params[:work]
|
74
74
|
@estimate = Estimate.new(params[:estimate])
|
75
75
|
@work_accounts = WorkAccount.find(:all)
|
76
|
-
@tasks = Task.find_open
|
76
|
+
@tasks = [@work.task] + Task.find_open
|
77
77
|
@users = User.find(:all)
|
78
78
|
end
|
79
79
|
|
@@ -91,6 +91,7 @@ class WorksController < ApplicationController
|
|
91
91
|
update_work
|
92
92
|
flash.discard
|
93
93
|
@field = params[:field] || 'started_at_time'
|
94
|
+
@work_accounts = WorkAccount.find(:all, :order => :name)
|
94
95
|
end
|
95
96
|
|
96
97
|
def update_time
|
@@ -133,6 +134,7 @@ class WorksController < ApplicationController
|
|
133
134
|
@works = Work.find_work_for_day @date
|
134
135
|
@started_works = Task.find_started
|
135
136
|
@work = Work.new(:started_at => Time.now, :completed_at => Time.now)
|
137
|
+
@work_accounts = WorkAccount.find(:all, :order => :name)
|
136
138
|
render :layout => 'wide'
|
137
139
|
end
|
138
140
|
|
@@ -140,12 +142,26 @@ class WorksController < ApplicationController
|
|
140
142
|
@year = (params[:year] && params[:year].to_i) || Date.today.year
|
141
143
|
@week = (params[:week] && params[:week].to_i) || Date.today.cweek
|
142
144
|
@rows = Work.works_for_week(@year, @week)
|
145
|
+
@first_date = Date.commercial(@year, @week, 1)
|
146
|
+
@last_date = @first_date + 6
|
147
|
+
@lock = WorkLock.find(:first, :conditions => ['user_id = ? AND start_on <= ? AND end_on >= ?', current_user.id, @first_date, @last_date])
|
148
|
+
render :layout => 'wide'
|
149
|
+
end
|
150
|
+
|
151
|
+
def weekly_work_sheet_by_work_account
|
152
|
+
@year = (params[:year] && params[:year].to_i) || Date.today.year
|
153
|
+
@week = (params[:week] && params[:week].to_i) || Date.today.cweek
|
154
|
+
@work_accounts = Work.works_for_week_by_work_account(@year, @week)
|
155
|
+
@first_date = Date.commercial(@year, @week, 1)
|
156
|
+
@last_date = @first_date + 6
|
157
|
+
@lock = WorkLock.find(:first, :conditions => ['user_id = ? AND start_on <= ? AND end_on >= ?', current_user.id, @first_date, @last_date])
|
143
158
|
render :layout => 'wide'
|
144
159
|
end
|
145
160
|
|
146
161
|
def timeliste
|
147
|
-
@
|
148
|
-
@
|
162
|
+
@year = (params[:year] && params[:year].to_i) || Date.today.year
|
163
|
+
@week = (params[:week] && params[:week].to_i) || Date.today.cweek
|
164
|
+
@work_totals_per_work_account = Work.work_totals_for_week(@year, @week)
|
149
165
|
headers["Content-Type"] = "application/vnd.ms-excel"
|
150
166
|
headers["Content-Disposition"] = 'attachment; filename="export.xml"'
|
151
167
|
render :layout => false
|
@@ -183,6 +199,12 @@ class WorksController < ApplicationController
|
|
183
199
|
|
184
200
|
render :partial => 'description_list'
|
185
201
|
end
|
202
|
+
|
203
|
+
def calculate_hours
|
204
|
+
calculated_duration = (Time.parse(params[:completed_at]) - Time.parse(params[:started_at]))
|
205
|
+
new_value = '%2d:%02d' % [calculated_duration / 3600, (calculated_duration % 3600) / 60]
|
206
|
+
render :update do |page| page['work_hours_time'].value = new_value end
|
207
|
+
end
|
186
208
|
|
187
209
|
private
|
188
210
|
|
@@ -194,7 +216,9 @@ class WorksController < ApplicationController
|
|
194
216
|
else
|
195
217
|
raise "Unknown time format: '#{params[:work][:hours_time]}'"
|
196
218
|
end
|
197
|
-
if
|
219
|
+
if params[:work][:completed_at]
|
220
|
+
t = Time.parse params[:work][:completed_at]
|
221
|
+
elsif @work && @work.started?
|
198
222
|
t = @work.started_at
|
199
223
|
else
|
200
224
|
t = Time.now
|
data/app/models/task.rb
CHANGED
@@ -54,7 +54,7 @@ class Task < ActiveRecord::Base
|
|
54
54
|
user_clause = " OR user_id = #{current_user.id}"
|
55
55
|
end
|
56
56
|
conditions = "completed_at IS NULL AND (user_id IS NULL#{user_clause})"
|
57
|
-
Work.find(:all, :conditions => conditions).map {|work| work.task}.sort_by do |t|
|
57
|
+
Work.find(:all, :conditions => conditions).map {|work| work.task}.compact.sort_by do |t|
|
58
58
|
[t.root_task.backlog.name, t.root_task.period.end_on, t.position || 0]
|
59
59
|
end
|
60
60
|
end
|
data/app/models/user.rb
CHANGED
@@ -8,6 +8,7 @@ class User < Party
|
|
8
8
|
# TODO (uwe): We need to specify :join_table, :foreign_key and :association_foreign_key
|
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
|
+
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'
|
11
12
|
|
12
13
|
attr_accessor :password_needs_confirmation
|
13
14
|
|
data/app/models/user_notify.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
class UserNotify < ActionMailer::Base
|
2
|
+
include Localization
|
3
|
+
|
2
4
|
def signup(user, password, url=nil)
|
3
5
|
setup_email(user)
|
4
6
|
|
@@ -40,6 +42,19 @@ class UserNotify < ActionMailer::Base
|
|
40
42
|
@body["app_name"] = UserSystem::CONFIG[:app_name].to_s
|
41
43
|
end
|
42
44
|
|
45
|
+
def monitoring(user, monitoring_user, action)
|
46
|
+
setup_email(user)
|
47
|
+
|
48
|
+
@subject += "#{monitoring_user.name} has #{l(action)} monitoring your time sheets."
|
49
|
+
|
50
|
+
@body["app_name"] = UserSystem::CONFIG[:app_name].to_s
|
51
|
+
@body["user"] = user
|
52
|
+
@body["monitoring_user"] = monitoring_user
|
53
|
+
@body["action"] = action
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
43
58
|
def setup_email(user)
|
44
59
|
@recipients = "#{user.email}"
|
45
60
|
@from = UserSystem::CONFIG[:email_from].to_s
|
data/app/models/work.rb
CHANGED
@@ -44,6 +44,28 @@ class Work < ActiveRecord::Base
|
|
44
44
|
works_by_row = works_per_day.transpose
|
45
45
|
end
|
46
46
|
|
47
|
+
# Return a hash with work accounts as keys an array of work hours totals per day as values:
|
48
|
+
# {
|
49
|
+
# <#WorkAccount#1> => [ 8, 7, 9, 12, 4, nil, nil],
|
50
|
+
# <#WorkAccount#2> => [nil, 1, nil, nil, 4, nil, nil],
|
51
|
+
# <#WorkAccount#3> => [nil, nil, nil, nil, nil, 4, 3],
|
52
|
+
# ]
|
53
|
+
def self.works_for_week_by_work_account(year, week_no, user = current_user)
|
54
|
+
first_date = Date.commercial(year, week_no, 1)
|
55
|
+
last_date = first_date + 6
|
56
|
+
works = find(:all, :conditions => "completed_at IS NOT NULL AND completed_at BETWEEN '#{first_date.to_time.iso8601}' AND '#{(last_date+1).to_time.iso8601}'", :order => 'completed_at, started_at')
|
57
|
+
result = {}
|
58
|
+
works.each do |work|
|
59
|
+
day_of_week = work.completed_at.to_date.cwday - 1
|
60
|
+
result[work.work_account] ||= []
|
61
|
+
result[work.work_account][day_of_week] ||= BigDecimal('0')
|
62
|
+
result[work.work_account][day_of_week] += work.hours
|
63
|
+
end
|
64
|
+
result.values.each {|work_account_totals| work_account_totals[6] ||= nil}
|
65
|
+
result.values.each {|work_account_totals| (0..6).each {|i| work_account_totals[i] = nil if work_account_totals[i] == 0}}
|
66
|
+
result
|
67
|
+
end
|
68
|
+
|
47
69
|
# Return a hash with an array of work totals per day:
|
48
70
|
# {
|
49
71
|
# backlog1.id => [[m, t, w, t, f, s, s], [m, t, w, t, f, s, s]],
|
@@ -52,12 +74,12 @@ class Work < ActiveRecord::Base
|
|
52
74
|
def self.work_totals_for_week(year, week_no, user = current_user)
|
53
75
|
first = Date.commercial(year, week_no, 1)
|
54
76
|
last = first + 7
|
55
|
-
works = find(:all, :conditions => "completed_at IS NOT NULL AND
|
77
|
+
works = find(:all, :conditions => "completed_at IS NOT NULL AND completed_at BETWEEN '#{first.to_time.iso8601}' AND '#{last.to_time.iso8601}'", :order => 'completed_at, started_at')
|
56
78
|
totals_per_work_account = {}
|
57
|
-
|
79
|
+
works.map{|w| w.work_account}.uniq.each do |work_account|
|
58
80
|
totals_per_work_account[work_account.id] = [[], []]
|
59
81
|
(0..6).each do |day|
|
60
|
-
works_for_day = works.select {|work| (work.work_account == work_account) && (work.
|
82
|
+
works_for_day = works.select {|work| (work.work_account == work_account) && (work.completed_at.to_date == (first + day)) && (work.user_id.nil? || (user && work.user_id == user.id)) }
|
61
83
|
invoice_works_for_day = works_for_day.select {|work| work.invoice? }
|
62
84
|
internal_works_for_day = works_for_day.select {|work| !work.invoice? }
|
63
85
|
|
@@ -74,8 +96,8 @@ class Work < ActiveRecord::Base
|
|
74
96
|
end
|
75
97
|
|
76
98
|
def self.find_work_for_day date
|
77
|
-
Work.find(:all, :conditions => "
|
78
|
-
:order => 'completed_at')
|
99
|
+
Work.find(:all, :conditions => "(completed_at IS NULL OR completed_at BETWEEN '#{date}' AND '#{date+1}') AND user_id = #{current_user.id}",
|
100
|
+
:order => 'completed_at, started_at')
|
79
101
|
end
|
80
102
|
|
81
103
|
def started?
|
@@ -94,7 +116,7 @@ class Work < ActiveRecord::Base
|
|
94
116
|
end
|
95
117
|
|
96
118
|
def completed_at_time
|
97
|
-
completed_at.strftime('%H:%M')
|
119
|
+
completed_at && completed_at.strftime('%H:%M')
|
98
120
|
end
|
99
121
|
|
100
122
|
def completed_at_time=(new_value)
|
data/app/models/work_account.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
class WorkAccount < ActiveRecord::Base
|
2
2
|
has_many :backlogs, :dependent => :nullify
|
3
3
|
has_many :works, :dependent => :destroy
|
4
|
+
has_and_belongs_to_many :work_lock_subscribers, :class_name => 'User', :join_table => "work_lock_subscriptions", :foreign_key => "work_account_id", :association_foreign_key => 'subscriber_user_id'
|
4
5
|
|
5
6
|
validates_inclusion_of :track_times, :in => [true, false], :allow_nil => true, :message => ActiveRecord::Errors.default_error_messages[:blank]
|
6
7
|
validates_length_of :invoice_code, :allow_nil => true, :maximum => 255
|
8
|
+
validates_uniqueness_of :name
|
7
9
|
|
8
10
|
def enable_invoicing?
|
9
11
|
invoice_code && invoice_code.length > 0
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class WorkLockNotify < ActionMailer::Base
|
2
|
+
def lock(user, lock_user, week, week_url, spreadsheet_url)
|
3
|
+
setup_email(user)
|
4
|
+
|
5
|
+
# Email header info
|
6
|
+
@subject += "#{lock_user.name} has marked week #{week} as locked."
|
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["name"] = "#{user.first_name} #{user.last_name}"
|
12
|
+
@body["login"] = user.login
|
13
|
+
@body["lock_user"] = lock_user
|
14
|
+
@body["week_url"] = week_url
|
15
|
+
@body["spreadsheet_url"] = spreadsheet_url
|
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
|
@@ -0,0 +1 @@
|
|
1
|
+
display_notice(page)
|
@@ -6,6 +6,7 @@
|
|
6
6
|
|
7
7
|
<% if user? %>
|
8
8
|
| <%= link_to l(:backlogs), :controller => 'backlogs', :action => :list %>
|
9
|
+
| <%= link_to l(:work_accounts), :controller => 'work_accounts', :action => :list %>
|
9
10
|
| <%= link_to l(:log_out), :controller => 'user', :action => :logout %>
|
10
11
|
<% else %>
|
11
12
|
| <%= detour_to l(:log_in), :controller => 'user', :action => :login %>
|
data/app/views/tasks/_task.rhtml
CHANGED
@@ -87,9 +87,9 @@
|
|
87
87
|
<% unless @task.work_started? -%>
|
88
88
|
<% if @task.backlog.enable_users? %>
|
89
89
|
<% if @task.users.include?(current_user) %>
|
90
|
-
<%=image_link_to_remote('grab.png', l(:grab_task), {:action => :
|
90
|
+
<%=image_link_to_remote('grab.png', l(:grab_task), with_detour({:controller => 'tasks', :action => :release, :id => @task}), nil, true)%>
|
91
91
|
<% else %>
|
92
|
-
<%=image_link_to_remote('grab_gray.png', l(:grab_task), {:action => :
|
92
|
+
<%=image_link_to_remote('grab_gray.png', l(:grab_task), with_detour({:controller => 'tasks', :action => :grab, :id => @task}), nil, true)%>
|
93
93
|
<% end %>
|
94
94
|
<% end %>
|
95
95
|
<%=image_link_to_remote('arrow_right.png', l(:move_to_next_period), {:action => :move_task_to_next_period, :id => @task}, nil, true) if @task.backlog.enable_periods? || @task.period_id%>
|
data/app/views/user/edit.rhtml
CHANGED
@@ -1,6 +1,19 @@
|
|
1
|
-
|
2
|
-
<% @page_title = "#{l :editing} #{l :user}" %>
|
1
|
+
<% @page_title = "#{l :editing} #{l :user}" %>
|
3
2
|
|
3
|
+
<div id="spotlight" title="<%= title_helper %>" class="form">
|
4
|
+
<% if @user == current_user %>
|
5
|
+
<% unless @user.work_lock_subscribers.empty? %>
|
6
|
+
<table align="right">
|
7
|
+
<tr><th>Subscribers</th></tr>
|
8
|
+
<% for subscriber in @user.work_lock_subscribers %>
|
9
|
+
<tr><td><%=detour_to subscriber.name, :action => :edit%></td></tr>
|
10
|
+
<% end %>
|
11
|
+
</table>
|
12
|
+
<% end %>
|
13
|
+
<% else %>
|
14
|
+
<% monitoring = @user.work_lock_subscribers.include? current_user %>
|
15
|
+
<%=image_link_to_remote "email#{'_grey' unless monitoring}.png", "#{l(monitoring ? :stop : :start)} #{l(:monitoring)}", {:action => :toggle_work_lock_monitoring, :id => @user.id}, {:id => :work_lock_monitor_icon, :style => 'float: right'} %>
|
16
|
+
<% end %>
|
4
17
|
<%= start_form_tag_helper %>
|
5
18
|
<%= render_partial 'edit', :user => @user, :submit => true %>
|
6
19
|
</form>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<%= error_messages_for 'work_lock' %>
|
2
|
+
|
3
|
+
<!--[form:work_lock]-->
|
4
|
+
<p><label for="work_lock_start_on">Start on</label><br/>
|
5
|
+
<%= date_select 'work_lock', 'start_on' %></p>
|
6
|
+
|
7
|
+
<p><label for="work_lock_end_on">End on</label><br/>
|
8
|
+
<%= date_select 'work_lock', 'end_on' %></p>
|
9
|
+
<!--[eoform:work_lock]-->
|
10
|
+
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<h1>Editing work_lock</h1>
|
2
|
+
|
3
|
+
<% form_tag :action => 'update', :id => @work_lock do %>
|
4
|
+
<%= render :partial => 'form' %>
|
5
|
+
<%= submit_tag 'Edit' %>
|
6
|
+
<% end %>
|
7
|
+
|
8
|
+
<%= link_to 'Show', :action => 'show', :id => @work_lock %> |
|
9
|
+
<%= link_to 'Back', :action => 'list' %>
|
@@ -0,0 +1,27 @@
|
|
1
|
+
<h1>Listing work_locks</h1>
|
2
|
+
|
3
|
+
<table>
|
4
|
+
<tr>
|
5
|
+
<% for column in WorkLock.content_columns %>
|
6
|
+
<th><%= column.human_name %></th>
|
7
|
+
<% end %>
|
8
|
+
</tr>
|
9
|
+
|
10
|
+
<% for work_lock in @work_locks %>
|
11
|
+
<tr>
|
12
|
+
<% for column in WorkLock.content_columns %>
|
13
|
+
<td><%=h work_lock.send(column.name) %></td>
|
14
|
+
<% end %>
|
15
|
+
<td><%= link_to 'Show', :action => 'show', :id => work_lock %></td>
|
16
|
+
<td><%= link_to 'Edit', :action => 'edit', :id => work_lock %></td>
|
17
|
+
<td><%= link_to 'Destroy', { :action => 'destroy', :id => work_lock }, :confirm => 'Are you sure?', :method => :post %></td>
|
18
|
+
</tr>
|
19
|
+
<% end %>
|
20
|
+
</table>
|
21
|
+
|
22
|
+
<%= link_to 'Previous page', { :page => @work_lock_pages.current.previous } if @work_lock_pages.current.previous %>
|
23
|
+
<%= link_to 'Next page', { :page => @work_lock_pages.current.next } if @work_lock_pages.current.next %>
|
24
|
+
|
25
|
+
<br />
|
26
|
+
|
27
|
+
<%= link_to 'New work_lock', :action => 'new' %>
|
@@ -1,3 +1,8 @@
|
|
1
|
+
<%=image_detour_to('hammer.png', l(:weekly_work_sheet), :controller => 'works', :action => :weekly_work_sheet_by_work_account)%>
|
2
|
+
<!--
|
1
3
|
<%=image_detour_to('hammer.png', l(:weekly_work_sheet), :controller => 'works', :action => :weekly_work_sheet)%>
|
4
|
+
-->
|
2
5
|
<%=link_to(image_tag(url_for("hammer.png"), :alt => l(:daily_work_sheet), :title => l(:daily_work_sheet), :class => 'image-submit'), :controller => 'works', :action => :daily_work_sheet)%>
|
6
|
+
<!--
|
3
7
|
<%=link_to(image_tag(url_for("hammer.png"), :alt => l(:edit_works), :title => l(:edit_works), :class => 'image-submit'), :controller => 'periods', :action => 'list_work', :id => (@period ? @period.id : (@backlog && @backlog.periods.first ? @backlog.periods.first.id : nil))) if @period || @backlog%>
|
8
|
+
-->
|
data/app/views/works/_form.rhtml
CHANGED
@@ -63,7 +63,10 @@
|
|
63
63
|
|
64
64
|
<% if @work.task.nil? || @work.task.track_done?%>
|
65
65
|
<p style="float: left;"><label for="work_hours"><%=l :hours%></label><br/>
|
66
|
-
<%=
|
66
|
+
<%=text_field 'work', 'hours_time', :class => :task_hours, :value => t(@work.hours) %></p>
|
67
|
+
|
68
|
+
<p style="float: left;"><br/><%=image_link_to_remote 'refresh.png', l(:refresh), :action => :calculate_hours, :started_at => "' + $('work_started_at').value + '", :completed_at => "' + $('work_completed_at').value + '" %></p>
|
69
|
+
|
67
70
|
<% end %>
|
68
71
|
|
69
72
|
<% if @work.task.nil? || @work.task.enable_invoicing? %>
|