Merge pull request #9314 from mfo/US/bulk-message-without-groupe-instructeur
ETQ instructeur, je peux envoyer un message a un utilisateur ayant un dossier qui n'a pas encore de groupe d'instructeur
This commit is contained in:
commit
493a948c59
23 changed files with 261 additions and 158 deletions
|
@ -4,7 +4,6 @@ class API::Public::V1::DossiersController < API::Public::V1::BaseController
|
||||||
def create
|
def create
|
||||||
dossier = Dossier.new(
|
dossier = Dossier.new(
|
||||||
revision: @procedure.active_revision,
|
revision: @procedure.active_revision,
|
||||||
groupe_instructeur: @procedure.defaut_groupe_instructeur_for_new_dossier,
|
|
||||||
state: Dossier.states.fetch(:brouillon),
|
state: Dossier.states.fetch(:brouillon),
|
||||||
prefilled: true
|
prefilled: true
|
||||||
)
|
)
|
||||||
|
|
|
@ -224,19 +224,18 @@ module Instructeurs
|
||||||
|
|
||||||
def email_usagers
|
def email_usagers
|
||||||
@procedure = procedure
|
@procedure = procedure
|
||||||
@commentaire = Commentaire.new
|
@bulk_messages = BulkMessage.includes(:groupe_instructeurs).where(groupe_instructeurs: { procedure: procedure })
|
||||||
@email_usagers_dossiers = email_usagers_dossiers
|
@bulk_message = current_instructeur.bulk_messages.build
|
||||||
@dossiers_count = @email_usagers_dossiers.count
|
@dossiers_without_groupe_count = procedure.dossiers.state_brouillon.for_groupe_instructeur(nil).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 })
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_multiple_commentaire
|
def create_multiple_commentaire
|
||||||
@procedure = procedure
|
@procedure = procedure
|
||||||
errors = []
|
errors = []
|
||||||
|
bulk_message = current_instructeur.bulk_messages.build(bulk_message_params)
|
||||||
email_usagers_dossiers.each do |dossier|
|
dossiers = procedure.dossiers.state_brouillon.for_groupe_instructeur(nil)
|
||||||
commentaire = CommentaireService.create(current_instructeur, dossier, commentaire_params)
|
dossiers.each do |dossier|
|
||||||
|
commentaire = CommentaireService.create(current_instructeur, dossier, bulk_message_params.except(:targets))
|
||||||
if commentaire.errors.empty?
|
if commentaire.errors.empty?
|
||||||
commentaire.dossier.update!(last_commentaire_updated_at: Time.zone.now)
|
commentaire.dossier.update!(last_commentaire_updated_at: Time.zone.now)
|
||||||
else
|
else
|
||||||
|
@ -244,8 +243,15 @@ module Instructeurs
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
valid_dossiers_count = email_usagers_dossiers.count - errors.count
|
valid_dossiers_count = dossiers.count - errors.count
|
||||||
create_bulk_message_mail(valid_dossiers_count, Dossier.states.fetch(:brouillon))
|
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?
|
if errors.empty?
|
||||||
flash[:notice] = "Tous les messages ont été envoyés avec succès"
|
flash[:notice] = "Tous les messages ont été envoyés avec succès"
|
||||||
|
@ -262,18 +268,6 @@ module Instructeurs
|
||||||
|
|
||||||
private
|
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
|
def assign_to_params
|
||||||
params.require(:assign_to)
|
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)
|
.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, [])
|
@current_filters ||= procedure_presentation.filters.fetch(statut, [])
|
||||||
end
|
end
|
||||||
|
|
||||||
def email_usagers_dossiers
|
def bulk_message_params
|
||||||
procedure.dossiers.state_brouillon.where(groupe_instructeur: current_instructeur.groupe_instructeur_ids).includes(:groupe_instructeur)
|
params.require(:bulk_message).permit(:body)
|
||||||
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -96,7 +96,6 @@ module Users
|
||||||
def build_prefilled_dossier
|
def build_prefilled_dossier
|
||||||
@prefilled_dossier = Dossier.new(
|
@prefilled_dossier = Dossier.new(
|
||||||
revision: @revision,
|
revision: @revision,
|
||||||
groupe_instructeur: @procedure.defaut_groupe_instructeur_for_new_dossier,
|
|
||||||
state: Dossier.states.fetch(:brouillon),
|
state: Dossier.states.fetch(:brouillon),
|
||||||
prefilled: true
|
prefilled: true
|
||||||
)
|
)
|
||||||
|
|
|
@ -375,7 +375,6 @@ module Users
|
||||||
|
|
||||||
dossier = Dossier.new(
|
dossier = Dossier.new(
|
||||||
revision: params[:brouillon] ? procedure.draft_revision : procedure.active_revision,
|
revision: params[:brouillon] ? procedure.draft_revision : procedure.active_revision,
|
||||||
groupe_instructeur: procedure.defaut_groupe_instructeur_for_new_dossier,
|
|
||||||
user: current_user,
|
user: current_user,
|
||||||
state: Dossier.states.fetch(:brouillon)
|
state: Dossier.states.fetch(:brouillon)
|
||||||
)
|
)
|
||||||
|
@ -542,6 +541,7 @@ module Users
|
||||||
|
|
||||||
def update_dossier_and_compute_errors
|
def update_dossier_and_compute_errors
|
||||||
errors = []
|
errors = []
|
||||||
|
|
||||||
@dossier.assign_attributes(champs_public_params)
|
@dossier.assign_attributes(champs_public_params)
|
||||||
if @dossier.champs_public_all.any?(&:changed_for_autosave?)
|
if @dossier.champs_public_all.any?(&:changed_for_autosave?)
|
||||||
@dossier.last_champ_updated_at = Time.zone.now
|
@dossier.last_champ_updated_at = Time.zone.now
|
||||||
|
@ -559,7 +559,6 @@ module Users
|
||||||
@dossier.valid?(**submit_validation_options)
|
@dossier.valid?(**submit_validation_options)
|
||||||
errors += format_errors(errors: @dossier.errors)
|
errors += format_errors(errors: @dossier.errors)
|
||||||
errors += format_errors(errors: @dossier.check_mandatory_and_visible_champs)
|
errors += format_errors(errors: @dossier.check_mandatory_and_visible_champs)
|
||||||
|
|
||||||
errors
|
errors
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -39,10 +39,10 @@ module ProcedureHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_send_groupe_message?(procedure)
|
def can_send_groupe_message?(procedure)
|
||||||
procedure.dossiers
|
total_groupe_instructeur_on_procedure = procedure.groupe_instructeurs.active.count
|
||||||
.state_brouillon
|
total_groupe_instructeur_on_instructeur = current_instructeur.groupe_instructeurs.active.where(procedure: procedure).count
|
||||||
.includes(:groupe_instructeur)
|
|
||||||
.exists?(groupe_instructeur: current_instructeur.groupe_instructeurs)
|
total_groupe_instructeur_on_procedure == total_groupe_instructeur_on_instructeur
|
||||||
end
|
end
|
||||||
|
|
||||||
def url_or_email_to_lien_dpo(procedure)
|
def url_or_email_to_lien_dpo(procedure)
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
class BulkMessage < ApplicationRecord
|
class BulkMessage < ApplicationRecord
|
||||||
belongs_to :instructeur
|
belongs_to :instructeur
|
||||||
has_and_belongs_to_many :groupe_instructeurs, -> { order(:label) }
|
has_and_belongs_to_many :groupe_instructeurs, -> { order(:label) }
|
||||||
has_one_attached :piece_jointe
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
class Commentaire < ApplicationRecord
|
class Commentaire < ApplicationRecord
|
||||||
include Discard::Model
|
include Discard::Model
|
||||||
|
|
||||||
belongs_to :dossier, inverse_of: :commentaires, touch: true, optional: false
|
belongs_to :dossier, inverse_of: :commentaires, touch: true, optional: false
|
||||||
|
|
||||||
belongs_to :instructeur, inverse_of: :commentaires, optional: true
|
belongs_to :instructeur, inverse_of: :commentaires, optional: true
|
||||||
belongs_to :expert, inverse_of: :commentaires, optional: true
|
belongs_to :expert, inverse_of: :commentaires, optional: true
|
||||||
has_one :dossier_correction, inverse_of: :commentaire, dependent: :nullify
|
has_one :dossier_correction, inverse_of: :commentaire, dependent: :nullify
|
||||||
|
|
|
@ -76,7 +76,8 @@ module DossierCloneConcern
|
||||||
end
|
end
|
||||||
|
|
||||||
def clone(user: nil, fork: false)
|
def clone(user: nil, fork: false)
|
||||||
dossier_attributes = [:autorisation_donnees, :revision_id, :groupe_instructeur_id]
|
dossier_attributes = [:autorisation_donnees, :revision_id]
|
||||||
|
dossier_attributes += [:groupe_instructeur_id] if fork
|
||||||
relationships = [:individual, :etablissement]
|
relationships = [:individual, :etablissement]
|
||||||
|
|
||||||
cloned_champs = champs
|
cloned_champs = champs
|
||||||
|
@ -95,7 +96,6 @@ module DossierCloneConcern
|
||||||
|
|
||||||
kopy.user = user || original.user
|
kopy.user = user || original.user
|
||||||
kopy.state = Dossier.states.fetch(:brouillon)
|
kopy.state = Dossier.states.fetch(:brouillon)
|
||||||
|
|
||||||
kopy.champs = cloned_champs.values.map do |(_, champ)|
|
kopy.champs = cloned_champs.values.map do |(_, champ)|
|
||||||
champ.dossier = kopy
|
champ.dossier = kopy
|
||||||
champ.parent = cloned_champs[champ.parent_id].second if champ.child?
|
champ.parent = cloned_champs[champ.parent_id].second if champ.child?
|
||||||
|
|
|
@ -215,7 +215,7 @@ class Dossier < ApplicationRecord
|
||||||
}
|
}
|
||||||
scope :for_procedure_preview, -> { where(for_procedure_preview: true) }
|
scope :for_procedure_preview, -> { where(for_procedure_preview: true) }
|
||||||
scope :for_editing_fork, -> { where.not(editing_fork_origin_id: nil) }
|
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_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 :order_by_created_at, -> (order = :asc) { order(depose_at: order, created_at: order, id: order) }
|
||||||
scope :updated_since, -> (since) { where('dossiers.updated_at >= ?', since) }
|
scope :updated_since, -> (since) { where('dossiers.updated_at >= ?', since) }
|
||||||
|
@ -852,7 +852,6 @@ class Dossier < ApplicationRecord
|
||||||
.passer_en_construction
|
.passer_en_construction
|
||||||
.processed_at
|
.processed_at
|
||||||
save!
|
save!
|
||||||
|
|
||||||
MailTemplatePresenterService.create_commentaire_for_state(self)
|
MailTemplatePresenterService.create_commentaire_for_state(self)
|
||||||
NotificationMailer.send_en_construction_notification(self).deliver_later
|
NotificationMailer.send_en_construction_notification(self).deliver_later
|
||||||
procedure.compute_dossiers_count
|
procedure.compute_dossiers_count
|
||||||
|
|
|
@ -30,7 +30,7 @@ class GroupeInstructeur < ApplicationRecord
|
||||||
scope :for_api_v2, -> { includes(procedure: [:administrateurs]) }
|
scope :for_api_v2, -> { includes(procedure: [:administrateurs]) }
|
||||||
scope :active, -> { where(closed: false) }
|
scope :active, -> { where(closed: false) }
|
||||||
scope :closed, -> { where(closed: true) }
|
scope :closed, -> { where(closed: true) }
|
||||||
|
scope :for_dossiers, -> (dossiers) { joins(:dossiers).where(dossiers: dossiers).distinct(:id) }
|
||||||
def add(instructeur)
|
def add(instructeur)
|
||||||
return if instructeur.nil?
|
return if instructeur.nil?
|
||||||
return if in?(instructeur.groupe_instructeurs)
|
return if in?(instructeur.groupe_instructeurs)
|
||||||
|
|
|
@ -666,12 +666,6 @@ class Procedure < ApplicationRecord
|
||||||
routing_enabled? || instructeurs_self_management_enabled?
|
routing_enabled? || instructeurs_self_management_enabled?
|
||||||
end
|
end
|
||||||
|
|
||||||
def defaut_groupe_instructeur_for_new_dossier
|
|
||||||
if !routing_enabled? || feature_enabled?(:procedure_routage_api)
|
|
||||||
defaut_groupe_instructeur
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def groupe_instructeurs_but_defaut
|
def groupe_instructeurs_but_defaut
|
||||||
groupe_instructeurs - [defaut_groupe_instructeur]
|
groupe_instructeurs - [defaut_groupe_instructeur]
|
||||||
end
|
end
|
||||||
|
|
|
@ -150,7 +150,7 @@ class ProcedureRevision < ApplicationRecord
|
||||||
|
|
||||||
def dossier_for_preview(user)
|
def dossier_for_preview(user)
|
||||||
dossier = Dossier
|
dossier = Dossier
|
||||||
.create_with(groupe_instructeur: procedure.defaut_groupe_instructeur_for_new_dossier, autorisation_donnees: true)
|
.create_with(autorisation_donnees: true)
|
||||||
.find_or_initialize_by(revision: self, user: user, for_procedure_preview: true, state: Dossier.states.fetch(:brouillon))
|
.find_or_initialize_by(revision: self, user: user, for_procedure_preview: true, state: Dossier.states.fetch(:brouillon))
|
||||||
|
|
||||||
if dossier.new_record?
|
if dossier.new_record?
|
||||||
|
|
|
@ -3,30 +3,35 @@
|
||||||
= render partial: 'administrateurs/breadcrumbs',
|
= render partial: 'administrateurs/breadcrumbs',
|
||||||
locals: { steps: [[@procedure.libelle.truncate_words(10), instructeur_procedure_path(@procedure)],
|
locals: { steps: [[@procedure.libelle.truncate_words(10), instructeur_procedure_path(@procedure)],
|
||||||
[t('.contact_users')]] }
|
[t('.contact_users')]] }
|
||||||
.messagerie.container
|
.messagerie.fr-container
|
||||||
- if @email_usagers_dossiers.present?
|
%h1 Contacter les usagers
|
||||||
%p.notice.mb-2.mt-4
|
%p.fr-highlight
|
||||||
= t('.notice', dossiers_count: pluralize(@dossiers_count, 'personne'), groupe_instructeurs: @groupe_instructeurs.join(', '))
|
= 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?
|
%p.mandatory-explanation= t('asterisk_html', scope: [:utils])
|
||||||
%section.list-avis.mt-8
|
|
||||||
%h1.tab-title
|
|
||||||
Messages envoyés précédemment
|
|
||||||
%span.fr-badge= @bulk_messages.count
|
|
||||||
|
|
||||||
%ul
|
= 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'})
|
||||||
- @bulk_messages.each do |message|
|
|
||||||
%li.one-avis.flex.align-start
|
.fr-mt-3w
|
||||||
.width-100
|
= f.submit t('views.shared.dossiers.messages.form.send_message'), class: 'fr-btn', data: { disable: true }
|
||||||
%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)
|
|
||||||
- else
|
- else
|
||||||
.page-title.center
|
.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
|
||||||
|
|
|
@ -1,20 +1,15 @@
|
||||||
= render NestedForms::FormOwnerComponent.new
|
= render NestedForms::FormOwnerComponent.new
|
||||||
= form_for(commentaire, url: form_url, html: { multipart: true, data: { controller: 'persisted-form', persisted_form_key_value: @dossier.present? ? dom_id(@dossier) : dom_id(@procedure, :bulk_message) } }) do |f|
|
= form_for(commentaire, url: form_url, html: { multipart: true, data: { controller: 'persisted-form', persisted_form_key_value: dom_id(@dossier) } }) do |f|
|
||||||
- dossier = commentaire.dossier
|
- dossier = commentaire.dossier
|
||||||
- placeholder = t('views.shared.dossiers.messages.form.write_message_to_administration_placeholder')
|
- placeholder = t('views.shared.dossiers.messages.form.write_message_to_administration_placeholder')
|
||||||
- if instructeur_signed_in? || administrateur_signed_in? || expert_signed_in?
|
- if instructeur_signed_in? || administrateur_signed_in? || expert_signed_in?
|
||||||
- placeholder = t('views.shared.dossiers.messages.form.write_message_placeholder')
|
- placeholder = t('views.shared.dossiers.messages.form.write_message_placeholder')
|
||||||
%p.mandatory-explanation= t('asterisk_html', scope: [:utils])
|
%p.mandatory-explanation= t('asterisk_html', scope: [:utils])
|
||||||
.fr-input-group
|
|
||||||
= label_tag :commentaire_body, class: 'fr-label' do
|
|
||||||
= t('message', scope: [:utils])
|
|
||||||
%span.mandatory *
|
|
||||||
= f.text_area :body, rows: 5, placeholder: placeholder, title: placeholder, required: true, class: 'fr-input message-textarea'
|
|
||||||
|
|
||||||
- disable_piece_jointe = defined?(disable_piece_jointe) ? disable_piece_jointe : false
|
= render Dsfr::InputComponent.new(form: f, attribute: :body, input_type: :text_area, opts: { rows: 5, placeholder: placeholder, title: placeholder, class: 'fr-input message-textarea'})
|
||||||
- if !disable_piece_jointe
|
|
||||||
.fr-mt-3w
|
.fr-mt-3w
|
||||||
= render Attachment::EditComponent.new(attached_file: commentaire.piece_jointe)
|
= render Attachment::EditComponent.new(attached_file: commentaire.piece_jointe)
|
||||||
|
|
||||||
.fr-mt-3w
|
.fr-mt-3w
|
||||||
= f.submit t('views.shared.dossiers.messages.form.send_message'), class: 'fr-btn', data: { disable: true }
|
= f.submit t('views.shared.dossiers.messages.form.send_message'), class: 'fr-btn', data: { disable: true }
|
||||||
|
|
9
config/locales/models/bulk_message/en.yml
Normal file
9
config/locales/models/bulk_message/en.yml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
en:
|
||||||
|
activerecord:
|
||||||
|
models:
|
||||||
|
bulk_message:
|
||||||
|
one: "Bulk message to users"
|
||||||
|
other: "Bulk message to users"
|
||||||
|
attributes:
|
||||||
|
bulk_message:
|
||||||
|
body: Content
|
9
config/locales/models/bulk_message/fr.yml
Normal file
9
config/locales/models/bulk_message/fr.yml
Normal file
|
@ -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 :"
|
|
@ -12,7 +12,10 @@ en:
|
||||||
copy_link_button: Copy the procedure link to clipboard
|
copy_link_button: Copy the procedure link to clipboard
|
||||||
email_usagers:
|
email_usagers:
|
||||||
contact_users: Contact users (draft)
|
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 <strong>1</strong> user."
|
||||||
|
other: "You will send a message to <strong>%{count}</strong> users."
|
||||||
administrators_list:
|
administrators_list:
|
||||||
title: "%{procedure_libelle} - n°%{procedure_id} - administrators"
|
title: "%{procedure_libelle} - n°%{procedure_id} - administrators"
|
||||||
stats:
|
stats:
|
||||||
|
|
|
@ -12,7 +12,10 @@ fr:
|
||||||
copy_link_button: Copier le lien de la démarche dans le presse-papiers
|
copy_link_button: Copier le lien de la démarche dans le presse-papiers
|
||||||
email_usagers:
|
email_usagers:
|
||||||
contact_users: Contacter les usagers (brouillon)
|
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 à <strong>1</strong> usager ayant un dossier en brouillon."
|
||||||
|
other: "Vous allez envoyer un message à <strong>%{count}</strong> usagers ayant un dossier en brouillon."
|
||||||
administrators_list:
|
administrators_list:
|
||||||
title: "%{procedure_libelle} - n°%{procedure_id} - administrateurs"
|
title: "%{procedure_libelle} - n°%{procedure_id} - administrateurs"
|
||||||
stats:
|
stats:
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
namespace :after_party do
|
||||||
|
desc 'Deployment task: reset_dossier_brouillon_groupe_instructeur_id'
|
||||||
|
task reset_dossier_brouillon_groupe_instructeur_id: :environment do
|
||||||
|
puts "Running deploy task 'reset_dossier_brouillon_groupe_instructeur_id'"
|
||||||
|
|
||||||
|
dossier_brouillon = Dossier.state_brouillon.where.not(groupe_instructeur_id: nil)
|
||||||
|
progress = ProgressReport.new(dossier_brouillon.count)
|
||||||
|
|
||||||
|
# Put your task implementation HERE.
|
||||||
|
dossier_brouillon.in_batches do |relation|
|
||||||
|
progress.inc(relation.count)
|
||||||
|
relation.update_all(groupe_instructeur_id: nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
progress.finish
|
||||||
|
# Update task as completed. If you remove the line below, the task will
|
||||||
|
# run with every deploy (or every time you call after_party:run).
|
||||||
|
AfterParty::TaskRecord
|
||||||
|
.create version: AfterParty::TaskRecorder.new(__FILE__).timestamp
|
||||||
|
end
|
||||||
|
end
|
|
@ -486,6 +486,76 @@ describe Instructeurs::ProceduresController, type: :controller do
|
||||||
end
|
end
|
||||||
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
|
describe '#download_export' do
|
||||||
let(:instructeur) { create(:instructeur) }
|
let(:instructeur) { create(:instructeur) }
|
||||||
let!(:procedure) { create(:procedure) }
|
let!(:procedure) { create(:procedure) }
|
||||||
|
@ -563,49 +633,4 @@ describe Instructeurs::ProceduresController, type: :controller do
|
||||||
it { is_expected.to have_http_status(:forbidden) }
|
it { is_expected.to have_http_status(:forbidden) }
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
|
@ -10,6 +10,26 @@ RSpec.describe ProcedureHelper, type: :helper do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'can_send_groupe_message?' do
|
||||||
|
let(:procedure) { create(:procedure, groupe_instructeurs: [gi1, gi2]) }
|
||||||
|
let(:current_instructeur) { create(:instructeur) }
|
||||||
|
subject { can_send_groupe_message?(procedure) }
|
||||||
|
|
||||||
|
context 'when current_instructeur is in all procedure.groupes_instructeur' do
|
||||||
|
let(:gi1) { create(:groupe_instructeur, instructeurs: [current_instructeur]) }
|
||||||
|
let(:gi2) { create(:groupe_instructeur, instructeurs: [current_instructeur]) }
|
||||||
|
it { is_expected.to be_truthy }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when current_instructeur is in all procedure.groupes_instructeur' do
|
||||||
|
let(:instructeur2) { create(:instructeur) }
|
||||||
|
let(:gi1) { create(:groupe_instructeur, instructeurs: [current_instructeur]) }
|
||||||
|
let(:gi2) { create(:groupe_instructeur, instructeurs: [instructeur2]) }
|
||||||
|
|
||||||
|
it { is_expected.to be_falsy }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe '#estimated_fill_duration_minutes' do
|
describe '#estimated_fill_duration_minutes' do
|
||||||
subject { estimated_fill_duration_minutes(procedure.reload) }
|
subject { estimated_fill_duration_minutes(procedure.reload) }
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,8 @@ RSpec.describe DossierCloneConcern do
|
||||||
describe '#clone' do
|
describe '#clone' do
|
||||||
let(:procedure) { create(:procedure, :with_type_de_champ, :with_type_de_champ_private) }
|
let(:procedure) { create(:procedure, :with_type_de_champ, :with_type_de_champ_private) }
|
||||||
let(:dossier) { create(:dossier, procedure: procedure) }
|
let(:dossier) { create(:dossier, procedure: procedure) }
|
||||||
let(:new_dossier) { dossier.clone }
|
let(:fork) { false }
|
||||||
|
let(:new_dossier) { dossier.clone(fork:) }
|
||||||
|
|
||||||
context 'reset most attributes' do
|
context 'reset most attributes' do
|
||||||
it { expect(new_dossier.id).not_to eq(dossier.id) }
|
it { expect(new_dossier.id).not_to eq(dossier.id) }
|
||||||
|
@ -49,7 +50,14 @@ RSpec.describe DossierCloneConcern do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'copies some attributes' do
|
context 'copies some attributes' do
|
||||||
it { expect(new_dossier.groupe_instructeur).to eq(dossier.groupe_instructeur) }
|
context 'when fork' do
|
||||||
|
let(:fork) { true }
|
||||||
|
it { expect(new_dossier.groupe_instructeur).to eq(dossier.groupe_instructeur) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when not forked' do
|
||||||
|
it { expect(new_dossier.groupe_instructeur).to be_nil }
|
||||||
|
end
|
||||||
it { expect(new_dossier.autorisation_donnees).to eq(dossier.autorisation_donnees) }
|
it { expect(new_dossier.autorisation_donnees).to eq(dossier.autorisation_donnees) }
|
||||||
it { expect(new_dossier.revision_id).to eq(dossier.revision_id) }
|
it { expect(new_dossier.revision_id).to eq(dossier.revision_id) }
|
||||||
it { expect(new_dossier.user_id).to eq(dossier.user_id) }
|
it { expect(new_dossier.user_id).to eq(dossier.user_id) }
|
||||||
|
|
|
@ -466,27 +466,63 @@ describe Dossier, type: :model do
|
||||||
after { Timecop.return }
|
after { Timecop.return }
|
||||||
|
|
||||||
context 'when dossier is en_construction' do
|
context 'when dossier is en_construction' do
|
||||||
before do
|
context 'when the procedure.routing_enabled? is false' do
|
||||||
dossier.passer_en_construction!
|
before do
|
||||||
dossier.reload
|
dossier.passer_en_construction!
|
||||||
|
dossier.reload
|
||||||
|
end
|
||||||
|
|
||||||
|
it { expect(dossier.state).to eq(Dossier.states.fetch(:en_construction)) }
|
||||||
|
it { expect(dossier.en_construction_at).to eq(beginning_of_day) }
|
||||||
|
it { expect(dossier.depose_at).to eq(beginning_of_day) }
|
||||||
|
it { expect(dossier.traitement.state).to eq(Dossier.states.fetch(:en_construction)) }
|
||||||
|
it { expect(dossier.traitement.processed_at).to eq(beginning_of_day) }
|
||||||
|
|
||||||
|
it 'should keep first en_construction_at date' do
|
||||||
|
Timecop.return
|
||||||
|
dossier.passer_en_instruction!(instructeur: instructeur)
|
||||||
|
dossier.repasser_en_construction!(instructeur: instructeur)
|
||||||
|
|
||||||
|
expect(dossier.traitements.size).to eq(3)
|
||||||
|
expect(dossier.traitements.first.processed_at).to eq(beginning_of_day)
|
||||||
|
expect(dossier.traitement.processed_at.round).to eq(dossier.en_construction_at.round)
|
||||||
|
expect(dossier.depose_at).to eq(beginning_of_day)
|
||||||
|
expect(dossier.en_construction_at).to be > beginning_of_day
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it { expect(dossier.state).to eq(Dossier.states.fetch(:en_construction)) }
|
context 'when the procedure.routing_enabled? is true' do
|
||||||
it { expect(dossier.en_construction_at).to eq(beginning_of_day) }
|
include Logic
|
||||||
it { expect(dossier.depose_at).to eq(beginning_of_day) }
|
let(:gi_libelle) { 'Paris' }
|
||||||
it { expect(dossier.traitement.state).to eq(Dossier.states.fetch(:en_construction)) }
|
let!(:procedure) do
|
||||||
it { expect(dossier.traitement.processed_at).to eq(beginning_of_day) }
|
create(:procedure,
|
||||||
|
types_de_champ_public: [
|
||||||
|
{ type: :drop_down_list, libelle: 'Votre ville', options: [gi_libelle, 'Lyon', 'Marseille'] },
|
||||||
|
{ type: :text, libelle: 'Un champ texte' }
|
||||||
|
])
|
||||||
|
end
|
||||||
|
let!(:drop_down_tdc) { procedure.draft_revision.types_de_champ.first }
|
||||||
|
let(:dossier) { create(:dossier, :brouillon, user:, procedure:, groupe_instructeur: nil) }
|
||||||
|
let(:gi) do
|
||||||
|
create(:groupe_instructeur,
|
||||||
|
routing_rule: ds_eq(champ_value(drop_down_tdc.stable_id),
|
||||||
|
constant(gi_libelle)))
|
||||||
|
end
|
||||||
|
|
||||||
it 'should keep first en_construction_at date' do
|
before do
|
||||||
Timecop.return
|
procedure.groupe_instructeurs = [gi]
|
||||||
dossier.passer_en_instruction!(instructeur: instructeur)
|
procedure.defaut_groupe_instructeur = gi
|
||||||
dossier.repasser_en_construction!(instructeur: instructeur)
|
procedure.save!
|
||||||
|
procedure.toggle_routing
|
||||||
|
dossier.champs.first.value = gi_libelle
|
||||||
|
dossier.save!
|
||||||
|
dossier.passer_en_construction!
|
||||||
|
dossier.reload
|
||||||
|
end
|
||||||
|
|
||||||
expect(dossier.traitements.size).to eq(3)
|
it 'RoutingEngine.compute' do
|
||||||
expect(dossier.traitements.first.processed_at).to eq(beginning_of_day)
|
expect(dossier.groupe_instructeur).not_to be_nil
|
||||||
expect(dossier.traitement.processed_at.round).to eq(dossier.en_construction_at.round)
|
end
|
||||||
expect(dossier.depose_at).to eq(beginning_of_day)
|
|
||||||
expect(dossier.en_construction_at).to be > beginning_of_day
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue