Merge pull request #3546 from betagouv/fix_2172_notifications
Instructeur : ajoute la possibilité de recevoir une notification par nouveau dossier
This commit is contained in:
commit
ffb5f7c22c
20 changed files with 339 additions and 3 deletions
|
@ -68,6 +68,7 @@ En local, un utilisateur de test est créé automatiquement, avec les identifian
|
|||
FindDubiousProceduresJob.set(cron: "0 0 * * *").perform_later
|
||||
Administrateurs::ActivateBeforeExpirationJob.set(cron: "0 8 * * *").perform_later
|
||||
WarnExpiringDossiersJob.set(cron: "0 0 1 * *").perform_later
|
||||
GestionnaireEmailNotificationJob.set(cron: "0 10 * * 1,2,3,4,5,6").perform_later
|
||||
|
||||
### Voir les emails envoyés en local
|
||||
|
||||
|
|
|
@ -3,3 +3,7 @@
|
|||
.mb-1 {
|
||||
margin-bottom: $default-spacer;
|
||||
}
|
||||
|
||||
.mr-1 {
|
||||
margin-right: $default-spacer;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,12 @@
|
|||
h1 {
|
||||
color: $black;
|
||||
font-size: 22px;
|
||||
margin-bottom: 2 * $default-padding;
|
||||
margin-bottom: 1 * $default-padding;
|
||||
}
|
||||
|
||||
a.notifications {
|
||||
display: inline-block;
|
||||
margin-bottom: 1 * $default-padding;
|
||||
}
|
||||
|
||||
.dossiers-table {
|
||||
|
|
|
@ -186,6 +186,18 @@ module NewGestionnaire
|
|||
end
|
||||
end
|
||||
|
||||
def email_notifications
|
||||
@procedure = procedure
|
||||
@assign_to = assign_to
|
||||
end
|
||||
|
||||
def update_email_notifications
|
||||
assign_to.update!(email_notifications_enabled: params[:assign_to][:email_notifications_enabled])
|
||||
|
||||
flash.notice = 'Vos notifications sont enregistrées.'
|
||||
redirect_to gestionnaire_procedure_path(procedure)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_field(table, column)
|
||||
|
@ -196,6 +208,10 @@ module NewGestionnaire
|
|||
field.values_at('table', 'column').join('/')
|
||||
end
|
||||
|
||||
def assign_to
|
||||
current_gestionnaire.assign_to.find_by(procedure: procedure)
|
||||
end
|
||||
|
||||
def statut
|
||||
@statut ||= (params[:statut].presence || 'a-suivre')
|
||||
end
|
||||
|
|
7
app/jobs/gestionnaire_email_notification_job.rb
Normal file
7
app/jobs/gestionnaire_email_notification_job.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
class GestionnaireEmailNotificationJob < ApplicationJob
|
||||
queue_as :cron
|
||||
|
||||
def perform(*args)
|
||||
NotificationService.send_gestionnaire_email_notification
|
||||
end
|
||||
end
|
|
@ -43,4 +43,11 @@ class GestionnaireMailer < ApplicationMailer
|
|||
|
||||
mail(to: gestionnaire.email, subject: subject)
|
||||
end
|
||||
|
||||
def send_notifications(gestionnaire, data)
|
||||
@data = data
|
||||
subject = "Vous avez du nouveau sur vos démarches"
|
||||
|
||||
mail(to: gestionnaire.email, subject: subject)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,6 +3,8 @@ class AssignTo < ApplicationRecord
|
|||
belongs_to :gestionnaire
|
||||
has_one :procedure_presentation, dependent: :destroy
|
||||
|
||||
scope :with_email_notifications, -> { where(email_notifications_enabled: true) }
|
||||
|
||||
def procedure_presentation_or_default_and_errors
|
||||
errors = reset_procedure_presentation_if_invalid
|
||||
[procedure_presentation || build_procedure_presentation, errors]
|
||||
|
|
|
@ -11,6 +11,10 @@ class Gestionnaire < ApplicationRecord
|
|||
|
||||
has_many :assign_to, dependent: :destroy
|
||||
has_many :procedures, through: :assign_to
|
||||
|
||||
has_many :assign_to_with_email_notifications, -> { with_email_notifications }, class_name: 'AssignTo'
|
||||
has_many :procedures_with_email_notifications, through: :assign_to_with_email_notifications, source: :procedure
|
||||
|
||||
has_many :dossiers, -> { state_not_brouillon }, through: :procedures
|
||||
has_many :follows
|
||||
has_many :followed_dossiers, through: :follows, source: :dossier
|
||||
|
@ -205,6 +209,25 @@ class Gestionnaire < ApplicationRecord
|
|||
trusted_device_token&.token_young?
|
||||
end
|
||||
|
||||
def email_notification_data
|
||||
procedures_with_email_notifications
|
||||
.reduce([]) do |acc, procedure|
|
||||
|
||||
h = {
|
||||
nb_en_construction: procedure.dossiers.en_construction.count,
|
||||
nb_notification: notifications_per_procedure(procedure).count
|
||||
}
|
||||
|
||||
if h[:nb_en_construction] > 0 || h[:nb_notification] > 0
|
||||
h[:procedure_id] = procedure.id
|
||||
h[:procedure_libelle] = procedure.libelle
|
||||
acc << h
|
||||
end
|
||||
|
||||
acc
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def annotations_hash(demande, annotations_privees, avis, messagerie)
|
||||
|
|
19
app/services/notification_service.rb
Normal file
19
app/services/notification_service.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
class NotificationService
|
||||
class << self
|
||||
def send_gestionnaire_email_notification
|
||||
Gestionnaire
|
||||
.includes(assign_to: { procedure: :dossiers })
|
||||
.where(assign_tos: { email_notifications_enabled: true })
|
||||
.find_in_batches { |gestionnaires| send_batch_of_gestionnaires_email_notification(gestionnaires) }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def send_batch_of_gestionnaires_email_notification(gestionnaires)
|
||||
gestionnaires
|
||||
.map { |gestionnaire| [gestionnaire, gestionnaire.email_notification_data] }
|
||||
.reject { |(_gestionnaire, data)| data.empty? }
|
||||
.each { |(gestionnaire, data)| GestionnaireMailer.send_notifications(gestionnaire, data).deliver_later }
|
||||
end
|
||||
end
|
||||
end
|
20
app/views/gestionnaire_mailer/send_notifications.html.haml
Normal file
20
app/views/gestionnaire_mailer/send_notifications.html.haml
Normal file
|
@ -0,0 +1,20 @@
|
|||
- content_for(:title, 'Du nouveau sur vos démarches')
|
||||
|
||||
%p
|
||||
Bonjour,
|
||||
|
||||
%p
|
||||
Vous avez du nouveau sur demarches-simplifiees.fr.
|
||||
|
||||
%ul
|
||||
- @data.each do |datum|
|
||||
%li
|
||||
= link_to(datum[:procedure_libelle], procedure_url(datum[:procedure_id]))
|
||||
- if datum[:nb_en_construction] > 0
|
||||
%br
|
||||
#{datum[:nb_en_construction]} #{'dossier'.pluralize(datum[:nb_en_construction])} en construction
|
||||
- if datum[:nb_notification] > 0
|
||||
%br
|
||||
#{datum[:nb_notification]} #{'notification'.pluralize(datum[:nb_notification])}
|
||||
|
||||
= render partial: "layouts/mailers/signature"
|
|
@ -0,0 +1,24 @@
|
|||
- content_for(:title, "Notifications pour #{@procedure.libelle}")
|
||||
|
||||
= render partial: 'new_administrateur/breadcrumbs',
|
||||
locals: { steps: [link_to(@procedure.libelle, procedure_path(@procedure)),
|
||||
'Notifications'] }
|
||||
|
||||
.container
|
||||
%h1 Notifications par email
|
||||
|
||||
= form_for @assign_to, url: update_email_notifications_gestionnaire_procedure_path(@procedure), html: { class: 'form' } do |form|
|
||||
= form.label :email_notification, "Recevoir une fois par jour, du lundi au samedi vers 10 h, un email de notification me signalant le dépôt de nouveaux dossiers ou des changements sur mes dossiers en cours"
|
||||
|
||||
.radios
|
||||
%label
|
||||
= form.radio_button :email_notifications_enabled, true
|
||||
Oui
|
||||
|
||||
%label
|
||||
= form.radio_button :email_notifications_enabled, false
|
||||
Non
|
||||
|
||||
.send-wrapper
|
||||
= link_to "Revenir à la procédure", gestionnaire_procedure_path(@procedure), class: 'button mr-1'
|
||||
= form.submit "Enregistrer", class: "button primary"
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
.procedure-header
|
||||
%h1= procedure_libelle @procedure
|
||||
= link_to 'configurez vos notifications', email_notifications_gestionnaire_procedure_path(@procedure), class: 'notifications'
|
||||
|
||||
|
||||
%ul.tabs
|
||||
= tab_item('à suivre',
|
||||
|
|
|
@ -315,6 +315,8 @@ Rails.application.routes.draw do
|
|||
post 'add_filter'
|
||||
get 'remove_filter/:statut/:table/:column/:value' => 'procedures#remove_filter', as: 'remove_filter'
|
||||
get 'download_dossiers'
|
||||
get 'email_notifications'
|
||||
patch 'update_email_notifications'
|
||||
|
||||
resources :dossiers, only: [:show], param: :dossier_id do
|
||||
member do
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
class AddEmailNotificationsColumnToAssignTo < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
add_column :assign_tos, :email_notifications_enabled, :boolean, default: false, null: false
|
||||
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_03_11_140926) do
|
||||
ActiveRecord::Schema.define(version: 2019_03_18_154812) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
@ -99,6 +99,7 @@ ActiveRecord::Schema.define(version: 2019_03_11_140926) do
|
|||
t.integer "procedure_id"
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.boolean "email_notifications_enabled", default: false, null: false
|
||||
t.index ["gestionnaire_id", "procedure_id"], name: "index_assign_tos_on_gestionnaire_id_and_procedure_id", unique: true
|
||||
t.index ["gestionnaire_id"], name: "index_assign_tos_on_gestionnaire_id"
|
||||
t.index ["procedure_id"], name: "index_assign_tos_on_procedure_id"
|
||||
|
|
|
@ -336,4 +336,25 @@ describe NewGestionnaire::ProceduresController, type: :controller do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#update_email_notifications' do
|
||||
let(:gestionnaire) { create(:gestionnaire) }
|
||||
let!(:procedure) { create(:procedure, gestionnaires: [gestionnaire]) }
|
||||
|
||||
context "when logged in" do
|
||||
before { sign_in(gestionnaire) }
|
||||
|
||||
it { expect(gestionnaire.procedures_with_email_notifications).to be_empty }
|
||||
|
||||
context 'when the gestionnaire update its preferences' do
|
||||
let(:assign_to) { gestionnaire.assign_to.find_by(procedure: procedure) }
|
||||
|
||||
before do
|
||||
patch :update_email_notifications, params: { procedure_id: procedure.id, assign_to: { id: assign_to.id, email_notifications_enabled: true } }
|
||||
end
|
||||
|
||||
it { expect(gestionnaire.procedures_with_email_notifications).to eq([procedure]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,6 +19,24 @@ class GestionnaireMailerPreview < ActionMailer::Preview
|
|||
GestionnaireMailer.user_to_gestionnaire(gestionnaire.email)
|
||||
end
|
||||
|
||||
def send_notifications
|
||||
data = [
|
||||
{
|
||||
procedure_libelle: 'une superbe démarche',
|
||||
procedure_id: 213,
|
||||
nb_en_construction: 2,
|
||||
nb_notification: 2
|
||||
},
|
||||
{
|
||||
procedure_libelle: 'une démarche incroyable',
|
||||
procedure_id: 213,
|
||||
nb_en_construction: 1,
|
||||
nb_notification: 1
|
||||
}
|
||||
]
|
||||
GestionnaireMailer.send_notifications(gestionnaire, data)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def gestionnaire
|
||||
|
@ -30,6 +48,10 @@ class GestionnaireMailerPreview < ActionMailer::Preview
|
|||
end
|
||||
|
||||
def procedure
|
||||
Procedure.new(id: 15)
|
||||
Procedure.new(id: 15, libelle: 'libelle')
|
||||
end
|
||||
|
||||
def dossier
|
||||
Dossier.new(id: 15, procedure: procedure)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -413,6 +413,53 @@ describe Gestionnaire, type: :model do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#email_notification_data' do
|
||||
let(:gestionnaire) { create(:gestionnaire) }
|
||||
let(:procedure_to_assign) { create(:procedure) }
|
||||
|
||||
before do
|
||||
create(:assign_to, gestionnaire: gestionnaire, procedure: procedure_to_assign, email_notifications_enabled: true)
|
||||
end
|
||||
|
||||
context 'when a dossier in construction exists' do
|
||||
let!(:dossier) { create(:dossier, procedure: procedure_to_assign, state: Dossier.states.fetch(:en_construction)) }
|
||||
|
||||
it do
|
||||
expect(gestionnaire.email_notification_data).to eq([
|
||||
{
|
||||
nb_en_construction: 1,
|
||||
nb_notification: 0,
|
||||
procedure_id: procedure_to_assign.id,
|
||||
procedure_libelle: procedure_to_assign.libelle
|
||||
}
|
||||
])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a notification exists' do
|
||||
before do
|
||||
allow(gestionnaire).to receive(:notifications_per_procedure)
|
||||
.with(procedure_to_assign)
|
||||
.and_return([1, 2, 3])
|
||||
end
|
||||
|
||||
it do
|
||||
expect(gestionnaire.email_notification_data).to eq([
|
||||
{
|
||||
nb_en_construction: 0,
|
||||
nb_notification: 3,
|
||||
procedure_id: procedure_to_assign.id,
|
||||
procedure_libelle: procedure_to_assign.libelle
|
||||
}
|
||||
])
|
||||
end
|
||||
end
|
||||
|
||||
context 'otherwise' do
|
||||
it { expect(gestionnaire.email_notification_data).to eq([]) }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def assign(procedure_to_assign)
|
||||
|
|
63
spec/services/notification_service_spec.rb
Normal file
63
spec/services/notification_service_spec.rb
Normal file
|
@ -0,0 +1,63 @@
|
|||
describe NotificationService do
|
||||
describe '.send_gestionnaire_email_notification' do
|
||||
let(:procedure) { create(:procedure) }
|
||||
|
||||
before do
|
||||
allow(GestionnaireMailer).to receive(:send_notifications)
|
||||
.and_return(double(deliver_later: true))
|
||||
end
|
||||
|
||||
subject { NotificationService.send_gestionnaire_email_notification }
|
||||
|
||||
context 'when a gestionnaire does not enable its email notification' do
|
||||
let!(:dossier) { create(:dossier, :en_construction, procedure: procedure) }
|
||||
let(:gestionnaire) { create(:gestionnaire) }
|
||||
|
||||
before { create(:assign_to, gestionnaire: gestionnaire, procedure: procedure) }
|
||||
|
||||
it do
|
||||
subject
|
||||
expect(GestionnaireMailer).not_to have_received(:send_notifications)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a gestionnaire enables its email_notification on one procedure' do
|
||||
let(:gestionnaire_with_email_notifications) { create(:gestionnaire) }
|
||||
|
||||
before do
|
||||
create(:assign_to,
|
||||
gestionnaire: gestionnaire_with_email_notifications,
|
||||
procedure: procedure,
|
||||
email_notifications_enabled: true)
|
||||
end
|
||||
|
||||
context "when there is no activity on the gestionnaire's procedures" do
|
||||
it do
|
||||
subject
|
||||
expect(GestionnaireMailer).not_to have_received(:send_notifications)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a dossier en construction exists on this procedure' do
|
||||
let!(:dossier) { create(:dossier, :en_construction, procedure: procedure) }
|
||||
|
||||
it do
|
||||
subject
|
||||
expect(GestionnaireMailer).to have_received(:send_notifications)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is a notification on this procedure' do
|
||||
before do
|
||||
allow_any_instance_of(Gestionnaire).to receive(:notifications_per_procedure)
|
||||
.and_return([12])
|
||||
end
|
||||
|
||||
it do
|
||||
subject
|
||||
expect(GestionnaireMailer).to have_received(:send_notifications)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,45 @@
|
|||
require 'rails_helper'
|
||||
|
||||
describe 'gestionnaire_mailer/send_notifications.html.haml', type: :view do
|
||||
let(:gestionnaire) { create(:gestionnaire) }
|
||||
|
||||
before do
|
||||
assign(:data, data)
|
||||
|
||||
render
|
||||
end
|
||||
|
||||
context 'when there is one dossier in contruction' do
|
||||
let(:data) do
|
||||
[
|
||||
{
|
||||
procedure_libelle: 'une superbe démarche',
|
||||
procedure_id: 213,
|
||||
nb_en_construction: 1,
|
||||
nb_notification: 0
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
it { expect(rendered).to have_link('une superbe démarche', href: procedure_url(213)) }
|
||||
it { expect(rendered).to have_text('une superbe démarche') }
|
||||
it { expect(rendered).to have_text('1 dossier en construction') }
|
||||
it { expect(rendered).not_to have_text('notification') }
|
||||
end
|
||||
|
||||
context 'when there is one notification' do
|
||||
let(:data) do
|
||||
[
|
||||
{
|
||||
procedure_libelle: 'une superbe démarche',
|
||||
procedure_id: 213,
|
||||
nb_en_construction: 0,
|
||||
nb_notification: 1
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
it { expect(rendered).not_to have_text('en construction') }
|
||||
it { expect(rendered).to have_text('1 notification') }
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue