Merge pull request #6347 from betagouv/feat/4386
ETQ admin/instructeur, je veux envoyer un message à tous les usagers d'un coup
This commit is contained in:
commit
4befa21a30
17 changed files with 239 additions and 17 deletions
|
@ -219,6 +219,51 @@ module Instructeurs
|
|||
@usual_traitement_time_by_month = @procedure.stats_usual_traitement_time_by_month_in_days
|
||||
end
|
||||
|
||||
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 })
|
||||
end
|
||||
|
||||
def create_multiple_commentaire
|
||||
@procedure = procedure
|
||||
errors = []
|
||||
|
||||
email_usagers_dossiers.each do |dossier|
|
||||
commentaire = CommentaireService.build(current_instructeur, dossier, commentaire_params)
|
||||
if commentaire.save
|
||||
commentaire.dossier.update!(last_commentaire_updated_at: Time.zone.now)
|
||||
else
|
||||
errors << dossier.id
|
||||
end
|
||||
end
|
||||
|
||||
valid_dossiers_count = email_usagers_dossiers.count - errors.count
|
||||
create_bulk_message_mail(valid_dossiers_count, Dossier.states.fetch(:brouillon))
|
||||
|
||||
if errors.empty?
|
||||
flash[:notice] = "Tous les messages ont été envoyés avec succès"
|
||||
else
|
||||
flash[:alert] = "Envoi terminé. Cependant #{errors.count} messages n'ont pas été envoyés"
|
||||
end
|
||||
redirect_to instructeur_procedure_path(@procedure)
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
private
|
||||
|
||||
def assign_to_params
|
||||
|
@ -288,5 +333,21 @@ module Instructeurs
|
|||
def current_filters
|
||||
@current_filters ||= procedure_presentation.filters[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)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -74,9 +74,19 @@ module NewAdministrateur
|
|||
.without_group(@groupe_instructeur)
|
||||
end
|
||||
|
||||
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
|
||||
target_group = procedure.groupe_instructeurs.find(params[:target_group])
|
||||
|
||||
reaffecter_bulk_messages(target_group)
|
||||
groupe_instructeur.dossiers.with_discarded.find_each do |dossier|
|
||||
dossier.assign_to_groupe_instructeur(target_group, current_administrateur)
|
||||
end
|
||||
|
|
|
@ -18,10 +18,11 @@ class DossierMailer < ApplicationMailer
|
|||
end
|
||||
end
|
||||
|
||||
def notify_new_answer(dossier)
|
||||
def notify_new_answer(dossier, body = nil)
|
||||
@dossier = dossier
|
||||
@service = dossier.procedure.service
|
||||
@logo_url = attach_logo(dossier.procedure)
|
||||
@body = body
|
||||
|
||||
subject = "Nouveau message pour votre dossier nº #{dossier.id} (#{dossier.procedure.libelle})"
|
||||
|
||||
|
|
18
app/models/bulk_message.rb
Normal file
18
app/models/bulk_message.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: bulk_messages
|
||||
#
|
||||
# id :bigint not null, primary key
|
||||
# body :text not null
|
||||
# dossier_count :integer
|
||||
# dossier_state :string
|
||||
# sent_at :datetime not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# instructeur_id :bigint not null
|
||||
#
|
||||
class BulkMessage < ApplicationRecord
|
||||
belongs_to :instructeur
|
||||
has_one_attached :piece_jointe
|
||||
has_and_belongs_to_many :groupe_instructeurs
|
||||
end
|
|
@ -18,7 +18,7 @@ class Commentaire < ApplicationRecord
|
|||
belongs_to :instructeur, optional: true
|
||||
belongs_to :expert, optional: true
|
||||
|
||||
validate :messagerie_available?, on: :create
|
||||
validate :messagerie_available?, on: :create, unless: -> { dossier.brouillon? }
|
||||
|
||||
has_one_attached :piece_jointe
|
||||
|
||||
|
@ -95,7 +95,7 @@ class Commentaire < ApplicationRecord
|
|||
end
|
||||
|
||||
def notify_user
|
||||
DossierMailer.notify_new_answer(dossier).deliver_later
|
||||
DossierMailer.notify_new_answer(dossier, body).deliver_later
|
||||
end
|
||||
|
||||
def messagerie_available?
|
||||
|
|
|
@ -26,6 +26,7 @@ class Instructeur < ApplicationRecord
|
|||
has_many :previously_followed_dossiers, -> { distinct }, through: :previous_follows, source: :dossier
|
||||
has_many :trusted_device_tokens, dependent: :destroy
|
||||
has_many :archives
|
||||
has_many :bulk_messages, dependent: :destroy
|
||||
|
||||
has_one :user, dependent: :nullify
|
||||
|
||||
|
|
|
@ -3,16 +3,19 @@
|
|||
|
||||
%p
|
||||
Bonjour,
|
||||
|
||||
%p
|
||||
%p
|
||||
Vous avez reçu un
|
||||
%strong nouveau message
|
||||
de la part du service en charge de votre dossier.
|
||||
|
||||
%p
|
||||
- if !@dossier.brouillon?
|
||||
%p
|
||||
Pour consulter le message et y répondre, cliquez sur le bouton ci-dessous :
|
||||
|
||||
= round_button('Lire le message', messagerie_dossier_url(@dossier), :primary)
|
||||
= round_button('Lire le message', messagerie_dossier_url(@dossier), :primary)
|
||||
- else
|
||||
%p{ style: "padding: 8px; color: #333333; background-color: #EEEEEE; font-size: 14px;" }
|
||||
= @body
|
||||
|
||||
= render 'layouts/mailers/signature', service: @service
|
||||
|
||||
|
|
17
app/views/instructeurs/procedures/email_usagers.html.haml
Normal file
17
app/views/instructeurs/procedures/email_usagers.html.haml
Normal file
|
@ -0,0 +1,17 @@
|
|||
- content_for(:title, "Contacter les usagers pour #{@procedure.libelle}")
|
||||
|
||||
= render partial: 'new_administrateur/breadcrumbs',
|
||||
locals: { steps: [link_to(@procedure.libelle, instructeur_procedure_path(@procedure)),
|
||||
'Contacter les usagers en brouillon'] }
|
||||
.messagerie.container
|
||||
- if @bulk_messages.present?
|
||||
%p.mb-2 Messages envoyés aux usagers :
|
||||
- @bulk_messages.each do |bulk_message|
|
||||
%li.mb-1 #{bulk_message.instructeur.email} a envoyé un message le #{bulk_message.sent_at.strftime('%d-%m-%Y')} à #{bulk_message.dossier_count} usagers, #{bulk_message.piece_jointe.blob.present? ? "avec" : "sans"} pièce jointe
|
||||
- if @email_usagers_dossiers.present?
|
||||
%p.notice.mb-2.mt-4 Vous allez envoyer un message à #{pluralize(@dossiers_count, 'personne')} dans les groupes instructeurs : #{@groupe_instructeurs.join(', ')}.
|
||||
= render partial: 'shared/dossiers/messages/form', locals: { commentaire: @commentaire, form_url: create_multiple_commentaire_instructeur_procedure_path(@procedure) }
|
||||
- else
|
||||
.page-title.center
|
||||
%h2 Il n'y a aucun dossier en brouillon dans vos groupes instructeurs
|
||||
|
|
@ -10,6 +10,10 @@
|
|||
.procedure-header
|
||||
%h1= procedure_libelle @procedure
|
||||
= link_to 'gestion des notifications', email_notifications_instructeur_procedure_path(@procedure), class: 'header-link'
|
||||
|
||||
- if @procedure.dossiers.state_brouillon.where(groupe_instructeur: current_instructeur.groupe_instructeur_ids).includes(:groupe_instructeur).present?
|
||||
|
||||
= link_to 'contacter les usagers en brouillon', email_usagers_instructeur_procedure_path(@procedure), class: 'header-link'
|
||||
|
|
||||
= link_to 'statistiques', stats_instructeur_procedure_path(@procedure), class: 'header-link'
|
||||
|
||||
|
|
|
@ -357,6 +357,8 @@ Rails.application.routes.draw do
|
|||
get 'email_notifications'
|
||||
patch 'update_email_notifications'
|
||||
get 'deleted_dossiers'
|
||||
get 'email_usagers'
|
||||
post 'create_multiple_commentaire'
|
||||
|
||||
resources :dossiers, only: [:show], param: :dossier_id do
|
||||
member do
|
||||
|
|
18
db/migrate/20210722083911_create_bulk_message_mails.rb
Normal file
18
db/migrate/20210722083911_create_bulk_message_mails.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
class CreateBulkMessageMails < ActiveRecord::Migration[6.1]
|
||||
def change
|
||||
create_table :bulk_messages do |t|
|
||||
t.text :body, null: false
|
||||
t.integer :dossier_count
|
||||
t.string :dossier_state
|
||||
t.datetime :sent_at, null: false
|
||||
t.bigint :instructeur_id, null: false
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
create_join_table :bulk_messages, :groupe_instructeurs, column_options: { null: true, foreign_key: true } do |t|
|
||||
t.index :bulk_message_id
|
||||
t.index :groupe_instructeur_id, name: :index_bulk_messages_groupe_instructeurs_on_gi_id
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
class AddUniqueIndexToBulkMessagesGroupeInstructeurs < ActiveRecord::Migration[6.1]
|
||||
def change
|
||||
add_index :bulk_messages_groupe_instructeurs, [:bulk_message_id, :groupe_instructeur_id], unique: true, name: :index_bulk_msg_gi_on_bulk_msg_id_and_gi_id
|
||||
end
|
||||
end
|
22
db/schema.rb
22
db/schema.rb
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2021_07_22_133553) do
|
||||
ActiveRecord::Schema.define(version: 2021_07_27_172504) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
@ -156,6 +156,24 @@ ActiveRecord::Schema.define(version: 2021_07_22_133553) do
|
|||
t.datetime "updated_at", null: false
|
||||
end
|
||||
|
||||
create_table "bulk_messages", force: :cascade do |t|
|
||||
t.text "body", null: false
|
||||
t.integer "dossier_count"
|
||||
t.string "dossier_state"
|
||||
t.datetime "sent_at", null: false
|
||||
t.bigint "instructeur_id", null: false
|
||||
t.datetime "created_at", precision: 6, null: false
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
end
|
||||
|
||||
create_table "bulk_messages_groupe_instructeurs", id: false, force: :cascade do |t|
|
||||
t.bigint "bulk_message_id"
|
||||
t.bigint "groupe_instructeur_id"
|
||||
t.index ["bulk_message_id", "groupe_instructeur_id"], name: "index_bulk_msg_gi_on_bulk_msg_id_and_gi_id", unique: true
|
||||
t.index ["bulk_message_id"], name: "index_bulk_messages_groupe_instructeurs_on_bulk_message_id"
|
||||
t.index ["groupe_instructeur_id"], name: "index_bulk_messages_groupe_instructeurs_on_gi_id"
|
||||
end
|
||||
|
||||
create_table "champs", id: :serial, force: :cascade do |t|
|
||||
t.string "value"
|
||||
t.integer "type_de_champ_id"
|
||||
|
@ -767,6 +785,8 @@ ActiveRecord::Schema.define(version: 2021_07_22_133553) do
|
|||
add_foreign_key "attestation_templates", "procedures"
|
||||
add_foreign_key "attestations", "dossiers"
|
||||
add_foreign_key "avis", "experts_procedures"
|
||||
add_foreign_key "bulk_messages_groupe_instructeurs", "bulk_messages"
|
||||
add_foreign_key "bulk_messages_groupe_instructeurs", "groupe_instructeurs"
|
||||
add_foreign_key "champs", "champs", column: "parent_id"
|
||||
add_foreign_key "closed_mails", "procedures"
|
||||
add_foreign_key "commentaires", "dossiers"
|
||||
|
|
|
@ -489,4 +489,48 @@ describe Instructeurs::ProceduresController, type: :controller do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#create_multiple_commentaire' do
|
||||
let(:instructeur) { create(:instructeur) }
|
||||
let!(:gi_p1_1) { GroupeInstructeur.create(label: '1', procedure: procedure) }
|
||||
let!(:gi_p1_2) { GroupeInstructeur.create(label: '2', procedure: procedure) }
|
||||
let(:body) { "avant\napres" }
|
||||
let!(:dossier) { create(:dossier, state: "brouillon", procedure: procedure, groupe_instructeur: procedure.groupe_instructeurs.first) }
|
||||
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.all.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.all.count).to eq(1)
|
||||
expect(BulkMessage.all.first.body).to eq("avant\napres")
|
||||
expect(BulkMessage.all.first.groupe_instructeurs.sort).to match([procedure.groupe_instructeurs.first, 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
|
||||
|
|
|
@ -137,12 +137,15 @@ describe NewAdministrateur::GroupeInstructeursController, type: :controller do
|
|||
|
||||
describe '#reaffecter' do
|
||||
let!(:gi_1_2) { procedure.groupe_instructeurs.create(label: 'groupe instructeur 2') }
|
||||
let!(:gi_1_3) { procedure.groupe_instructeurs.create(label: 'groupe instructeur 3') }
|
||||
let!(:dossier12) { create(:dossier, :en_construction, :with_individual, procedure: procedure, groupe_instructeur: gi_1_1) }
|
||||
let!(:dossier_discarded) do
|
||||
dossier = create(:dossier, :en_construction, :with_individual, procedure: procedure, groupe_instructeur: gi_1_1)
|
||||
dossier.discard!
|
||||
dossier
|
||||
end
|
||||
let!(:instructeur) { create(:instructeur) }
|
||||
let!(:bulk_message) { BulkMessage.create(dossier_count: 2, dossier_state: "brouillon", body: "hello", sent_at: Time.zone.now, groupe_instructeurs: [gi_1_1, gi_1_3], instructeur: instructeur) }
|
||||
|
||||
describe 'when the new group is a group of the procedure' do
|
||||
before do
|
||||
|
@ -153,6 +156,7 @@ describe NewAdministrateur::GroupeInstructeursController, type: :controller do
|
|||
target_group: gi_1_2.id
|
||||
}
|
||||
dossier12.reload
|
||||
bulk_message.reload
|
||||
end
|
||||
|
||||
it { expect(response).to redirect_to(admin_procedure_groupe_instructeurs_path(procedure)) }
|
||||
|
@ -160,6 +164,7 @@ describe NewAdministrateur::GroupeInstructeursController, type: :controller do
|
|||
it { expect(gi_1_2.dossiers.with_discarded.count).to be(2) }
|
||||
it { expect(gi_1_2.dossiers.last.id).to be(dossier12.id) }
|
||||
it { expect(dossier12.groupe_instructeur.id).to be(gi_1_2.id) }
|
||||
it { expect(bulk_message.groupe_instructeurs).to eq([gi_1_2, gi_1_3]) }
|
||||
end
|
||||
|
||||
describe 'when the target group is not a possible group' do
|
||||
|
@ -174,9 +179,11 @@ describe NewAdministrateur::GroupeInstructeursController, type: :controller do
|
|||
}
|
||||
before do
|
||||
dossier12.reload
|
||||
bulk_message.reload
|
||||
end
|
||||
|
||||
it { expect { subject }.to raise_error(ActiveRecord::RecordNotFound) }
|
||||
it { expect(bulk_message.groupe_instructeurs).to eq([gi_1_1, gi_1_3]) }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -26,11 +26,23 @@ RSpec.describe DossierMailer, type: :mailer do
|
|||
it_behaves_like 'a dossier notification'
|
||||
end
|
||||
|
||||
describe '.notify_new_answer' do
|
||||
describe '.notify_new_answer with dossier brouillon' do
|
||||
let(:dossier) { create(:dossier, procedure: build(:simple_procedure)) }
|
||||
|
||||
subject { described_class.notify_new_answer(dossier) }
|
||||
|
||||
it { expect(subject.subject).to include("Nouveau message") }
|
||||
it { expect(subject.subject).to include(dossier.id.to_s) }
|
||||
it { expect(subject.body).not_to include(messagerie_dossier_url(dossier)) }
|
||||
|
||||
it_behaves_like 'a dossier notification'
|
||||
end
|
||||
|
||||
describe '.notify_new_answer with dossier en construction' do
|
||||
let(:dossier) { create(:dossier, state: "en_construction", procedure: build(:simple_procedure)) }
|
||||
|
||||
subject { described_class.notify_new_answer(dossier) }
|
||||
|
||||
it { expect(subject.subject).to include("Nouveau message") }
|
||||
it { expect(subject.subject).to include(dossier.id.to_s) }
|
||||
it { expect(subject.body).to include(messagerie_dossier_url(dossier)) }
|
||||
|
|
|
@ -3,7 +3,6 @@ describe Commentaire do
|
|||
it { is_expected.to have_db_column(:body) }
|
||||
it { is_expected.to have_db_column(:created_at) }
|
||||
it { is_expected.to have_db_column(:updated_at) }
|
||||
it { is_expected.to belong_to(:dossier) }
|
||||
|
||||
describe 'messagerie_available validation' do
|
||||
subject { commentaire.valid?(:create) }
|
||||
|
@ -18,7 +17,7 @@ describe Commentaire do
|
|||
let(:dossier) { create :dossier, :archived }
|
||||
let(:commentaire) { build :commentaire, dossier: dossier }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
context 'on a dossier en_construction' do
|
||||
|
|
Loading…
Reference in a new issue