From 425d8867be4e9db06ca8ff838a77ac7f07435d0b Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 13 Jul 2023 17:58:11 +0200 Subject: [PATCH] amelioration(bulk_messages): ETQ instructeur, je peux envoyer un message aux usagers ayant un dossier en brouillon --- .../instructeurs/procedures_controller.rb | 54 +++----- app/controllers/users/dossiers_controller.rb | 1 + app/models/bulk_message.rb | 1 - app/models/commentaire.rb | 2 - app/models/dossier.rb | 2 +- app/models/groupe_instructeur.rb | 2 +- .../procedures/email_usagers.html.haml | 49 ++++---- config/locales/models/bulk_message/en.yml | 9 ++ config/locales/models/bulk_message/fr.yml | 9 ++ .../views/instructeurs/procedures/en.yml | 7 +- .../views/instructeurs/procedures/fr.yml | 5 +- .../procedures_controller_spec.rb | 115 +++++++++++------- 12 files changed, 145 insertions(+), 111 deletions(-) create mode 100644 config/locales/models/bulk_message/en.yml create mode 100644 config/locales/models/bulk_message/fr.yml diff --git a/app/controllers/instructeurs/procedures_controller.rb b/app/controllers/instructeurs/procedures_controller.rb index 5642d1fda..02640933e 100644 --- a/app/controllers/instructeurs/procedures_controller.rb +++ b/app/controllers/instructeurs/procedures_controller.rb @@ -224,19 +224,18 @@ module Instructeurs def email_usagers @procedure = procedure - @commentaire = Commentaire.new - @email_usagers_dossiers = email_usagers_dossiers - @dossiers_count = @email_usagers_dossiers.count - @groupe_instructeurs = email_usagers_groupe_instructeurs_label - @bulk_messages = BulkMessage.includes(:groupe_instructeurs).where(groupe_instructeurs: { id: current_instructeur.groupe_instructeur_ids, procedure: procedure }) + @bulk_messages = BulkMessage.includes(:groupe_instructeurs).where(groupe_instructeurs: { procedure: procedure }) + @bulk_message = current_instructeur.bulk_messages.build + @dossiers_without_groupe_count = procedure.dossiers.state_brouillon.for_groupe_instructeur(nil).count end def create_multiple_commentaire @procedure = procedure errors = [] - - email_usagers_dossiers.each do |dossier| - commentaire = CommentaireService.create(current_instructeur, dossier, commentaire_params) + bulk_message = current_instructeur.bulk_messages.build(bulk_message_params) + dossiers = procedure.dossiers.state_brouillon.for_groupe_instructeur(nil) + dossiers.each do |dossier| + commentaire = CommentaireService.create(current_instructeur, dossier, bulk_message_params.except(:targets)) if commentaire.errors.empty? commentaire.dossier.update!(last_commentaire_updated_at: Time.zone.now) else @@ -244,8 +243,15 @@ module Instructeurs end end - valid_dossiers_count = email_usagers_dossiers.count - errors.count - create_bulk_message_mail(valid_dossiers_count, Dossier.states.fetch(:brouillon)) + valid_dossiers_count = dossiers.count - errors.count + bulk_message.assign_attributes( + dossier_count: valid_dossiers_count, + dossier_state: Dossier.states.fetch(:brouillon), + sent_at: Time.zone.now, + instructeur_id: current_instructeur.id, + groupe_instructeurs: GroupeInstructeur.for_dossiers(dossiers) + ) + bulk_message.save! if errors.empty? flash[:notice] = "Tous les messages ont été envoyés avec succès" @@ -262,18 +268,6 @@ module Instructeurs private - def create_bulk_message_mail(dossier_count, dossier_state) - BulkMessage.create( - dossier_count: dossier_count, - dossier_state: dossier_state, - body: commentaire_params[:body], - sent_at: Time.zone.now, - instructeur_id: current_instructeur.id, - piece_jointe: commentaire_params[:piece_jointe], - groupe_instructeurs: email_usagers_groupe_instructeurs - ) - end - def assign_to_params params.require(:assign_to) .permit(:instant_expert_avis_email_notifications_enabled, :instant_email_dossier_notifications_enabled, :instant_email_message_notifications_enabled, :daily_email_notifications_enabled, :weekly_email_notifications_enabled) @@ -355,20 +349,8 @@ module Instructeurs @current_filters ||= procedure_presentation.filters.fetch(statut, []) end - def email_usagers_dossiers - procedure.dossiers.state_brouillon.where(groupe_instructeur: current_instructeur.groupe_instructeur_ids).includes(:groupe_instructeur) - end - - def email_usagers_groupe_instructeurs_label - email_usagers_dossiers.map(&:groupe_instructeur).uniq.map(&:label) - end - - def email_usagers_groupe_instructeurs - email_usagers_dossiers.map(&:groupe_instructeur).uniq - end - - def commentaire_params - params.require(:commentaire).permit(:body, :piece_jointe) + def bulk_message_params + params.require(:bulk_message).permit(:body) end end end diff --git a/app/controllers/users/dossiers_controller.rb b/app/controllers/users/dossiers_controller.rb index 92e9c25b5..91419ad23 100644 --- a/app/controllers/users/dossiers_controller.rb +++ b/app/controllers/users/dossiers_controller.rb @@ -541,6 +541,7 @@ module Users def update_dossier_and_compute_errors errors = [] + @dossier.assign_attributes(champs_public_params) if @dossier.champs_public_all.any?(&:changed_for_autosave?) @dossier.last_champ_updated_at = Time.zone.now diff --git a/app/models/bulk_message.rb b/app/models/bulk_message.rb index 6de6905a3..8f54de120 100644 --- a/app/models/bulk_message.rb +++ b/app/models/bulk_message.rb @@ -1,5 +1,4 @@ class BulkMessage < ApplicationRecord belongs_to :instructeur has_and_belongs_to_many :groupe_instructeurs, -> { order(:label) } - has_one_attached :piece_jointe end diff --git a/app/models/commentaire.rb b/app/models/commentaire.rb index 4cac74bf1..4edbb0110 100644 --- a/app/models/commentaire.rb +++ b/app/models/commentaire.rb @@ -1,8 +1,6 @@ class Commentaire < ApplicationRecord include Discard::Model - belongs_to :dossier, inverse_of: :commentaires, touch: true, optional: false - belongs_to :instructeur, inverse_of: :commentaires, optional: true belongs_to :expert, inverse_of: :commentaires, optional: true has_one :dossier_correction, inverse_of: :commentaire, dependent: :nullify diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 12050c35a..d55bf99e2 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -215,7 +215,7 @@ class Dossier < ApplicationRecord } scope :for_procedure_preview, -> { where(for_procedure_preview: true) } scope :for_editing_fork, -> { where.not(editing_fork_origin_id: nil) } - + scope :for_groupe_instructeur, -> (groupe_instructeurs) { where(groupe_instructeur: groupe_instructeurs) } scope :order_by_updated_at, -> (order = :desc) { order(updated_at: order) } scope :order_by_created_at, -> (order = :asc) { order(depose_at: order, created_at: order, id: order) } scope :updated_since, -> (since) { where('dossiers.updated_at >= ?', since) } diff --git a/app/models/groupe_instructeur.rb b/app/models/groupe_instructeur.rb index 87224cf7b..cad3b2638 100644 --- a/app/models/groupe_instructeur.rb +++ b/app/models/groupe_instructeur.rb @@ -30,7 +30,7 @@ class GroupeInstructeur < ApplicationRecord scope :for_api_v2, -> { includes(procedure: [:administrateurs]) } scope :active, -> { where(closed: false) } scope :closed, -> { where(closed: true) } - + scope :for_dossiers, -> (dossiers) { joins(:dossiers).where(dossiers: dossiers).distinct(:id) } def add(instructeur) return if instructeur.nil? return if in?(instructeur.groupe_instructeurs) diff --git a/app/views/instructeurs/procedures/email_usagers.html.haml b/app/views/instructeurs/procedures/email_usagers.html.haml index 115a47a33..61af93444 100644 --- a/app/views/instructeurs/procedures/email_usagers.html.haml +++ b/app/views/instructeurs/procedures/email_usagers.html.haml @@ -3,30 +3,35 @@ = render partial: 'administrateurs/breadcrumbs', locals: { steps: [[@procedure.libelle.truncate_words(10), instructeur_procedure_path(@procedure)], [t('.contact_users')]] } -.messagerie.container - - if @email_usagers_dossiers.present? - %p.notice.mb-2.mt-4 - = t('.notice', dossiers_count: pluralize(@dossiers_count, 'personne'), groupe_instructeurs: @groupe_instructeurs.join(', ')) +.messagerie.fr-container + %h1 Contacter les usagers + %p.fr-highlight + = t('.hint', count: @dossiers_without_groupe_count).html_safe - = render partial: 'shared/dossiers/messages/form', locals: { commentaire: @commentaire, form_url: create_multiple_commentaire_instructeur_procedure_path(@procedure), disable_piece_jointe: true } + - if @dossiers_without_groupe_count.positive? + = form_for(@bulk_message, url: create_multiple_commentaire_instructeur_procedure_path, html: { data: { controller: 'persisted-form', persisted_form_key_value: dom_id(@procedure, :bulk_message) } }) do |f| - - if @bulk_messages.present? - %section.list-avis.mt-8 - %h1.tab-title - Messages envoyés précédemment - %span.fr-badge= @bulk_messages.count + %p.mandatory-explanation= t('asterisk_html', scope: [:utils]) - %ul - - @bulk_messages.each do |message| - %li.one-avis.flex.align-start - .width-100 - %h2.claimant - %span.email= message.instructeur.email - %span.date message envoyé à #{@dossiers_count} usagers le #{message.sent_at.strftime('%d/%m/%y à %H:%M')} - %p= message.body - .answer.flex.align-start - - if message.piece_jointe.present? - = render Attachment::ShowComponent.new(attachment: message.piece_jointe.attachment) + = render Dsfr::InputComponent.new(form: f, attribute: :body, input_type: :text_area, opts: { rows: 5, placeholder: t('views.shared.dossiers.messages.form.write_message_placeholder'), title: t('views.shared.dossiers.messages.form.write_message_placeholder'), class: 'fr-input message-textarea'}) + + .fr-mt-3w + = f.submit t('views.shared.dossiers.messages.form.send_message'), class: 'fr-btn', data: { disable: true } - else .page-title.center - %h2 Il n’y a aucun dossier en brouillon dans vos groupes d’instructeurs + %h2 Il n’y a aucun dossier en brouillon + + - if @bulk_messages.present? + %section.list-avis.mt-8 + %h1.tab-title + Messages envoyés précédemment + %span.fr-badge= @bulk_messages.count + + %ul + - @bulk_messages.each do |message| + %li.one-avis.flex.align-start + .width-100 + %h2.claimant + %span.email= message.instructeur.email + %span.date message envoyé à #{@dossiers_count} usagers le #{message.sent_at.strftime('%d/%m/%y à %H:%M')} + %p= message.body diff --git a/config/locales/models/bulk_message/en.yml b/config/locales/models/bulk_message/en.yml new file mode 100644 index 000000000..c1a59b743 --- /dev/null +++ b/config/locales/models/bulk_message/en.yml @@ -0,0 +1,9 @@ +en: + activerecord: + models: + bulk_message: + one: "Bulk message to users" + other: "Bulk message to users" + attributes: + bulk_message: + body: Content diff --git a/config/locales/models/bulk_message/fr.yml b/config/locales/models/bulk_message/fr.yml new file mode 100644 index 000000000..04c830d1f --- /dev/null +++ b/config/locales/models/bulk_message/fr.yml @@ -0,0 +1,9 @@ +fr: + activerecord: + models: + bulk_message: + one: "Message aux usagers" + other: "Messages aux usagers" + attributes: + bulk_message: + body: "Message envoyé aux destinataires :" diff --git a/config/locales/views/instructeurs/procedures/en.yml b/config/locales/views/instructeurs/procedures/en.yml index dab0dce11..e8d267ca7 100644 --- a/config/locales/views/instructeurs/procedures/en.yml +++ b/config/locales/views/instructeurs/procedures/en.yml @@ -12,8 +12,11 @@ en: copy_link_button: Copy the procedure link to clipboard email_usagers: contact_users: Contact users (draft) - notice: "You will send a message to %{dossiers_count} whose files are in draft, in the instructor groups : %{groupe_instructeurs}." + hint: + zero: "There is no user with a draft." + one: "You will send a message to 1 user." + other: "You will send a message to %{count} users." administrators_list: title: "%{procedure_libelle} - n°%{procedure_id} - administrators" - stats: + stats: title: Statistics diff --git a/config/locales/views/instructeurs/procedures/fr.yml b/config/locales/views/instructeurs/procedures/fr.yml index e2d643394..886464b85 100644 --- a/config/locales/views/instructeurs/procedures/fr.yml +++ b/config/locales/views/instructeurs/procedures/fr.yml @@ -12,7 +12,10 @@ fr: copy_link_button: Copier le lien de la démarche dans le presse-papiers email_usagers: contact_users: Contacter les usagers (brouillon) - notice: "Vous allez envoyer un message à %{dossiers_count} dont les dossiers sont en brouillon, dans les groupes instructeurs : %{groupe_instructeurs}." + hint: + zero: "Aucun usager n'a de dossier en brouillon." + one: "Vous allez envoyer un message à 1 usager ayant un dossier en brouillon." + other: "Vous allez envoyer un message à %{count} usagers ayant un dossier en brouillon." administrators_list: title: "%{procedure_libelle} - n°%{procedure_id} - administrateurs" stats: diff --git a/spec/controllers/instructeurs/procedures_controller_spec.rb b/spec/controllers/instructeurs/procedures_controller_spec.rb index a6b7614f0..ee7b4c19f 100644 --- a/spec/controllers/instructeurs/procedures_controller_spec.rb +++ b/spec/controllers/instructeurs/procedures_controller_spec.rb @@ -486,6 +486,76 @@ describe Instructeurs::ProceduresController, type: :controller do end end + describe '#email_usagers' do + let(:instructeur) { create(:instructeur) } + let(:procedure) { create(:procedure) } + let!(:gi_1) { create(:groupe_instructeur, label: 'gi_1', procedure: procedure, instructeurs: [instructeur]) } + let!(:dossier_without_groupe) { create(:dossier, :brouillon, procedure: procedure, groupe_instructeur: nil) } + + subject do + get :email_usagers, params: { procedure_id: procedure.id } + end + + it { is_expected.to redirect_to(new_user_session_path) } + + context 'when authenticated' do + before { sign_in(instructeur.user) } + it 'lists dossier brouillon in groupe_instructeur as well as dossiers_brouillon outside groupe_instructeur' do + is_expected.to have_http_status(200) + expect(assigns(:dossiers_without_groupe_count)).to eq(1) + end + end + end + + describe '#create_multiple_commentaire' do + let(:instructeur) { create(:instructeur) } + let!(:gi_p1_1) { create(:groupe_instructeur, label: '1', procedure: procedure, instructeurs: [instructeur]) } + let!(:gi_p1_2) { create(:groupe_instructeur, label: '2', procedure: procedure) } + let(:body) { "avant\napres" } + let(:bulk_message) { BulkMessage.first } + let!(:dossier) { create(:dossier, state: "brouillon", procedure: procedure, groupe_instructeur: gi_p1_1) } + let!(:dossier_2) { create(:dossier, state: "brouillon", procedure: procedure, groupe_instructeur: gi_p1_1) } + let!(:dossier_3) { create(:dossier, state: "brouillon", procedure: procedure, groupe_instructeur: gi_p1_2) } + let!(:procedure) { create(:procedure, :published, instructeurs: [instructeur]) } + + before do + sign_in(instructeur.user) + procedure + end + + let!(:dossier_4) { create(:dossier, state: "brouillon", procedure: procedure, groupe_instructeur: nil) } + before do + post :create_multiple_commentaire, + params: { + procedure_id: procedure.id, + bulk_message: { body: body } + } + end + + it "creates a commentaire for 1 dossiers" do + expect(Commentaire.count).to eq(1) + expect(dossier.commentaires).to eq([]) + expect(dossier_2.commentaires).to eq([]) + expect(dossier_3.commentaires).to eq([]) + expect(dossier_4.commentaires.first.body).to eq("avant\napres") + end + + it "creates a Bulk Message for 2 groupes instructeurs" do + expect(BulkMessage.count).to eq(1) + expect(bulk_message.body).to eq("avant\napres") + expect(bulk_message.groupe_instructeurs).to be_empty + end + + it "creates a flash notice" do + expect(flash.notice).to be_present + expect(flash.notice).to eq("Tous les messages ont été envoyés avec succès") + end + + it "redirect to instructeur_procedure_path" do + expect(response).to redirect_to instructeur_procedure_path(procedure) + end + end + describe '#download_export' do let(:instructeur) { create(:instructeur) } let!(:procedure) { create(:procedure) } @@ -563,49 +633,4 @@ describe Instructeurs::ProceduresController, type: :controller do it { is_expected.to have_http_status(:forbidden) } end end - - describe '#create_multiple_commentaire' do - let(:instructeur) { create(:instructeur) } - let!(:gi_p1_1) { create(:groupe_instructeur, label: '1', procedure: procedure) } - let!(:gi_p1_2) { create(:groupe_instructeur, label: '2', procedure: procedure) } - let(:body) { "avant\napres" } - let(:bulk_message) { BulkMessage.first } - let!(:dossier) { create(:dossier, state: "brouillon", procedure: procedure, groupe_instructeur: gi_p1_1) } - let!(:dossier_2) { create(:dossier, state: "brouillon", procedure: procedure, groupe_instructeur: gi_p1_1) } - let!(:dossier_3) { create(:dossier, state: "brouillon", procedure: procedure, groupe_instructeur: gi_p1_2) } - let!(:procedure) { create(:procedure, :published, instructeurs: [instructeur]) } - - before do - sign_in(instructeur.user) - instructeur.groupe_instructeurs << gi_p1_1 - procedure - post :create_multiple_commentaire, - params: { - procedure_id: procedure.id, - commentaire: { body: body } - } - end - - it "creates a commentaire for 2 dossiers" do - expect(Commentaire.count).to eq(2) - expect(dossier.commentaires.first.body).to eq("avant\napres") - expect(dossier_2.commentaires.first.body).to eq("avant\napres") - expect(dossier_3.commentaires).to eq([]) - end - - it "creates a Bulk Message for 2 groupes instructeurs" do - expect(BulkMessage.count).to eq(1) - expect(bulk_message.body).to eq("avant\napres") - expect(bulk_message.groupe_instructeurs).to match([gi_p1_1]) - end - - it "creates a flash notice" do - expect(flash.notice).to be_present - expect(flash.notice).to eq("Tous les messages ont été envoyés avec succès") - end - - it "redirect to instructeur_procedure_path" do - expect(response).to redirect_to instructeur_procedure_path(procedure) - end - end end