4288 - Gérer les groupes d'instructeurs (#4317)
Gestion des groupes d'instructeurs
This commit is contained in:
commit
a1088599d1
22 changed files with 667 additions and 21 deletions
16
Gemfile.lock
16
Gemfile.lock
|
@ -74,8 +74,8 @@ GEM
|
||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
minitest (~> 5.1)
|
minitest (~> 5.1)
|
||||||
tzinfo (~> 1.1)
|
tzinfo (~> 1.1)
|
||||||
addressable (2.5.2)
|
addressable (2.7.0)
|
||||||
public_suffix (>= 2.0.2, < 4.0)
|
public_suffix (>= 2.0.2, < 5.0)
|
||||||
administrate (0.11.0)
|
administrate (0.11.0)
|
||||||
actionpack (>= 4.2, < 6.0)
|
actionpack (>= 4.2, < 6.0)
|
||||||
actionview (>= 4.2, < 6.0)
|
actionview (>= 4.2, < 6.0)
|
||||||
|
@ -119,18 +119,18 @@ GEM
|
||||||
browser (2.5.3)
|
browser (2.5.3)
|
||||||
builder (3.2.3)
|
builder (3.2.3)
|
||||||
byebug (10.0.2)
|
byebug (10.0.2)
|
||||||
capybara (3.12.0)
|
capybara (3.29.0)
|
||||||
addressable
|
addressable
|
||||||
mini_mime (>= 0.1.3)
|
mini_mime (>= 0.1.3)
|
||||||
nokogiri (~> 1.8)
|
nokogiri (~> 1.8)
|
||||||
rack (>= 1.6.0)
|
rack (>= 1.6.0)
|
||||||
rack-test (>= 0.6.3)
|
rack-test (>= 0.6.3)
|
||||||
regexp_parser (~> 1.2)
|
regexp_parser (~> 1.5)
|
||||||
xpath (~> 3.2)
|
xpath (~> 3.2)
|
||||||
capybara-email (3.0.1)
|
capybara-email (3.0.1)
|
||||||
capybara (>= 2.4, < 4.0)
|
capybara (>= 2.4, < 4.0)
|
||||||
mail
|
mail
|
||||||
capybara-screenshot (1.0.22)
|
capybara-screenshot (1.0.23)
|
||||||
capybara (>= 1.0, < 4)
|
capybara (>= 1.0, < 4)
|
||||||
launchy
|
launchy
|
||||||
capybara-selenium (0.0.6)
|
capybara-selenium (0.0.6)
|
||||||
|
@ -373,7 +373,7 @@ GEM
|
||||||
mime-types-data (~> 3.2015)
|
mime-types-data (~> 3.2015)
|
||||||
mime-types-data (3.2018.0812)
|
mime-types-data (3.2018.0812)
|
||||||
mimemagic (0.3.3)
|
mimemagic (0.3.3)
|
||||||
mini_mime (1.0.1)
|
mini_mime (1.0.2)
|
||||||
mini_portile2 (2.4.0)
|
mini_portile2 (2.4.0)
|
||||||
minitest (5.11.3)
|
minitest (5.11.3)
|
||||||
momentjs-rails (2.20.1)
|
momentjs-rails (2.20.1)
|
||||||
|
@ -448,7 +448,7 @@ GEM
|
||||||
pry-byebug (3.6.0)
|
pry-byebug (3.6.0)
|
||||||
byebug (~> 10.0)
|
byebug (~> 10.0)
|
||||||
pry (~> 0.10)
|
pry (~> 0.10)
|
||||||
public_suffix (3.0.3)
|
public_suffix (4.0.1)
|
||||||
puma (3.12.0)
|
puma (3.12.0)
|
||||||
pundit (2.0.1)
|
pundit (2.0.1)
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
|
@ -512,7 +512,7 @@ GEM
|
||||||
execjs
|
execjs
|
||||||
railties (>= 3.2)
|
railties (>= 3.2)
|
||||||
tilt
|
tilt
|
||||||
regexp_parser (1.3.0)
|
regexp_parser (1.6.0)
|
||||||
request_store (1.4.1)
|
request_store (1.4.1)
|
||||||
rack (>= 1.4)
|
rack (>= 1.4)
|
||||||
responders (3.0.0)
|
responders (3.0.0)
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
.groupe-instructeur {
|
||||||
|
.actions {
|
||||||
|
width: 200px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
@import "colors";
|
@import "colors";
|
||||||
|
@import "constants";
|
||||||
|
|
||||||
.pull-left {
|
.pull-left {
|
||||||
float: left;
|
float: left;
|
||||||
|
@ -48,3 +49,7 @@
|
||||||
background: $orange-bg;
|
background: $orange-bg;
|
||||||
color: $black;
|
color: $black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mt-2 {
|
||||||
|
margin-top: 2 * $default-spacer;
|
||||||
|
}
|
||||||
|
|
|
@ -264,7 +264,7 @@ module Instructeurs
|
||||||
end
|
end
|
||||||
|
|
||||||
def ensure_ownership!
|
def ensure_ownership!
|
||||||
if !procedure.defaut_groupe_instructeur.instructeurs.include?(current_instructeur)
|
if !current_instructeur.procedures.include?(procedure)
|
||||||
flash[:alert] = "Vous n'avez pas accès à cette démarche"
|
flash[:alert] = "Vous n'avez pas accès à cette démarche"
|
||||||
redirect_to root_path
|
redirect_to root_path
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
module NewAdministrateur
|
||||||
|
class GroupeInstructeursController < AdministrateurController
|
||||||
|
ITEMS_PER_PAGE = 25
|
||||||
|
|
||||||
|
def index
|
||||||
|
@procedure = procedure
|
||||||
|
|
||||||
|
@groupes_instructeurs = paginated_groupe_instructeurs
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
@procedure = procedure
|
||||||
|
@groupe_instructeur = groupe_instructeur
|
||||||
|
@instructeurs = paginated_instructeurs
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@groupe_instructeur = procedure
|
||||||
|
.groupe_instructeurs
|
||||||
|
.new(label: label, instructeurs: [current_administrateur.instructeur])
|
||||||
|
|
||||||
|
if @groupe_instructeur.save
|
||||||
|
redirect_to procedure_groupe_instructeur_path(procedure, @groupe_instructeur),
|
||||||
|
notice: "Le groupe d’instructeurs « #{label} » a été créé."
|
||||||
|
else
|
||||||
|
@procedure = procedure
|
||||||
|
@groupes_instructeurs = paginated_groupe_instructeurs
|
||||||
|
|
||||||
|
flash[:alert] = "le nom « #{label} » est déjà pris par un autre groupe."
|
||||||
|
render :index
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
@groupe_instructeur = groupe_instructeur
|
||||||
|
|
||||||
|
if @groupe_instructeur.update(label: label)
|
||||||
|
redirect_to procedure_groupe_instructeur_path(procedure, groupe_instructeur),
|
||||||
|
notice: "Le nom est à présent « #{label} »."
|
||||||
|
else
|
||||||
|
@procedure = procedure
|
||||||
|
@instructeurs = paginated_instructeurs
|
||||||
|
|
||||||
|
flash[:alert] = "le nom « #{label} » est déjà pris par un autre groupe."
|
||||||
|
render :show
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_instructeur
|
||||||
|
@instructeur = Instructeur.find_by(email: instructeur_email) ||
|
||||||
|
create_instructeur(instructeur_email)
|
||||||
|
|
||||||
|
if groupe_instructeur.instructeurs.include?(@instructeur)
|
||||||
|
flash[:alert] = "L’instructeur « #{instructeur_email} » est déjà dans le groupe."
|
||||||
|
|
||||||
|
else
|
||||||
|
groupe_instructeur.instructeurs << @instructeur
|
||||||
|
flash[:notice] = "L’instructeur « #{instructeur_email} » a été affecté au groupe."
|
||||||
|
GroupeInstructeurMailer
|
||||||
|
.add_instructeur(groupe_instructeur, @instructeur, current_user.email)
|
||||||
|
.deliver_later
|
||||||
|
end
|
||||||
|
|
||||||
|
redirect_to procedure_groupe_instructeur_path(procedure, groupe_instructeur)
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_instructeur
|
||||||
|
if groupe_instructeur.instructeurs.one?
|
||||||
|
flash[:alert] = "Suppression impossible : il doit y avoir au moins un instructeur dans le groupe"
|
||||||
|
|
||||||
|
else
|
||||||
|
@instructeur = Instructeur.find(instructeur_id)
|
||||||
|
groupe_instructeur.instructeurs.destroy(@instructeur)
|
||||||
|
flash[:notice] = "L’instructeur « #{@instructeur.email} » a été retiré du groupe."
|
||||||
|
GroupeInstructeurMailer
|
||||||
|
.remove_instructeur(groupe_instructeur, @instructeur, current_user.email)
|
||||||
|
.deliver_later
|
||||||
|
end
|
||||||
|
|
||||||
|
redirect_to procedure_groupe_instructeur_path(procedure, groupe_instructeur)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_routing_criteria_name
|
||||||
|
procedure.update!(routing_criteria_name: routing_criteria_name)
|
||||||
|
|
||||||
|
redirect_to procedure_groupe_instructeurs_path(procedure),
|
||||||
|
notice: "Le libellé est maintenant « #{procedure.routing_criteria_name} »."
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def create_instructeur(email)
|
||||||
|
user = User.create_or_promote_to_instructeur(
|
||||||
|
email,
|
||||||
|
SecureRandom.hex,
|
||||||
|
administrateurs: [current_administrateur]
|
||||||
|
)
|
||||||
|
user.invite!
|
||||||
|
user.instructeur
|
||||||
|
end
|
||||||
|
|
||||||
|
def procedure
|
||||||
|
current_administrateur
|
||||||
|
.procedures
|
||||||
|
.includes(:groupe_instructeurs)
|
||||||
|
.find(params[:procedure_id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def groupe_instructeur
|
||||||
|
procedure.groupe_instructeurs.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def instructeur_email
|
||||||
|
params[:instructeur][:email].strip.downcase
|
||||||
|
end
|
||||||
|
|
||||||
|
def instructeur_id
|
||||||
|
params[:instructeur][:id]
|
||||||
|
end
|
||||||
|
|
||||||
|
def label
|
||||||
|
params[:groupe_instructeur][:label]
|
||||||
|
end
|
||||||
|
|
||||||
|
def paginated_groupe_instructeurs
|
||||||
|
procedure
|
||||||
|
.groupe_instructeurs
|
||||||
|
.page(params[:page])
|
||||||
|
.per(ITEMS_PER_PAGE)
|
||||||
|
.order(:label)
|
||||||
|
end
|
||||||
|
|
||||||
|
def paginated_instructeurs
|
||||||
|
groupe_instructeur
|
||||||
|
.instructeurs
|
||||||
|
.page(params[:page])
|
||||||
|
.per(ITEMS_PER_PAGE)
|
||||||
|
.order(:email)
|
||||||
|
end
|
||||||
|
|
||||||
|
def routing_criteria_name
|
||||||
|
params[:procedure][:routing_criteria_name]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,7 +1,7 @@
|
||||||
module DossierLinkHelper
|
module DossierLinkHelper
|
||||||
def dossier_linked_path(user, dossier)
|
def dossier_linked_path(user, dossier)
|
||||||
if user.is_a?(Instructeur)
|
if user.is_a?(Instructeur)
|
||||||
if dossier.procedure.defaut_groupe_instructeur.instructeurs.include?(user)
|
if user.groupe_instructeurs.include?(dossier.groupe_instructeur)
|
||||||
instructeur_dossier_path(dossier.procedure, dossier)
|
instructeur_dossier_path(dossier.procedure, dossier)
|
||||||
else
|
else
|
||||||
avis = dossier.avis.find_by(instructeur: user)
|
avis = dossier.avis.find_by(instructeur: user)
|
||||||
|
|
25
app/mailers/groupe_instructeur_mailer.rb
Normal file
25
app/mailers/groupe_instructeur_mailer.rb
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
class GroupeInstructeurMailer < ApplicationMailer
|
||||||
|
layout 'mailers/layout'
|
||||||
|
|
||||||
|
def add_instructeur(group, instructeur, current_instructeur_email)
|
||||||
|
@email = instructeur.email
|
||||||
|
@group = group
|
||||||
|
@current_instructeur_email = current_instructeur_email
|
||||||
|
|
||||||
|
subject = "Ajout d’un instructeur dans le groupe \"#{group.label}\""
|
||||||
|
|
||||||
|
emails = @group.instructeurs.pluck(:email)
|
||||||
|
mail(bcc: emails, subject: subject)
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_instructeur(group, instructeur, current_instructeur_email)
|
||||||
|
@email = instructeur.email
|
||||||
|
@group = group
|
||||||
|
@current_instructeur_email = current_instructeur_email
|
||||||
|
|
||||||
|
subject = "Suppression d’un instructeur dans le groupe \"#{group.label}\""
|
||||||
|
|
||||||
|
emails = @group.instructeurs.pluck(:email)
|
||||||
|
mail(bcc: emails, subject: subject)
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,4 +4,9 @@ class GroupeInstructeur < ApplicationRecord
|
||||||
has_many :assign_tos
|
has_many :assign_tos
|
||||||
has_many :instructeurs, through: :assign_tos, dependent: :destroy
|
has_many :instructeurs, through: :assign_tos, dependent: :destroy
|
||||||
has_many :dossiers
|
has_many :dossiers
|
||||||
|
|
||||||
|
validates :label, presence: { message: 'doit être renseigné' }, allow_nil: false
|
||||||
|
validates :label, uniqueness: { scope: :procedure, message: 'existe déjà' }
|
||||||
|
|
||||||
|
before_validation -> { label&.strip! }
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
%p
|
||||||
|
Bonjour,
|
||||||
|
|
||||||
|
%p
|
||||||
|
L’instructeur « #{@email} » a été affecté au groupe « #{@group.label} » par « #{@current_instructeur_email} », en charge de la démarche « #{@group.procedure.libelle} ».
|
||||||
|
|
||||||
|
%p
|
||||||
|
Cliquez sur le lien ci-dessous pour voir la liste des instructeurs de ce groupe :
|
||||||
|
= link_to(@group.label, procedure_groupe_instructeur_url(@group.procedure, @group))
|
||||||
|
|
||||||
|
= render partial: "layouts/mailers/signature"
|
|
@ -0,0 +1,11 @@
|
||||||
|
%p
|
||||||
|
Bonjour,
|
||||||
|
|
||||||
|
%p
|
||||||
|
L’instructeur « #{@email} » a été retiré du groupe « #{@group.label} » par « #{@current_instructeur_email} », en charge de la démarche « #{@group.procedure.libelle} ».
|
||||||
|
|
||||||
|
%p
|
||||||
|
Cliquez sur le lien ci-dessous pour voir la liste des instructeurs de ce groupe :
|
||||||
|
= link_to(@group.label, procedure_groupe_instructeur_url(@group.procedure, @group))
|
||||||
|
|
||||||
|
= render partial: "layouts/mailers/signature"
|
|
@ -3,7 +3,7 @@
|
||||||
.container
|
.container
|
||||||
.page-title
|
.page-title
|
||||||
Résultat de la recherche :
|
Résultat de la recherche :
|
||||||
= pluralize(@dossiers.count, "dossier trouvé", "dossiers trouvés")
|
= t('pluralize.dossier_trouve', count: @dossiers.count)
|
||||||
|
|
||||||
- if @dossiers.present?
|
- if @dossiers.present?
|
||||||
%table.table.dossiers-table.hoverable
|
%table.table.dossiers-table.hoverable
|
||||||
|
|
|
@ -27,14 +27,20 @@
|
||||||
%p.missing-steps (à compléter)
|
%p.missing-steps (à compléter)
|
||||||
|
|
||||||
%a#onglet-administrateurs{ href: url_for(procedure_administrateurs_path(@procedure)) }
|
%a#onglet-administrateurs{ href: url_for(procedure_administrateurs_path(@procedure)) }
|
||||||
.procedure-list-element{ class: ('active' if active == 'Administrateurs') }
|
.procedure-list-element
|
||||||
Administrateurs
|
Administrateurs
|
||||||
|
|
||||||
%a#onglet-instructeurs{ href: url_for(admin_procedure_assigns_path(@procedure)) }
|
- if !feature_enabled?(:routage)
|
||||||
.procedure-list-element{ class: ('active' if active == 'Instructeurs') }
|
%a#onglet-instructeurs{ href: url_for(admin_procedure_assigns_path(@procedure)) }
|
||||||
Instructeurs
|
.procedure-list-element{ class: ('active' if active == 'Instructeurs') }
|
||||||
- if @procedure.missing_steps.include?(:instructeurs)
|
Instructeurs
|
||||||
%p.missing-steps (à compléter)
|
- if @procedure.missing_steps.include?(:instructeurs)
|
||||||
|
%p.missing-steps (à compléter)
|
||||||
|
|
||||||
|
- if feature_enabled?(:routage)
|
||||||
|
%a#onglet-instructeurs{ href: url_for(procedure_groupe_instructeurs_path(@procedure)) }
|
||||||
|
.procedure-list-element
|
||||||
|
Groupe d'instructeurs
|
||||||
|
|
||||||
- if !@procedure.locked?
|
- if !@procedure.locked?
|
||||||
%a#onglet-champs{ href: champs_procedure_path(@procedure) }
|
%a#onglet-champs{ href: champs_procedure_path(@procedure) }
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
= render partial: 'new_administrateur/breadcrumbs',
|
||||||
|
locals: { steps: [link_to('Démarches', admin_procedures_path),
|
||||||
|
link_to(@procedure.libelle, admin_procedure_path(@procedure)),
|
||||||
|
'Groupes d’instructeurs'] }
|
||||||
|
|
||||||
|
.container.groupe-instructeur
|
||||||
|
.card
|
||||||
|
= form_for @procedure,
|
||||||
|
url: { action: :update_routing_criteria_name },
|
||||||
|
html: { class: 'form' } do |f|
|
||||||
|
|
||||||
|
= f.label :routing_criteria_name do
|
||||||
|
Libellé du routage
|
||||||
|
%span.notice Ce texte apparaitra sur le formulaire usager comme le libellé d'une liste
|
||||||
|
= f.text_field :routing_criteria_name, placeholder: 'Votre ville', required: true
|
||||||
|
= f.submit 'Renommer', class: 'button primary send'
|
||||||
|
|
||||||
|
.card
|
||||||
|
.card-title Gestion des Groupes
|
||||||
|
|
||||||
|
= form_for :groupe_instructeur, html: { class: 'form' } do |f|
|
||||||
|
= f.label :label do
|
||||||
|
Ajouter un groupe
|
||||||
|
%span.notice Ce groupe sera un choix de la liste « #{@procedure.routing_criteria_name} » .
|
||||||
|
= f.text_field :label, placeholder: 'Ville de Bordeaux', required: true
|
||||||
|
= f.submit 'Ajouter le groupe', class: 'button primary send'
|
||||||
|
|
||||||
|
%table.table.mt-2
|
||||||
|
%thead
|
||||||
|
%tr
|
||||||
|
%th{ colspan: 2 } Liste des groupes
|
||||||
|
%tbody
|
||||||
|
- @groupes_instructeurs.each do |group|
|
||||||
|
%tr
|
||||||
|
%td= group.label
|
||||||
|
%td.actions= link_to "voir", procedure_groupe_instructeur_path(@procedure, group)
|
||||||
|
|
||||||
|
= paginate @groupes_instructeurs
|
|
@ -0,0 +1,47 @@
|
||||||
|
= render partial: 'new_administrateur/breadcrumbs',
|
||||||
|
locals: { steps: [link_to('Démarches', admin_procedures_path),
|
||||||
|
link_to(@procedure.libelle, admin_procedure_path(@procedure)),
|
||||||
|
link_to('Groupes d’instructeurs', procedure_groupe_instructeurs_path(@procedure)),
|
||||||
|
@groupe_instructeur.label] }
|
||||||
|
|
||||||
|
.container.groupe-instructeur
|
||||||
|
.rename_form_block
|
||||||
|
.flex.baseline-start
|
||||||
|
%h1 Groupe « #{@groupe_instructeur.label} »
|
||||||
|
|
||||||
|
.card.mt-2
|
||||||
|
= form_for @groupe_instructeur,
|
||||||
|
url: procedure_groupe_instructeur_path(@procedure, @groupe_instructeur),
|
||||||
|
html: { class: 'form' } do |f|
|
||||||
|
|
||||||
|
= f.label :label, 'Nom du groupe'
|
||||||
|
= f.text_field :label, placeholder: 'Ville de Bordeaux', required: true
|
||||||
|
= f.submit 'Renommer', class: 'button primary send'
|
||||||
|
|
||||||
|
.card
|
||||||
|
.card-title Gestion des instructeurs
|
||||||
|
= form_for :instructeur,
|
||||||
|
url: { action: :add_instructeur },
|
||||||
|
html: { class: 'form' } do |f|
|
||||||
|
|
||||||
|
= f.label :email do
|
||||||
|
Affecter un nouvel instructeur
|
||||||
|
= f.email_field :email, placeholder: 'marie.dupont@exemple.fr', required: true
|
||||||
|
= f.submit 'Affecter', class: 'button primary send'
|
||||||
|
|
||||||
|
%table.table.mt-2
|
||||||
|
%thead
|
||||||
|
%tr
|
||||||
|
%th{ colspan: 2 } Instructeurs affectés
|
||||||
|
%tbody
|
||||||
|
- @instructeurs.each do |instructeur|
|
||||||
|
%tr
|
||||||
|
%td= instructeur.email
|
||||||
|
%td.actions= button_to 'retirer',
|
||||||
|
{ action: :remove_instructeur },
|
||||||
|
{ method: :delete,
|
||||||
|
data: { confirm: "Êtes-vous sûr de vouloir retirer l’instructeur « #{instructeur.email} » du groupe « #{@groupe_instructeur.label} » ?" },
|
||||||
|
params: { instructeur: { id: instructeur.id }},
|
||||||
|
class: 'button' }
|
||||||
|
|
||||||
|
= paginate @instructeurs
|
|
@ -313,3 +313,7 @@ fr:
|
||||||
zero: archivé
|
zero: archivé
|
||||||
one: archivé
|
one: archivé
|
||||||
other: archivés
|
other: archivés
|
||||||
|
dossier_trouve:
|
||||||
|
zero: 0 dossier trouvé
|
||||||
|
one: 1 dossier trouvé
|
||||||
|
other: "%{count} dossiers trouvés"
|
||||||
|
|
|
@ -350,6 +350,17 @@ Rails.application.routes.draw do
|
||||||
get 'annotations'
|
get 'annotations'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
resources :groupe_instructeurs, only: [:index, :show, :create, :update] do
|
||||||
|
member do
|
||||||
|
post 'add_instructeur'
|
||||||
|
delete 'remove_instructeur'
|
||||||
|
end
|
||||||
|
|
||||||
|
collection do
|
||||||
|
patch 'update_routing_criteria_name'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
resources :administrateurs, controller: 'procedure_administrateurs', only: [:index, :create, :destroy]
|
resources :administrateurs, controller: 'procedure_administrateurs', only: [:index, :create, :destroy]
|
||||||
|
|
||||||
resources :types_de_champ, only: [:create, :update, :destroy] do
|
resources :types_de_champ, only: [:create, :update, :destroy] do
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
class AddDefaultValueToRoutingCriteriaName < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
change_column :procedures, :routing_criteria_name, :text, default: "Votre ville"
|
||||||
|
end
|
||||||
|
end
|
|
@ -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: 2019_10_14_160538) do
|
ActiveRecord::Schema.define(version: 2019_10_23_183120) 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"
|
||||||
|
@ -486,7 +486,7 @@ ActiveRecord::Schema.define(version: 2019_10_14_160538) do
|
||||||
t.string "path", null: false
|
t.string "path", null: false
|
||||||
t.string "declarative_with_state"
|
t.string "declarative_with_state"
|
||||||
t.text "monavis_embed"
|
t.text "monavis_embed"
|
||||||
t.text "routing_criteria_name"
|
t.text "routing_criteria_name", default: "Votre ville"
|
||||||
t.boolean "csv_export_queued"
|
t.boolean "csv_export_queued"
|
||||||
t.boolean "xlsx_export_queued"
|
t.boolean "xlsx_export_queued"
|
||||||
t.boolean "ods_export_queued"
|
t.boolean "ods_export_queued"
|
||||||
|
|
|
@ -0,0 +1,162 @@
|
||||||
|
describe NewAdministrateur::GroupeInstructeursController, type: :controller do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:admin) { create(:administrateur) }
|
||||||
|
let(:procedure) { create(:procedure, :published, administrateurs: [admin]) }
|
||||||
|
let!(:gi_1_1) { procedure.defaut_groupe_instructeur }
|
||||||
|
|
||||||
|
let(:procedure2) { create(:procedure, :published) }
|
||||||
|
let!(:gi_2_2) { procedure2.groupe_instructeurs.create(label: 'groupe instructeur 2 2') }
|
||||||
|
|
||||||
|
before { sign_in(admin.user) }
|
||||||
|
|
||||||
|
describe '#index' do
|
||||||
|
context 'of a procedure I own' do
|
||||||
|
let!(:gi_1_2) { procedure.groupe_instructeurs.create(label: 'groupe instructeur 2') }
|
||||||
|
|
||||||
|
before { get :index, params: { procedure_id: procedure.id } }
|
||||||
|
|
||||||
|
context 'when a procedure has multiple groups' do
|
||||||
|
it { expect(response).to have_http_status(:ok) }
|
||||||
|
it { expect(response.body).to include(gi_1_1.label) }
|
||||||
|
it { expect(response.body).to include(gi_1_2.label) }
|
||||||
|
it { expect(response.body).not_to include(gi_2_2.label) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#show' do
|
||||||
|
context 'of a group I belong to' do
|
||||||
|
before { get :show, params: { procedure_id: procedure.id, id: gi_1_1.id } }
|
||||||
|
|
||||||
|
it { expect(response).to have_http_status(:ok) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#create' do
|
||||||
|
before do
|
||||||
|
post :create,
|
||||||
|
params: {
|
||||||
|
procedure_id: procedure.id,
|
||||||
|
groupe_instructeur: { label: label }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with a valid name' do
|
||||||
|
let(:label) { "nouveau_groupe" }
|
||||||
|
|
||||||
|
it { expect(flash.notice).to be_present }
|
||||||
|
it { expect(response).to redirect_to(procedure_groupe_instructeur_path(procedure, procedure.groupe_instructeurs.last)) }
|
||||||
|
it { expect(procedure.groupe_instructeurs.count).to eq(2) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with an invalid group name' do
|
||||||
|
let(:label) { gi_1_1.label }
|
||||||
|
|
||||||
|
it { expect(response).to render_template(:index) }
|
||||||
|
it { expect(procedure.groupe_instructeurs.count).to eq(1) }
|
||||||
|
it { expect(flash.alert).to be_present }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#update' do
|
||||||
|
let(:new_name) { 'nouveau nom du groupe' }
|
||||||
|
|
||||||
|
before do
|
||||||
|
patch :update,
|
||||||
|
params: {
|
||||||
|
procedure_id: procedure.id,
|
||||||
|
id: gi_1_1.id,
|
||||||
|
groupe_instructeur: { label: new_name }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it { expect(gi_1_1.reload.label).to eq(new_name) }
|
||||||
|
it { expect(response).to redirect_to(procedure_groupe_instructeur_path(procedure, gi_1_1)) }
|
||||||
|
it { expect(flash.notice).to be_present }
|
||||||
|
|
||||||
|
context 'when the name is already taken' do
|
||||||
|
let!(:gi_1_2) { procedure.groupe_instructeurs.create(label: 'groupe instructeur 2') }
|
||||||
|
let(:new_name) { gi_1_2.label }
|
||||||
|
|
||||||
|
it { expect(gi_1_1.reload.label).not_to eq(new_name) }
|
||||||
|
it { expect(flash.alert).to be_present }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#add_instructeur' do
|
||||||
|
let!(:instructeur) { create(:instructeur) }
|
||||||
|
before do
|
||||||
|
gi_1_1.instructeurs << instructeur
|
||||||
|
|
||||||
|
post :add_instructeur,
|
||||||
|
params: {
|
||||||
|
procedure_id: procedure.id,
|
||||||
|
id: gi_1_1.id,
|
||||||
|
instructeur: { email: new_instructeur_email }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'of a new instructeur' do
|
||||||
|
let(:new_instructeur_email) { 'new_instructeur@mail.com' }
|
||||||
|
|
||||||
|
it { expect(gi_1_1.instructeurs.pluck(:email)).to include(new_instructeur_email) }
|
||||||
|
it { expect(flash.notice).to be_present }
|
||||||
|
it { expect(response).to redirect_to(procedure_groupe_instructeur_path(procedure, gi_1_1)) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'of an instructeur already in the group' do
|
||||||
|
let(:new_instructeur_email) { instructeur.email }
|
||||||
|
|
||||||
|
it { expect(flash.alert).to be_present }
|
||||||
|
it { expect(response).to redirect_to(procedure_groupe_instructeur_path(procedure, procedure.defaut_groupe_instructeur)) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#remove_instructeur' do
|
||||||
|
let!(:instructeur) { create(:instructeur) }
|
||||||
|
|
||||||
|
before { gi_1_1.instructeurs << admin.instructeur << instructeur }
|
||||||
|
|
||||||
|
def remove_instructeur(email)
|
||||||
|
delete :remove_instructeur,
|
||||||
|
params: {
|
||||||
|
procedure_id: procedure.id,
|
||||||
|
id: gi_1_1.id,
|
||||||
|
instructeur: { id: admin.instructeur.id }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when there are many instructeurs' do
|
||||||
|
before { remove_instructeur(admin.user.email) }
|
||||||
|
|
||||||
|
it { expect(gi_1_1.instructeurs).to include(instructeur) }
|
||||||
|
it { expect(gi_1_1.reload.instructeurs.count).to eq(1) }
|
||||||
|
it { expect(response).to redirect_to(procedure_groupe_instructeur_path(procedure, gi_1_1)) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when there is only one instructeur' do
|
||||||
|
before do
|
||||||
|
remove_instructeur(admin.user.email)
|
||||||
|
remove_instructeur(instructeur.email)
|
||||||
|
end
|
||||||
|
|
||||||
|
it { expect(gi_1_1.instructeurs).to include(instructeur) }
|
||||||
|
it { expect(gi_1_1.instructeurs.count).to eq(1) }
|
||||||
|
it { expect(flash.alert).to eq('Suppression impossible : il doit y avoir au moins un instructeur dans le groupe') }
|
||||||
|
it { expect(response).to redirect_to(procedure_groupe_instructeur_path(procedure, gi_1_1)) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#update_routing_criteria_name' do
|
||||||
|
before do
|
||||||
|
patch :update_routing_criteria_name,
|
||||||
|
params: {
|
||||||
|
procedure_id: procedure.id,
|
||||||
|
procedure: { routing_criteria_name: 'new name !' }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it { expect(procedure.reload.routing_criteria_name).to eq('new name !') }
|
||||||
|
end
|
||||||
|
end
|
126
spec/features/routing/full_scenario_spec.rb
Normal file
126
spec/features/routing/full_scenario_spec.rb
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
feature 'The routing' do
|
||||||
|
let(:procedure) { create(:procedure, :with_service, :for_individual) }
|
||||||
|
let(:administrateur) { create(:administrateur, procedures: [procedure]) }
|
||||||
|
let(:scientifique_user) { create(:user) }
|
||||||
|
let(:litteraire_user) { create(:user) }
|
||||||
|
|
||||||
|
before { Flipper.enable_actor(:routage, administrateur.user) }
|
||||||
|
|
||||||
|
scenario 'works' do
|
||||||
|
login_as administrateur.user, scope: :user
|
||||||
|
|
||||||
|
visit admin_procedure_path(procedure.id)
|
||||||
|
click_on "Groupe d'instructeurs"
|
||||||
|
|
||||||
|
# rename routing criteria to spécialité
|
||||||
|
fill_in 'procedure_routing_criteria_name', with: 'spécialité'
|
||||||
|
click_on 'Renommer'
|
||||||
|
expect(procedure.reload.routing_criteria_name).to eq('spécialité')
|
||||||
|
|
||||||
|
# rename defaut groupe to littéraire
|
||||||
|
click_on 'voir'
|
||||||
|
fill_in 'groupe_instructeur_label', with: 'littéraire'
|
||||||
|
click_on 'Renommer'
|
||||||
|
|
||||||
|
# add victor to littéraire groupe
|
||||||
|
fill_in 'instructeur_email', with: 'victor@inst.com'
|
||||||
|
perform_enqueued_jobs { click_on 'Affecter' }
|
||||||
|
victor = User.find_by(email: 'victor@inst.com').instructeur
|
||||||
|
|
||||||
|
click_on "Groupes d’instructeurs"
|
||||||
|
|
||||||
|
# add scientifique groupe
|
||||||
|
fill_in 'groupe_instructeur_label', with: 'scientifique'
|
||||||
|
click_on 'Ajouter le groupe'
|
||||||
|
|
||||||
|
# add marie to scientifique groupe
|
||||||
|
fill_in 'instructeur_email', with: 'marie@inst.com'
|
||||||
|
perform_enqueued_jobs { click_on 'Affecter' }
|
||||||
|
marie = User.find_by(email: 'marie@inst.com').instructeur
|
||||||
|
|
||||||
|
# publish
|
||||||
|
publish_procedure(procedure)
|
||||||
|
log_out
|
||||||
|
|
||||||
|
# 2 users fill a dossier in each group
|
||||||
|
user_send_dossier(scientifique_user, 'scientifique')
|
||||||
|
user_send_dossier(litteraire_user, 'littéraire')
|
||||||
|
|
||||||
|
# the litteraires instructeurs only manage the litteraires dossiers
|
||||||
|
register_instructeur_and_log_in(victor.email)
|
||||||
|
click_on procedure.libelle
|
||||||
|
expect(page).to have_text(litteraire_user.email)
|
||||||
|
expect(page).not_to have_text(scientifique_user.email)
|
||||||
|
|
||||||
|
# the search only show litteraires dossiers
|
||||||
|
fill_in 'q', with: scientifique_user.email
|
||||||
|
click_on 'Rechercher'
|
||||||
|
expect(page).to have_text('0 dossier trouvé')
|
||||||
|
|
||||||
|
fill_in 'q', with: litteraire_user.email
|
||||||
|
click_on 'Rechercher'
|
||||||
|
expect(page).to have_text('1 dossier trouvé')
|
||||||
|
|
||||||
|
## and the result is clickable
|
||||||
|
click_on litteraire_user.email
|
||||||
|
expect(page).to have_current_path(instructeur_dossier_path(procedure, litteraire_user.dossiers.first))
|
||||||
|
|
||||||
|
log_out
|
||||||
|
|
||||||
|
# the scientifiques instructeurs only manage the scientifiques dossiers
|
||||||
|
register_instructeur_and_log_in(marie.email)
|
||||||
|
click_on procedure.libelle
|
||||||
|
expect(page).not_to have_text(litteraire_user.email)
|
||||||
|
expect(page).to have_text(scientifique_user.email)
|
||||||
|
log_out
|
||||||
|
|
||||||
|
# TODO: notifications tests
|
||||||
|
end
|
||||||
|
|
||||||
|
def publish_procedure(procedure)
|
||||||
|
click_on procedure.libelle
|
||||||
|
find('#publish-procedure').click
|
||||||
|
within '#publish-modal' do
|
||||||
|
fill_in 'lien_site_web', with: 'http://some.website'
|
||||||
|
click_on 'publish'
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(page).to have_text('Démarche publiée')
|
||||||
|
end
|
||||||
|
|
||||||
|
def user_send_dossier(user, groupe)
|
||||||
|
login_as user, scope: :user
|
||||||
|
visit commencer_path(path: procedure.reload.path)
|
||||||
|
click_on 'Commencer la démarche'
|
||||||
|
|
||||||
|
fill_in 'individual_nom', with: 'Nom'
|
||||||
|
fill_in 'individual_prenom', with: 'Prenom'
|
||||||
|
click_button('Continuer')
|
||||||
|
|
||||||
|
select(groupe, from: 'dossier_groupe_instructeur_id')
|
||||||
|
|
||||||
|
click_on 'Déposer le dossier'
|
||||||
|
|
||||||
|
log_out
|
||||||
|
end
|
||||||
|
|
||||||
|
def register_instructeur_and_log_in(email)
|
||||||
|
confirmation_email = emails_sent_to(email)
|
||||||
|
.filter { |m| m.subject == 'Activez votre compte instructeur' }
|
||||||
|
.first
|
||||||
|
token_params = confirmation_email.body.match(/token=[^"]+/)
|
||||||
|
|
||||||
|
visit "users/activate?#{token_params}"
|
||||||
|
fill_in :user_password, with: 'démarches-simplifiées-pwd'
|
||||||
|
|
||||||
|
click_button 'Définir le mot de passe'
|
||||||
|
|
||||||
|
expect(page).to have_content 'Mot de passe enregistré'
|
||||||
|
end
|
||||||
|
|
||||||
|
def log_out
|
||||||
|
click_on 'Se déconnecter'
|
||||||
|
end
|
||||||
|
end
|
|
@ -15,10 +15,11 @@ describe DossierLinkHelper do
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when access as instructeur" do
|
context "when access as instructeur" do
|
||||||
let(:dossier) { create(:dossier) }
|
let(:procedure) { create(:procedure, :routee) }
|
||||||
|
let(:dossier) { create(:dossier, groupe_instructeur: procedure.groupe_instructeurs.last) }
|
||||||
let(:instructeur) { create(:instructeur) }
|
let(:instructeur) { create(:instructeur) }
|
||||||
|
|
||||||
before { dossier.procedure.defaut_groupe_instructeur.instructeurs << instructeur }
|
before { procedure.groupe_instructeurs.last.instructeurs << instructeur }
|
||||||
|
|
||||||
it { expect(helper.dossier_linked_path(instructeur, dossier)).to eq(instructeur_dossier_path(dossier.procedure, dossier)) }
|
it { expect(helper.dossier_linked_path(instructeur, dossier)).to eq(instructeur_dossier_path(dossier.procedure, dossier)) }
|
||||||
end
|
end
|
||||||
|
|
38
spec/models/groupe_instructeur_spec.rb
Normal file
38
spec/models/groupe_instructeur_spec.rb
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe GroupeInstructeur, type: :model do
|
||||||
|
let(:procedure) { create(:procedure) }
|
||||||
|
subject { GroupeInstructeur.new(label: label, procedure: procedure) }
|
||||||
|
|
||||||
|
context 'with no label provided' do
|
||||||
|
let(:label) { '' }
|
||||||
|
|
||||||
|
it { is_expected.to be_invalid }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with a valid label' do
|
||||||
|
let(:label) { 'Préfecture de la Marne' }
|
||||||
|
|
||||||
|
it { is_expected.to be_valid }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with a label with extra spaces' do
|
||||||
|
let(:label) { 'Préfecture de la Marne ' }
|
||||||
|
before do
|
||||||
|
subject.save
|
||||||
|
subject.reload
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to be_valid }
|
||||||
|
it { expect(subject.label).to eq("Préfecture de la Marne") }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with a label already used for this procedure' do
|
||||||
|
let(:label) { 'Préfecture de la Marne' }
|
||||||
|
before do
|
||||||
|
GroupeInstructeur.create!(label: label, procedure: procedure)
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to be_invalid }
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue