Project

General

Profile

Patch #26534 » allow_project_bulk_edit_for_time_entries.patch

Marius BĂLTEANU, 2017-07-26 00:16

View differences:

app/controllers/timelog_controller.rb
164 164
  end
165 165

  
166 166
  def bulk_edit
167
    @available_activities = @projects.map(&:activities).reduce(:&)
167
    @target_projects = Project.allowed_to(:log_time).to_a
168 168
    @custom_fields = TimeEntry.first.available_custom_fields.select {|field| field.format.bulk_edit_supported}
169
    if params[:time_entry]
170
      @target_project = @target_projects.detect {|p| p.id.to_s == params[:time_entry][:project_id].to_s}
171
    end
172
    if @target_project
173
      @available_activities = @target_project.activities
174
    else
175
      @available_activities = @projects.map(&:activities).reduce(:&)
176
    end
177
    @time_entry_params = params[:time_entry] || {}
178
    @time_entry_params[:custom_field_values] ||= {}
169 179
  end
170 180

  
171 181
  def bulk_update
app/helpers/application_helper.rb
1479 1479
    encoding = l(:general_csv_encoding)
1480 1480
  end
1481 1481

  
1482
  # Returns an array of error messages for bulk edited items (issues, time entries)
1483
  def bulk_edit_error_messages(items)
1484
    messages = {}
1485
    items.each do |item|
1486
      item.errors.full_messages.each do |message|
1487
        messages[message] ||= []
1488
        messages[message] << item
1489
      end
1490
    end
1491
    messages.map { |message, items|
1492
      "#{message}: " + items.map {|i| "##{i.id}"}.join(', ')
1493
    }
1494
  end
1495

  
1482 1496
  private
1483 1497

  
1484 1498
  def wiki_helper
app/helpers/issues_helper.rb
164 164
    end
165 165
  end
166 166

  
167
  # Returns an array of error messages for bulk edited issues
168
  def bulk_edit_error_messages(issues)
169
    messages = {}
170
    issues.each do |issue|
171
      issue.errors.full_messages.each do |message|
172
        messages[message] ||= []
173
        messages[message] << issue
174
      end
175
    end
176
    messages.map { |message, issues|
177
      "#{message}: " + issues.map {|i| "##{i.id}"}.join(', ')
178
    }
179
 end
180

  
181 167
  # Returns a link for adding a new subtask to the given issue
182 168
  def link_to_new_subtask(issue)
183 169
    attrs = {
app/views/timelog/bulk_edit.html.erb
28 28
<div class="box tabular">
29 29
  <div>
30 30
    <p>
31
      <label><%= l(:field_project) %></label>
32
      <%= select_tag('time_entry[project_id]', project_tree_options_for_select(@target_projects,
33
        :include_blank => l(:label_no_change_option), :selected => @target_project),
34
        :onchange => "updateBulkEditFrom('#{escape_javascript url_for(:action => 'bulk_edit', :format => 'js')}')" ) %>
35
    </p>
36
    <p>
31 37
      <label for="time_entry_issue_id"><%= l(:field_issue) %></label>
32 38
      <%= text_field :time_entry, :issue_id, :size => 6 %>
39
      <label class="inline"><%= check_box_tag 'time_entry[issue_id]', 'none', (@time_entry_params[:issue_id] == 'none'), :id => nil, :data => {:disables => '#time_entry_issue_id'} %><%= l(:button_clear) %></label>
40
      <span id="time_entry_issue"></span>
33 41
    </p>
34 42

  
35 43
    <p>
36 44
      <label for="time_entry_spent_on"><%= l(:field_spent_on) %></label>
37
      <%= date_field :time_entry, :spent_on, :size => 10 %><%= calendar_for('time_entry_spent_on') %>
45
      <%= date_field :time_entry, :spent_on, :size => 10, :value => @time_entry_params[:spent_on] %><%= calendar_for('time_entry_spent_on') %>
38 46
    </p>
39 47

  
40 48
    <p>
41 49
      <label for="time_entry_hours"><%= l(:field_hours) %></label>
42
      <%= text_field :time_entry, :hours, :size => 6 %>
50
      <%= text_field :time_entry, :hours, :size => 6, :value => @time_entry_params[:hours] %>
43 51
    </p>
44 52

  
45 53
    <% if @available_activities.any? %>
46 54
    <p>
47 55
      <label for="time_entry_activity_id"><%= l(:field_activity) %></label>
48
      <%= select_tag('time_entry[activity_id]', content_tag('option', l(:label_no_change_option), :value => '') + options_from_collection_for_select(@available_activities, :id, :name)) %>
56
      <%= select_tag('time_entry[activity_id]', content_tag('option', l(:label_no_change_option), :value => '') + options_from_collection_for_select(@available_activities, :id, :name, @time_entry_params[:activity_id])) %>
49 57
    </p>
50 58
    <% end %>
51 59

  
52 60
    <p>
53 61
      <label for="time_entry_comments"><%= l(:field_comments) %></label>
54
      <%= text_field(:time_entry, :comments, :size => 100) %>
62
      <%= text_field(:time_entry, :comments, :size => 100, :value => @time_entry_params[:comments]) %>
55 63
    </p>
56 64

  
57 65
    <% @custom_fields.each do |custom_field| %>
58
      <p><label><%= h(custom_field.name) %></label> <%= custom_field_tag_for_bulk_edit('time_entry', custom_field, @time_entries) %></p>
66
      <p><label><%= h(custom_field.name) %></label> <%= custom_field_tag_for_bulk_edit('time_entry', custom_field, @time_entries, @time_entry_params[:custom_field_values][custom_field.id.to_s]) %></p>
59 67
    <% end %>
60 68

  
61 69
    <%= call_hook(:view_time_entries_bulk_edit_details_bottom, { :time_entries => @time_entries }) %>
......
64 72

  
65 73
<p><%= submit_tag l(:button_submit) %></p>
66 74
<% end %>
75

  
76
<%= javascript_tag do %>
77
  $(document).ready(function(){
78
    $('#time_entry_project_id').change(function(){
79
      <!-- $('#time_entry_issue_id').val(''); -->
80
      $('#time_entry_issue').text('');
81
    });
82
  });
83

  
84
  <% if @project || @target_project %>
85
    observeAutocompleteField('time_entry_issue_id',
86
      function(request, callback) {
87
        var url = '<%= j auto_complete_issues_path %>';
88
        var data = {
89
          term: request.term
90
        };
91
        var project_id;
92
        <% if @project %>
93
          project_id = '<%= @project.id %>';
94
        <% end %>
95

  
96
        current_project_id = $('#time_entry_project_id').val();
97
        if(current_project_id === ''){
98
          data['project_id'] = project_id;
99
        } else {
100
          data['project_id'] = current_project_id;
101
        }
102

  
103
        $.get(url, data, null, 'json')
104
          .done(function(data){
105
            callback(data);
106
          })
107
          .fail(function(jqXHR, status, error){
108
            callback([]);
109
          });
110
      },
111
      {
112
        select: function(event, ui, data) {
113
          $('#time_entry_issue').text(ui.item.label);
114
        }
115
      }
116
    );
117
  <% end %>
118
<% end %>
app/views/timelog/bulk_edit.js.erb
1
$('#content').html('<%= escape_javascript(render :template => 'timelog/bulk_edit', :formats => [:html]) %>');
config/routes.rb
158 158
        end
159 159
      end
160 160
    end
161
  
161

  
162 162
    match 'wiki/index', :controller => 'wiki', :action => 'index', :via => :get
163 163
    resources :wiki, :except => [:index, :create], :as => 'wiki_page' do
164 164
      member do
......
234 234
  match '/time_entries/destroy', :to => 'timelog#destroy', :via => :delete
235 235
  # Used to update the new time entry form
236 236
  post '/time_entries/new', :to => 'timelog#new'
237
  # Used to update the bulk edit time entry form
238
  post '/time_entries/bulk_edit', :to => 'timelog#bulk_edit'
237 239

  
238 240
  get 'projects/:id/activity', :to => 'activities#index', :as => :project_activity
239 241
  get 'activity', :to => 'activities#index'
test/functional/timelog_controller_test.rb
545 545
    end
546 546

  
547 547
    assert_select 'form#bulk_edit_form[action=?]', '/time_entries/bulk_update' do
548
      assert_select 'select[name=?]', 'time_entry[project_id]'
549

  
550
      # Clear issue checkbox
551
      assert_select 'input[name=?][value=?]', 'time_entry[issue_id]', 'none'
552

  
548 553
      # System wide custom field
549 554
      assert_select 'select[name=?]', 'time_entry[custom_field_values][10]'
550 555

  
......
563 568
    assert_response :success
564 569
  end
565 570

  
571
  def test_get_bulk_edit_on_different_projects_should_propose_only_common_activites
572
    project = Project.find(3)
573
    TimeEntryActivity.create!(:name => 'QA', :project => project, :parent => TimeEntryActivity.find_by_name('QA'), :active => false)
574
    @request.session[:user_id] = 1
575

  
576
    get :bulk_edit, :params => {:ids => [1, 2, 4]}
577
    assert_response :success
578
    assert_select 'select[id=?]', 'time_entry_activity_id' do
579
      assert_select 'option', 3
580
      assert_select 'option[value=?]', '11', 0, :text => 'QA'
581
    end
582
  end
583

  
584
  def test_get_bulk_edit_on_same_project_should_propose_project_activities
585
    project = Project.find(1)
586
    override_activity = TimeEntryActivity.create!({:name => "QA override", :parent => TimeEntryActivity.find_by_name("QA"), :project => project})
587

  
588
    @request.session[:user_id] = 1
589

  
590
    get :bulk_edit, :params => {:ids => [1, 2]}
591
    assert_response :success
592

  
593
    assert_select 'select[id=?]', 'time_entry_activity_id' do
594
      assert_select 'option', 4
595
      assert_select 'option[value=?]', override_activity.id.to_s, :text => 'QA override'
596
    end
597
  end
598

  
566 599
  def test_bulk_edit_with_edit_own_time_entries_permission
567 600
    @request.session[:user_id] = 2
568 601
    Role.find_by_name('Manager').remove_permission! :edit_time_entries
test/integration/routing/timelog_test.rb
55 55

  
56 56
  def test_timelogs_bulk_edit
57 57
    should_route 'GET /time_entries/bulk_edit' => 'timelog#bulk_edit'
58
    should_route 'POST /time_entries/bulk_edit' => 'timelog#bulk_edit'
58 59
    should_route 'POST /time_entries/bulk_update' => 'timelog#bulk_update'
59 60
    should_route 'DELETE /time_entries/destroy' => 'timelog#destroy'
60 61
  end
    (1-1/1)