bulk_time_entry_plugin 0.4.0 → 0.5.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/Rakefile +2 -1
- data/VERSION +1 -1
- data/app/controllers/bulk_time_entries_controller.rb +31 -55
- data/app/helpers/bulk_time_entries_helper.rb +19 -15
- data/app/views/bulk_time_entries/_activities_selector.html.erb +6 -0
- data/app/views/bulk_time_entries/_time_entry.html.erb +6 -6
- data/app/views/bulk_time_entries/add_entry.js.rjs +1 -0
- data/app/views/bulk_time_entries/load_assigned_issues.js.rjs +3 -0
- data/app/views/bulk_time_entries/save.js.rjs +12 -0
- data/config/locales/de.yml +6 -0
- data/config/locales/nl.yml +7 -0
- data/init.rb +23 -1
- data/lang/de.yml +5 -0
- data/lang/nl.yml +6 -0
- data/lib/bulk_time_entry_compatibility.rb +0 -19
- data/lib/bulk_time_entry_plugin/patches/time_entry_patch.rb +25 -0
- data/rails/init.rb +1 -13
- data/test/functional/bulk_time_entries_controller_test.rb +130 -1
- data/test/test_helper.rb +7 -3
- data/test/unit/helpers/bulk_time_entries_helper_test.rb +88 -0
- data/test/unit/lib/bulk_time_entry_plugin/patches/time_entry_patch_test.rb +49 -0
- metadata +15 -2
data/Rakefile
CHANGED
@@ -6,7 +6,7 @@ Dir[File.expand_path(File.dirname(__FILE__)) + "/lib/tasks/**/*.rake"].sort.each
|
|
6
6
|
RedminePluginSupport::Base.setup do |plugin|
|
7
7
|
plugin.project_name = 'bulk_time_entry_plugin'
|
8
8
|
plugin.default_task = [:test]
|
9
|
-
plugin.tasks = [:doc, :release, :clean, :test, :db]
|
9
|
+
plugin.tasks = [:doc, :release, :clean, :test, :db, :metrics]
|
10
10
|
# TODO: gem not getting this automaticly
|
11
11
|
plugin.redmine_root = File.expand_path(File.dirname(__FILE__) + '/../../../')
|
12
12
|
end
|
@@ -35,3 +35,4 @@ begin
|
|
35
35
|
rescue LoadError
|
36
36
|
puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
37
37
|
end
|
38
|
+
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.5.0
|
@@ -4,55 +4,45 @@ class BulkTimeEntriesController < ApplicationController
|
|
4
4
|
layout 'base'
|
5
5
|
before_filter :load_activities
|
6
6
|
before_filter :load_allowed_projects
|
7
|
+
before_filter :load_first_project
|
8
|
+
before_filter :check_for_no_projects
|
7
9
|
|
8
10
|
helper :custom_fields
|
11
|
+
include BulkTimeEntriesHelper
|
9
12
|
|
10
13
|
protect_from_forgery :only => [:index, :save]
|
11
14
|
|
12
15
|
def index
|
13
16
|
@time_entries = [TimeEntry.new(:spent_on => Date.today.to_s)]
|
14
|
-
|
15
|
-
if @projects.empty?
|
16
|
-
render :action => 'no_projects'
|
17
|
-
end
|
18
17
|
end
|
19
18
|
|
20
|
-
def get_issues(project_id)
|
21
|
-
@issues = Issue.find(:all, :conditions => { :project_id => project_id })
|
22
|
-
end
|
23
|
-
|
24
19
|
def load_assigned_issues
|
25
|
-
get_issues
|
26
|
-
|
27
|
-
|
20
|
+
@issues = get_issues(params[:project_id])
|
21
|
+
@selected_project = BulkTimeEntriesController.allowed_project?(params[:project_id])
|
22
|
+
respond_to do |format|
|
23
|
+
format.js {}
|
28
24
|
end
|
29
25
|
end
|
30
26
|
|
31
27
|
|
32
28
|
def save
|
33
29
|
if request.post?
|
34
|
-
@
|
30
|
+
@unsaved_entries = {}
|
31
|
+
@saved_entries = {}
|
35
32
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
@
|
40
|
-
|
41
|
-
@
|
42
|
-
@time_entry.user = User.current
|
43
|
-
unless @time_entry.save
|
44
|
-
page.replace "entry_#{html_id}", :partial => 'time_entry', :object => @time_entry
|
45
|
-
else
|
46
|
-
time_entry_target = if @time_entry.issue
|
47
|
-
"#{h(@time_entry.project.name)} - #{h(@time_entry.issue.subject)}"
|
48
|
-
else
|
49
|
-
"#{h(@time_entry.project.name)}"
|
50
|
-
end
|
51
|
-
page.replace_html "entry_#{html_id}", "<div class='flash notice'>#{l(:text_time_added_to_project, :count => @time_entry.hours, :target => time_entry_target)}#{" (#{@time_entry.comments})" unless @time_entry.comments.blank?}.</div>"
|
52
|
-
end
|
33
|
+
params[:time_entries].each_pair do |html_id, entry|
|
34
|
+
time_entry = TimeEntry.create_bulk_time_entry(entry)
|
35
|
+
if time_entry.new_record?
|
36
|
+
@unsaved_entries[html_id] = time_entry
|
37
|
+
else
|
38
|
+
@saved_entries[html_id] = time_entry
|
53
39
|
end
|
54
40
|
end
|
55
|
-
|
41
|
+
|
42
|
+
respond_to do |format|
|
43
|
+
format.js {}
|
44
|
+
end
|
45
|
+
end
|
56
46
|
end
|
57
47
|
|
58
48
|
def add_entry
|
@@ -65,18 +55,14 @@ class BulkTimeEntriesController < ApplicationController
|
|
65
55
|
|
66
56
|
@time_entry = TimeEntry.new(:spent_on => spent_on.to_s)
|
67
57
|
respond_to do |format|
|
68
|
-
format.js
|
69
|
-
render :update do |page|
|
70
|
-
page.insert_html :bottom, 'entries', :partial => 'time_entry', :object => @time_entry
|
71
|
-
end
|
72
|
-
end
|
58
|
+
format.js {}
|
73
59
|
end
|
74
60
|
end
|
75
61
|
|
76
62
|
private
|
77
63
|
|
78
64
|
def load_activities
|
79
|
-
@activities =
|
65
|
+
@activities = TimeEntryActivity.all
|
80
66
|
end
|
81
67
|
|
82
68
|
def load_allowed_projects
|
@@ -84,28 +70,18 @@ class BulkTimeEntriesController < ApplicationController
|
|
84
70
|
Project.allowed_to_condition(User.current, :log_time))
|
85
71
|
end
|
86
72
|
|
87
|
-
def
|
88
|
-
|
73
|
+
def load_first_project
|
74
|
+
@first_project = @projects.sort_by(&:lft).first unless @projects.empty?
|
89
75
|
end
|
90
76
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
def project_tree_options_for_select(projects)
|
96
|
-
result = []
|
97
|
-
user_projects_by_root = projects.group_by(&:root)
|
98
|
-
user_projects_by_root.keys.sort.each do |root|
|
99
|
-
result << [h(root.name), root.id]
|
100
|
-
user_projects_by_root[root].sort.each do |project|
|
101
|
-
next if project == root
|
102
|
-
result << ["» #{h(project.name)}", project.id]
|
103
|
-
end
|
104
|
-
end
|
105
|
-
return result
|
77
|
+
def check_for_no_projects
|
78
|
+
if @projects.empty?
|
79
|
+
render :action => 'no_projects'
|
80
|
+
return false
|
106
81
|
end
|
107
|
-
helper_method :project_tree_options_for_select
|
108
|
-
|
109
82
|
end
|
110
83
|
|
84
|
+
def self.allowed_project?(project_id)
|
85
|
+
return User.current.projects.find_by_id(project_id, :conditions => Project.allowed_to_condition(User.current, :log_time))
|
86
|
+
end
|
111
87
|
end
|
@@ -11,28 +11,32 @@ module BulkTimeEntriesHelper
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def grouped_options_for_issues(issues)
|
14
|
-
open_issues =
|
15
|
-
closed_issues = []
|
16
|
-
issues.each do |issue|
|
17
|
-
if issue.closed?
|
18
|
-
closed_issues << issue
|
19
|
-
else
|
20
|
-
open_issues << issue
|
21
|
-
end
|
22
|
-
end
|
14
|
+
closed_issues, open_issues = *issues.partition {|issue| issue.closed?}
|
23
15
|
|
24
16
|
html = '<option></option>'
|
25
17
|
unless open_issues.empty?
|
26
|
-
html <<
|
27
|
-
html << options_from_collection_for_select(open_issues, :id, :to_s)
|
28
|
-
html << "</optgroup>"
|
18
|
+
html << labeled_option_group_from_collection_for_select(:label_open_issues, open_issues)
|
29
19
|
end
|
30
20
|
|
31
21
|
unless closed_issues.empty?
|
32
|
-
html <<
|
33
|
-
html << options_from_collection_for_select(closed_issues, :id, :to_s)
|
34
|
-
html << "</optgroup>"
|
22
|
+
html << labeled_option_group_from_collection_for_select(:label_closed_issues, closed_issues)
|
35
23
|
end
|
36
24
|
html
|
37
25
|
end
|
26
|
+
|
27
|
+
def labeled_option_group_from_collection_for_select(label, collection)
|
28
|
+
html = "<optgroup label='#{l(label)}'>"
|
29
|
+
html << options_from_collection_for_select(collection, :id, :to_s)
|
30
|
+
html << "</optgroup>"
|
31
|
+
html
|
32
|
+
end
|
33
|
+
|
34
|
+
def get_issues(project_id)
|
35
|
+
project = BulkTimeEntriesController.allowed_project?(project_id)
|
36
|
+
if project
|
37
|
+
project.issues.all(:order => 'id ASC')
|
38
|
+
else
|
39
|
+
[]
|
40
|
+
end
|
41
|
+
end
|
38
42
|
end
|
@@ -4,15 +4,15 @@
|
|
4
4
|
@object = time_entry
|
5
5
|
@object_name = 'time_entry' -%>
|
6
6
|
<div class="box" id="entry_<%= rnd %>">
|
7
|
-
<%= error_messages_for 'time_entry' %>
|
7
|
+
<%= error_messages_for 'time_entry', :object => time_entry %>
|
8
8
|
<p>
|
9
9
|
<%= label_for_field :project_id, rnd, :required => true %>
|
10
|
-
<% projects = project_tree_options_for_select(
|
10
|
+
<% projects = project_tree_options_for_select(@projects, :selected => @first_project) %>
|
11
11
|
<%= f.select :project_id, projects, {},
|
12
12
|
{ :onchange => "new Ajax.Request('#{url_for :action => 'load_assigned_issues'}', { parameters: { project_id: $F(this), entry_id: $(this).up(1).id } } )" } %>
|
13
13
|
</p>
|
14
14
|
<p id="entry_<%= rnd %>_issues">
|
15
|
-
<% @issues =
|
15
|
+
<% @issues = get_issues @first_project.id %>
|
16
16
|
<%= render :partial => 'issues_selector', :locals => { :issues => @issues, :f => f, :rnd => rnd } %>
|
17
17
|
</p>
|
18
18
|
<p>
|
@@ -27,9 +27,8 @@
|
|
27
27
|
<%= label_for_field :comments, rnd %>
|
28
28
|
<%= f.text_field :comments, :size => 100, :maxlength => 255 %>
|
29
29
|
</p>
|
30
|
-
<p>
|
31
|
-
<%=
|
32
|
-
<%= f.select :activity_id, (@activities.collect {|p| [p.name, p.id]}) %>
|
30
|
+
<p id="entry_<%= rnd %>_activities">
|
31
|
+
<%= render :partial => 'activities_selector', :locals => {:rnd => rnd, :activities => @first_project.activities } %>
|
33
32
|
</p>
|
34
33
|
<% time_entry.custom_field_values.each do |value| %>
|
35
34
|
<p><%= custom_field_tag_with_label "time_entries[#{rnd}]", value %></p>
|
@@ -37,4 +36,5 @@
|
|
37
36
|
</div>
|
38
37
|
<% end %>
|
39
38
|
|
39
|
+
<%= javascript_tag("Element.scrollTo('time_entries_#{time_entry.id}_project_id')") %>
|
40
40
|
<%= javascript_tag("Form.Element.focus('time_entries_#{time_entry.id}_project_id')") %>
|
@@ -0,0 +1 @@
|
|
1
|
+
page.insert_html :bottom, 'entries', :partial => 'time_entry', :object => @time_entry
|
@@ -0,0 +1,3 @@
|
|
1
|
+
page.replace_html params[:entry_id]+'_issues', :partial => 'issues_selector', :locals => { :issues => @issues, :rnd => params[:entry_id].split('_')[1] }
|
2
|
+
|
3
|
+
page.replace_html params[:entry_id]+'_activities', :partial => 'activities_selector', :locals => { :rnd => params[:entry_id].split('_')[1], :activities => (@selected_project.present? ? @selected_project.activities : []) }
|
@@ -0,0 +1,12 @@
|
|
1
|
+
@unsaved_entries.each do |html_id, entry|
|
2
|
+
page.replace "entry_#{html_id}", :partial => 'time_entry', :object => entry
|
3
|
+
end
|
4
|
+
|
5
|
+
@saved_entries.each do |html_id, entry|
|
6
|
+
time_entry_target = if entry.issue
|
7
|
+
"#{h(entry.project.name)} - #{h(entry.issue.subject)}"
|
8
|
+
else
|
9
|
+
"#{h(entry.project.name)}"
|
10
|
+
end
|
11
|
+
page.replace_html "entry_#{html_id}", "<div class='flash notice'>#{l(:text_time_added_to_project, :count => entry.hours, :target => time_entry_target)}#{" (#{entry.comments})" unless entry.comments.blank?}.</div>"
|
12
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
de:
|
2
|
+
bulk_time_entry_title: Zeiterfassung
|
3
|
+
label_bulk_time_entries: Zeiterfassung
|
4
|
+
label_bulk_time_add_another: Weiteren Eintrag hinzufügen
|
5
|
+
text_bulk_time_entry_permission: Sie haben keine Berechtigung um Zeiten für irgendein Projekt zu erfassen
|
6
|
+
text_time_added_to_project: "{{count}} Stunde/n im Projekt {{target}} erfasst"
|
@@ -0,0 +1,7 @@
|
|
1
|
+
nl:
|
2
|
+
bulk_time_entry_title: Bulk tijd loggen
|
3
|
+
label_bulk_time_entries: Bulk tijd loggen
|
4
|
+
label_bulk_time_add_another: Voeg extra toe
|
5
|
+
text_bulk_time_entry_permission: Je hebt geen rechten om op een project tijd te loggen
|
6
|
+
text_time_added_to_project: %s uren toegevoegd aan project
|
7
|
+
|
data/init.rb
CHANGED
@@ -1 +1,23 @@
|
|
1
|
-
require
|
1
|
+
require 'redmine'
|
2
|
+
|
3
|
+
config.gem 'fastercsv' if respond_to? :config
|
4
|
+
|
5
|
+
Redmine::Plugin.register :bulk_time_entry_plugin do
|
6
|
+
name 'Bulk Time Entry'
|
7
|
+
author 'Eric Davis'
|
8
|
+
description 'This is a plugin to enter multiple time entries at one time.'
|
9
|
+
version '0.5.0'
|
10
|
+
|
11
|
+
requires_redmine :version_or_higher => '0.9.0'
|
12
|
+
|
13
|
+
menu :top_menu, :bulk_time_entry, {:controller => "bulk_time_entries", :action => 'index'},
|
14
|
+
:caption => :bulk_time_entry_title, :if => Proc.new{User.current.allowed_to?(:log_time, nil, :global => true)}
|
15
|
+
end
|
16
|
+
|
17
|
+
# Patches to the Redmine core.
|
18
|
+
require 'dispatcher'
|
19
|
+
|
20
|
+
Dispatcher.to_prepare :bulk_time_entry_plugin do
|
21
|
+
require_dependency 'time_entry'
|
22
|
+
TimeEntry.send(:include, BulkTimeEntryPlugin::Patches::TimeEntryPatch)
|
23
|
+
end
|
data/lang/de.yml
ADDED
@@ -0,0 +1,5 @@
|
|
1
|
+
bulk_time_entry_title: Zeiterfassung
|
2
|
+
label_bulk_time_entries: Zeiterfassung
|
3
|
+
label_bulk_time_add_another: Weiteren Eintrag hinzufügen
|
4
|
+
text_bulk_time_entry_permission: Sie haben keine Berechtigung um Zeiten für irgendein Projekt zu erfassen
|
5
|
+
text_time_added_to_project: %s Stunde/n zum Projekt hinzugefügt
|
data/lang/nl.yml
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
bulk_time_entry_title: Bulk tijd loggen
|
2
|
+
label_bulk_time_entries: Bulk tijd loggen
|
3
|
+
label_bulk_time_add_another: Voeg extra toe
|
4
|
+
text_bulk_time_entry_permission: Je hebt geen rechten om op een project tijd te loggen
|
5
|
+
text_time_added_to_project: %s uren toegevoegd aan project
|
6
|
+
|
@@ -1,22 +1,3 @@
|
|
1
|
-
begin
|
2
|
-
require 'time_entry_activity'
|
3
|
-
rescue LoadError
|
4
|
-
# TimeEntryActivity is not available
|
5
|
-
end
|
6
|
-
|
7
1
|
# Wrappers around the Redmine core API changes between versions
|
8
2
|
module BulkTimeEntryCompatibility
|
9
|
-
class Enumeration
|
10
|
-
# Wrapper around Redmine's API since Enumerations changed in r2472
|
11
|
-
# This can be removed once 0.9.0 is stable
|
12
|
-
def self.activities
|
13
|
-
if Object.const_defined?('TimeEntryActivity')
|
14
|
-
return ::TimeEntryActivity.all
|
15
|
-
elsif ::Enumeration.respond_to?(:activities)
|
16
|
-
return ::Enumeration.activities
|
17
|
-
else
|
18
|
-
return ::Enumeration::get_values('ACTI')
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
3
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module BulkTimeEntryPlugin
|
2
|
+
module Patches
|
3
|
+
module TimeEntryPatch
|
4
|
+
def self.included(base)
|
5
|
+
base.extend ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
|
10
|
+
def create_bulk_time_entry(entry)
|
11
|
+
time_entry = TimeEntry.new(entry)
|
12
|
+
time_entry.hours = nil if time_entry.hours.blank? or time_entry.hours <= 0
|
13
|
+
if BulkTimeEntriesController.allowed_project?(entry[:project_id])
|
14
|
+
time_entry.project_id = entry[:project_id] # project_id is protected from mass assignment
|
15
|
+
end
|
16
|
+
time_entry.user = User.current
|
17
|
+
time_entry.save
|
18
|
+
time_entry
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/rails/init.rb
CHANGED
@@ -1,13 +1 @@
|
|
1
|
-
require
|
2
|
-
|
3
|
-
config.gem 'fastercsv' if respond_to? :config
|
4
|
-
|
5
|
-
Redmine::Plugin.register :bulk_time_entry_plugin do
|
6
|
-
name 'Bulk Time Entry'
|
7
|
-
author 'Eric Davis'
|
8
|
-
description 'This is a plugin to enter multiple time entries at one time.'
|
9
|
-
version '0.4.0'
|
10
|
-
|
11
|
-
menu :top_menu, :bulk_time_entry, {:controller => "bulk_time_entries", :action => 'index'},
|
12
|
-
:caption => :bulk_time_entry_title, :if => Proc.new{User.current.allowed_to?(:log_time, nil, :global => true)}
|
13
|
-
end
|
1
|
+
require File.dirname(__FILE__) + "/../init"
|
@@ -10,6 +10,8 @@ class BulkTimeEntriesControllerTest < ActionController::TestCase
|
|
10
10
|
|
11
11
|
should_have_before_filter :load_activities
|
12
12
|
should_have_before_filter :load_allowed_projects
|
13
|
+
should_have_before_filter :load_first_project
|
14
|
+
should_have_before_filter :check_for_no_projects
|
13
15
|
|
14
16
|
context "GET to :index" do
|
15
17
|
context "as a user without any projects" do
|
@@ -21,7 +23,7 @@ class BulkTimeEntriesControllerTest < ActionController::TestCase
|
|
21
23
|
end
|
22
24
|
|
23
25
|
should_respond_with :success
|
24
|
-
|
26
|
+
should_not_assign_to :time_entries
|
25
27
|
should_assign_to :projects
|
26
28
|
should_render_template :no_projects
|
27
29
|
|
@@ -38,7 +40,134 @@ class BulkTimeEntriesControllerTest < ActionController::TestCase
|
|
38
40
|
should_respond_with :success
|
39
41
|
should_assign_to :time_entries
|
40
42
|
should_assign_to :projects
|
43
|
+
should_assign_to :first_project
|
41
44
|
should_render_template :index
|
42
45
|
end
|
43
46
|
end
|
47
|
+
|
48
|
+
context "POST to :save with valid params and a valid user" do
|
49
|
+
setup do
|
50
|
+
@project = Project.generate!
|
51
|
+
@activity = TimeEntryActivity.generate!
|
52
|
+
|
53
|
+
generate_user_and_login_for_project(@project)
|
54
|
+
@issue = Issue.generate!(:project => @project, :tracker => @project.trackers.first, :priority => IssuePriority.generate!(:name => 'Low'))
|
55
|
+
end
|
56
|
+
|
57
|
+
should "save a new time entry" do
|
58
|
+
|
59
|
+
assert_difference('TimeEntry.count',2) do
|
60
|
+
post :save, :time_entries => {
|
61
|
+
"1234" => {
|
62
|
+
"comments" => 'a comment',
|
63
|
+
"project_id" => @project.id,
|
64
|
+
"issue_id" => @issue.id,
|
65
|
+
"activity_id" => @activity.id,
|
66
|
+
"spent_on" => Date.today.to_s,
|
67
|
+
"hours" => '42'
|
68
|
+
},
|
69
|
+
"9658" => {
|
70
|
+
"comments" => 'a comment',
|
71
|
+
"project_id" => @project.id,
|
72
|
+
"issue_id" => @issue.id,
|
73
|
+
"activity_id" => @activity.id,
|
74
|
+
"spent_on" => Date.today.to_s,
|
75
|
+
"hours" => '42'
|
76
|
+
}
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
should "replace the time entry form with a flash message" do
|
83
|
+
post :save, :time_entries => {
|
84
|
+
"1234" => {
|
85
|
+
"comments" => 'a comment',
|
86
|
+
"project_id" => @project.id,
|
87
|
+
"issue_id" => @issue.id,
|
88
|
+
"activity_id" => @activity.id,
|
89
|
+
"spent_on" => Date.today.to_s,
|
90
|
+
"hours" => '42'
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
assert_select_rjs :replace_html, 'entry_1234', :text => /notice/
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context "POST to :save with invalid params and a valid user" do
|
99
|
+
setup do
|
100
|
+
@project = Project.generate!
|
101
|
+
@activity = TimeEntryActivity.generate!
|
102
|
+
|
103
|
+
generate_user_and_login_for_project(@project)
|
104
|
+
@issue = Issue.generate!(:project => @project, :tracker => @project.trackers.first, :priority => IssuePriority.generate!(:name => 'Low'))
|
105
|
+
end
|
106
|
+
should "not save a time entry" do
|
107
|
+
|
108
|
+
assert_no_difference('TimeEntry.count') do
|
109
|
+
post :save, :time_entries => {
|
110
|
+
"1234" => {
|
111
|
+
"comments" => 'a comment',
|
112
|
+
"project_id" => @project.id,
|
113
|
+
"issue_id" => @issue.id,
|
114
|
+
"activity_id" => @activity.id,
|
115
|
+
"spent_on" => '',
|
116
|
+
"hours" => '42'
|
117
|
+
}
|
118
|
+
}
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
should "re-render the time entry from" do
|
124
|
+
post :save, :time_entries => {
|
125
|
+
"1234" => {
|
126
|
+
"comments" => 'a comment',
|
127
|
+
"project_id" => @project.id,
|
128
|
+
"issue_id" => @issue.id,
|
129
|
+
"activity_id" => @activity.id,
|
130
|
+
"spent_on" => '',
|
131
|
+
"hours" => '42'
|
132
|
+
}
|
133
|
+
}
|
134
|
+
|
135
|
+
assert_select_rjs :replace, 'entry_1234', :text => /blank/
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context "POST to :load_assigned_issues with an allowed project" do
|
140
|
+
setup do
|
141
|
+
@project = Project.generate!
|
142
|
+
generate_user_and_login_for_project(@project)
|
143
|
+
end
|
144
|
+
|
145
|
+
should "replace the issues selector" do
|
146
|
+
post :load_assigned_issues, :entry_id => '1234', :project_id => @project.id.to_s
|
147
|
+
assert_select_rjs :replace_html, '1234_issues'
|
148
|
+
end
|
149
|
+
|
150
|
+
should "replace the activities selector" do
|
151
|
+
post :load_assigned_issues, :entry_id => '1234', :project_id => @project.id.to_s
|
152
|
+
assert_select_rjs :replace_html, '1234_activities'
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context "POST to :load_assigned_issues with an unallowed project" do
|
157
|
+
setup do
|
158
|
+
@project = Project.generate!
|
159
|
+
generate_user_and_login_for_project(@project)
|
160
|
+
@unallowed_project = Project.generate!
|
161
|
+
end
|
162
|
+
|
163
|
+
should "replace the issues selector with an empty select field" do
|
164
|
+
post :load_assigned_issues, :entry_id => '1234', :project_id => @unallowed_project.id.to_s
|
165
|
+
assert_select_rjs :replace_html, '1234_issues', :text => /no data to display/
|
166
|
+
end
|
167
|
+
|
168
|
+
should "replace the activities selector with an empty select field" do
|
169
|
+
post :load_assigned_issues, :entry_id => '1234', :project_id => @unallowed_project.id.to_s
|
170
|
+
assert_select_rjs :replace_html, '1234_activities', :text => /no data to display/
|
171
|
+
end
|
172
|
+
end
|
44
173
|
end
|
data/test/test_helper.rb
CHANGED
@@ -8,9 +8,13 @@ Engines::Testing.set_fixture_path
|
|
8
8
|
class ActiveSupport::TestCase
|
9
9
|
def setup
|
10
10
|
User.anonymous
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
begin
|
12
|
+
Role.non_member
|
13
|
+
rescue
|
14
|
+
non_member = Role.generate!
|
15
|
+
non_member.builtin = Role::BUILTIN_NON_MEMBER
|
16
|
+
non_member.save!
|
17
|
+
end
|
14
18
|
end
|
15
19
|
end
|
16
20
|
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../test_helper'
|
2
|
+
|
3
|
+
class BulkTimeEntriesHelperTest < HelperTestCase
|
4
|
+
include ApplicationHelper
|
5
|
+
include BulkTimeEntriesHelper
|
6
|
+
include ActionController::Assertions::SelectorAssertions
|
7
|
+
|
8
|
+
# Used by assert_select
|
9
|
+
def html_document
|
10
|
+
HTML::Document.new(@response.body)
|
11
|
+
end
|
12
|
+
|
13
|
+
def setup
|
14
|
+
@response = ActionController::TestResponse.new
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
context "#grouped_options_for_issues" do
|
19
|
+
setup do
|
20
|
+
@project = Project.generate!
|
21
|
+
end
|
22
|
+
|
23
|
+
context "with no issues" do
|
24
|
+
should "return an empty option" do
|
25
|
+
@response.body = grouped_options_for_issues([])
|
26
|
+
assert_select 'option', :text => ''
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "with issues" do
|
31
|
+
setup do
|
32
|
+
closed = IssueStatus.generate!(:is_closed => true)
|
33
|
+
@issues = [
|
34
|
+
Issue.generate_for_project!(@project, :tracker => @project.trackers.first),
|
35
|
+
Issue.generate_for_project!(@project, :tracker => @project.trackers.first, :status => closed),
|
36
|
+
Issue.generate_for_project!(@project, :tracker => @project.trackers.first),
|
37
|
+
Issue.generate_for_project!(@project, :tracker => @project.trackers.first)
|
38
|
+
]
|
39
|
+
@response.body = grouped_options_for_issues(@issues)
|
40
|
+
end
|
41
|
+
|
42
|
+
should 'render an option tag per issue plan a blank one' do
|
43
|
+
assert_select 'option', :count => 5
|
44
|
+
end
|
45
|
+
|
46
|
+
should 'group the open issues with the Open Issues label' do
|
47
|
+
assert_select 'optgroup[label=?]', l(:label_open_issues) do
|
48
|
+
assert_select 'option', :count => 3
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
should 'group the closed issues with the Closed Issues label' do
|
53
|
+
assert_select 'optgroup[label=?]', l(:label_closed_issues) do
|
54
|
+
assert_select 'option', :count => 1
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context "#get_issues" do
|
61
|
+
context "as a user without any projects" do
|
62
|
+
should "be empty" do
|
63
|
+
@user = User.generate_with_protected!
|
64
|
+
User.current = @user
|
65
|
+
@project = Project.generate!
|
66
|
+
Issue.generate_for_project!(@project)
|
67
|
+
Issue.generate_for_project!(@project)
|
68
|
+
|
69
|
+
assert get_issues(@project.id).empty?
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "as a user with a project" do
|
74
|
+
should "return the project's issues" do
|
75
|
+
@project = Project.generate!
|
76
|
+
generate_user_and_login_for_project(@project)
|
77
|
+
User.current = @user
|
78
|
+
issue1 = Issue.generate_for_project!(@project)
|
79
|
+
issue2 = Issue.generate_for_project!(@project)
|
80
|
+
|
81
|
+
result = get_issues(@project.id)
|
82
|
+
assert_equal 2, result.size
|
83
|
+
assert result.include? issue1
|
84
|
+
assert result.include? issue2
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class BulkTimeEntryPlugin::Patches::TimeEntryPatchTest < ActiveSupport::TestCase
|
4
|
+
context "TimeEntry#create_bulk_time_entry" do
|
5
|
+
setup do
|
6
|
+
User.current = @user = User.generate_with_protected!
|
7
|
+
@project = Project.generate!
|
8
|
+
@role = Role.generate!(:permissions => Redmine::AccessControl.permissions.collect(&:name))
|
9
|
+
Member.generate!(:project => @project, :roles => [@role], :principal => @user)
|
10
|
+
@valid_params = {:project_id => @project.id, :hours => 5, :activity_id => TimeEntryActivity.generate!.id, :spent_on => Date.today.to_s}
|
11
|
+
end
|
12
|
+
|
13
|
+
should "return the unsaved TimeEntry if the current user is not allowed to log time to the project" do
|
14
|
+
@role.update_attributes(:permissions => [])
|
15
|
+
|
16
|
+
assert_no_difference('TimeEntry.count') do
|
17
|
+
@entry = TimeEntry.create_bulk_time_entry(@valid_params)
|
18
|
+
end
|
19
|
+
assert_equal false, @entry.valid?
|
20
|
+
end
|
21
|
+
|
22
|
+
context "saving a valid record" do
|
23
|
+
should "save a new Time Entry record" do
|
24
|
+
assert_difference('TimeEntry.count') do
|
25
|
+
@entry = TimeEntry.create_bulk_time_entry(@valid_params)
|
26
|
+
end
|
27
|
+
|
28
|
+
assert @entry.is_a? TimeEntry
|
29
|
+
end
|
30
|
+
|
31
|
+
should "set the project of the new record" do
|
32
|
+
@entry = TimeEntry.create_bulk_time_entry(@valid_params)
|
33
|
+
assert_equal @project, @entry.project
|
34
|
+
end
|
35
|
+
|
36
|
+
should "assign the current user" do
|
37
|
+
@entry = TimeEntry.create_bulk_time_entry(@valid_params)
|
38
|
+
assert_equal @user, @entry.user
|
39
|
+
end
|
40
|
+
|
41
|
+
should "set hours to nil if they are passed in as 0" do
|
42
|
+
@entry = TimeEntry.create_bulk_time_entry(@valid_params.merge(:hours => 0))
|
43
|
+
assert_equal @user, @entry.user
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bulk_time_entry_plugin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Davis
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2010-02-25 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -32,30 +32,41 @@ files:
|
|
32
32
|
- app/controllers/bulk_time_entries_controller.rb
|
33
33
|
- app/helpers/bulk_time_entries_helper.rb
|
34
34
|
- app/models/bulk_time_entry.rb
|
35
|
+
- app/views/bulk_time_entries/_activities_selector.html.erb
|
35
36
|
- app/views/bulk_time_entries/_issues_selector.html.erb
|
36
37
|
- app/views/bulk_time_entries/_time_entry.html.erb
|
38
|
+
- app/views/bulk_time_entries/add_entry.js.rjs
|
37
39
|
- app/views/bulk_time_entries/index.html.erb
|
40
|
+
- app/views/bulk_time_entries/load_assigned_issues.js.rjs
|
38
41
|
- app/views/bulk_time_entries/no_projects.html.erb
|
42
|
+
- app/views/bulk_time_entries/save.js.rjs
|
43
|
+
- config/locales/de.yml
|
39
44
|
- config/locales/en.yml
|
40
45
|
- config/locales/fr.yml
|
41
46
|
- config/locales/hu.yml
|
42
47
|
- config/locales/it.yml
|
43
48
|
- config/locales/ko.yml
|
49
|
+
- config/locales/nl.yml
|
44
50
|
- config/locales/pl.yml
|
45
51
|
- init.rb
|
52
|
+
- lang/de.yml
|
46
53
|
- lang/en.yml
|
47
54
|
- lang/fr.yml
|
48
55
|
- lang/hu.yml
|
49
56
|
- lang/it.yml
|
50
57
|
- lang/ko.yml
|
58
|
+
- lang/nl.yml
|
51
59
|
- lang/pl.yml
|
52
60
|
- lib/bulk_time_entry_compatibility.rb
|
61
|
+
- lib/bulk_time_entry_plugin/patches/time_entry_patch.rb
|
53
62
|
- lib/tasks/import_from_csv.rake
|
54
63
|
- rails/init.rb
|
55
64
|
- test/functional/bulk_time_entries_controller_test.rb
|
56
65
|
- test/test_helper.rb
|
57
66
|
- test/unit/bulk_time_entry_test.rb
|
58
67
|
- test/unit/bulk_time_entry_transaction_test.rb
|
68
|
+
- test/unit/helpers/bulk_time_entries_helper_test.rb
|
69
|
+
- test/unit/lib/bulk_time_entry_plugin/patches/time_entry_patch_test.rb
|
59
70
|
- test/unit/sanity_test.rb
|
60
71
|
has_rdoc: true
|
61
72
|
homepage: https://projects.littlestreamsoftware.com/projects/redmine-bte
|
@@ -89,5 +100,7 @@ test_files:
|
|
89
100
|
- test/test_helper.rb
|
90
101
|
- test/unit/bulk_time_entry_transaction_test.rb
|
91
102
|
- test/unit/bulk_time_entry_test.rb
|
103
|
+
- test/unit/helpers/bulk_time_entries_helper_test.rb
|
104
|
+
- test/unit/lib/bulk_time_entry_plugin/patches/time_entry_patch_test.rb
|
92
105
|
- test/unit/sanity_test.rb
|
93
106
|
- test/functional/bulk_time_entries_controller_test.rb
|