Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#55771] Warn user that the type is not available when moving a work package #15887

Merged
merged 3 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions app/controllers/work_packages/moves_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def within_frontend_treshold?
# rubocop:disable Metrics/AbcSize
def perform_in_frontend
call = job_class
.perform_now(**job_args)
.perform_now(**job_args)

if call.success? && @work_packages.any?
flash[:notice] = call.message
Expand All @@ -70,6 +70,7 @@ def perform_in_frontend
redirect_back_or_default(project_work_packages_path(@project))
end
end

# rubocop:enable Metrics/AbcSize

def perform_in_background
Expand Down Expand Up @@ -114,13 +115,29 @@ def prepare_for_work_package_move
@allowed_projects = WorkPackage.allowed_target_projects_on_move(current_user)
@target_project = @allowed_projects.detect { |p| p.id.to_s == params[:new_project_id].to_s } if params[:new_project_id]
@target_project ||= @project
@types = @target_project.types
@types = @target_project.types.order(:position)
@target_type = @types.find { |t| t.id.to_s == params[:new_type_id].to_s }
@unavailable_type_in_target_project = set_unavailable_type_in_target_project
@available_versions = @target_project.assignable_versions
@available_statuses = Workflow.available_statuses(@project)
@notes = params[:notes] || ""
end

def set_unavailable_type_in_target_project
if @target_project == @project
false
elsif @target_type.nil?
work_packages_and_descendants = WorkPackageHierarchy.where(ancestor_id: @work_packages.select(:id))
Type.where(id: work_packages_and_descendants.select(:id))
.select("distinct id")
.pluck(:id)
.difference(@types.pluck(:id))
.any?
else
@types.exclude?(@target_type)
end
end

def attributes_for_create
permitted_params
.move_work_package
Expand Down
243 changes: 128 additions & 115 deletions app/views/work_packages/moves/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ See COPYRIGHT and LICENSE files for more details.
<% end -%>
</ul>

<%= styled_form_tag({action: 'create'},
<%= styled_form_tag({ action: 'create' },
id: 'move_form',
data: {
'controller': 'refresh-on-form-changes',
Expand All @@ -54,134 +54,147 @@ See COPYRIGHT and LICENSE files for more details.
<%= hidden_field_tag 'ids[]', wp.id %>
<% end %>
<%= back_url_hidden_field_tag %>
<section class="form--section">
<fieldset class="form--fieldset">
<legend class="form--fieldset-legend"><%= t(:label_change_properties) %></legend>
<div class="grid-block grid-block_visible-overflow">
<div class="grid-content medium-6">
<div class="form--field">
<label class="form--label" for="new_project_id"><%= WorkPackage.human_attribute_name(:project) %>:</label>
<div class="form--field-container">
<%= angular_component_tag 'opce-project-autocompleter',
inputs: {
filters: [{ name: 'user_action', operator: '=', values: ['work_packages/move'] }],
inputName: 'new_project_id',
model: @target_project,
appendTo: 'body',
hiddenFieldAction: 'change->refresh-on-form-changes#triggerReload',
clearable: false,
},
id: 'new_project_id',
class: 'remote-field--input',
data: {
'test-selector': 'new_project_id'
}
%>
</div>
</div>
<div class="form--field">
<label class="form--label" for="new_type_id"><%= WorkPackage.human_attribute_name(:type) %>:</label>
<div class="form--field-container">
<%= styled_select_tag("new_type_id",
content_tag('option', t(:label_no_change_option), value: '') +
options_from_collection_for_select(@types, "id", "name", @target_type&.id),
data: {
'action': 'change->refresh-on-form-changes#triggerReload'
}) %>
</div>
<section class="form--section">
<fieldset class="form--fieldset">
<legend class="form--fieldset-legend"><%= t(:label_change_properties) %></legend>
<div class="grid-block grid-block_visible-overflow">
<div class="grid-content medium-6">
<div class="form--field">
<label class="form--label" for="new_project_id"><%= WorkPackage.human_attribute_name(:project) %>:</label>
<div class="form--field-container">
<%= angular_component_tag 'opce-project-autocompleter',
inputs: {
filters: [{ name: 'user_action', operator: '=', values: ['work_packages/move'] }],
inputName: 'new_project_id',
model: @target_project,
appendTo: 'body',
hiddenFieldAction: 'change->refresh-on-form-changes#triggerReload',
clearable: false,
},
id: 'new_project_id',
class: 'remote-field--input',
data: {
'test-selector': 'new_project_id'
}
%>
</div>
<div class="form--field">
<label class="form--label" for='status_id'><%= WorkPackage.human_attribute_name(:status) %></label>
<div class="form--field-container">
<%= styled_select_tag('status_id',
content_tag('option', t(:label_no_change_option), value: '') +
options_from_collection_for_select(@available_statuses, :id, :name)) %>
</div>
</div>
<div class="form--field">
<label class="form--label" for="new_type_id"><%= WorkPackage.human_attribute_name(:type) %>:</label>
<div class="form--field-container">
<%= styled_select_tag("new_type_id",
content_tag('option', t(:label_no_change_option), value: '') +
options_from_collection_for_select(@types, "id", "name", @target_type&.id),
data: {
'action': 'change->refresh-on-form-changes#triggerReload'
}) %>
</div>
<div class="form--field">
<label class="form--label" for='version_id'><%= WorkPackage.human_attribute_name(:version) %></label>
<div class="form--field-container">
<%= styled_select_tag('version_id',
content_tag('option', t(:label_no_change_option), value: '') +
version_options_for_select(@available_versions)) %>
<% if @unavailable_type_in_target_project %>
<div class="op-toast -warning icon-warning">
<div class="op-toast--content">
<p>
<% if @work_packages.size > 1 %>
<%= t("work_packages.move.bulk_current_type_not_available_in_target_project") %>
<% else %>
<%= t("work_packages.move.current_type_not_available_in_target_project") %>
<% end %>
</p>
</div>
</div>
<% end %>
</div>
<div class="form--field">
<label class="form--label" for='status_id'><%= WorkPackage.human_attribute_name(:status) %></label>
<div class="form--field-container">
<%= styled_select_tag('status_id',
content_tag('option', t(:label_no_change_option), value: '') +
options_from_collection_for_select(@available_statuses, :id, :name)) %>
</div>
<div class="form--field">
<label class="form--label" for='priority_id'><%= WorkPackage.human_attribute_name(:priority) %></label>
<div class="form--field-container">
<%= styled_select_tag('priority_id',
content_tag('option', t(:label_no_change_option), value: '') +
options_from_collection_for_select(IssuePriority.active, :id, :name)) %>
</div>
</div>
<div class="form--field">
<label class="form--label" for='version_id'><%= WorkPackage.human_attribute_name(:version) %></label>
<div class="form--field-container">
<%= styled_select_tag('version_id',
content_tag('option', t(:label_no_change_option), value: '') +
version_options_for_select(@available_versions)) %>
</div>
<div class="form--field">
<label class="form--label" for='assigned_to_id'><%= WorkPackage.human_attribute_name(:assigned_to) %></label>
<div class="form--field-container">
<%= styled_select_tag('assigned_to_id',
content_tag('option', t(:label_no_change_option), value: '') +
content_tag('option', t(:label_nobody), value: 'none') +
options_from_collection_for_select(Principal.possible_assignee(@target_project), :id, :name)) %>
</div>
</div>
<div class="form--field">
<label class="form--label" for='priority_id'><%= WorkPackage.human_attribute_name(:priority) %></label>
<div class="form--field-container">
<%= styled_select_tag('priority_id',
content_tag('option', t(:label_no_change_option), value: '') +
options_from_collection_for_select(IssuePriority.active, :id, :name)) %>
</div>
<div class="form--field">
<label class="form--label" for='responsible_id'><%= WorkPackage.human_attribute_name(:responsible) %></label>
<div class="form--field-container">
<%= styled_select_tag('responsible_id',
content_tag('option', t(:label_no_change_option), value: '') +
content_tag('option', t(:label_nobody), value: 'none') +
options_from_collection_for_select(Principal.possible_assignee(@target_project), :id, :name)) %>
</div>
</div>
<div class="form--field">
<label class="form--label" for='assigned_to_id'><%= WorkPackage.human_attribute_name(:assigned_to) %></label>
<div class="form--field-container">
<%= styled_select_tag('assigned_to_id',
content_tag('option', t(:label_no_change_option), value: '') +
content_tag('option', t(:label_nobody), value: 'none') +
options_from_collection_for_select(Principal.possible_assignee(@target_project), :id, :name)) %>
</div>
<div class="form--field">
<%= styled_label_tag :budget_id, Budget.model_name.human %>
<%= styled_select_tag('budget_id', (@target_project == @project ? content_tag('option', t(:label_no_change_option), :value => '') : "") +
content_tag('option', t(:label_none), :value => 'none') +
options_from_collection_for_select(@target_project.budgets, :id, :subject)) %>
</div>
<div class="form--field">
<label class="form--label" for='responsible_id'><%= WorkPackage.human_attribute_name(:responsible) %></label>
<div class="form--field-container">
<%= styled_select_tag('responsible_id',
content_tag('option', t(:label_no_change_option), value: '') +
content_tag('option', t(:label_nobody), value: 'none') +
options_from_collection_for_select(Principal.possible_assignee(@target_project), :id, :name)) %>
</div>
</div>
<div class="grid-content medium-6">
<div class="form--field">
<label class="form--label" for='start_date'><%= WorkPackage.human_attribute_name(:start_date) %></label>
<div class="form--field-container -visible-overflow">
<%= angular_component_tag 'op-basic-single-date-picker',
inputs: {
id: "start_date",
name: "start_date"
}
%>
</div>
<div class="form--field">
<%= styled_label_tag :budget_id, Budget.model_name.human %>
<%= styled_select_tag('budget_id', (@target_project == @project ? content_tag('option', t(:label_no_change_option), :value => '') : "") +
content_tag('option', t(:label_none), :value => 'none') +
options_from_collection_for_select(@target_project.budgets, :id, :subject)) %>
</div>
</div>
<div class="grid-content medium-6">
<div class="form--field">
<label class="form--label" for='start_date'><%= WorkPackage.human_attribute_name(:start_date) %></label>
<div class="form--field-container -visible-overflow">
<%= angular_component_tag 'op-basic-single-date-picker',
inputs: {
id: "start_date",
name: "start_date"
}
%>
</div>
<div class="form--field">
<label class="form--label" for='due_date'><%= WorkPackage.human_attribute_name(:due_date) %></label>
<div class="form--field-container -visible-overflow">
<%= angular_component_tag 'op-basic-single-date-picker',
inputs: {
id: "due_date",
name: "due_date"
}
%>
</div>
</div>
<div class="form--field">
<label class="form--label" for='due_date'><%= WorkPackage.human_attribute_name(:due_date) %></label>
<div class="form--field-container -visible-overflow">
<%= angular_component_tag 'op-basic-single-date-picker',
inputs: {
id: "due_date",
name: "due_date"
}
%>
</div>
<% if @target_type %>
<% @target_type.custom_fields.required.each do |custom_field| %>
<div class="form--field">
<%= blank_custom_field_label_tag('', custom_field) %>
<div class="form--field-container">
<%= custom_field_tag_for_bulk_edit('', custom_field, @project) %>
</div>
</div>
<% if @target_type %>
<% @target_type.custom_fields.required.each do |custom_field| %>
<div class="form--field">
<%= blank_custom_field_label_tag('', custom_field) %>
<div class="form--field-container">
<%= custom_field_tag_for_bulk_edit('', custom_field, @project) %>
</div>
<% end %>
</div>
<% end %>
</div>
<% end %>
</div>
</fieldset>
<fieldset class="form--fieldset">
<legend class="form--fieldset-legend"><%= Journal.human_attribute_name(:notes) %></legend>
<%= label_tag 'notes', Journal.human_attribute_name(:notes), class: 'hidden-for-sighted' %>
<%= styled_text_area_tag 'notes', @notes, cols: 60, rows: 10, class: 'wiki-edit', with_text_formatting: true %>
</fieldset>
<%= call_hook(:view_work_packages_move_bottom, work_packages: @work_packages, target_project: @target_project, copy: !!@copy) %>
</section>
</div>
</fieldset>
<fieldset class="form--fieldset">
<legend class="form--fieldset-legend"><%= Journal.human_attribute_name(:notes) %></legend>
<%= label_tag 'notes', Journal.human_attribute_name(:notes), class: 'hidden-for-sighted' %>
<%= styled_text_area_tag 'notes', @notes, cols: 60, rows: 10, class: 'wiki-edit', with_text_formatting: true %>
</fieldset>
<%= call_hook(:view_work_packages_move_bottom, work_packages: @work_packages, target_project: @target_project, copy: !!@copy) %>
</section>
<% if @copy %>
<%= hidden_field_tag("copy") %>
<%= styled_submit_tag t(:button_copy), class: '-primary' %>
Expand Down
8 changes: 8 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,14 @@ Project attributes and sections are defined in the <a href=%{admin_settings_url}
move:
no_common_statuses_exists: "There is no status available for all selected work packages. Their status cannot be changed."
unsupported_for_multiple_projects: "Bulk move/copy is not supported for work packages from multiple projects"
current_type_not_available_in_target_project: >
The current type of the work package is not enabled in the target project.
Please enable the type in the target project if you'd like them to remain unchanged.
Otherwise, the work package's type will be automatically re-assigned leading to potential data loss.
bulk_current_type_not_available_in_target_project: >
The current types of the work packages aren't enabled in the target project.
Please enable the types in the target project if you'd like them to remain unchanged.
Otherwise, the work packages' types will be automatically re-assigned leading to potential data loss.

sharing:
missing_workflow_warning:
Expand Down
Loading