commit
6dadc2422f
43 changed files with 812 additions and 137 deletions
2
Gemfile
2
Gemfile
|
@ -4,7 +4,7 @@ gem 'aasm'
|
|||
gem 'actiontext', git: 'https://github.com/kobaltz/actiontext.git', branch: 'archive', require: 'action_text' # Port of ActionText to Rails 5
|
||||
gem 'active_link_to' # Automatically set a class on active links
|
||||
gem 'active_model_serializers'
|
||||
gem 'activestorage-openstack', git: 'https://github.com/fredZen/activestorage-openstack.git', branch: 'frederic/fix_upload_signature'
|
||||
gem 'activestorage-openstack'
|
||||
gem 'administrate'
|
||||
gem 'after_party'
|
||||
gem 'anchored'
|
||||
|
|
34
Gemfile.lock
34
Gemfile.lock
|
@ -1,14 +1,3 @@
|
|||
GIT
|
||||
remote: https://github.com/fredZen/activestorage-openstack.git
|
||||
revision: c71d5107a51701eab9d9267dd0000e6c1cf3e39a
|
||||
branch: frederic/fix_upload_signature
|
||||
specs:
|
||||
activestorage-openstack (0.5.0)
|
||||
fog-openstack (~> 1.0)
|
||||
marcel
|
||||
mime-types
|
||||
rails (~> 5.2.0)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/kobaltz/actiontext.git
|
||||
revision: ef59c4ba99d1b7614dd47f5a294eef553224db88
|
||||
|
@ -75,13 +64,18 @@ GEM
|
|||
actionpack (= 5.2.2.1)
|
||||
activerecord (= 5.2.2.1)
|
||||
marcel (~> 0.3.1)
|
||||
activestorage-openstack (1.0.0)
|
||||
fog-openstack (~> 1.0)
|
||||
marcel
|
||||
mime-types
|
||||
rails (<= 6)
|
||||
activesupport (5.2.2.1)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 0.7, < 2)
|
||||
minitest (~> 5.1)
|
||||
tzinfo (~> 1.1)
|
||||
addressable (2.5.2)
|
||||
public_suffix (>= 2.0.2, < 4.0)
|
||||
addressable (2.7.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
administrate (0.11.0)
|
||||
actionpack (>= 4.2, < 6.0)
|
||||
actionview (>= 4.2, < 6.0)
|
||||
|
@ -125,18 +119,18 @@ GEM
|
|||
browser (2.5.3)
|
||||
builder (3.2.3)
|
||||
byebug (10.0.2)
|
||||
capybara (3.12.0)
|
||||
capybara (3.29.0)
|
||||
addressable
|
||||
mini_mime (>= 0.1.3)
|
||||
nokogiri (~> 1.8)
|
||||
rack (>= 1.6.0)
|
||||
rack-test (>= 0.6.3)
|
||||
regexp_parser (~> 1.2)
|
||||
regexp_parser (~> 1.5)
|
||||
xpath (~> 3.2)
|
||||
capybara-email (3.0.1)
|
||||
capybara (>= 2.4, < 4.0)
|
||||
mail
|
||||
capybara-screenshot (1.0.22)
|
||||
capybara-screenshot (1.0.23)
|
||||
capybara (>= 1.0, < 4)
|
||||
launchy
|
||||
capybara-selenium (0.0.6)
|
||||
|
@ -379,7 +373,7 @@ GEM
|
|||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2018.0812)
|
||||
mimemagic (0.3.3)
|
||||
mini_mime (1.0.1)
|
||||
mini_mime (1.0.2)
|
||||
mini_portile2 (2.4.0)
|
||||
minitest (5.11.3)
|
||||
momentjs-rails (2.20.1)
|
||||
|
@ -454,7 +448,7 @@ GEM
|
|||
pry-byebug (3.6.0)
|
||||
byebug (~> 10.0)
|
||||
pry (~> 0.10)
|
||||
public_suffix (3.0.3)
|
||||
public_suffix (4.0.1)
|
||||
puma (3.12.0)
|
||||
pundit (2.0.1)
|
||||
activesupport (>= 3.0.0)
|
||||
|
@ -518,7 +512,7 @@ GEM
|
|||
execjs
|
||||
railties (>= 3.2)
|
||||
tilt
|
||||
regexp_parser (1.3.0)
|
||||
regexp_parser (1.6.0)
|
||||
request_store (1.4.1)
|
||||
rack (>= 1.4)
|
||||
responders (3.0.0)
|
||||
|
@ -717,7 +711,7 @@ DEPENDENCIES
|
|||
actiontext!
|
||||
active_link_to
|
||||
active_model_serializers
|
||||
activestorage-openstack!
|
||||
activestorage-openstack
|
||||
administrate
|
||||
after_party
|
||||
anchored
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
.groupe-instructeur {
|
||||
.actions {
|
||||
width: 200px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
@import "colors";
|
||||
@import "constants";
|
||||
|
||||
.pull-left {
|
||||
float: left;
|
||||
|
@ -48,3 +49,7 @@
|
|||
background: $orange-bg;
|
||||
color: $black;
|
||||
}
|
||||
|
||||
.mt-2 {
|
||||
margin-top: 2 * $default-spacer;
|
||||
}
|
||||
|
|
|
@ -10,28 +10,41 @@ module CreateAvisConcern
|
|||
# the :emails parameter is a 1-element array.
|
||||
# Hence the call to first
|
||||
# https://github.com/rails/rails/issues/17225
|
||||
emails = create_avis_params[:emails].first.split(',').map(&:strip)
|
||||
expert_emails = create_avis_params[:emails].first.split(',').map(&:strip)
|
||||
allowed_dossiers = [dossier]
|
||||
|
||||
if create_avis_params[:invite_linked_dossiers].present?
|
||||
allowed_dossiers += dossier.linked_dossiers
|
||||
end
|
||||
|
||||
create_results = Avis.create(
|
||||
emails.map do |email|
|
||||
{
|
||||
email: email,
|
||||
introduction: create_avis_params[:introduction],
|
||||
claimant: current_instructeur,
|
||||
dossier: dossier,
|
||||
confidentiel: confidentiel
|
||||
}
|
||||
expert_emails.flat_map do |email|
|
||||
allowed_dossiers.map do |dossier|
|
||||
{
|
||||
email: email,
|
||||
introduction: create_avis_params[:introduction],
|
||||
claimant: current_instructeur,
|
||||
dossier: dossier,
|
||||
confidentiel: confidentiel
|
||||
}
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
persisted, failed = create_results.partition(&:persisted?)
|
||||
|
||||
if persisted.any?
|
||||
sent_emails_addresses = persisted.map(&:email_to_display).join(", ")
|
||||
flash.notice = "Une demande d'avis a été envoyée à #{sent_emails_addresses}"
|
||||
sent_emails_addresses = []
|
||||
persisted.each do |avis|
|
||||
dossier.demander_un_avis!(avis)
|
||||
avis.dossier.demander_un_avis!(avis)
|
||||
|
||||
if avis.dossier == dossier
|
||||
AvisMailer.avis_invitation(avis).deliver_later
|
||||
sent_emails_addresses << avis.email_to_display
|
||||
end
|
||||
end
|
||||
|
||||
flash.notice = "Une demande d'avis a été envoyée à #{sent_emails_addresses.uniq.join(", ")}"
|
||||
end
|
||||
|
||||
if failed.any?
|
||||
|
@ -41,13 +54,13 @@ module CreateAvisConcern
|
|||
|
||||
# When an error occurs, return the avis back to the controller
|
||||
# to give the user a chance to correct and resubmit
|
||||
Avis.new(create_avis_params.merge(emails: [failed.map(&:email).join(", ")]))
|
||||
Avis.new(create_avis_params.merge(emails: [failed.map(&:email).uniq.join(", ")]))
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def create_avis_params
|
||||
params.require(:avis).permit(:introduction, :confidentiel, emails: [])
|
||||
params.require(:avis).permit(:introduction, :confidentiel, :invite_linked_dossiers, emails: [])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -264,7 +264,7 @@ module Instructeurs
|
|||
end
|
||||
|
||||
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"
|
||||
redirect_to root_path
|
||||
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
|
|
@ -538,7 +538,7 @@ enum TypeDeChamp {
|
|||
multiple_drop_down_list
|
||||
|
||||
"""
|
||||
Nombre entier
|
||||
Nombre
|
||||
"""
|
||||
number
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
module DossierLinkHelper
|
||||
def dossier_linked_path(user, dossier)
|
||||
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)
|
||||
else
|
||||
avis = dossier.avis.find_by(instructeur: user)
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
module ActiveStorage
|
||||
# Wraps an ActiveStorage::Service to route direct upload and direct download URLs through our proxy,
|
||||
# thus avoiding exposing the storage provider’s URL to our end-users.
|
||||
class Service::DsProxyService < SimpleDelegator
|
||||
attr_reader :wrapped
|
||||
|
||||
def self.build(wrapped:, configurator:, **options)
|
||||
new(wrapped: configurator.build(wrapped))
|
||||
end
|
||||
|
||||
def initialize(wrapped:)
|
||||
@wrapped = wrapped
|
||||
super(wrapped)
|
||||
end
|
||||
|
||||
def url(*args)
|
||||
url = wrapped.url(*args)
|
||||
publicize(url)
|
||||
end
|
||||
|
||||
def url_for_direct_upload(*args)
|
||||
url = wrapped.url_for_direct_upload(*args)
|
||||
publicize(url)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def object_for(key, &block)
|
||||
blob_url = url(key)
|
||||
if block_given?
|
||||
request = Typhoeus::Request.new(blob_url)
|
||||
request.on_headers do |response|
|
||||
if response.code != 200
|
||||
raise Fog::OpenStack::Storage::NotFound.new
|
||||
end
|
||||
end
|
||||
request.on_body do |chunk|
|
||||
yield chunk
|
||||
end
|
||||
request.run
|
||||
else
|
||||
response = Typhoeus.get(blob_url)
|
||||
if response.success?
|
||||
response
|
||||
else
|
||||
raise Fog::OpenStack::Storage::NotFound.new
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def publicize(url)
|
||||
search = %r{^https://[^/]+/v1/AUTH_[a-f0-9]{32}}
|
||||
replace = 'https://static.demarches-simplifiees.fr'
|
||||
url.gsub(search, replace)
|
||||
end
|
||||
end
|
||||
end
|
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
|
|
@ -12,7 +12,6 @@ class Avis < ApplicationRecord
|
|||
|
||||
before_validation -> { sanitize_email(:email) }
|
||||
before_create :try_to_assign_instructeur
|
||||
after_create :notify_instructeur
|
||||
|
||||
default_scope { joins(:dossier) }
|
||||
scope :with_answer, -> { where.not(answer: nil) }
|
||||
|
@ -24,6 +23,7 @@ class Avis < ApplicationRecord
|
|||
# The form allows subtmitting avis requests to several emails at once,
|
||||
# hence this virtual attribute.
|
||||
attr_accessor :emails
|
||||
attr_accessor :invite_linked_dossiers
|
||||
|
||||
def email_to_display
|
||||
instructeur&.email || email
|
||||
|
@ -49,10 +49,6 @@ class Avis < ApplicationRecord
|
|||
|
||||
private
|
||||
|
||||
def notify_instructeur
|
||||
AvisMailer.avis_invitation(self).deliver_later
|
||||
end
|
||||
|
||||
def try_to_assign_instructeur
|
||||
instructeur = Instructeur.find_by(email: email)
|
||||
if instructeur
|
||||
|
|
|
@ -11,7 +11,7 @@ class Champ < ApplicationRecord
|
|||
belongs_to :etablissement, dependent: :destroy
|
||||
has_many :champs, -> { ordered }, foreign_key: :parent_id, inverse_of: :parent, dependent: :destroy
|
||||
|
||||
delegate :libelle, :type_champ, :order_place, :mandatory?, :description, :drop_down_list, :exclude_from_export?, :exclude_from_view?, :repetition?, to: :type_de_champ
|
||||
delegate :libelle, :type_champ, :order_place, :mandatory?, :description, :drop_down_list, :exclude_from_export?, :exclude_from_view?, :repetition?, :dossier_link?, to: :type_de_champ
|
||||
|
||||
scope :updated_since?, -> (date) { where('champs.updated_at > ?', date) }
|
||||
scope :public_only, -> { where(private: false) }
|
||||
|
|
|
@ -517,6 +517,10 @@ class Dossier < ApplicationRecord
|
|||
self.individual = Individual.create_from_france_connect(fc_information)
|
||||
end
|
||||
|
||||
def linked_dossiers
|
||||
Dossier.where(id: champs.filter(&:dossier_link?).map(&:value).compact)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def log_dossier_operation(author, operation, subject = nil)
|
||||
|
|
|
@ -4,4 +4,9 @@ class GroupeInstructeur < ApplicationRecord
|
|||
has_many :assign_tos
|
||||
has_many :instructeurs, through: :assign_tos, dependent: :destroy
|
||||
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
|
||||
|
|
|
@ -157,6 +157,10 @@ class TypeDeChamp < ApplicationRecord
|
|||
type_champ == TypeDeChamp.type_champs.fetch(:repetition)
|
||||
end
|
||||
|
||||
def dossier_link?
|
||||
type_champ == TypeDeChamp.type_champs.fetch(:dossier_link)
|
||||
end
|
||||
|
||||
def public?
|
||||
!private?
|
||||
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"
|
|
@ -28,7 +28,7 @@
|
|||
= f.submit 'Envoyer votre avis', class: 'button send'
|
||||
|
||||
- if !@dossier.termine?
|
||||
= render partial: "instructeurs/shared/avis/form", locals: { url: avis_instructeur_avis_path(@avis), must_be_confidentiel: @avis.confidentiel?, avis: @new_avis }
|
||||
= render partial: "instructeurs/shared/avis/form", locals: { url: avis_instructeur_avis_path(@avis), linked_dossiers: @dossier.linked_dossiers, must_be_confidentiel: @avis.confidentiel?, avis: @new_avis }
|
||||
|
||||
- if @dossier.avis_for(current_instructeur).present?
|
||||
= render partial: 'instructeurs/shared/avis/list', locals: { avis: @dossier.avis_for(current_instructeur), avis_seen_at: nil }
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
.container
|
||||
- if !@dossier.termine?
|
||||
= render partial: "instructeurs/shared/avis/form", locals: { url: avis_instructeur_dossier_path(@dossier.procedure, @dossier), must_be_confidentiel: false, avis: @avis }
|
||||
= render partial: "instructeurs/shared/avis/form", locals: { url: avis_instructeur_dossier_path(@dossier.procedure, @dossier), linked_dossiers: @dossier.linked_dossiers, must_be_confidentiel: false, avis: @avis }
|
||||
|
||||
- if @dossier.avis.present?
|
||||
= render partial: 'instructeurs/shared/avis/list', locals: { avis: @dossier.avis, avis_seen_at: @avis_seen_at }
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
.container
|
||||
.page-title
|
||||
Résultat de la recherche :
|
||||
= pluralize(@dossiers.count, "dossier trouvé", "dossiers trouvés")
|
||||
= t('pluralize.dossier_trouve', count: @dossiers.count)
|
||||
|
||||
- if @dossiers.present?
|
||||
%table.table.dossiers-table.hoverable
|
||||
|
|
|
@ -6,6 +6,10 @@
|
|||
= f.email_field :emails, placeholder: 'Adresses email, séparées par des virgules', required: true, multiple: true, onchange: "javascript:DS.replaceSemicolonByComma(event);"
|
||||
= f.text_area :introduction, rows: 3, value: avis.introduction || 'Bonjour, merci de me donner votre avis sur ce dossier.', required: true
|
||||
|
||||
- if linked_dossiers.present?
|
||||
= f.check_box :invite_linked_dossiers, value: false
|
||||
= 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
|
||||
- if must_be_confidentiel
|
||||
%p.confidentiel.flex
|
||||
|
|
|
@ -27,14 +27,20 @@
|
|||
%p.missing-steps (à compléter)
|
||||
|
||||
%a#onglet-administrateurs{ href: url_for(procedure_administrateurs_path(@procedure)) }
|
||||
.procedure-list-element{ class: ('active' if active == 'Administrateurs') }
|
||||
.procedure-list-element
|
||||
Administrateurs
|
||||
|
||||
%a#onglet-instructeurs{ href: url_for(admin_procedure_assigns_path(@procedure)) }
|
||||
.procedure-list-element{ class: ('active' if active == 'Instructeurs') }
|
||||
Instructeurs
|
||||
- if @procedure.missing_steps.include?(:instructeurs)
|
||||
%p.missing-steps (à compléter)
|
||||
- if !feature_enabled?(:routage)
|
||||
%a#onglet-instructeurs{ href: url_for(admin_procedure_assigns_path(@procedure)) }
|
||||
.procedure-list-element{ class: ('active' if active == 'Instructeurs') }
|
||||
Instructeurs
|
||||
- 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?
|
||||
%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
|
|
@ -93,7 +93,7 @@ Rails.application.configure do
|
|||
# the I18n.default_locale when a translation cannot be found).
|
||||
config.i18n.fallbacks = true
|
||||
|
||||
config.active_storage.service = :proxied
|
||||
config.active_storage.service = :openstack
|
||||
|
||||
# Send deprecation notices to registered listeners.
|
||||
config.active_support.deprecation = :notify
|
||||
|
|
|
@ -7,3 +7,33 @@ ActiveStorage::Service.url_expires_in = 1.hour
|
|||
# cleaner (as it allows to enqueue the virus scan on attachment creation, rather
|
||||
# than on blob creation).
|
||||
ActiveSupport.on_load(:active_storage_blob) { include BlobVirusScanner }
|
||||
|
||||
# When an OpenStack service is initialized it makes a request to fetch
|
||||
# `publicURL` to use for all operations. We intercept the method that reads
|
||||
# this url and replace the host with DS_Proxy host. This way all the operation
|
||||
# are performed through DS_Proxy.
|
||||
#
|
||||
# https://github.com/fog/fog-openstack/blob/37621bb1d5ca78d037b3c56bd307f93bba022ae1/lib/fog/openstack/auth/catalog/v2.rb#L16
|
||||
require 'fog/openstack/auth/catalog/v2'
|
||||
|
||||
module Fog::OpenStack::Auth::Catalog
|
||||
class V2
|
||||
def endpoint_url(endpoint, interface)
|
||||
url = endpoint["#{interface}URL"]
|
||||
|
||||
if interface == 'public'
|
||||
publicize(url)
|
||||
else
|
||||
url
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def publicize(url)
|
||||
search = %r{^https://[^/]+/}
|
||||
replace = 'https://static.demarches-simplifiees.fr/'
|
||||
url.gsub(search, replace)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -313,3 +313,7 @@ fr:
|
|||
zero: archivé
|
||||
one: archivé
|
||||
other: archivés
|
||||
dossier_trouve:
|
||||
zero: 0 dossier trouvé
|
||||
one: 1 dossier trouvé
|
||||
other: "%{count} dossiers trouvés"
|
||||
|
|
|
@ -5,3 +5,8 @@ fr:
|
|||
attributes:
|
||||
avis:
|
||||
answer: "Réponse"
|
||||
helpers:
|
||||
label:
|
||||
invite_linked_dossiers:
|
||||
one: Inviter aussi l'expert sur le dossier lié n° %{ids}
|
||||
other: Inviter aussi l'expert sur les dossiers liés n° %{ids}
|
||||
|
|
|
@ -9,7 +9,7 @@ fr:
|
|||
textarea: 'Zone de texte'
|
||||
date: 'Date'
|
||||
datetime: 'Date et Heure'
|
||||
number: 'Nombre entier'
|
||||
number: 'Nombre'
|
||||
decimal_number: 'Nombre décimal'
|
||||
integer_number: 'Nombre entier'
|
||||
checkbox: 'Case à cocher'
|
||||
|
|
|
@ -350,6 +350,17 @@ Rails.application.routes.draw do
|
|||
get 'annotations'
|
||||
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 :types_de_champ, only: [:create, :update, :destroy] do
|
||||
|
|
|
@ -4,9 +4,6 @@ local:
|
|||
test:
|
||||
service: Disk
|
||||
root: <%= Rails.root.join("tmp/storage") %>
|
||||
proxied:
|
||||
service: DsProxy
|
||||
wrapped: openstack
|
||||
openstack:
|
||||
service: OpenStack
|
||||
container: "<%= ENV['FOG_ACTIVESTORAGE_DIRECTORY'] %>"
|
||||
|
|
|
@ -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.
|
||||
|
||||
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
|
||||
enable_extension "plpgsql"
|
||||
|
@ -486,7 +486,7 @@ ActiveRecord::Schema.define(version: 2019_10_14_160538) do
|
|||
t.string "path", null: false
|
||||
t.string "declarative_with_state"
|
||||
t.text "monavis_embed"
|
||||
t.text "routing_criteria_name"
|
||||
t.text "routing_criteria_name", default: "Votre ville"
|
||||
t.boolean "csv_export_queued"
|
||||
t.boolean "xlsx_export_queued"
|
||||
t.boolean "ods_export_queued"
|
||||
|
|
|
@ -123,9 +123,10 @@ describe Instructeurs::AvisController, type: :controller do
|
|||
let(:intro) { 'introduction' }
|
||||
let(:created_avis) { Avis.last }
|
||||
let!(:old_avis_count) { Avis.count }
|
||||
let(:invite_linked_dossiers) { nil }
|
||||
|
||||
before do
|
||||
post :create_avis, params: { id: previous_avis.id, avis: { emails: emails, introduction: intro, confidentiel: asked_confidentiel } }
|
||||
post :create_avis, params: { id: previous_avis.id, avis: { emails: emails, introduction: intro, confidentiel: asked_confidentiel, invite_linked_dossiers: invite_linked_dossiers } }
|
||||
end
|
||||
|
||||
context 'when an invalid email' do
|
||||
|
@ -180,6 +181,34 @@ describe Instructeurs::AvisController, type: :controller do
|
|||
it { expect(created_avis.confidentiel).to be(true) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'with linked dossiers' do
|
||||
let(:asked_confidentiel) { false }
|
||||
let(:previous_avis_confidentiel) { false }
|
||||
let(:dossier) { create(:dossier, :en_construction, :with_dossier_link, procedure: procedure) }
|
||||
|
||||
it do
|
||||
expect(flash.notice).to eq("Une demande d'avis a été envoyée à a@b.com")
|
||||
expect(Avis.count).to eq(old_avis_count + 1)
|
||||
expect(created_avis.email).to eq("a@b.com")
|
||||
expect(created_avis.dossier).to eq(dossier)
|
||||
end
|
||||
|
||||
context 'checked' do
|
||||
let(:invite_linked_dossiers) { true }
|
||||
let(:created_avis) { Avis.last(2).first }
|
||||
let(:linked_avis) { Avis.last }
|
||||
let(:linked_dossier) { dossier.reload.linked_dossiers.first }
|
||||
|
||||
it do
|
||||
expect(flash.notice).to eq("Une demande d'avis a été envoyée à a@b.com")
|
||||
expect(Avis.count).to eq(old_avis_count + 2)
|
||||
expect(created_avis.email).to eq("a@b.com")
|
||||
expect(created_avis.dossier).to eq(dossier)
|
||||
expect(linked_avis.dossier).to eq(linked_dossier)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -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
|
|
@ -54,10 +54,30 @@ FactoryBot.define do
|
|||
|
||||
trait :with_dossier_link do
|
||||
after(:create) do |dossier, _evaluator|
|
||||
# create linked dossier
|
||||
linked_dossier = create(:dossier)
|
||||
type_de_champ = dossier.procedure.types_de_champ.find { |t| t.type_champ == TypeDeChamp.type_champs.fetch(:dossier_link) }
|
||||
champ = dossier.champs.find { |c| c.type_de_champ == type_de_champ }
|
||||
|
||||
# find first type de champ dossier_link
|
||||
type_de_champ = dossier.procedure.types_de_champ.find do |t|
|
||||
t.type_champ == TypeDeChamp.type_champs.fetch(:dossier_link)
|
||||
end
|
||||
|
||||
# if type de champ does not exist create it
|
||||
if !type_de_champ
|
||||
type_de_champ = create(:type_de_champ_dossier_link, procedure: dossier.procedure)
|
||||
end
|
||||
|
||||
# find champ with the type de champ
|
||||
champ = dossier.reload.champs.find do |c|
|
||||
c.type_de_champ == type_de_champ
|
||||
end
|
||||
|
||||
# if champ does not exist create it
|
||||
if !champ
|
||||
champ = create(:champ_dossier_link, dossier: dossier, type_de_champ: type_de_champ)
|
||||
end
|
||||
|
||||
# set champ value with linked dossier
|
||||
champ.value = linked_dossier.id
|
||||
champ.save!
|
||||
end
|
||||
|
|
|
@ -8,7 +8,7 @@ feature 'Inviting an expert:' do
|
|||
let(:expert) { create(:instructeur, password: expert_password) }
|
||||
let(:expert_password) { 'mot de passe d’expert' }
|
||||
let(:procedure) { create(:procedure, :published, instructeurs: [instructeur]) }
|
||||
let(:dossier) { create(:dossier, state: Dossier.states.fetch(:en_construction), procedure: procedure) }
|
||||
let(:dossier) { create(:dossier, :en_construction, :with_dossier_link, procedure: procedure) }
|
||||
|
||||
context 'as an Instructeur' do
|
||||
scenario 'I can invite an expert' do
|
||||
|
@ -20,6 +20,7 @@ feature 'Inviting an expert:' do
|
|||
|
||||
fill_in 'avis_emails', with: 'expert1@exemple.fr, expert2@exemple.fr'
|
||||
fill_in 'avis_introduction', with: 'Bonjour, merci de me donner votre avis sur ce dossier.'
|
||||
check 'avis_invite_linked_dossiers'
|
||||
page.select 'confidentiel', from: 'avis_confidentiel'
|
||||
|
||||
perform_enqueued_jobs do
|
||||
|
@ -34,6 +35,8 @@ feature 'Inviting an expert:' do
|
|||
expect(page).to have_content('Bonjour, merci de me donner votre avis sur ce dossier.')
|
||||
end
|
||||
|
||||
expect(Avis.count).to eq(4)
|
||||
expect(all_emails.size).to eq(2)
|
||||
invitation_email = open_email('expert2@exemple.fr')
|
||||
avis = Avis.find_by(email: 'expert2@exemple.fr')
|
||||
sign_up_link = sign_up_instructeur_avis_path(avis.id, avis.email)
|
||||
|
|
|
@ -7,8 +7,7 @@ feature 'Instructing a dossier:' do
|
|||
let!(:instructeur) { create(:instructeur, password: password) }
|
||||
|
||||
let!(:procedure) { create(:procedure, :published, instructeurs: [instructeur]) }
|
||||
let!(:dossier) { create(:dossier, state: Dossier.states.fetch(:en_construction), procedure: procedure) }
|
||||
|
||||
let!(:dossier) { create(:dossier, :en_construction, procedure: procedure) }
|
||||
context 'the instructeur is also a user' do
|
||||
scenario 'a instructeur can fill a dossier' do
|
||||
visit commencer_path(path: procedure.path)
|
||||
|
|
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
|
||||
|
||||
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) }
|
||||
|
||||
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)) }
|
||||
end
|
||||
|
|
|
@ -87,18 +87,6 @@ RSpec.describe Avis, type: :model do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#notify_instructeur' do
|
||||
context 'when an avis is created' do
|
||||
before do
|
||||
avis_invitation_double = double('avis_invitation', deliver_later: true)
|
||||
allow(AvisMailer).to receive(:avis_invitation).and_return(avis_invitation_double)
|
||||
Avis.create(claimant: claimant, email: 'email@l.com')
|
||||
end
|
||||
|
||||
it { expect(AvisMailer).to have_received(:avis_invitation) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#try_to_assign_instructeur' do
|
||||
let!(:instructeur) { create(:instructeur) }
|
||||
let(:avis) { Avis.create(claimant: claimant, email: email, dossier: create(:dossier)) }
|
||||
|
|
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