Merge pull request #9706 from demarches-simplifiees/feat/9678

ETQ usager passant par un mandataire, je suis notifié des changements d'état de mon dossier par email
This commit is contained in:
Lisa Durand 2023-12-14 16:57:07 +00:00 committed by GitHub
commit 569d4af6f7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
49 changed files with 657 additions and 98 deletions

View file

@ -128,7 +128,7 @@ module Users
@dossier = dossier
@no_description = true
if @dossier.individual.update(individual_params)
if @dossier.update(dossier_params) && @dossier.individual.valid?
@dossier.update!(autorisation_donnees: true, identity_updated_at: Time.zone.now)
flash.notice = t('.identity_saved')
@ -138,7 +138,7 @@ module Users
redirect_to brouillon_dossier_path(@dossier)
end
else
flash.now.alert = @dossier.individual.errors.full_messages
flash.now.alert = @dossier.individual.errors.full_messages + @dossier.errors.full_messages
render :identite
end
end
@ -589,8 +589,8 @@ module Users
render :siret
end
def individual_params
params.require(:individual).permit(:gender, :nom, :prenom, :birthdate)
def dossier_params
params.require(:dossier).permit(:for_tiers, :mandataire_first_name, :mandataire_last_name, individual_attributes: [:gender, :nom, :prenom, :birthdate, :email, :notification_method])
end
def siret_params

View file

@ -108,6 +108,10 @@ module DossierHelper
end
def demandeur_dossier(dossier)
if dossier.procedure.for_individual? && dossier.for_tiers?
return t('shared.dossiers.beneficiaire', mandataire: dossier.mandataire_full_name, beneficiaire: "#{dossier&.individual&.prenom} #{dossier&.individual&.nom}")
end
if dossier.procedure.for_individual?
return "#{dossier&.individual&.nom} #{dossier&.individual&.prenom}"
end

View file

@ -0,0 +1,93 @@
import { ApplicationController } from './application_controller';
export class ForTiersController extends ApplicationController {
static targets = [
'mandataireFirstName',
'mandataireLastName',
'forTiers',
'mandataireBlock',
'beneficiaireNotificationBlock',
'email',
'notificationMethod',
'mandataireTitle',
'beneficiaireTitle',
'emailInput'
];
declare mandataireFirstNameTarget: HTMLInputElement;
declare mandataireLastNameTarget: HTMLInputElement;
declare forTiersTargets: NodeListOf<HTMLInputElement>;
declare mandataireBlockTarget: HTMLElement;
declare beneficiaireNotificationBlockTarget: HTMLElement;
declare notificationMethodTargets: NodeListOf<HTMLInputElement>;
declare emailTarget: HTMLInputElement;
declare mandataireTitleTarget: HTMLElement;
declare beneficiaireTitleTarget: HTMLElement;
declare emailInput: HTMLInputElement;
connect() {
const emailInputElement = this.emailTarget.querySelector('input');
if (emailInputElement) {
this.emailInput = emailInputElement;
}
this.toggleFieldRequirements();
this.addAllEventListeners();
}
addAllEventListeners() {
this.forTiersTargets.forEach((radio) => {
radio.addEventListener('change', () => this.toggleFieldRequirements());
});
this.notificationMethodTargets.forEach((radio) => {
radio.addEventListener('change', () => this.toggleEmailInput());
});
}
toggleFieldRequirements() {
const forTiersSelected = this.isForTiersSelected();
this.toggleDisplay(this.mandataireBlockTarget, forTiersSelected);
this.toggleDisplay(
this.beneficiaireNotificationBlockTarget,
forTiersSelected
);
this.mandataireFirstNameTarget.required = forTiersSelected;
this.mandataireLastNameTarget.required = forTiersSelected;
this.mandataireTitleTarget.classList.toggle('hidden', forTiersSelected);
this.beneficiaireTitleTarget.classList.toggle('hidden', !forTiersSelected);
this.notificationMethodTargets.forEach((radio) => {
radio.required = forTiersSelected;
});
this.toggleEmailInput();
}
isForTiersSelected() {
return Array.from(this.forTiersTargets).some(
(radio) => radio.checked && radio.value === 'true'
);
}
toggleDisplay(element: HTMLElement, shouldDisplay: boolean) {
element.classList.toggle('hidden', !shouldDisplay);
}
toggleEmailInput() {
const isEmailSelected = this.isEmailSelected();
const forTiersSelected = this.isForTiersSelected();
if (this.emailInput) {
this.emailInput.required = forTiersSelected && isEmailSelected;
if (!isEmailSelected) {
this.emailInput.value = '';
}
this.toggleDisplay(this.emailTarget, forTiersSelected && isEmailSelected);
}
}
isEmailSelected() {
return Array.from(this.notificationMethodTargets).some(
(radio) => radio.value === 'email' && radio.checked
);
}
}

View file

@ -6,7 +6,7 @@
# The subject and body of a Notification can be customized by each demarche.
#
class NotificationMailer < ApplicationMailer
before_action :set_dossier
before_action :set_dossier, except: [:send_notification_for_tiers]
before_action :set_services_publics_plus, only: :send_notification
helper ServiceHelper
@ -24,6 +24,21 @@ class NotificationMailer < ApplicationMailer
end
end
def send_notification_for_tiers(dossier)
@dossier = dossier
if @dossier.individual.no_notification?
mail.perform_deliveries = false
return
end
@subject = "Votre dossier rempli par le mandataire #{@dossier.mandataire_first_name} #{@dossier.mandataire_last_name} a été mis à jour"
@email = @dossier.individual.email
@logo_url = procedure_logo_url(@dossier.procedure)
mail(subject: @subject, to: @email, template_name: 'send_notification_for_tiers')
end
def self.send_en_construction_notification(dossier)
with(dossier: dossier, state: Dossier.states.fetch(:en_construction)).send_notification
end

View file

@ -447,6 +447,9 @@ class Dossier < ApplicationRecord
validates :user, presence: true, if: -> { deleted_user_email_never_send.nil? }, unless: -> { prefilled }
validates :individual, presence: true, if: -> { revision.procedure.for_individual? }
validates :mandataire_first_name, presence: true, if: :for_tiers?
validates :mandataire_last_name, presence: true, if: :for_tiers?
validates :for_tiers, inclusion: { in: [true, false] }, if: -> { revision&.procedure&.for_individual? }
validates_associated :prefilled_champs_public, on: :champs_public_value
@ -901,6 +904,7 @@ class Dossier < ApplicationRecord
save!
MailTemplatePresenterService.create_commentaire_for_state(self, Dossier.states.fetch(:en_construction))
NotificationMailer.send_en_construction_notification(self).deliver_later
NotificationMailer.send_notification_for_tiers(self).deliver_later if self.for_tiers?
procedure.compute_dossiers_count
RoutingEngine.compute(self)
end
@ -931,6 +935,7 @@ class Dossier < ApplicationRecord
MailTemplatePresenterService.create_commentaire_for_state(self, Dossier.states.fetch(:en_instruction))
if !disable_notification
NotificationMailer.send_en_instruction_notification(self).deliver_later
NotificationMailer.send_notification_for_tiers(self).deliver_later if self.for_tiers?
end
log_dossier_operation(instructeur, :passer_en_instruction)
end
@ -947,6 +952,7 @@ class Dossier < ApplicationRecord
save!
MailTemplatePresenterService.create_commentaire_for_state(self, Dossier.states.fetch(:en_instruction))
NotificationMailer.send_en_instruction_notification(self).deliver_later
NotificationMailer.send_notification_for_tiers(self).deliver_later if self.for_tiers?
if procedure.sva_svr_enabled?
# TODO: handle serialization errors when SIRET demandeur was not completed
@ -1024,6 +1030,7 @@ class Dossier < ApplicationRecord
MailTemplatePresenterService.create_commentaire_for_state(self, Dossier.states.fetch(:accepte))
if !disable_notification
NotificationMailer.send_accepte_notification(self).deliver_later
NotificationMailer.send_notification_for_tiers(self).deliver_later if self.for_tiers?
end
send_dossier_decision_to_experts(self)
log_dossier_operation(instructeur, :accepter, self)
@ -1049,6 +1056,7 @@ class Dossier < ApplicationRecord
remove_titres_identite!
MailTemplatePresenterService.create_commentaire_for_state(self, Dossier.states.fetch(:accepte))
NotificationMailer.send_accepte_notification(self).deliver_later
NotificationMailer.send_notification_for_tiers(self).deliver_later if self.for_tiers?
log_automatic_dossier_operation(:accepter, self)
end
@ -1074,6 +1082,7 @@ class Dossier < ApplicationRecord
if !disable_notification
NotificationMailer.send_refuse_notification(self).deliver_later
NotificationMailer.send_notification_for_tiers(self).deliver_later if self.for_tiers?
end
send_dossier_decision_to_experts(self)
log_dossier_operation(instructeur, :refuser, self)
@ -1093,6 +1102,7 @@ class Dossier < ApplicationRecord
remove_titres_identite!
MailTemplatePresenterService.create_commentaire_for_state(self, Dossier.states.fetch(:refuse))
NotificationMailer.send_refuse_notification(self).deliver_later
NotificationMailer.send_notification_for_tiers(self).deliver_later if self.for_tiers?
log_automatic_dossier_operation(:refuser, self)
end
@ -1118,6 +1128,7 @@ class Dossier < ApplicationRecord
if !disable_notification
NotificationMailer.send_sans_suite_notification(self).deliver_later
NotificationMailer.send_notification_for_tiers(self).deliver_later if self.for_tiers?
end
send_dossier_decision_to_experts(self)
log_dossier_operation(instructeur, :classer_sans_suite, self)
@ -1373,6 +1384,10 @@ class Dossier < ApplicationRecord
groupe_instructeur&.contact_information || procedure.service
end
def mandataire_full_name
"#{mandataire_first_name} #{mandataire_last_name}"
end
private
def create_missing_traitemets

View file

@ -1,10 +1,21 @@
class Individual < ApplicationRecord
enum notification_method: {
email: 'email',
no_notification: 'no_notification'
}
belongs_to :dossier, optional: false
validates :dossier_id, uniqueness: true
validates :gender, presence: true, allow_nil: false, on: :update
validates :nom, presence: true, allow_blank: false, allow_nil: false, on: :update
validates :prenom, presence: true, allow_blank: false, allow_nil: false, on: :update
validates :notification_method, presence: true,
inclusion: { in: notification_methods.keys },
if: -> { dossier.for_tiers? },
on: :update
validates :email, presence: true, if: -> { dossier.for_tiers? && self.email? }, on: :update
GENDER_MALE = "M."
GENDER_FEMALE = 'Mme'

View file

@ -1,5 +1,5 @@
class DossierProjectionService
class DossierProjection < Struct.new(:dossier_id, :state, :archived, :hidden_by_user_at, :hidden_by_administration_at, :batch_operation_id, :sva_svr_decision_on, :corrections, :columns) do
class DossierProjection < Struct.new(:dossier_id, :state, :archived, :hidden_by_user_at, :hidden_by_administration_at, :for_tiers, :prenom, :nom, :batch_operation_id, :sva_svr_decision_on, :corrections, :columns) do
def pending_correction?
return false if corrections.blank?
@ -29,9 +29,12 @@ class DossierProjectionService
batch_operation_field = { TABLE => 'self', COLUMN => 'batch_operation_id' }
hidden_by_user_at_field = { TABLE => 'self', COLUMN => 'hidden_by_user_at' }
hidden_by_administration_at_field = { TABLE => 'self', COLUMN => 'hidden_by_administration_at' }
for_tiers_field = { TABLE => 'self', COLUMN => 'for_tiers' }
individual_first_name = { TABLE => 'individual', COLUMN => 'prenom' }
individual_last_name = { TABLE => 'individual', COLUMN => 'nom' }
sva_svr_decision_on_field = { TABLE => 'self', COLUMN => 'sva_svr_decision_on' }
dossier_corrections = { TABLE => 'dossier_corrections', COLUMN => 'resolved_at' }
([state_field, archived_field, sva_svr_decision_on_field, hidden_by_user_at_field, hidden_by_administration_at_field, batch_operation_field, dossier_corrections] + fields) # the view needs state and archived dossier attributes
([state_field, archived_field, sva_svr_decision_on_field, hidden_by_user_at_field, hidden_by_administration_at_field, for_tiers_field, individual_first_name, individual_last_name, batch_operation_field, dossier_corrections] + fields) # the view needs state and archived dossier attributes
.each { |f| f[:id_value_h] = {} }
.group_by { |f| f[TABLE] } # one query per table
.each do |table, fields|
@ -55,7 +58,7 @@ class DossierProjectionService
.pluck(:id, *fields.map { |f| f[COLUMN].to_sym })
.each do |id, *columns|
fields.zip(columns).each do |field, value|
if [state_field, archived_field, hidden_by_user_at_field, hidden_by_administration_at_field, batch_operation_field, sva_svr_decision_on_field].include?(field)
if [state_field, archived_field, hidden_by_user_at_field, hidden_by_administration_at_field, for_tiers_field, batch_operation_field, sva_svr_decision_on_field].include?(field)
field[:id_value_h][id] = value
else
field[:id_value_h][id] = value&.strftime('%d/%m/%Y') # other fields are datetime
@ -130,6 +133,9 @@ class DossierProjectionService
archived_field[:id_value_h][dossier_id],
hidden_by_user_at_field[:id_value_h][dossier_id],
hidden_by_administration_at_field[:id_value_h][dossier_id],
for_tiers_field[:id_value_h][dossier_id],
individual_first_name[:id_value_h][dossier_id],
individual_last_name[:id_value_h][dossier_id],
batch_operation_field[:id_value_h][dossier_id],
sva_svr_decision_on_field[:id_value_h][dossier_id],
dossier_corrections[:id_value_h][dossier_id],

View file

@ -33,6 +33,10 @@
%legend.fr-fieldset__legend#new-account-legend
%h2.fr-h6= I18n.t('views.users.sessions.new.subtitle')
= render Dsfr::AlertComponent.new(state: :info, size: :sm, extra_class_names: 'fr-mb-2w') do |c|
- c.body do
= t('views.users.sessions.new.for_tiers_alert')
.fr-fieldset__element
%p.fr-text--sm= t('utils.mandatory_champs')

View file

@ -3,7 +3,7 @@
%h2 Identité du demandeur
= render partial: "shared/dossiers/user_infos", locals: { user_deleted: @dossier.user_deleted?, email: @dossier.user_email_for(:display) }
= render partial: "shared/dossiers/user_infos", locals: { user_deleted: @dossier.user_deleted?, email: @dossier.user_email_for(:display), for_tiers: @dossier.for_tiers? }
- if @dossier.etablissement.present?
= render partial: "shared/dossiers/identite_entreprise", locals: { etablissement: @dossier.etablissement, profile: 'instructeur' }

View file

@ -164,6 +164,10 @@
%span.cell-link
= column
= "- #{t('views.instructeurs.dossiers.deleted_by_user')}" if p.hidden_by_user_at.present?
- elsif p.for_tiers
%a.cell-link{ href: path }
= "#{column} (#{t('views.instructeurs.dossiers.acts_on_behalf')} #{p.prenom} #{p.nom})"
= "- #{t('views.instructeurs.dossiers.deleted_by_user')}" if p.hidden_by_user_at.present?
- else
%a.cell-link{ href: path }
= column

View file

@ -0,0 +1,26 @@
- content_for :procedure_logo do
= render 'layouts/mailers/logo', url: @logo_url
%p
= t("layouts.mailers.for_tiers.good_morning")
%p
= t("layouts.mailers.for_tiers.first_part",
dossier_id: number_with_delimiter(@dossier.id),
mandataire_first_name: @dossier.mandataire_first_name,
mandataire_last_name: @dossier.mandataire_last_name)
%span{ :style => "font-weight: bold;" }
= @dossier.procedure.libelle
= t("layouts.mailers.for_tiers.#{@dossier.state}", processed_at: l(@dossier.updated_at.to_date) )
%p
= t("layouts.mailers.for_tiers.second_part")
= "#{mail_to(@dossier.user.email)}."
%p
= t(:best_regards, scope: [:views, :shared, :greetings])
%br
= t('layouts.mailers.signature.team')
#{APPLICATION_NAME.gsub(".","&#8288;.").html_safe}

View file

@ -11,6 +11,17 @@
- if dossier.depose_at.present?
= render partial: "shared/dossiers/infos_generales", locals: { dossier: dossier }
- if dossier.for_tiers?
%h2.fr-h6.fr-background-alt--grey.fr-mb-0.flex
.flex-grow.fr-py-3v.fr-px-2w= t('views.shared.dossiers.demande.mandataire_identity')
- if dossier.individual.present? && profile == 'usager' && !dossier.read_only?
= link_to t('views.shared.dossiers.demande.edit_identity'), identite_dossier_path(dossier), class: 'fr-py-3v fr-btn fr-btn--tertiary-no-outline'
= render partial: "shared/dossiers/mandataire_infos", locals: { user_deleted: dossier.user_deleted?, email: dossier.user_email_for(:display), dossier: dossier }
.tab-title
%h2.fr-h6.fr-background-alt--grey.fr-mb-0.flex
.flex-grow.fr-py-3v.fr-px-2w
@ -27,10 +38,11 @@
= link_to t('views.shared.dossiers.demande.edit_identity'), identite_dossier_path(dossier), class: 'fr-py-3v fr-btn fr-btn--tertiary-no-outline'
= render partial: "shared/dossiers/user_infos", locals: { user_deleted: dossier.user_deleted?, email: dossier.user_email_for(:display) }
= render partial: "shared/dossiers/user_infos", locals: { user_deleted: dossier.user_deleted?, email: dossier.user_email_for(:display), for_tiers: dossier.for_tiers?, beneficiaire_mail: dossier.for_tiers? ? dossier.individual.email : ""}
- if dossier.individual.present?
= render partial: "shared/dossiers/identite_individual", locals: { individual: dossier.individual }
= render partial: "shared/dossiers/identite_individual", locals: { dossier: dossier }
- if dossier.etablissement.present?
.fr-mt-1w.fr-mb-4w.fr-px-2w

View file

@ -1,16 +1,17 @@
= render Dossiers::RowShowComponent.new(label: t('views.users.dossiers.identite.civility')) do |c|
- c.with_value do
%p= individual.gender
%p= dossier.individual.gender
= render Dossiers::RowShowComponent.new(label: t('views.users.dossiers.identite.first_name')) do |c|
- c.with_value do
%p= individual.prenom
%p= dossier.individual.prenom
= render Dossiers::RowShowComponent.new(label: t('views.users.dossiers.identite.last_name')) do |c|
- c.with_value do
%p= individual.nom
%p= dossier.individual.nom
- if individual.birthdate.present?
- if dossier.individual.birthdate.present?
= render Dossiers::RowShowComponent.new(label: t('views.users.dossiers.identite.birthdate')) do |c|
- c.with_value do
%p= try_format_date(individual.birthdate)
%p= try_format_date(dossier.individual.birthdate)

View file

@ -0,0 +1,11 @@
= render Dossiers::RowShowComponent.new(label: 'Email', profile: @profile) do |c|
- c.with_value do
= user_deleted ? "#{email} (lusager a supprimé son compte)" : email
= render Dossiers::RowShowComponent.new(label: t('views.users.dossiers.identite.first_name')) do |c|
- c.with_value do
= dossier.mandataire_first_name
= render Dossiers::RowShowComponent.new(label: t('views.users.dossiers.identite.last_name')) do |c|
- c.with_value do
= dossier.mandataire_last_name

View file

@ -1,3 +1,6 @@
= render Dossiers::RowShowComponent.new(label: 'Email', profile: @profile) do |c|
- c.with_value do
= user_deleted ? "#{email} (lusager a supprimé son compte)" : email
- if for_tiers
= beneficiaire_mail
- else
= user_deleted ? "#{email} (lusager a supprimé son compte)" : email

View file

@ -13,9 +13,8 @@
- else
= dossier.procedure.libelle
- if demandeur_dossier(dossier).present?
%p.fr-icon--sm.fr-icon-user-line
= demandeur_dossier(dossier)
%p.fr-icon--sm.fr-icon-user-line
= demandeur_dossier(dossier)
- if dossier.hidden_by_user?
%p.fr-icon--sm.fr-icon-delete-line

View file

@ -1,10 +1,13 @@
= render Dropdown::MenuComponent.new(wrapper: :div, wrapper_options: { class: ['dossier-show', 'edit-identity-action'] }, menu_options: { class:['edit-identity-content'] }) do |menu|
- menu.with_button_inner_html do
= t("views.shared.dossiers.demande.my_identity")
- if dossier.for_tiers?
= t("views.shared.dossiers.demande.requester_identity")
- else
= t("views.shared.dossiers.demande.individual_identity")
- menu.with_form do
- if dossier.procedure.for_individual
= render partial: "shared/dossiers/identite_individual", locals: { individual: dossier.individual }
= render partial: "shared/dossiers/identite_individual", locals: { dossier: dossier }
= link_to t('views.shared.dossiers.demande.edit_identity'), identite_dossier_path(dossier), class: 'fr-btn fr-btn--secondary fr-btn--sm fr-my-1w'
@ -12,4 +15,3 @@
= render partial: "shared/dossiers/identite_entreprise", locals: { etablissement: dossier.etablissement, short_identity: true, profile: "usager" }
= link_to t('views.shared.dossiers.demande.edit_siret'), siret_dossier_path(dossier), class: 'fr-btn fr-btn--secondary fr-btn--sm fr-my-1w'

View file

@ -3,37 +3,92 @@
= render partial: "shared/dossiers/submit_is_over", locals: { dossier: @dossier }
- if !dossier_submission_is_closed?(@dossier)
= form_for @dossier.individual, url: update_identite_dossier_path(@dossier), html: { class: "form" } do |f|
= form_for @dossier, url: update_identite_dossier_path(@dossier), html: { class: "form", "data-controller" => "for-tiers" } do |f|
%fieldset#radio-rich-hint.fr-fieldset{ "aria-labelledby" => "radio-rich-hint-legend radio-rich-hint-messages" }
%legend#radio-rich-hint-legend.fr-fieldset__legend--regular.fr-fieldset__legend
Ce dossier est
%fieldset.fr-fieldset
%legend.fr-fieldset__legend--regular.fr-fieldset__legend
%h2.fr-h4= t('views.users.dossiers.identite.identity_data')
.fr-fieldset__element
.fr-radio-group.fr-radio-rich
= f.radio_button :for_tiers, false, required: true, id: "radio-self-manage", "data-action" => "click->for-tiers#toggleFieldRequirements", "data-for-tiers-target" => "forTiers"
%label.fr-label{ for: "radio-self-manage" }
= t('activerecord.attributes.dossier.for_tiers.false')
.fr-radio-rich__img
%span.fr-icon-user-fill
.fr-fieldset__element
.fr-radio-group.fr-radio-rich
= f.radio_button :for_tiers, true, required: true, id: "radio-tiers-manage", "data-action" => "click->for-tiers#toggleFieldRequirements", "data-for-tiers-target" => "forTiers"
%label.fr-label{ for: "radio-tiers-manage" }
= t('activerecord.attributes.dossier.for_tiers.true')
.fr-radio-rich__img
%span.fr-icon-parent-fill
.mandataire-infos{ "data-for-tiers-target" => "mandataireBlock" }
.fr-alert.fr-alert--info.fr-mb-2w
%p.fr-notice__text= t('views.users.dossiers.identite.callout_text')
%fieldset.fr-fieldset
%legend.fr-fieldset__legend--regular.fr-fieldset__legend
%h2.fr-h4= t('views.users.dossiers.identite.self_title')
.fr-fieldset__element.fr-fieldset__element--short-text
= render Dsfr::InputComponent.new(form: f, attribute: :mandataire_first_name, opts: { "data-for-tiers-target" => "mandataireFirstName" })
.fr-fieldset__element.fr-fieldset__element--short-text
= render Dsfr::InputComponent.new(form: f, attribute: :mandataire_last_name, opts: { "data-for-tiers-target" => "mandataireLastName" })
= f.fields_for :individual, include_id: false do |individual|
.individual-infos
%fieldset.fr-fieldset
%legend.fr-fieldset__legend--regular.fr-fieldset__legend{ "data-for-tiers-target" => "mandataireTitle" }
%h2.fr-h4= t('views.users.dossiers.identite.self_title')
%legend.fr-fieldset__legend--regular.fr-fieldset__legend.hidden{ "data-for-tiers-target" => "beneficiaireTitle" }
%h2.fr-h4= t('views.users.dossiers.identite.beneficiaire_title')
%legend.fr-fieldset__legend--regular.fr-fieldset__legend
= t('activerecord.attributes.individual.gender')
= render EditableChamp::AsteriskMandatoryComponent.new
.fr-fieldset__element
.fr-radio-group
= f.radio_button :gender, Individual::GENDER_FEMALE, required: true, id: "identite_champ_radio_#{Individual::GENDER_FEMALE}"
= individual.radio_button :gender, Individual::GENDER_FEMALE, required: true, id: "identite_champ_radio_#{Individual::GENDER_FEMALE}"
%label.fr-label{ for: "identite_champ_radio_#{Individual::GENDER_FEMALE}" }
= Individual.human_attribute_name('gender.female')
.fr-fieldset__element
.fr-radio-group
= f.radio_button :gender, Individual::GENDER_MALE, required: true, id: "identite_champ_radio_#{Individual::GENDER_MALE}"
= individual.radio_button :gender, Individual::GENDER_MALE, required: true, id: "identite_champ_radio_#{Individual::GENDER_MALE}"
%label.fr-label{ for: "identite_champ_radio_#{Individual::GENDER_MALE}" }
= Individual.human_attribute_name('gender.male')
.fr-fieldset__element.fr-fieldset__element--short-text
= render Dsfr::InputComponent.new(form: f, attribute: :prenom, opts: { autocomplete: 'given-name' })
.fr-fieldset__element.fr-fieldset__element--short-text
= render Dsfr::InputComponent.new(form: individual, attribute: :prenom, opts: { autocomplete: 'given-name' })
.fr-fieldset__element.fr-fieldset__element--short-text
= render Dsfr::InputComponent.new(form: f, attribute: :nom, opts: { autocomplete: 'family-name' })
.fr-fieldset__element.fr-fieldset__element--short-text
= render Dsfr::InputComponent.new(form: individual, attribute: :nom, opts: { autocomplete: 'family-name' })
- if @dossier.procedure.ask_birthday?
.fr-fieldset__element
= render Dsfr::InputComponent.new(form: f, attribute: :birthdate, input_type: :date_field,
opts: { placeholder: 'Format : AAAA-MM-JJ', max: Date.today.iso8601, min: "1900-01-01", autocomplete: 'bday' })
%fieldset.fr-fieldset{ "data-for-tiers-target" => "beneficiaireNotificationBlock" }
%legend.fr-fieldset__legend--regular.fr-fieldset__legend
= t('activerecord.attributes.individual.notification_method')
= render EditableChamp::AsteriskMandatoryComponent.new
.fr-fieldset__element
= f.submit t('views.users.dossiers.identite.continue'), class: "fr-btn fr-btn--lg fr-my-2w"
- Individual.notification_methods.each do |method, _|
.fr-fieldset__element
.fr-radio-group
= individual.radio_button :notification_method, method, id: "notification_method_#{method}", "data-action" => "for-tiers#toggleFieldRequirements", "data-for-tiers-target" => "notificationMethod"
%label.fr-label{ for: "notification_method_#{method}" }
= t("activerecord.attributes.individual.notification_methods.#{method}")
.fr-fieldset__element.fr-fieldset__element--short-text.hidden{ "data-for-tiers-target" => "email" }
= render Dsfr::InputComponent.new(form: individual, attribute: :email)
- if @dossier.procedure.ask_birthday?
.fr-fieldset__element
= render Dsfr::InputComponent.new(form: individual, attribute: :birthdate, input_type: :date_field,
opts: { placeholder: 'Format : AAAA-MM-JJ', max: Date.today.iso8601, min: "1900-01-01", autocomplete: 'bday' })
= f.submit t('views.users.dossiers.identite.continue'), class: "fr-btn"

View file

@ -340,8 +340,9 @@ en:
write_message_to_administration_placeholder: "Write your message to the administration here"
demande:
requester_identity: "Identity of the requester"
mandataire_identity: "Identity of toe mandatary"
requester_identity_updated_at: "updated on %{date}"
my_identity: "My identity"
individual_identity: "Your identity"
form: "Form"
edit_siret: "Edit SIRET"
edit_identity: "Edit identity data"
@ -368,6 +369,7 @@ en:
archived_dossier: "This file will be kept for an additional month"
delete_dossier: "Delete file"
deleted_by_user: "File deleted by user"
acts_on_behalf: "acts for"
deleted_by_administration: "File deleted by administration"
restore: "Restore"
filters:
@ -397,7 +399,9 @@ en:
dossiers:
archived_dossier: "Your file will be kept %{duree_conservation_dossiers_dans_ds} more months"
identite:
identity_data: Identity data
self_title: Your identity
callout_text: "You are acting as a proxy for a principal, either professionally (such as accountant, lawyer) or personally (family). Make sure to respect the conditions of Articles 1984 to 1990 of the Civil Code."
beneficiaire_title: "Identity of the beneficiary"
identity_siret: Identify your establishment
civility: Civility
first_name: First Name
@ -509,6 +513,7 @@ en:
state_civil_servant: Are you a state civil servant?
connect_with_agent_connect: Visit our dedicated page
subtitle: "Sign in with my account"
for_tiers_alert: If you are completing a forme for someone else, you must use your own credentials.
passwords:
edit:
subtitle: Change password

View file

@ -341,8 +341,9 @@ fr:
demande:
en_construction: "Date de dépôt du dossier"
requester_identity: "Identité du demandeur"
mandataire_identity: "Identité du mandataire"
requester_identity_updated_at: "modifiée le %{date}"
my_identity: "Mon identité"
individual_identity: "Votre identité"
form: "Sections du formulaire"
edit_siret: "Modifier le SIRET"
edit_identity: "Modifier lidentité"
@ -372,6 +373,7 @@ fr:
archived_dossier: "Le dossier sera conservé 1 mois supplémentaire"
delete_dossier: "Supprimer le dossier"
deleted_by_user: "Dossier supprimé par lusager"
acts_on_behalf: "agit pour"
deleted_by_administration: "Dossier supprimé par ladministration"
restore: "Restaurer"
filters:
@ -401,7 +403,9 @@ fr:
dossiers:
archived_dossier: "Votre dossier sera conservé %{duree_conservation_dossiers_dans_ds} mois supplémentaire"
identite:
identity_data: Identité du demandeur
self_title: Votre identité
callout_text: Vous agissez en tant que mandataire, soit professionnellement (comme expert-comptable, avocat) soit personnellement (famille). Assurez-vous de respecter les conditions des Articles 1984 à 1990 du Code civil.
beneficiaire_title: Identité du bénéficiaire
identity_siret: Identifier votre établissement
civility: Civilité
first_name: Prénom
@ -513,6 +517,7 @@ fr:
state_civil_servant: Vous êtes agent de la fonction publique dÉtat ?
connect_with_agent_connect: Accédez à notre page dédiée
subtitle: "Se connecter avec son compte"
for_tiers_alert: Si vous remplissez un dossier pour un tiers, vous devez utiliser vos propres identifiants.
passwords:
edit:
subtitle: Changement de mot de passe

View file

@ -13,6 +13,11 @@ fr:
state: "État"
autorisation_donnees: Acceptation des CGU
pending_correction: Demande de correction
mandataire_first_name: Prénom
mandataire_last_name: Nom
for_tiers:
false: Pour vous
true: "Pour un bénéficiaire : membre de la famille, proche, mandant..."
dossier/state: &state
brouillon: "Brouillon"
en_construction: "En construction"

View file

@ -6,6 +6,11 @@ en:
nom: Last name
prenom: First name
birthdate: Date de naissance
email: Email
notification_method: "Notify beneficiary :"
notification_methods:
email: By e-mail
no_notification: No notification
individual/gender:
female: Ms
male: Mr

View file

@ -6,6 +6,11 @@ fr:
nom: Nom
prenom: Prénom
birthdate: Date de naissance
email: Email
notification_method: "Notifier le bénéficiaire :"
notification_methods:
email: Par e-mail
no_notification: Pas de notification
individual/gender:
female: Madame
male: Monsieur

View file

@ -1,6 +1,7 @@
en:
shared:
dossiers:
beneficiaire: '%{beneficiaire}, file fill by %{mandataire}'
france_connect_informations:
details_no_name: "The file was submitted by a FranceConnect account."
details: "The file was submitted by the account of %{name}."

View file

@ -1,6 +1,7 @@
fr:
shared:
dossiers:
beneficiaire: '%{beneficiaire}, dossier rempli par %{mandataire}'
france_connect_informations:
details_no_name: "Le dossier a été déposé par un compte FranceConnect."
details: "Le dossier a été déposé par le compte de %{name}."

View file

@ -14,3 +14,12 @@ en:
schedule: "Schedule :"
signature:
team: "The team"
for_tiers:
good_morning: Good morning,
first_part: We inform you that file no. %{dossier_id}, completed on your behalf by %{mandataire_first_name} %{mandataire_last_name}, on the procedure
second_part: To find out more, please contact
en_construction: has been submited %{processed_at}.
en_instruction: has been received and taken care of on %{processed_at}. The administration is reviewing your file.
accepte: has been accepted on %{processed_at}.
refuse: has been refused on %{processed_at}.
sans_suite: has been closed without continuation on %{processed_at}.

View file

@ -15,3 +15,12 @@ fr:
schedule: "Horaires :"
signature:
team: "Léquipe"
for_tiers:
good_morning: Bonjour,
first_part: Nous vous informons que le dossier nº %{dossier_id}, rempli en votre nom par %{mandataire_first_name} %{mandataire_last_name}, sur la démarche
second_part: Pour en savoir plus, veuillez vous rapprocher de
en_construction: a été déposé le %{processed_at}.
en_instruction: a bien été reçu et pris en charge le %{processed_at}. Il va maintenant être examiné par le service.
accepte: a été accepté le %{processed_at}.
refuse: a été refusé le %{processed_at}.
sans_suite: a été classé sans suite le %{processed_at}.

View file

@ -0,0 +1,12 @@
class CombineDossierAndIndividualUpdates < ActiveRecord::Migration[7.0]
disable_ddl_transaction!
def change
add_column :dossiers, :mandataire_first_name, :string
add_column :dossiers, :mandataire_last_name, :string
add_column :dossiers, :for_tiers, :boolean, default: false, null: false
add_column :individuals, :notification_method, :string
add_column :individuals, :email, :string
add_index :individuals, :email, algorithm: :concurrently
end
end

View file

@ -419,6 +419,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_11_28_071043) do
t.datetime "en_construction_close_to_expiration_notice_sent_at", precision: nil
t.datetime "en_instruction_at", precision: nil
t.boolean "for_procedure_preview", default: false, null: false
t.boolean "for_tiers", default: false, null: false
t.boolean "forced_groupe_instructeur", default: false, null: false
t.bigint "groupe_instructeur_id"
t.datetime "groupe_instructeur_updated_at", precision: nil
@ -431,6 +432,8 @@ ActiveRecord::Schema[7.0].define(version: 2023_11_28_071043) do
t.datetime "last_champ_private_updated_at", precision: nil
t.datetime "last_champ_updated_at", precision: nil
t.datetime "last_commentaire_updated_at", precision: nil
t.string "mandataire_first_name"
t.string "mandataire_last_name"
t.text "motivation"
t.bigint "parent_dossier_id"
t.string "prefill_token"
@ -674,11 +677,14 @@ ActiveRecord::Schema[7.0].define(version: 2023_11_28_071043) do
t.date "birthdate"
t.datetime "created_at", precision: nil
t.integer "dossier_id"
t.string "email"
t.string "gender"
t.string "nom"
t.string "notification_method"
t.string "prenom"
t.datetime "updated_at", precision: nil
t.index ["dossier_id"], name: "index_individuals_on_dossier_id", unique: true
t.index ["email"], name: "index_individuals_on_email"
end
create_table "initiated_mails", id: :serial, force: :cascade do |t|

View file

@ -7,6 +7,8 @@ describe Instructeurs::DossiersController, type: :controller do
let(:instructeurs) { [instructeur] }
let(:procedure) { create(:procedure, :published, :for_individual, instructeurs: instructeurs) }
let(:dossier) { create(:dossier, :en_construction, :with_individual, procedure: procedure) }
let(:dossier_for_tiers) { create(:dossier, :en_construction, :for_tiers_with_notification, procedure: procedure) }
let(:dossier_for_tiers_without_notif) { create(:dossier, :en_construction, :for_tiers_without_notification, procedure: procedure) }
let(:fake_justificatif) { fixture_file_upload('spec/fixtures/files/piece_justificative_0.pdf', 'application/pdf') }
before { sign_in(instructeur.user) }
@ -333,6 +335,58 @@ describe Instructeurs::DossiersController, type: :controller do
end
end
context "with for_tiers" do
before do
dossier_for_tiers.passer_en_instruction!(instructeur: instructeur)
sign_in(instructeur.user)
end
context 'without continuation' do
subject { post :terminer, params: { process_action: "classer_sans_suite", procedure_id: procedure.id, dossier_id: dossier_for_tiers.id }, format: :turbo_stream }
it 'Notification email is sent' do
expect(NotificationMailer).to receive(:send_sans_suite_notification)
.with(dossier_for_tiers).and_return(NotificationMailer)
expect(NotificationMailer).to receive(:deliver_later)
expect(NotificationMailer).to receive(:send_notification_for_tiers)
.with(dossier_for_tiers).and_return(NotificationMailer)
expect(NotificationMailer).to receive(:deliver_later)
subject
end
it '2 emails are sent' do
expect { perform_enqueued_jobs { subject } }.to change { ActionMailer::Base.deliveries.count }.by(2)
end
end
end
context "with for_tiers_without_notif" do
before do
dossier_for_tiers_without_notif.passer_en_instruction!(instructeur: instructeur)
sign_in(instructeur.user)
end
context 'without continuation' do
subject { post :terminer, params: { process_action: "classer_sans_suite", procedure_id: procedure.id, dossier_id: dossier_for_tiers_without_notif.id }, format: :turbo_stream }
it 'Notification email is sent' do
expect(NotificationMailer).to receive(:send_sans_suite_notification)
.with(dossier_for_tiers_without_notif).and_return(NotificationMailer)
expect(NotificationMailer).to receive(:deliver_later)
expect(NotificationMailer).to receive(:send_notification_for_tiers)
.with(dossier_for_tiers_without_notif).and_return(NotificationMailer)
expect(NotificationMailer).to receive(:deliver_later)
subject
end
it 'only one email is sent' do
expect { perform_enqueued_jobs { subject } }.to change { ActionMailer::Base.deliveries.count }.by(1)
end
end
end
context "with classer_sans_suite" do
before do
dossier.passer_en_instruction!(instructeur: instructeur)
@ -354,6 +408,9 @@ describe Instructeurs::DossiersController, type: :controller do
.with(dossier).and_return(NotificationMailer)
expect(NotificationMailer).to receive(:deliver_later)
expect(NotificationMailer).not_to receive(:send_notification_for_tiers)
.with(dossier)
subject
end

View file

@ -164,7 +164,7 @@ describe Users::DossiersController, type: :controller do
let(:dossier) { create(:dossier, user: user, procedure: procedure) }
let(:now) { Time.zone.parse('01/01/2100') }
subject { post :update_identite, params: { id: dossier.id, individual: individual_params } }
subject { post :update_identite, params: { id: dossier.id, dossier: dossier_params } }
before do
sign_in(user)
@ -174,7 +174,7 @@ describe Users::DossiersController, type: :controller do
end
context 'with correct individual and dossier params' do
let(:individual_params) { { gender: 'M', nom: 'Mouse', prenom: 'Mickey' } }
let(:dossier_params) { { individual_attributes: { gender: 'M', nom: 'Mouse', prenom: 'Mickey' } } }
it do
expect(response).to redirect_to(brouillon_dossier_path(dossier))
@ -184,7 +184,7 @@ describe Users::DossiersController, type: :controller do
context 'when the identite cannot be updated by the user' do
let(:dossier) { create(:dossier, :with_individual, :en_instruction, user: user, procedure: procedure) }
let(:individual_params) { { gender: 'M', nom: 'Mouse', prenom: 'Mickey' } }
let(:dossier_params) { { individual_attributes: { gender: 'M', nom: 'Mouse', prenom: 'Mickey' } } }
it 'redirects to the dossiers list' do
expect(response).to redirect_to(dossier_path(dossier))
@ -193,13 +193,40 @@ describe Users::DossiersController, type: :controller do
end
context 'with incorrect individual and dossier params' do
let(:individual_params) { { gender: '', nom: '', prenom: '' } }
let(:dossier_params) { { individual_attributes: { gender: '', nom: '', prenom: '' } } }
it do
expect(response).not_to have_http_status(:redirect)
expect(flash[:alert]).to include("Le champ « Civilité » doit être rempli", "Le champ « Nom » doit être rempli", "Le champ « Prénom » doit être rempli")
end
end
context 'when a dossier is in broullon, for_tiers and we want to update the individual' do
let(:dossier) { create(:dossier, :for_tiers_without_notification, state: "brouillon", user: user, procedure: procedure) }
let(:dossier_params) { { individual_attributes: { gender: 'M', nom: 'Mouse', prenom: 'Mickey', email: 'mickey@gmail.com', notification_method: 'email' } } }
it 'updates the individual with valid notification_method' do
dossier.reload
individual = dossier.individual.reload
expect(individual.errors.full_messages).to be_empty
expect(individual.notification_method).to eq('email')
expect(individual.email).to eq('mickey@gmail.com')
expect(response).to redirect_to(brouillon_dossier_path(dossier))
end
context 'when we want to change the mandataire' do
let(:dossier_params) { { mandataire_first_name: "Jean", mandataire_last_name: "Dupont" } }
it 'updates the dossier mandataire first and last name' do
dossier.reload
individual = dossier.individual.reload
expect(dossier.errors.full_messages).to be_empty
expect(dossier.mandataire_first_name).to eq('Jean')
expect(dossier.mandataire_last_name).to eq('Dupont')
expect(dossier.mandataire_full_name).to eq('Jean Dupont')
end
end
end
end
describe '#siret' do

View file

@ -43,6 +43,40 @@ FactoryBot.define do
end
end
trait :for_tiers_with_notification do
for_tiers { true }
mandataire_first_name { 'John' }
mandataire_last_name { 'Doe' }
transient do
for_individual? { true }
end
after(:build) do |dossier, _evaluator|
if !dossier.procedure.for_individual?
raise 'Inconsistent factory: attempting to create a dossier :with_individual on a procedure that is not `for_individual?`'
end
dossier.individual = build(:individual, :with_notification, dossier: dossier)
end
end
trait :for_tiers_without_notification do
for_tiers { true }
mandataire_first_name { 'John' }
mandataire_last_name { 'Doe' }
transient do
for_individual? { true }
end
after(:build) do |dossier, _evaluator|
if !dossier.procedure.for_individual?
raise 'Inconsistent factory: attempting to create a dossier :with_individual on a procedure that is not `for_individual?`'
end
dossier.individual = build(:individual, :without_notification, dossier: dossier)
end
end
trait :with_individual do
transient do
for_individual? { true }

View file

@ -12,5 +12,14 @@ FactoryBot.define do
prenom { nil }
birthdate { nil }
end
trait :with_notification do
notification_method { :email }
email { 'julien.xavier@test.com' }
end
trait :without_notification do
notification_method { :no_notification }
end
end
end

View file

@ -3,6 +3,16 @@ RSpec.describe NotificationMailer, type: :mailer do
let(:user) { create(:user) }
let(:procedure) { create(:simple_procedure, :with_service) }
describe 'send_notification_for_tiers' do
let(:dossier_for_tiers) { create(:dossier, :en_construction, :for_tiers_with_notification, procedure: create(:simple_procedure)) }
subject { described_class.send_notification_for_tiers(dossier_for_tiers) }
it { expect(subject.subject).to include("Votre dossier rempli par le mandataire #{dossier_for_tiers.mandataire_first_name} #{dossier_for_tiers.mandataire_last_name} a été mis à jour") }
it { expect(subject.to).to eq([dossier_for_tiers.individual.email]) }
it { expect(subject.body).to include("Pour en savoir plus, veuillez vous rapprocher de\r\n<a href=\"mailto:#{dossier_for_tiers.user.email}\">#{dossier_for_tiers.user.email}</a>.") }
end
describe 'send_en_construction_notification' do
let(:dossier) { create(:dossier, :en_construction, :with_individual, user: user, procedure:) }

View file

@ -19,6 +19,10 @@ class NotificationMailerPreview < ActionMailer::Preview
NotificationMailer.send_sans_suite_notification(dossier)
end
def send_notification_for_tiers
NotificationMailer.send_notification_for_tiers(dossier)
end
private
def dossier

View file

@ -33,5 +33,15 @@ describe Individual do
it { expect(individual.birthdate).to be_nil }
end
end
context 'when an individual has an invalid notification_method' do
let(:invalid_individual) { build(:individual, notification_method: 'invalid_method') }
it 'raises an ArgumentError' do
expect {
invalid_individual.valid?
}.to raise_error(ArgumentError, "'invalid_method' is not a valid notification_method")
end
end
end
end

View file

@ -3,8 +3,11 @@ shared_examples "the user has got a prefilled dossier, owned by themselves" do
expect(dossier.user).to eq(user)
expect(page).to have_current_path identite_dossier_path(procedure.dossiers.last)
expect(page).to have_field('Prénom', with: prenom_value)
expect(page).to have_field('Nom', with: nom_value)
within('.individual-infos') do
expect(page).to have_field('Prénom', with: prenom_value)
expect(page).to have_field('Nom', with: nom_value)
end
click_on 'Continuer'
expect(page).to have_current_path(brouillon_dossier_path(dossier))

View file

@ -122,8 +122,10 @@ describe 'wcag rules for usager', js: true do
click_on 'Commencer la démarche'
find('label', text: 'Monsieur')
fill_in('Prénom', with: 'prenom')
fill_in('Nom', with: 'nom')
within('.individual-infos') do
fill_in('Prénom', with: 'prenom')
fill_in('Nom', with: 'nom')
end
click_on 'Continuer'
expect(page).to be_axe_clean

View file

@ -267,8 +267,10 @@ describe 'fetch API Particulier Data', js: true do
click_on 'Commencer la démarche'
find('label', text: 'Monsieur').click
fill_in('Prénom', with: 'prenom')
fill_in('Nom', with: 'nom')
within('.individual-infos') do
fill_in('Prénom', with: 'prenom')
fill_in('Nom', with: 'nom')
end
click_button('Continuer')
@ -325,9 +327,10 @@ describe 'fetch API Particulier Data', js: true do
click_on 'Commencer la démarche'
find('label', text: 'Monsieur').click
fill_in('Prénom', with: 'Georges')
fill_in('Nom', with: 'Moustaki')
within('.individual-infos') do
fill_in('Prénom', with: 'Georges')
fill_in('Nom', with: 'Moustaki')
end
click_button('Continuer')
fill_in "Identifiant", with: 'wrong code'
@ -398,9 +401,10 @@ describe 'fetch API Particulier Data', js: true do
click_on 'Commencer la démarche'
find('label', text: 'Madame').click
fill_in('Prénom', with: 'Dubois')
fill_in('Nom', with: 'Angela Claire Louise')
within('.individual-infos') do
fill_in('Prénom', with: 'Angela Claire Louise')
fill_in('Nom', with: 'Dubois')
end
click_button('Continuer')
fill_in "INE", with: 'wrong code'
@ -461,9 +465,10 @@ describe 'fetch API Particulier Data', js: true do
click_on 'Commencer la démarche'
find('label', text: 'Madame').click
fill_in('Nom', with: 'FERRI')
fill_in('Prénom', with: 'Karine')
within('.individual-infos') do
fill_in('Prénom', with: 'Karine')
fill_in('Nom', with: 'FERRI')
end
click_button('Continuer')
fill_in 'Le numéro fiscal', with: numero_fiscal

View file

@ -263,8 +263,8 @@ describe 'The routing with rules', js: true do
click_on 'Commencer la démarche'
find('label', text: 'Monsieur').click
fill_in('Prénom', with: 'Prenom')
fill_in('Nom', with: 'Nom')
fill_in('Prénom', with: 'prenom', visible: true)
fill_in('Nom', with: 'Nom', visible: true)
click_button('Continuer')
# the old system should not be present

View file

@ -43,7 +43,7 @@ describe 'Signin in:' do
expect(page).to have_current_path identite_dossier_path(user.reload.dossiers.last)
expect(page).to have_content(procedure.libelle)
expect(page).to have_content "Identité du demandeur"
expect(page).to have_content "Votre identité"
end
end

View file

@ -626,7 +626,7 @@ describe 'The user' do
visit "/commencer/#{procedure.path}"
click_on 'Commencer la démarche'
expect(page).to have_content("Identité du demandeur")
expect(page).to have_content("Votre identité")
expect(page).to have_current_path(identite_dossier_path(user_dossier))
end
@ -653,8 +653,8 @@ describe 'The user' do
def fill_individual
find('label', text: 'Monsieur').click
fill_in('Prénom', with: 'prenom')
fill_in('Nom', with: 'nom')
fill_in('Prénom', with: 'prenom', visible: true)
fill_in('Nom', with: 'Nom', visible: true)
click_on 'Continuer'
expect(page).to have_current_path(brouillon_dossier_path(user_dossier))
end

View file

@ -1,4 +1,4 @@
describe 'Creating a new dossier:' do
describe 'Creating a new dossier:', js: true do
let(:user) { create(:user) }
let(:siret) { '41816609600051' }
let(:siren) { siret[0...9] }
@ -22,8 +22,8 @@ describe 'Creating a new dossier:' do
expect(page).to have_title(libelle)
find('label', text: 'Monsieur').click
fill_in('Prénom', with: 'Prenom')
fill_in('Nom', with: 'Nom')
fill_in('Prénom', with: 'prenom', visible: true)
fill_in('Nom', with: 'nom', visible: true)
end
shared_examples 'the user can create a new draft' do
@ -31,26 +31,20 @@ describe 'Creating a new dossier:' do
click_button('Continuer')
expect(page).to have_current_path(brouillon_dossier_path(procedure.dossiers.last))
expect(user.dossiers.first.individual.birthdate).to eq(expected_birthday)
end
end
context 'when the birthday is asked' do
let(:ask_birthday) { true }
let(:expected_birthday) { Date.new(1987, 10, 14) }
let(:expected_birthday) { Date.new(1987, 12, 10) }
before do
fill_in 'Date de naissance', with: birthday_format
end
context 'when the browser supports `type=date` input fields' do
let(:birthday_format) { '1987-10-14' }
it_behaves_like 'the user can create a new draft'
end
context 'when the browser does not support `type=date` input fields' do
let(:birthday_format) { '1987-10-14' }
context 'when the birthday is asked' do
let(:birthday_format) { '12-10-1987' }
it_behaves_like 'the user can create a new draft'
end
end
@ -60,6 +54,50 @@ describe 'Creating a new dossier:' do
let(:expected_birthday) { nil }
it_behaves_like 'the user can create a new draft'
end
context 'when individual fill dossier for a tiers' do
it 'completes the form with email notification method selected' do
find('label', text: 'Pour un bénéficiaire : membre de la famille, proche, mandant...').click
within('.mandataire-infos') do
fill_in('Prénom', with: 'John')
fill_in('Nom', with: 'Doe')
end
find('label', text: 'Monsieur').click
within('.individual-infos') do
fill_in('Prénom', with: 'prenom')
fill_in('Nom', with: 'nom')
end
find('label', text: 'Par e-mail').click
fill_in('dossier_individual_attributes_email', with: 'prenom.nom@mail.com')
click_button('Continuer')
expect(procedure.dossiers.last.individual.notification_method == 'email')
expect(page).to have_current_path(brouillon_dossier_path(procedure.dossiers.last))
end
it 'completes the form with no notification method selected' do
find('label', text: 'Pour un bénéficiaire : membre de la famille, proche, mandant...').click
within('.mandataire-infos') do
fill_in('Prénom', with: 'John')
fill_in('Nom', with: 'Doe')
end
find('label', text: 'Monsieur').click
within('.individual-infos') do
fill_in('Prénom', with: 'prenom')
fill_in('Nom', with: 'nom')
end
find('label', text: 'Pas de notification').click
click_button('Continuer')
expect(procedure.dossiers.last.individual.notification_method.empty?)
expect(page).to have_current_path(brouillon_dossier_path(procedure.dossiers.last))
end
end
end
context 'when identifying through SIRET' do

View file

@ -75,8 +75,10 @@ describe 'dropdown list with other option activated', js: true do
def fill_individual
find('label', text: 'Monsieur').click
fill_in('Prénom', with: 'prenom')
fill_in('Nom', with: 'nom')
within('.individual-infos') do
fill_in('Prénom', with: 'prenom')
fill_in('Nom', with: 'nom')
end
click_on 'Continuer'
expect(page).to have_current_path(brouillon_dossier_path(user_dossier))
end

View file

@ -75,14 +75,16 @@ describe 'linked dropdown lists' do
expect(page).to have_current_path(commencer_path(path: procedure.path))
click_on 'Commencer la démarche'
expect(page).to have_content("Identité du demandeur")
expect(page).to have_content("Votre identité")
expect(page).to have_current_path(identite_dossier_path(user_dossier))
end
def fill_individual
find('label', text: 'Monsieur').click
fill_in('Prénom', with: 'prenom')
fill_in('Nom', with: 'nom')
within('.individual-infos') do
fill_in('Prénom', with: 'prenom')
fill_in('Nom', with: 'nom')
end
click_on 'Continuer'
expect(page).to have_current_path(brouillon_dossier_path(user_dossier))
end

View file

@ -8,6 +8,7 @@ describe 'user access to the list of their dossiers', js: true do
let!(:dossier_refuse) { create(:dossier, :refuse, user: user) }
let!(:dossier_a_corriger) { create(:dossier_correction, dossier: dossier_en_construction_2) }
let!(:dossier_archived) { create(:dossier, :en_instruction, :archived, user: user) }
let!(:dossier_for_tiers) { create(:dossier, :en_instruction, :for_tiers_with_notification, user: user) }
let(:dossiers_per_page) { 25 }
let(:last_updated_dossier) { dossier_en_construction }
@ -30,7 +31,7 @@ describe 'user access to the list of their dossiers', js: true do
expect(page).to have_content(dossier_en_construction.procedure.libelle)
expect(page).to have_content(dossier_en_instruction.procedure.libelle)
expect(page).to have_content(dossier_archived.procedure.libelle)
expect(page).to have_text('5 en cours')
expect(page).to have_text('6 en cours')
expect(page).to have_text('2 traités')
end
@ -52,17 +53,26 @@ describe 'user access to the list of their dossiers', js: true do
scenario 'the user can navigate through the other pages' do
expect(page).not_to have_link(dossier_en_instruction.procedure.libelle)
page.click_link("Suivant")
page.click_link("Suivant")
expect(page).to have_link(dossier_en_instruction.procedure.libelle)
expect(page).to have_text('5 en cours')
expect(page).to have_text('6 en cours')
expect(page).to have_text('2 traités')
end
end
context 'when the dossier is for_tiers' do
let(:individual) { dossier_for_tiers.individual }
it 'displays the name of the mandataire' do
expect(page).to have_content("#{individual.prenom} #{individual.nom}, dossier rempli par #{dossier_for_tiers.mandataire_full_name}")
end
end
context 'when user uses filter' do
scenario 'user filters state on tab "en-cours"' do
expect(page).to have_text('5 en cours')
expect(page).to have_text('6 en cours')
expect(page).to have_text('2 traités')
expect(page).to have_text('5 sur 5 dossiers')
expect(page).to have_text('6 sur 6 dossiers')
click_on('Sélectionner un filtre')
expect(page).to have_select 'Statut', selected: 'Sélectionner un statut', options: ['Sélectionner un statut', 'Brouillon', 'En construction', 'En instruction', 'À corriger']
@ -82,7 +92,7 @@ describe 'user access to the list of their dossiers', js: true do
scenario 'user filters state on tab "traité"' do
visit dossiers_path(statut: 'traites')
expect(page).to have_text('5 en cours')
expect(page).to have_text('6 en cours')
expect(page).to have_text('2 traités')
expect(page).to have_text('2 sur 2 dossiers')
@ -102,11 +112,11 @@ describe 'user access to the list of their dossiers', js: true do
scenario 'user filters by created_at' do
dossier_en_construction.update!(created_at: Date.yesterday)
expect(page).to have_text('5 sur 5 dossiers')
expect(page).to have_text('6 sur 6 dossiers')
click_on('Sélectionner un filtre')
fill_in 'from_created_at_date', with: Date.today
click_on('Appliquer les filtres')
expect(page).to have_text('4 sur 4 dossiers')
expect(page).to have_text('5 sur 5 dossiers')
end
scenario 'user uses multiple filters' do
@ -114,11 +124,11 @@ describe 'user access to the list of their dossiers', js: true do
expect(page).to have_select 'Statut', selected: 'Sélectionner un statut', options: ['Sélectionner un statut', 'Brouillon', 'En construction', 'En instruction', 'À corriger']
expect(page).to have_text('5 sur 5 dossiers')
expect(page).to have_text('6 sur 6 dossiers')
click_on('Sélectionner un filtre')
fill_in 'from_created_at_date', with: Date.today
click_on('Appliquer les filtres')
expect(page).to have_text('4 sur 4 dossiers')
expect(page).to have_text('5 sur 5 dossiers')
expect(page).to have_text('1 filtre actif')
click_on('Sélectionner un filtre')
@ -133,7 +143,7 @@ describe 'user access to the list of their dossiers', js: true do
expect(page).to have_text('1 dossier')
expect(page).to have_text('3 filtres actifs')
click_on('3 filtres actifs')
expect(page).to have_text('5 sur 5 dossiers')
expect(page).to have_text('6 sur 6 dossiers')
expect(page).not_to have_text('5 filtres actifs')
end
end
@ -275,7 +285,7 @@ describe 'user access to the list of their dossiers', js: true do
describe "filter by procedure" do
context "when dossiers are on different procedures" do
it "can filter by procedure" do
expect(page).to have_text('5 en cours')
expect(page).to have_text('6 en cours')
expect(page).to have_text('2 traités')
expect(page).to have_select('procedure_id', selected: 'Toutes les démarches')
select dossier_brouillon.procedure.libelle, from: 'procedure_id'

View file

@ -35,6 +35,15 @@ describe 'users/dossiers/brouillon', type: :view do
let(:procedure) { create(:procedure) }
it { is_expected.not_to have_link("Télécharger le guide de la démarche") }
end
context 'when a dossier is for_tiers and the dossier en construction with email notification' do
let(:dossier) { create(:dossier, :for_tiers_with_notification) }
it 'displays the informations of the beneficiaire' do
expect(rendered).to have_text("Identité du demandeur")
expect(rendered).not_to have_text("Votre identité")
end
end
end
context "as an administrateur" do

View file

@ -48,4 +48,15 @@ describe 'users/dossiers/demande', type: :view do
expect(view.content_for(:notice_info)).to have_text("Le dossier a été déposé par le compte de #{france_connect_information.given_name} #{france_connect_information.family_name}, authentifié par FranceConnect le #{france_connect_information.updated_at.strftime('%d/%m/%Y')}")
end
end
context 'when a dossier is for_tiers and the dossier is en_construction with email notification' do
let(:dossier) { create(:dossier, :en_construction, :for_tiers_with_notification) }
it 'displays the informations of the mandataire' do
expect(rendered).to have_text('Identité du mandataire')
expect(rendered).to have_text(dossier.mandataire_first_name.to_s)
expect(rendered).to have_text(dossier.mandataire_last_name.to_s)
expect(rendered).to have_text(dossier.individual.email.to_s)
end
end
end

View file

@ -10,8 +10,10 @@ describe 'users/dossiers/identite', type: :view do
subject! { render }
it 'has identity fields' do
expect(rendered).to have_field('Prénom')
expect(rendered).to have_field('Nom')
within('.individual-infos') do
expect(rendered).to have_field(id: 'Prenom')
expect(rendered).to have_field(id: 'Nom')
end
end
context 'when the demarche asks for the birthdate' do