demarches-normaliennes/app/controllers/administrateurs/groupe_instructeurs_controller.rb

481 lines
18 KiB
Ruby
Raw Normal View History

module Administrateurs
2019-10-17 15:13:50 +02:00
class GroupeInstructeursController < AdministrateurController
2021-06-09 17:51:35 +02:00
include ActiveSupport::NumberHelper
2023-04-26 17:33:14 +02:00
include Logic
include UninterlacePngConcern
include GroupeInstructeursSignatureConcern
before_action :ensure_not_super_admin!, only: [:add_instructeur]
2019-10-17 15:13:50 +02:00
ITEMS_PER_PAGE = 25
2021-06-09 17:51:35 +02:00
CSV_MAX_SIZE = 1.megabytes
CSV_ACCEPTED_CONTENT_TYPES = [
"text/csv",
"application/vnd.ms-excel"
]
2019-10-17 15:13:50 +02:00
def index
@procedure = procedure
@groupes_instructeurs = paginated_groupe_instructeurs
2019-10-17 15:13:50 +02:00
2022-10-17 11:01:55 +02:00
@instructeurs = paginated_instructeurs
@available_instructeur_emails = available_instructeur_emails
2019-10-17 15:13:50 +02:00
end
2023-04-26 17:31:05 +02:00
def options
@procedure = procedure
end
2023-04-26 17:31:49 +02:00
def ajout
redirect_to admin_procedure_groupe_instructeurs_path(procedure) if procedure.groupe_instructeurs.one?
@procedure = procedure
@groupes_instructeurs = paginated_groupe_instructeurs
end
2023-04-26 17:33:14 +02:00
def simple_routing
@procedure = procedure
end
def create_simple_routing
@procedure = procedure
stable_id = params[:create_simple_routing][:stable_id].to_i
tdc = @procedure.active_revision.routable_types_de_champ.find { |tdc| tdc.stable_id == stable_id }
case tdc.type_champ
when TypeDeChamp.type_champs.fetch(:communes), TypeDeChamp.type_champs.fetch(:departements), TypeDeChamp.type_champs.fetch(:epci)
tdc_options = APIGeoService.departements.map { ["#{_1[:code]} #{_1[:name]}", _1[:code]] }
create_groups_from_territorial_tdc(tdc_options, stable_id)
when TypeDeChamp.type_champs.fetch(:regions)
tdc_options = APIGeoService.regions.map { ["#{_1[:code]} #{_1[:name]}", _1[:code]] }
create_groups_from_territorial_tdc(tdc_options, stable_id)
when TypeDeChamp.type_champs.fetch(:drop_down_list)
tdc_options = tdc.drop_down_options.reject(&:empty?)
create_groups_from_drop_down_list_tdc(tdc_options, stable_id)
end
if tdc.drop_down_other?
routing_rule = ds_eq(champ_value(stable_id), constant(Champs::DropDownListChamp::OTHER))
@procedure
.groupe_instructeurs
.find_or_create_by(label: 'Autre')
.update(instructeurs: [current_administrateur.instructeur], routing_rule:)
2023-04-26 17:33:14 +02:00
end
@procedure.toggle_routing
2023-04-26 17:33:14 +02:00
defaut = @procedure.defaut_groupe_instructeur
if !tdc_options.include?(defaut.label)
new_defaut = @procedure.reload.groupe_instructeurs_but_defaut.first
@procedure.update!(defaut_groupe_instructeur: new_defaut)
reaffecter_all_dossiers_to_defaut_groupe
defaut.instructeurs.each { new_defaut.add(_1) }
defaut.destroy!
end
flash.notice = 'Les groupes instructeurs ont été ajoutés'
redirect_to admin_procedure_groupe_instructeurs_path(@procedure)
2023-04-26 17:33:14 +02:00
end
2023-04-27 10:10:57 +02:00
def wizard
if params[:choice][:state] == 'routage_custom'
new_label = procedure.defaut_groupe_instructeur.label + ' bis'
procedure.groupe_instructeurs
.create({ label: new_label, instructeurs: [current_administrateur.instructeur] })
procedure.toggle_routing
2023-04-27 10:10:57 +02:00
redirect_to admin_procedure_groupe_instructeurs_path(procedure)
elsif params[:choice][:state] == 'routage_simple'
redirect_to simple_routing_admin_procedure_groupe_instructeurs_path
end
end
2023-04-27 10:11:27 +02:00
def destroy_all_groups_but_defaut
reaffecter_all_dossiers_to_defaut_groupe
procedure.groupe_instructeurs_but_defaut.each(&:destroy!)
procedure.update!(routing_enabled: false)
2023-04-27 10:11:27 +02:00
procedure.defaut_groupe_instructeur.update!(
routing_rule: nil,
label: GroupeInstructeur::DEFAUT_LABEL,
closed: false
)
flash.notice = 'Tous les groupes instructeurs ont été supprimés'
redirect_to admin_procedure_groupe_instructeurs_path(procedure)
end
2019-10-23 20:22:56 +02:00
def show
@procedure = procedure
@groupe_instructeur = groupe_instructeur
2019-10-23 20:54:59 +02:00
@instructeurs = paginated_instructeurs
@available_instructeur_emails = available_instructeur_emails
2019-10-23 20:22:56 +02:00
end
2019-10-23 20:51:25 +02:00
def create
@groupe_instructeur = procedure
.groupe_instructeurs
.new({ instructeurs: [current_administrateur.instructeur] }.merge(groupe_instructeur_params))
2019-10-23 20:51:25 +02:00
2023-01-12 14:09:43 +01:00
if @groupe_instructeur.save
procedure.toggle_routing
routing_notice = " et le routage a été activé" if procedure.groupe_instructeurs.active.size == 2
redirect_to admin_procedure_groupe_instructeur_path(procedure, @groupe_instructeur),
2023-01-12 15:17:42 +01:00
notice: "Le groupe dinstructeurs « #{@groupe_instructeur.label} » a été créé#{routing_notice}."
2023-01-12 14:09:43 +01:00
else
2019-10-23 20:51:25 +02:00
@procedure = procedure
@instructeurs = paginated_instructeurs
2019-10-23 20:51:25 +02:00
@groupes_instructeurs = paginated_groupe_instructeurs
flash.now[:alert] = @groupe_instructeur.errors.full_messages
2019-10-23 20:51:25 +02:00
render :index
end
end
2019-10-23 20:54:59 +02:00
def update
@groupe_instructeur = groupe_instructeur
2023-04-24 15:47:46 +02:00
if @groupe_instructeur.update(groupe_instructeur_params)
procedure.toggle_routing
redirect_to admin_procedure_groupe_instructeur_path(procedure, groupe_instructeur),
2023-01-12 15:17:42 +01:00
notice: "Le nom est à présent « #{@groupe_instructeur.label} »."
2023-01-12 14:09:43 +01:00
else
2019-10-23 20:54:59 +02:00
@procedure = procedure
@instructeurs = paginated_instructeurs
@available_instructeur_emails = available_instructeur_emails
2019-10-23 20:54:59 +02:00
flash.now[:alert] = @groupe_instructeur.errors.full_messages
2019-10-23 20:54:59 +02:00
render :show
end
end
2023-04-24 15:47:46 +02:00
def update_state
@groupe_instructeur = procedure.groupe_instructeurs.find(params[:groupe_instructeur_id])
if @groupe_instructeur.update(closed: params[:closed])
2023-04-24 15:47:46 +02:00
state_for_notice = @groupe_instructeur.closed ? 'désactivé' : 'activé'
redirect_to admin_procedure_groupe_instructeur_path(procedure, @groupe_instructeur),
notice: "Le groupe #{@groupe_instructeur.label} est #{state_for_notice}."
else
redirect_to admin_procedure_groupe_instructeur_path(procedure, @groupe_instructeur),
alert: @groupe_instructeur.errors.messages_for(:closed).to_sentence
2023-04-24 15:47:46 +02:00
end
end
def destroy
@groupe_instructeur = groupe_instructeur
if @groupe_instructeur.dossiers.present?
2023-01-12 14:02:01 +01:00
flash[:alert] = "Impossible de supprimer un groupe avec des dossiers. Il faut le réaffecter avant"
2020-01-20 11:27:41 +01:00
elsif procedure.groupe_instructeurs.one?
2023-01-12 14:02:01 +01:00
flash[:alert] = "Suppression impossible : il doit y avoir au moins un groupe instructeur sur chaque procédure"
elsif @groupe_instructeur.id == procedure.defaut_groupe_instructeur.id
flash[:alert] = "Suppression impossible : le groupe « #{@groupe_instructeur.label} » est le groupe par défaut."
else
2023-01-12 15:33:56 +01:00
@groupe_instructeur.destroy!
if procedure.groupe_instructeurs.active.one?
procedure.toggle_routing
2023-04-27 10:11:27 +02:00
procedure.defaut_groupe_instructeur.update!(
routing_rule: nil,
label: GroupeInstructeur::DEFAUT_LABEL,
closed: false
)
2023-01-12 15:33:56 +01:00
routing_notice = " et le routage a été désactivé"
end
2023-01-12 14:02:01 +01:00
flash[:notice] = "le groupe « #{@groupe_instructeur.label} » a été supprimé#{routing_notice}."
end
redirect_to admin_procedure_groupe_instructeurs_path(procedure)
end
def reaffecter_dossiers
@procedure = procedure
@groupe_instructeur = groupe_instructeur
@groupes_instructeurs = paginated_groupe_instructeurs
.without_group(@groupe_instructeur)
end
2021-07-26 16:06:09 +02:00
def reaffecter_bulk_messages(target_group)
bulk_messages = BulkMessage.joins(:groupe_instructeurs).where(groupe_instructeurs: { id: groupe_instructeur.id })
bulk_messages.each do |bulk_message|
bulk_message.groupe_instructeurs.delete(groupe_instructeur)
if !bulk_message.groupe_instructeur_ids.include?(target_group.id)
bulk_message.groupe_instructeurs << target_group
end
end
end
def reaffecter
2020-01-20 11:27:41 +01:00
target_group = procedure.groupe_instructeurs.find(params[:target_group])
2021-07-26 16:06:09 +02:00
reaffecter_bulk_messages(target_group)
groupe_instructeur.dossiers.find_each do |dossier|
dossier.assign_to_groupe_instructeur(target_group, DossierAssignment.modes.fetch(:manual), current_administrateur)
end
2020-01-20 11:27:41 +01:00
flash[:notice] = "Les dossiers du groupe « #{groupe_instructeur.label} » ont été réaffectés au groupe « #{target_group.label} »."
redirect_to admin_procedure_groupe_instructeurs_path(procedure)
end
2023-04-27 10:11:27 +02:00
def reaffecter_all_dossiers_to_defaut_groupe
procedure.groupe_instructeurs_but_defaut.each do |gi|
gi.dossiers.find_each do |dossier|
dossier.assign_to_groupe_instructeur(procedure.defaut_groupe_instructeur, DossierAssignment.modes.fetch(:manual), current_administrateur)
2023-04-27 10:11:27 +02:00
end
end
end
2019-10-10 19:36:56 +02:00
def add_instructeur
emails = params['emails'].presence || [].to_json
emails = JSON.parse(emails).map { EmailSanitizableConcern::EmailSanitizer.sanitize(_1) }
2019-10-10 19:36:56 +02:00
2023-02-21 16:37:26 +01:00
instructeurs, invalid_emails = groupe_instructeur.add_instructeurs(emails:)
if invalid_emails.present?
flash[:alert] = t('.wrong_address',
count: invalid_emails.size,
emails: invalid_emails.join(', '))
end
if instructeurs.present?
flash[:notice] = if procedure.routing_enabled?
t('.assignment',
count: instructeurs.size,
emails: instructeurs.map(&:email).join(', '),
groupe: groupe_instructeur.label)
else
"Les instructeurs ont bien été affectés à la démarche"
end
GroupeInstructeurMailer
.notify_added_instructeurs(groupe_instructeur, instructeurs, current_administrateur.email)
.deliver_later
2019-10-10 19:36:56 +02:00
end
2022-11-04 10:10:57 +01:00
if procedure.routing_enabled?
redirect_to admin_procedure_groupe_instructeur_path(procedure, groupe_instructeur)
else
redirect_to admin_procedure_groupe_instructeurs_path(procedure)
end
2019-10-10 19:36:56 +02:00
end
2019-10-09 18:23:29 +02:00
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 = groupe_instructeur.instructeurs.find_by(id: instructeur_id)
if groupe_instructeur.remove(instructeur)
flash[:notice] = if instructeur.in?(procedure.instructeurs)
"Linstructeur « #{instructeur.email} » a été retiré du groupe."
else
"Linstructeur a bien été désaffecté de la démarche"
end
GroupeInstructeurMailer
.notify_removed_instructeur(groupe_instructeur, instructeur, current_administrateur.email)
.deliver_later
else
flash[:alert] = if procedure.routing_enabled?
if instructeur.present?
"Linstructeur « #{instructeur.email} » nest pas dans le groupe."
else
"Linstructeur nest pas dans le groupe."
end
else
"Linstructeur nest pas affecté à la démarche"
end
end
2019-10-09 18:23:29 +02:00
end
2022-11-04 10:10:57 +01:00
if procedure.routing_enabled?
redirect_to admin_procedure_groupe_instructeur_path(procedure, groupe_instructeur)
else
redirect_to admin_procedure_groupe_instructeurs_path(procedure)
end
2019-10-09 18:23:29 +02:00
end
def update_instructeurs_self_management_enabled
procedure.update!(instructeurs_self_management_enabled_params)
2023-04-26 17:31:05 +02:00
redirect_to options_admin_procedure_groupe_instructeurs_path(procedure),
notice: "Lautogestion des instructeurs est #{procedure.instructeurs_self_management_enabled? ? "activée" : "désactivée"}."
end
2021-06-09 17:51:35 +02:00
def import
if procedure.publiee_or_close?
if !CSV_ACCEPTED_CONTENT_TYPES.include?(csv_file.content_type) && !CSV_ACCEPTED_CONTENT_TYPES.include?(marcel_content_type)
flash[:alert] = "Importation impossible : veuillez importer un fichier CSV"
2021-06-09 17:51:35 +02:00
elsif csv_file.size > CSV_MAX_SIZE
flash[:alert] = "Importation impossible : le poids du fichier est supérieur à #{number_to_human_size(CSV_MAX_SIZE)}"
2021-06-09 17:51:35 +02:00
else
file = csv_file.read
base_encoding = CharlockHolmes::EncodingDetector.detect(file)
csv_content = ACSV::CSV.new_for_ruby3(file.encode("UTF-8", base_encoding[:encoding], invalid: :replace, replace: ""), headers: true, header_converters: :downcase).map(&:to_h)
if csv_content.first.has_key?("groupe") && csv_content.first.has_key?("email")
groupes_emails = csv_content.map { |r| r.to_h.slice('groupe', 'email') }
added_instructeurs_by_group, invalid_emails = InstructeursImportService.import_groupes(procedure, groupes_emails)
added_instructeurs_by_group.each do |groupe, added_instructeurs|
if added_instructeurs.present?
GroupeInstructeurMailer
.notify_added_instructeurs(groupe, added_instructeurs, current_administrateur.email)
.deliver_later
end
flash_message_for_import(invalid_emails)
end
elsif csv_content.first.has_key?("email") && !csv_content.map(&:to_h).first.keys.many? && procedure.groupe_instructeurs.one?
instructors_emails = csv_content.map(&:to_h)
added_instructeurs, invalid_emails = InstructeursImportService.import_instructeurs(procedure, instructors_emails)
if added_instructeurs.present?
GroupeInstructeurMailer
.notify_added_instructeurs(groupe_instructeur, added_instructeurs, current_administrateur.email)
.deliver_later
end
flash_message_for_import(invalid_emails)
else
flash_message_for_invalid_csv
end
redirect_to admin_procedure_groupe_instructeurs_path(procedure)
2021-06-09 17:51:35 +02:00
end
end
end
2021-10-04 16:02:19 +02:00
def export_groupe_instructeurs
groupe_instructeurs = procedure.groupe_instructeurs
data = CSV.generate(headers: true) do |csv|
column_names = ["Groupe", "Email"]
csv << column_names
groupe_instructeurs.each do |gi|
gi.instructeurs.each do |instructeur|
csv << [gi.label, instructeur.email]
end
end
end
respond_to do |format|
format.csv { send_data data, filename: "#{procedure.id}-groupe-instructeurs-#{Date.today}.csv" }
end
end
2019-10-17 15:13:50 +02:00
private
def closed_params?
2023-04-24 15:47:46 +02:00
params[:closed] == "1"
end
2019-10-17 15:13:50 +02:00
def procedure
current_administrateur
.procedures
.includes(:groupe_instructeurs)
.find(params[:procedure_id])
end
2019-10-23 20:22:56 +02:00
def groupe_instructeur
if params[:id].present?
procedure.groupe_instructeurs.find(params[:id])
else
procedure.defaut_groupe_instructeur
end
2019-10-23 20:22:56 +02:00
end
2019-10-23 20:51:25 +02:00
2019-10-09 18:23:29 +02:00
def instructeur_id
params[:instructeur][:id]
end
def groupe_instructeur_params
2023-04-24 15:47:46 +02:00
params.require(:groupe_instructeur).permit(:label)
2019-10-23 20:51:25 +02:00
end
def signature_params
params.require(:groupe_instructeur).permit(:signature)
end
2019-10-23 20:51:25 +02:00
def paginated_groupe_instructeurs
2023-05-09 11:15:02 +02:00
groupes = if params[:q].present?
query = ActiveRecord::Base.sanitize_sql_like(params[:q])
procedure
.groupe_instructeurs
.where('unaccent(label) ILIKE unaccent(?)', "%#{query}%")
else
procedure.groupe_instructeurs
end
if params[:filter] == '1'
groupes = Kaminari.paginate_array(groupes.filter(&:routing_to_configure?))
end
2023-05-09 11:15:02 +02:00
groupes
2019-10-23 20:51:25 +02:00
.page(params[:page])
.per(ITEMS_PER_PAGE)
end
2019-10-23 20:54:59 +02:00
def paginated_instructeurs
groupe_instructeur
.instructeurs
.page(params[:page])
.per(ITEMS_PER_PAGE)
.order(:email)
end
2019-10-17 10:57:58 +02:00
def available_instructeur_emails
all = current_administrateur.instructeurs.map(&:email)
assigned = groupe_instructeur.instructeurs.map(&:email)
(all - assigned).sort
end
2021-06-09 17:51:35 +02:00
def csv_file
params[:csv_file]
2021-06-09 17:51:35 +02:00
end
def marcel_content_type
Marcel::MimeType.for(csv_file.read, name: csv_file.original_filename, declared_type: csv_file.content_type)
2021-06-09 17:51:35 +02:00
end
def instructeurs_self_management_enabled_params
params.require(:procedure).permit(:instructeurs_self_management_enabled)
end
def routing_enabled_params
{ routing_enabled: params.require(:routing) == 'enable' }
end
def flash_message_for_import(result)
if result.blank?
flash[:notice] = "La liste des instructeurs a été importée avec succès"
else
flash[:alert] = "Import terminé. Cependant les emails suivants ne sont pas pris en compte: #{result.join(', ')}"
end
end
def flash_message_for_invalid_csv
flash[:alert] = "Importation impossible, veuillez importer un csv suivant #{view_context.link_to('ce modèle', "/csv/import-instructeurs-test.csv")} pour une procédure sans routage ou #{view_context.link_to('celui-ci', "/csv/#{I18n.locale}/import-groupe-test.csv")} pour une procédure routée"
end
def create_groups_from_territorial_tdc(tdc_options, stable_id)
tdc_options.each do |label, code|
routing_rule = ds_eq(champ_value(stable_id), constant(code))
@procedure
.groupe_instructeurs
.find_or_create_by(label: label)
.update(instructeurs: [current_administrateur.instructeur], routing_rule:)
end
end
def create_groups_from_drop_down_list_tdc(tdc_options, stable_id)
tdc_options.each do |label|
routing_rule = ds_eq(champ_value(stable_id), constant(label))
@procedure
.groupe_instructeurs
.find_or_create_by(label: label)
.update(instructeurs: [current_administrateur.instructeur], routing_rule:)
end
end
2019-10-17 15:13:50 +02:00
end
end