Merge pull request #6052 from betagouv/main

2021-04-06-01
This commit is contained in:
Kara Diaby 2021-04-06 11:41:24 +02:00 committed by GitHub
commit e6faafa314
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 262 additions and 66 deletions

View file

@ -28,3 +28,7 @@
display: inline-block; display: inline-block;
width: 5px; width: 5px;
} }
.text-center {
text-align: center;
}

View file

@ -9,7 +9,8 @@ module CreateAvisConcern
# the :emails parameter is a 1-element array. # the :emails parameter is a 1-element array.
# Hence the call to first # Hence the call to first
# https://github.com/rails/rails/issues/17225 # https://github.com/rails/rails/issues/17225
expert_emails = create_avis_params[:emails].first.split(',').map(&:strip) expert_emails = create_avis_params[:emails].presence || [].to_json
expert_emails = JSON.parse(expert_emails).map(&:strip).map(&:downcase)
allowed_dossiers = [dossier] allowed_dossiers = [dossier]
if create_avis_params[:invite_linked_dossiers].present? if create_avis_params[:invite_linked_dossiers].present?
@ -65,6 +66,6 @@ module CreateAvisConcern
end end
def create_avis_params def create_avis_params
params.require(:avis).permit(:introduction_file, :introduction, :confidentiel, :invite_linked_dossiers, emails: []) params.require(:avis).permit(:introduction_file, :introduction, :confidentiel, :invite_linked_dossiers, :emails)
end end
end end

View file

@ -60,6 +60,7 @@ module Instructeurs
def avis def avis
@avis_seen_at = current_instructeur.follows.find_by(dossier: dossier)&.avis_seen_at @avis_seen_at = current_instructeur.follows.find_by(dossier: dossier)&.avis_seen_at
@avis = Avis.new @avis = Avis.new
@experts_emails = dossier.procedure.experts_procedures.where(revoked_at: nil).map(&:expert).map(&:email).sort
end end
def personnes_impliquees def personnes_impliquees

View file

@ -0,0 +1,43 @@
module NewAdministrateur
class ExpertsProceduresController < AdministrateurController
before_action :retrieve_procedure, only: [:add_expert_to_procedure, :revoke_expert_from_procedure]
def add_expert_to_procedure
emails = params['emails'].presence || [].to_json
emails = JSON.parse(emails).map(&:strip).map(&:downcase)
valid_users, invalid_users = emails
.map { |email| User.create_or_promote_to_expert(email, SecureRandom.hex) }
.partition(&:valid?)
if invalid_users.any?
flash[:alert] = invalid_users
.filter { |user| user.errors.present? }
.map { |user| "#{user.email} : #{user.errors.full_messages_for(:email).join(', ')}" }
end
if valid_users.present?
valid_users.each do |user|
experts_procedure = ExpertsProcedure.find_or_create_by(expert: user.expert, procedure: @procedure)
if !experts_procedure.revoked_at.nil?
experts_procedure.update!(revoked_at: nil)
end
end
flash[:notice] = t('.experts_assignment',
count: valid_users.count,
value: valid_users.map(&:email).join(', '),
procedure: @procedure.id)
end
redirect_to admin_procedure_invited_expert_list_path(@procedure)
end
def revoke_expert_from_procedure
expert_procedure = ExpertsProcedure.find_by!(procedure: @procedure, id: params[:expert_procedure][:id])
expert_email = expert_procedure.expert.email
expert_procedure.update!(revoked_at: Time.zone.now)
flash[:notice] = "#{expert_email} a été révoqué de la démarche et ne pourra plus déposer d'avis."
redirect_to admin_procedure_invited_expert_list_path(@procedure)
end
end
end

View file

@ -186,7 +186,8 @@ module NewAdministrateur
end end
def invited_expert_list def invited_expert_list
@experts_procedure = @procedure.experts_procedures.sort_by { |expert_procedure| expert_procedure.expert.email } @experts_procedure = @procedure.experts_procedures.where(revoked_at: nil).sort_by { |expert_procedure| expert_procedure.expert.email }
@experts_emails = experts_procedure_emails
end end
def update_allow_decision_access def update_allow_decision_access
@ -198,6 +199,10 @@ module NewAdministrateur
private private
def experts_procedure_emails
@procedure.experts.map(&:email).sort
end
def apercu_tab def apercu_tab
params[:tab] || 'dossier' params[:tab] || 'dossier'
end end

View file

@ -360,12 +360,12 @@ module Users
if champs_params[:dossier] if champs_params[:dossier]
@dossier.assign_attributes(champs_params[:dossier]) @dossier.assign_attributes(champs_params[:dossier])
# FIXME in some cases a removed repetition bloc row is submitted. # FIXME: in some cases a removed repetition bloc row is submitted.
# In this case it will be trated as a new records and action will fail. # In this case it will be treated as a new record, and the action will fail.
@dossier.champs.filter(&:repetition?).each do |champ| @dossier.champs.filter(&:repetition?).each do |champ|
champ.champs = champ.champs.filter(&:persisted?) champ.champs = champ.champs.filter(&:persisted?)
end end
if @dossier.champs.any?(&:changed?) if @dossier.champs.any?(&:changed_for_autosave?)
@dossier.last_champ_updated_at = Time.zone.now @dossier.last_champ_updated_at = Time.zone.now
end end
if !@dossier.save if !@dossier.save

View file

@ -18,7 +18,7 @@
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champ < ApplicationRecord class Champ < ApplicationRecord
belongs_to :dossier, -> { with_discarded }, inverse_of: :champs, touch: true, optional: false belongs_to :dossier, -> { with_discarded }, inverse_of: false, touch: true, optional: false
belongs_to :type_de_champ, inverse_of: :champ, optional: false belongs_to :type_de_champ, inverse_of: :champ, optional: false
belongs_to :parent, class_name: 'Champ', optional: true belongs_to :parent, class_name: 'Champ', optional: true
has_many :commentaires has_many :commentaires

View file

@ -65,8 +65,8 @@ class Dossier < ApplicationRecord
has_one_attached :justificatif_motivation has_one_attached :justificatif_motivation
has_one_attached :pdf_export_for_instructeur has_one_attached :pdf_export_for_instructeur
has_many :champs, -> { root.public_ordered }, inverse_of: :dossier, dependent: :destroy has_many :champs, -> { root.public_ordered }, inverse_of: false, dependent: :destroy
has_many :champs_private, -> { root.private_ordered }, class_name: 'Champ', inverse_of: :dossier, dependent: :destroy has_many :champs_private, -> { root.private_ordered }, class_name: 'Champ', inverse_of: false, dependent: :destroy
has_many :commentaires, inverse_of: :dossier, dependent: :destroy has_many :commentaires, inverse_of: :dossier, dependent: :destroy
has_many :invites, dependent: :destroy has_many :invites, dependent: :destroy
has_many :follows, -> { active }, inverse_of: :dossier has_many :follows, -> { active }, inverse_of: :dossier

View file

@ -4,6 +4,7 @@
# #
# id :bigint not null, primary key # id :bigint not null, primary key
# allow_decision_access :boolean default(FALSE), not null # allow_decision_access :boolean default(FALSE), not null
# revoked_at :datetime
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# expert_id :bigint not null # expert_id :bigint not null

View file

@ -4,7 +4,7 @@
# #
# id :bigint not null, primary key # id :bigint not null, primary key
# format :string not null # format :string not null
# key :text # key :text not null
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# #

View file

@ -1,30 +1,38 @@
%section.ask-avis - if @dossier.procedure.feature_enabled?(:admin_affect_experts_to_avis).blank?
%h1.tab-title Inviter des personnes à donner leur avis %section.ask-avis
%p.avis-notice Les invités pourront consulter le dossier, donner un avis et contribuer au fil de messagerie. Ils ne pourront pas modifier le dossier. %h1.tab-title Inviter des personnes à donner leur avis
%p.avis-notice Les invités pourront consulter le dossier, donner un avis et contribuer au fil de messagerie. Ils ne pourront pas modifier le dossier.
= form_for avis, url: url, html: { class: 'form' } do |f| = form_for avis, url: url, html: { class: 'form' } do |f|
= f.email_field :emails, placeholder: 'Adresses email, séparées par des virgules', required: true, multiple: true, onchange: "javascript:DS.replaceSemicolonByComma(event);" - hidden_field_id = SecureRandom.uuid
= f.text_area :introduction, rows: 3, value: avis.introduction || 'Bonjour, merci de me donner votre avis sur ce dossier.', required: true = hidden_field_tag 'avis[emails]', nil, data: { uuid: hidden_field_id }
%p.tab-title Ajouter une pièce jointe = react_component("ComboMultipleDropdownList",
.form-group options: [],
= text_upload_and_render f, avis.introduction_file selected: [], disabled: [],
hiddenFieldId: hidden_field_id,
label: 'avis_emails',
acceptNewValues: true)
= f.text_area :introduction, rows: 3, value: avis.introduction || 'Bonjour, merci de me donner votre avis sur ce dossier.', required: true
%p.tab-title Ajouter une pièce jointe
.form-group
= text_upload_and_render f, avis.introduction_file
- if linked_dossiers.present? - if linked_dossiers.present?
= f.check_box :invite_linked_dossiers, {}, true, false = f.check_box :invite_linked_dossiers, {}, true, false
= f.label :invite_linked_dossiers, t('helpers.label.invite_linked_dossiers', count: linked_dossiers.length, ids: linked_dossiers.map(&:id).to_sentence) = f.label :invite_linked_dossiers, t('helpers.label.invite_linked_dossiers', count: linked_dossiers.length, ids: linked_dossiers.map(&:id).to_sentence)
.flex.justify-between.align-baseline .flex.justify-between.align-baseline
- if must_be_confidentiel - if must_be_confidentiel
%p.confidentiel.flex %p.confidentiel.flex
%span.icon.lock %span.icon.lock
%span %span
Cet avis sera confidentiel : il ne sera pas affiché aux autres experts consultés, mais sera visible par les instructeurs. Cet avis sera confidentiel : il ne sera pas affiché aux autres experts consultés, mais sera visible par les instructeurs.
- else - else
.confidentiel-wrapper .confidentiel-wrapper
= f.label :confidentiel, 'Cet avis sera ' = f.label :confidentiel, 'Cet avis sera '
= f.select :confidentiel, [['partagé avec les autres experts', false], ['confidentiel', true]], {}, onchange: "javascript:DS.toggleCondidentielExplanation(event);" = f.select :confidentiel, [['partagé avec les autres experts', false], ['confidentiel', true]], {}, onchange: "javascript:DS.toggleCondidentielExplanation(event);"
.confidentiel-explanation.hidden .confidentiel-explanation.hidden
Il ne sera pas affiché aux autres experts consultés, mais sera visible par les instructeurs. Il ne sera pas affiché aux autres experts consultés, mais sera visible par les instructeurs.
= f.submit 'Demander un avis', class: 'button primary send' = f.submit 'Demander un avis', class: 'button primary send'

View file

@ -1,9 +1,22 @@
%section.ask-avis %section.ask-avis
%h1.tab-title Inviter des personnes à donner leur avis %h1.tab-title Inviter des personnes à donner leur avis
%p.avis-notice Les invités pourront consulter le dossier, donner un avis et contribuer au fil de messagerie. Ils ne pourront pas modifier le dossier. %p.avis-notice Les invités pourront consulter le dossier, donner un avis et contribuer au fil de messagerie. Ils ne pourront pas modifier le dossier.
- if @dossier.procedure.feature_enabled?(:admin_affect_experts_to_avis)
%p.avis-notice Choisissez des experts à qui vous souhaitez demander un avis parmi la liste prédéfinie par les administrateurs de la démarche
- else
%p.avis-notice Entrez les adresses email des experts à qui vous souhaitez demander un avis
= form_for avis, url: url, html: { class: 'form' } do |f| = form_for avis, url: url, html: { class: 'form' } do |f|
= f.email_field :emails, placeholder: 'Adresses email, séparées par des virgules', required: true, multiple: true, onchange: "javascript:DS.replaceSemicolonByComma(event);" - hidden_field_id = SecureRandom.uuid
= hidden_field_tag 'avis[emails]', nil, data: { uuid: hidden_field_id }
= react_component("ComboMultipleDropdownList",
options: @dossier.procedure.feature_enabled?(:admin_affect_experts_to_avis) ? @experts_emails : [],
selected: [],
disabled: [],
hiddenFieldId: hidden_field_id,
label: 'avis_emails',
id: 'avis_emails',
acceptNewValues: @dossier.procedure.feature_enabled?(:admin_affect_experts_to_avis).blank?)
= f.text_area :introduction, rows: 3, value: avis.introduction || 'Bonjour, merci de me donner votre avis sur ce dossier.', required: true = f.text_area :introduction, rows: 3, value: avis.introduction || 'Bonjour, merci de me donner votre avis sur ce dossier.', required: true
%p.tab-title Ajouter une pièce jointe %p.tab-title Ajouter une pièce jointe
.form-group .form-group

View file

@ -5,12 +5,35 @@
.container .container
%h1.page-title.mt-2 Experts invités sur #{@procedure.libelle} %h1.page-title.mt-2 Experts invités sur #{@procedure.libelle}
.container.groupe-instructeur
.card
.card-title Affecter des experts à la démarche
= form_for :experts_procedure,
url: admin_procedure_add_expert_to_procedure_path(@procedure),
html: { class: 'form' } do |f|
.instructeur-wrapper
%p.notice Pendant l'instruction d'un dossier, les instructeurs peuvent demander leur avis à un ou plusieurs experts.
%p.notice Entrez les adresses email des experts que vous souhaitez affecter à cette démarche
- hidden_field_id = SecureRandom.uuid
= hidden_field_tag :emails, nil, data: { uuid: hidden_field_id }
= react_component("ComboMultipleDropdownList",
options: [],
selected: [], disabled: [],
hiddenFieldId: hidden_field_id,
label: 'email expert',
acceptNewValues: true)
= f.submit 'Affecter à la démarche', class: 'button primary send'
- if @experts_procedure.present? - if @experts_procedure.present?
%table.table.mt-2 %table.table.mt-2
%thead %thead
%tr %tr
%th Liste des experts %th Liste des experts
- if @procedure.feature_enabled?(:make_experts_notifiable) %th Nombre d'avis
- if @procedure.feature_enabled?(:admin_affect_experts_to_avis)
%th Notifier des décisions sur les dossiers %th Notifier des décisions sur les dossiers
%tbody %tbody
- @experts_procedure.each do |expert_procedure| - @experts_procedure.each do |expert_procedure|
@ -18,8 +41,10 @@
%td %td
%span.icon.person %span.icon.person
= expert_procedure.expert.email = expert_procedure.expert.email
- if @procedure.feature_enabled?(:make_experts_notifiable) %td.text-center
%td = expert_procedure.avis.count
- if @procedure.feature_enabled?(:admin_affect_experts_to_avis)
%td.text-center
= form_for expert_procedure, = form_for expert_procedure,
url: admin_procedure_update_allow_decision_access_path(expert_procedure: expert_procedure), url: admin_procedure_update_allow_decision_access_path(expert_procedure: expert_procedure),
remote: true, remote: true,
@ -31,6 +56,12 @@
%span.toggle-switch-control.round %span.toggle-switch-control.round
%span.toggle-switch-label.on %span.toggle-switch-label.on
%span.toggle-switch-label.off %span.toggle-switch-label.off
%td.actions= button_to 'retirer',
{ action: "revoke_expert_from_procedure", :controller=>"new_administrateur/experts_procedures" },
{ method: :put,
data: { confirm: "Êtes-vous sûr de vouloir révoquer l'expert « #{expert_procedure.expert.email} » de la démarche #{expert_procedure.procedure.libelle} ? Les instructeurs ne pourront plus lui demander d'avis" },
params: { expert_procedure: { id: expert_procedure.id }},
class: 'button' }
- else - else
.blank-tab .blank-tab
%h2.empty-text Aucun expert invité pour le moment. %h2.empty-text Aucun expert invité pour le moment.

View file

@ -1,5 +1,12 @@
fr: fr:
new_administrateur: new_administrateur:
experts_procedures:
wrong_address:
one: "%{value} nest pas une adresse email valide"
other: "%{value} ne sont pas des adresses emails valides"
experts_assignment:
one: "L'expert %{value} a été affecté à la démarche n° %{procedure}"
other: "Les experts %{value} ont été affectés à la démarche n° %{procedure}"
groupe_instructeurs: groupe_instructeurs:
index: index:
existing_groupe: existing_groupe:

View file

@ -408,7 +408,8 @@ Rails.application.routes.draw do
post 'transfer' => 'procedures#transfer', as: :transfer post 'transfer' => 'procedures#transfer', as: :transfer
get 'invited_expert_list' get 'invited_expert_list'
put 'update_allow_decision_access' => 'procedures#update_allow_decision_access', as: :update_allow_decision_access put 'update_allow_decision_access' => 'procedures#update_allow_decision_access', as: :update_allow_decision_access
post 'add_expert_to_procedure' => 'experts_procedures#add_expert_to_procedure', as: :add_expert_to_procedure
put 'revoke_expert_from_procedure' => 'experts_procedures#revoke_expert_from_procedure', as: :revoke_expert_from_procedure
resources :mail_templates, only: [:edit, :update] resources :mail_templates, only: [:edit, :update]
resources :groupe_instructeurs, only: [:index, :show, :create, :update, :destroy] do resources :groupe_instructeurs, only: [:index, :show, :create, :update, :destroy] do

View file

@ -0,0 +1,5 @@
class AddRevokedAtToExpertsProcedures < ActiveRecord::Migration[6.1]
def change
add_column :experts_procedures, :revoked_at, :datetime
end
end

View file

@ -0,0 +1,5 @@
class ExportsKeyNotNull < ActiveRecord::Migration[6.1]
def change
change_column_null :exports, :key, false
end
end

View file

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2021_03_31_123709) do ActiveRecord::Schema.define(version: 2021_04_02_163003) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -345,6 +345,7 @@ ActiveRecord::Schema.define(version: 2021_03_31_123709) do
t.boolean "allow_decision_access", default: false, null: false t.boolean "allow_decision_access", default: false, null: false
t.datetime "created_at", precision: 6, null: false t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false
t.datetime "revoked_at"
t.index ["expert_id", "procedure_id"], name: "index_experts_procedures_on_expert_id_and_procedure_id", unique: true t.index ["expert_id", "procedure_id"], name: "index_experts_procedures_on_expert_id_and_procedure_id", unique: true
t.index ["expert_id"], name: "index_experts_procedures_on_expert_id" t.index ["expert_id"], name: "index_experts_procedures_on_expert_id"
t.index ["procedure_id"], name: "index_experts_procedures_on_procedure_id" t.index ["procedure_id"], name: "index_experts_procedures_on_procedure_id"
@ -354,7 +355,7 @@ ActiveRecord::Schema.define(version: 2021_03_31_123709) do
t.string "format", null: false t.string "format", null: false
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.text "key" t.text "key", null: false
t.index ["format", "key"], name: "index_exports_on_format_and_key", unique: true t.index ["format", "key"], name: "index_exports_on_format_and_key", unique: true
end end

View file

@ -159,7 +159,7 @@ describe Experts::AvisController, type: :controller do
let(:previous_avis_confidentiel) { false } let(:previous_avis_confidentiel) { false }
let(:asked_confidentiel) { false } let(:asked_confidentiel) { false }
let(:intro) { 'introduction' } let(:intro) { 'introduction' }
let(:emails) { ["toto@totomail.com"] } let(:emails) { "[\"toto@totomail.com\"]" }
let(:invite_linked_dossiers) { nil } let(:invite_linked_dossiers) { nil }
before do before do
@ -176,7 +176,7 @@ describe Experts::AvisController, type: :controller do
describe '#create_avis' do describe '#create_avis' do
let!(:previous_avis) { create(:avis, dossier: dossier, claimant: claimant, experts_procedure: experts_procedure, confidentiel: previous_avis_confidentiel) } let!(:previous_avis) { create(:avis, dossier: dossier, claimant: claimant, experts_procedure: experts_procedure, confidentiel: previous_avis_confidentiel) }
let(:emails) { ['a@b.com'] } let(:emails) { "[\"a@b.com\"]" }
let(:intro) { 'introduction' } let(:intro) { 'introduction' }
let(:created_avis) { Avis.last } let(:created_avis) { Avis.last }
let!(:old_avis_count) { Avis.count } let!(:old_avis_count) { Avis.count }
@ -194,7 +194,7 @@ describe Experts::AvisController, type: :controller do
context 'when an invalid email' do context 'when an invalid email' do
let(:previous_avis_confidentiel) { false } let(:previous_avis_confidentiel) { false }
let(:asked_confidentiel) { false } let(:asked_confidentiel) { false }
let(:emails) { ["toto.fr"] } let(:emails) { "[\"toto.fr\"]" }
it { expect(response).to render_template :instruction } it { expect(response).to render_template :instruction }
it { expect(flash.alert).to eq(["toto.fr : Email n'est pas valide"]) } it { expect(flash.alert).to eq(["toto.fr : Email n'est pas valide"]) }
@ -205,7 +205,7 @@ describe Experts::AvisController, type: :controller do
context 'ask review with attachment' do context 'ask review with attachment' do
let(:previous_avis_confidentiel) { false } let(:previous_avis_confidentiel) { false }
let(:asked_confidentiel) { false } let(:asked_confidentiel) { false }
let(:emails) { ["toto@totomail.com"] } let(:emails) { "[\"toto@totomail.com\"]" }
it { expect(created_avis.introduction_file).to be_attached } it { expect(created_avis.introduction_file).to be_attached }
it { expect(created_avis.introduction_file.filename).to eq("piece_justificative_0.pdf") } it { expect(created_avis.introduction_file.filename).to eq("piece_justificative_0.pdf") }
@ -216,7 +216,7 @@ describe Experts::AvisController, type: :controller do
context 'with multiple emails' do context 'with multiple emails' do
let(:asked_confidentiel) { false } let(:asked_confidentiel) { false }
let(:previous_avis_confidentiel) { false } let(:previous_avis_confidentiel) { false }
let(:emails) { ["toto.fr,titi@titimail.com"] } let(:emails) { "[\"toto.fr\",\"titi@titimail.com\"]" }
it { expect(response).to render_template :instruction } it { expect(response).to render_template :instruction }
it { expect(flash.alert).to eq(["toto.fr : Email n'est pas valide"]) } it { expect(flash.alert).to eq(["toto.fr : Email n'est pas valide"]) }

View file

@ -448,7 +448,7 @@ describe Instructeurs::DossiersController, type: :controller do
} }
end end
let(:emails) { ['email@a.com'] } let(:emails) { "[\"email@a.com\"]" }
context "notifications updates" do context "notifications updates" do
context 'when an instructeur follows the dossier' do context 'when an instructeur follows the dossier' do
@ -476,7 +476,7 @@ describe Instructeurs::DossiersController, type: :controller do
it { expect(response).to redirect_to(avis_instructeur_dossier_path(dossier.procedure, dossier)) } it { expect(response).to redirect_to(avis_instructeur_dossier_path(dossier.procedure, dossier)) }
context "with an invalid email" do context "with an invalid email" do
let(:emails) { ['emaila.com'] } let(:emails) { "[\"emaila.com\"]" }
before { subject } before { subject }
@ -487,7 +487,7 @@ describe Instructeurs::DossiersController, type: :controller do
end end
context 'with multiple emails' do context 'with multiple emails' do
let(:emails) { ["toto.fr,titi@titimail.com"] } let(:emails) { "[\"toto.fr\",\"titi@titimail.com\"]" }
before { subject } before { subject }

View file

@ -0,0 +1,49 @@
describe NewAdministrateur::ExpertsProceduresController, type: :controller do
let(:admin) { create(:administrateur) }
before do
sign_in(admin.user)
end
describe '#add_expert_to_procedure' do
let(:procedure) { create :procedure, administrateur: admin }
let(:expert) { create(:expert) }
let(:expert2) { create(:expert) }
subject do
post :add_expert_to_procedure,
params: { procedure_id: procedure.id, emails: "[\"#{expert.email}\",\"#{expert2.email}\"]" }
end
before do
subject
end
context 'of multiple experts' do
it { expect(procedure.experts.include?(expert)).to be_truthy }
it { expect(procedure.experts.include?(expert2)).to be_truthy }
it { expect(flash.notice).to be_present }
it { expect(response).to redirect_to(admin_procedure_invited_expert_list_path(procedure)) }
end
end
describe '#revoke_expert_from_procedure' do
let(:procedure) { create :procedure, administrateur: admin }
let(:expert) { create(:expert) }
let(:expert_procedure) { ExpertsProcedure.create(expert: expert, procedure: procedure) }
subject do
put :revoke_expert_from_procedure, params: { procedure_id: procedure.id, expert_procedure: { id: expert_procedure.id } }
end
before do
subject
expert_procedure.reload
end
context 'of multiple experts' do
it { expect(expert_procedure.revoked_at).to be_present }
it { expect(flash.notice).to be_present }
it { expect(response).to redirect_to(admin_procedure_invited_expert_list_path(procedure)) }
end
end
end

View file

@ -619,13 +619,14 @@ describe Users::DossiersController, type: :controller do
it 'updates the champs' do it 'updates the champs' do
subject subject
expect(first_champ.reload.value).to eq('beautiful value') expect(first_champ.reload.value).to eq('beautiful value')
expect(first_champ.dossier.reload.last_champ_updated_at).to eq(now)
expect(piece_justificative_champ.reload.piece_justificative_file).to be_attached expect(piece_justificative_champ.reload.piece_justificative_file).to be_attached
end end
it 'updates the dossier modification date' do it 'updates the dossier timestamps' do
subject subject
expect(dossier.reload.updated_at.year).to eq(2100) dossier.reload
expect(dossier.updated_at).to eq(now)
expect(dossier.last_champ_updated_at).to eq(now)
end end
it 'updates the dossier state' do it 'updates the dossier state' do
@ -635,22 +636,34 @@ describe Users::DossiersController, type: :controller do
it { is_expected.to redirect_to(demande_dossier_path(dossier)) } it { is_expected.to redirect_to(demande_dossier_path(dossier)) }
context 'when only files champs are modified' do context 'when only a single file champ are modified' do
# A bug in ActiveRecord causes records changed through grand-parent <-> parent <-> child
# relationships do not touch the grand-parent record on change.
# This situation is hit when updating just the attachment of a champ (and not the
# champ itself).
#
# This test ensures that, whatever workaround we wrote for this, it still works properly.
#
# See https://github.com/rails/rails/issues/26726
let(:submit_payload) do let(:submit_payload) do
{ {
id: dossier.id, id: dossier.id,
dossier: { dossier: {
champs_attributes: { champs_attributes: [
id: piece_justificative_champ.id, {
piece_justificative_file: file id: piece_justificative_champ.id,
} piece_justificative_file: file
}
]
} }
} }
end end
it 'updates the dossier modification date' do it 'updates the dossier timestamps' do
subject subject
expect(dossier.reload.updated_at.year).to eq(2100) dossier.reload
expect(dossier.updated_at).to eq(now)
expect(dossier.last_champ_updated_at).to eq(now)
end end
end end
end end
@ -667,6 +680,12 @@ describe Users::DossiersController, type: :controller do
it { expect(response).to render_template(:modifier) } it { expect(response).to render_template(:modifier) }
it { expect(flash.alert).to eq(['nop']) } it { expect(flash.alert).to eq(['nop']) }
it 'does not update the dossier timestamps' do
dossier.reload
expect(dossier.updated_at).not_to eq(now)
expect(dossier.last_champ_updated_at).not_to eq(now)
end
it 'does not send an email' do it 'does not send an email' do
expect(NotificationMailer).not_to receive(:send_initiated_notification) expect(NotificationMailer).not_to receive(:send_initiated_notification)

View file

@ -1,4 +1,4 @@
feature 'Inviting an expert:' do feature 'Inviting an expert:', js: true do
include ActiveJob::TestHelper include ActiveJob::TestHelper
include ActionView::Helpers include ActionView::Helpers
@ -21,7 +21,7 @@ feature 'Inviting an expert:' do
click_on 'Avis externes' click_on 'Avis externes'
expect(page).to have_current_path(avis_instructeur_dossier_path(procedure, dossier)) expect(page).to have_current_path(avis_instructeur_dossier_path(procedure, dossier))
fill_in 'avis_emails', with: "#{expert.email}, #{expert2.email}" page.execute_script("document.querySelector('#avis_emails').value = '[\"#{expert.email}\",\"#{expert2.email}\"]'")
fill_in 'avis_introduction', with: 'Bonjour, merci de me donner votre avis sur ce dossier.' fill_in 'avis_introduction', with: 'Bonjour, merci de me donner votre avis sur ce dossier.'
check 'avis_invite_linked_dossiers' check 'avis_invite_linked_dossiers'
page.select 'confidentiel', from: 'avis_confidentiel' page.select 'confidentiel', from: 'avis_confidentiel'

View file

@ -1,4 +1,4 @@
feature 'Instructing a dossier:' do feature 'Instructing a dossier:', js: true do
include ActiveJob::TestHelper include ActiveJob::TestHelper
let(:password) { 'my-s3cure-p4ssword' } let(:password) { 'my-s3cure-p4ssword' }
@ -101,11 +101,13 @@ feature 'Instructing a dossier:' do
click_on 'Avis externes' click_on 'Avis externes'
expect(page).to have_current_path(avis_instructeur_dossier_path(procedure, dossier)) expect(page).to have_current_path(avis_instructeur_dossier_path(procedure, dossier))
expert_email_formated = "[\"expert@tps.com\"]"
expert_email = 'expert@tps.com' expert_email = 'expert@tps.com'
ask_confidential_avis(expert_email, 'a good introduction') ask_confidential_avis(expert_email_formated, 'a good introduction')
expert_email_formated = "[\"#{instructeur2.email}\"]"
expert_email = instructeur2.email expert_email = instructeur2.email
ask_confidential_avis(expert_email, 'a good introduction') ask_confidential_avis(expert_email_formated, 'a good introduction')
click_on 'Personnes impliquées' click_on 'Personnes impliquées'
expect(page).to have_text(expert_email) expect(page).to have_text(expert_email)
@ -206,7 +208,7 @@ feature 'Instructing a dossier:' do
end end
def ask_confidential_avis(to, introduction) def ask_confidential_avis(to, introduction)
fill_in 'avis_emails', with: to page.execute_script("document.querySelector('#avis_emails').value = '#{to}'")
fill_in 'avis_introduction', with: introduction fill_in 'avis_introduction', with: introduction
select 'confidentiel', from: 'avis_confidentiel' select 'confidentiel', from: 'avis_confidentiel'
click_on 'Demander un avis' click_on 'Demander un avis'