diff --git a/Gemfile.lock b/Gemfile.lock
index 01cbdf8d33..a5f376ada5 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -711,7 +711,7 @@ GEM
unicode-emoji (~> 4.0, >= 4.0.4)
unicode-emoji (4.0.4)
uniform_notifier (1.16.0)
- uri (0.13.0)
+ uri (1.0.3)
validate_email (0.1.6)
activemodel (>= 3.0)
mail (>= 2.2.5)
diff --git a/app/controllers/admin/organisations_controller.rb b/app/controllers/admin/organisations_controller.rb
index 9cbdf53fd4..d3813c4d40 100644
--- a/app/controllers/admin/organisations_controller.rb
+++ b/app/controllers/admin/organisations_controller.rb
@@ -2,7 +2,6 @@ class Admin::OrganisationsController < AgentAuthController
respond_to :html, :json
before_action :set_organisation, except: :index
- before_action :follow_unique, only: :index
def index
@organisations_by_territory = policy_scope(current_agent.organisations, policy_scope_class: Agent::OrganisationPolicy::Scope)
@@ -73,11 +72,4 @@ def organisation_params
def new_organisation_params
params.require(:organisation).permit(:name, :territory_id)
end
-
- def follow_unique
- accessible_organisations = policy_scope(Organisation, policy_scope_class: Agent::OrganisationPolicy::Scope)
- return if params[:follow_unique].blank? || accessible_organisations.count != 1
-
- redirect_to admin_organisation_agent_agenda_path(accessible_organisations.first, current_agent)
- end
end
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 7bb4888865..896abc8262 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -12,7 +12,7 @@ class Admin::UsersController < AgentAuthController
family_situation number_of_children
notify_by_sms notify_by_email
case_number address_details
- notes logement ants_pre_demande_number notification_email
+ logement ants_pre_demande_number notification_email
].freeze
PERMITTED_NESTED_ATTRIBUTES = {
@@ -51,7 +51,7 @@ def create
prepare_create
authorize(@user, policy_class: Agent::UserPolicy)
@user.skip_confirmation_notification!
- user_persisted = @user_form.save
+ user_persisted = @user_form.save(annotation_content: params.dig(:user, :annotation_content), current_territory:)
if invite_user?(@user, params)
@user.invite!(domain: current_domain)
@@ -83,7 +83,7 @@ def update
@user.assign_attributes(user_params)
@user_form = user_form_object
@user.skip_reconfirmation! if @user.encrypted_password.blank?
- user_updated = @user_form.save
+ user_updated = @user_form.save(annotation_content: params.dig(:user, :annotation_content), current_territory:)
if from_modal?
respond_modal_with @user_form, location: modal_return_location
elsif user_updated
diff --git a/app/controllers/agent_connect_controller.rb b/app/controllers/agent_connect_controller.rb
index 638e959f20..c5d6222ca0 100644
--- a/app/controllers/agent_connect_controller.rb
+++ b/app/controllers/agent_connect_controller.rb
@@ -14,7 +14,7 @@ def auth
redirect_to auth_client.redirect_url(agent_connect_callback_url), allow_other_host: true
end
- def callback
+ def callback # rubocop:disable Metrics/PerceivedComplexity
callback_client = AgentConnectOpenIdClient::Callback.new(
session_state: session.delete(:agent_connect_state),
params_state: params[:state],
@@ -33,6 +33,10 @@ def callback
# voir https://github.com/numerique-gouv/agentconnect-documentation/blob/main/doc_fs/donnees_fournies.md#le-champ-sub
agent = Agent.active.find_by(email: callback_client.user_email)
+ if current_domain.allow_agent_creation_with_agent_connect
+ agent ||= Agent.new(email: callback_client.user_email, password: SecureRandom.base64(32))
+ end
+
if agent
agent.update!(
connected_with_agent_connect: true,
diff --git a/app/controllers/agents/pages_controller.rb b/app/controllers/agents/pages_controller.rb
new file mode 100644
index 0000000000..dfaa24fc15
--- /dev/null
+++ b/app/controllers/agents/pages_controller.rb
@@ -0,0 +1,23 @@
+class Agents::PagesController < AgentAuthController
+ layout "application"
+
+ CONTACT_TEAM_URL = "https://cal.com/forms/937585aa-48a4-4efd-a642-961fad79c9c5".freeze
+
+ def home
+ skip_authorization
+
+ accessible_organisations = policy_scope(Organisation, policy_scope_class: Agent::OrganisationPolicy::Scope)
+
+ if accessible_organisations.count == 1
+ redirect_to admin_organisation_agent_agenda_path(accessible_organisations.first, current_agent)
+ elsif accessible_organisations.count > 1
+ redirect_to admin_organisations_path
+ end
+ end
+
+ private
+
+ def pundit_user
+ AgentContext.new(current_agent)
+ end
+end
diff --git a/app/controllers/static_pages_controller.rb b/app/controllers/static_pages_controller.rb
index b4824f5223..2cfe2af22d 100644
--- a/app/controllers/static_pages_controller.rb
+++ b/app/controllers/static_pages_controller.rb
@@ -1,7 +1,10 @@
class StaticPagesController < ApplicationController
def mds
- redirect_to root_path unless current_domain == Domain::RDV_SOLIDARITES
- render layout: "application_base"
+ if current_domain == Domain::RDV_SOLIDARITES
+ render layout: "application_base"
+ else
+ redirect_to root_path
+ end
end
def accessibility; end
diff --git a/app/controllers/super_admins/comptes_controller.rb b/app/controllers/super_admins/comptes_controller.rb
index 9114d98f45..ed92fcb9ca 100644
--- a/app/controllers/super_admins/comptes_controller.rb
+++ b/app/controllers/super_admins/comptes_controller.rb
@@ -1,5 +1,14 @@
module SuperAdmins
class ComptesController < SuperAdmins::ApplicationController
+ def new
+ @agent = Agent.find_by(id: params[:agent_id])
+ if @agent
+ authorize_resource(@agent)
+ end
+
+ super
+ end
+
def create
compte_params[:agent][:invited_by] = current_super_admin
compte = Compte.new(compte_params, current_domain)
@@ -24,7 +33,7 @@ def compte_params
territory: %i[name departement_number],
organisation: %i[name ants_connectable],
lieu: %i[address latitude longitude],
- agent: %i[first_name last_name email service_ids]
+ agent: %i[id first_name last_name email service_ids]
)
end
end
diff --git a/app/form_models/admin/user_form.rb b/app/form_models/admin/user_form.rb
index e8877c8139..312776a411 100644
--- a/app/form_models/admin/user_form.rb
+++ b/app/form_models/admin/user_form.rb
@@ -20,8 +20,15 @@ def valid?
super && user.valid? # order is important here
end
- def save
- valid? && user.save
+ def save(annotation_content:, current_territory:)
+ return false unless valid?
+
+ user.transaction do
+ if user.save
+ user.annotate!(annotation_content, territory: current_territory)
+ true
+ end
+ end
end
private
diff --git a/app/form_models/compte.rb b/app/form_models/compte.rb
index 9eb3c0f027..2e091f9b10 100644
--- a/app/form_models/compte.rb
+++ b/app/form_models/compte.rb
@@ -26,10 +26,7 @@ def save!
organisation.save!
lieu.save!
- self.agent = Agent.invite!(@attributes[:agent].merge(
- password: SecureRandom.base64(32),
- roles_attributes: [{ organisation: organisation, access_level: AgentRole::ACCESS_LEVEL_ADMIN }]
- ))
+ self.agent = find_or_invite_agent(organisation)
agent.services.each do |service|
TerritoryService.create!(service: service, territory: territory)
@@ -65,6 +62,23 @@ def to_s
private
+ def find_or_invite_agent(organisation)
+ if @attributes.dig(:agent, :id)
+ Agent.find(@attributes.dig(:agent, :id)).tap do |agent|
+ agent.update(
+ @attributes[:agent].merge(
+ roles_attributes: [{ organisation: organisation, access_level: AgentRole::ACCESS_LEVEL_ADMIN }]
+ )
+ )
+ end
+ else
+ Agent.invite!(@attributes[:agent].merge(
+ roles_attributes: [{ organisation: organisation, access_level: AgentRole::ACCESS_LEVEL_ADMIN }],
+ password: SecureRandom.base64(32)
+ ))
+ end
+ end
+
def create_mairie_motifs!
service = Service.find_by(name: Service::MAIRIE)
diff --git a/app/form_models/merge_users_form.rb b/app/form_models/merge_users_form.rb
index 3512999537..b928cac5c7 100644
--- a/app/form_models/merge_users_form.rb
+++ b/app/form_models/merge_users_form.rb
@@ -92,7 +92,11 @@ def attributes_to_merge
end
def values_for(attribute)
- [user1&.send(attribute), user2&.send(attribute)]
+ if attribute == :annotation_content
+ [user1.annotation_for(@organisation.territory), user2.annotation_for(@organisation.territory)]
+ else
+ [user1&.send(attribute), user2&.send(attribute)]
+ end
end
def different_users?
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 39965623f8..7fb377bfdb 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -113,9 +113,12 @@ def boolean_attribute_tag(object, attribute_name)
boolean_tag(value) { object.class.human_attribute_value(attribute_name, value) }
end
- def object_attribute_tag(object, attribute_name, value = nil)
+ def object_attribute_tag(object, attribute_name, value = :delegate_to_object)
name = object.class.human_attribute_name(attribute_name)
- value ||= object.human_attribute_value(attribute_name)
+
+ if value == :delegate_to_object
+ value = object.human_attribute_value(attribute_name)
+ end
tag.strong(tag.span(name) + tag.span(" : ")) +
tag.span(value.presence || "Non renseigné", class: class_names("text-muted": value.blank?))
diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb
index 5b5d521c3d..20dc22be12 100644
--- a/app/helpers/users_helper.rb
+++ b/app/helpers/users_helper.rb
@@ -116,8 +116,9 @@ def clickable_user_phone_number(user)
user.responsible_phone_number.present? ? link_to(user.responsible_phone_number, "tel:#{user.responsible_or_self.phone_number_formatted}") : nil
end
- def formatted_user_notes(user)
- user.notes.present? ? simple_format(user.notes) : nil
+ def formatted_user_annotation(user, current_territory)
+ annotation = user.annotation_for(current_territory)
+ annotation.present? ? simple_format(annotation) : nil
end
def user_soft_delete_confirm_message(user)
@@ -180,10 +181,10 @@ def default_service_selection_from(source)
:responsible
end
- def user_merge_attribute_value(user, attribute)
+ def user_merge_attribute_value(user, attribute, current_territory)
return birth_date_and_age(user) if attribute == :birth_date
return user.responsible&.full_name if attribute == :responsible_id
- return formatted_user_notes(user) if attribute == :notes
+ return formatted_user_annotation(user, current_territory) if attribute == :annotation_content
return user&.human_attribute_value(:logement) if attribute == :logement
user.send(attribute)
diff --git a/app/javascript/stylesheets/administrate/base/_forms.scss b/app/javascript/stylesheets/administrate/base/_forms.scss
index 695b440f86..e2e637fc2d 100644
--- a/app/javascript/stylesheets/administrate/base/_forms.scss
+++ b/app/javascript/stylesheets/administrate/base/_forms.scss
@@ -107,3 +107,7 @@ select {
outline-offset: $focus-outline-offset;
}
}
+
+.form-group {
+ margin: 8px 0;
+}
diff --git a/app/models/agent.rb b/app/models/agent.rb
index 216dc9b00e..af0f0e0e06 100644
--- a/app/models/agent.rb
+++ b/app/models/agent.rb
@@ -100,7 +100,7 @@ def timeout_in = 14.days # Used by Devise's :timeoutable
# * it validates :email (the invite_key) specifically with Devise.email_regexp.
validates :first_name, presence: true, unless: -> { allow_blank_name || is_an_intervenant? }
validates :last_name, presence: true, unless: -> { allow_blank_name }
- validates :agent_services, presence: true
+ validates :agent_services, presence: true, unless: -> { roles.none? }
# Hooks
before_destroy :prevent_destroy_if_rdvs
diff --git a/app/models/annotation.rb b/app/models/annotation.rb
index bb77f8a0a4..040f31a30c 100644
--- a/app/models/annotation.rb
+++ b/app/models/annotation.rb
@@ -4,4 +4,14 @@ class Annotation < ApplicationRecord
validates :content, presence: true
validates :territory_id, uniqueness: { scope: :user_id }
+
+ def self.upsert!(content, user:, territory:)
+ if content.present?
+ annotation = find_or_initialize_by(user:, territory:)
+ annotation.content = content
+ annotation.save!
+ else
+ find_by(user:, territory:)&.destroy!
+ end
+ end
end
diff --git a/app/models/domain.rb b/app/models/domain.rb
index 62ec2640c7..4823b80c88 100644
--- a/app/models/domain.rb
+++ b/app/models/domain.rb
@@ -16,6 +16,7 @@
:support_email,
:secretariat_email,
:verticale,
+ :allow_agent_creation_with_agent_connect,
keyword_init: true
)
@@ -36,6 +37,7 @@ class Domain
france_connect_enabled: true,
support_email: "support@rdv-solidarites.fr",
verticale: :rdv_solidarites,
+ allow_agent_creation_with_agent_connect: false,
secretariat_email: "secretariat-auto@rdv-solidarites.fr"
# secretariat_email est utilisé comme adresse de "Reply-To" pour les e-mails
# qui contiennent des ICS. Lorsque l'événement ICS est acceptée par le
@@ -58,6 +60,7 @@ class Domain
france_connect_enabled: false,
support_email: "support@rdv-aide-numerique.fr",
verticale: :rdv_aide_numerique,
+ allow_agent_creation_with_agent_connect: false,
secretariat_email: "secretariat-auto@rdv-solidarites.fr"
),
@@ -76,6 +79,7 @@ class Domain
france_connect_enabled: true,
support_email: "support@rdv-service-public.fr",
verticale: :rdv_mairie,
+ allow_agent_creation_with_agent_connect: true,
secretariat_email: "secretariat-auto@rdv-service-public.fr"
),
].freeze
diff --git a/app/models/territory.rb b/app/models/territory.rb
index b95999f201..4185b7d82d 100644
--- a/app/models/territory.rb
+++ b/app/models/territory.rb
@@ -91,7 +91,7 @@ class Territory < ApplicationRecord
}.freeze
OPTIONAL_FIELD_TOGGLES = {
- enable_notes_field: :notes,
+ enable_notes_field: :annotation_content,
enable_logement_field: :logement,
}.merge(SOCIAL_FIELD_TOGGLES).freeze
diff --git a/app/models/user.rb b/app/models/user.rb
index af60a46a1b..d5b05deb15 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1,5 +1,5 @@
class User < ApplicationRecord
- self.ignored_columns += %i[invitations_count]
+ self.ignored_columns += %i[invitations_count notes]
# Mixins
has_paper_trail(
@@ -77,8 +77,6 @@ def self.search_options
# Hooks
before_save :set_email_to_null_if_blank
before_save :clear_notification_email_if_email_present
- after_create :create_annotations # backfill temporaire, première étape de migration
- after_update :sync_annotations # backfill temporaire, première étape de migration
# Scopes
default_scope { where(deleted_at: nil) }
@@ -265,6 +263,14 @@ def cleanup_annotations
annotations.where.not(territory: territories.reload).each(&:destroy!)
end
+ def annotation_for(territory)
+ annotations.find_by(territory: territory)&.content
+ end
+
+ def annotate!(content, territory:)
+ Annotation.upsert!(content, user: self, territory:)
+ end
+
protected
def generate_rdv_invitation_token
@@ -298,30 +304,6 @@ def set_email_to_null_if_blank
self.email = nil if email.blank?
end
- def create_annotations
- return if notes.blank?
-
- territories.distinct.map.each do |territory|
- annotation = annotations.find_or_initialize_by(territory:)
- annotation.content = notes
- annotation.save!
- end
- end
-
- def sync_annotations
- return unless notes_previously_changed?
-
- if notes.present?
- territories.distinct.map.each do |territory|
- annotation = annotations.find_or_initialize_by(territory:)
- annotation.content = notes
- annotation.save!
- end
- else
- annotations.destroy_all
- end
- end
-
def clear_notification_email_if_email_present
self.notification_email = nil if email.present?
end
diff --git a/app/services/admin_creates_agent.rb b/app/services/admin_creates_agent.rb
index b077d23c8f..23993464e9 100644
--- a/app/services/admin_creates_agent.rb
+++ b/app/services/admin_creates_agent.rb
@@ -12,6 +12,9 @@ def call
@agent = find_agent
if @agent
+ if @agent.services.none?
+ @agent.update(service_ids: @agent_params[:service_ids])
+ end
add_agent_to_organisations
@warning_message = self.class.check_agent_service(@agent, @agent_params[:service_ids])
elsif @access_level == "intervenant"
diff --git a/app/services/concerns/users/creneaux_wizard_concern.rb b/app/services/concerns/users/creneaux_wizard_concern.rb
index e00586c644..d3b12364cc 100644
--- a/app/services/concerns/users/creneaux_wizard_concern.rb
+++ b/app/services/concerns/users/creneaux_wizard_concern.rb
@@ -1,8 +1,7 @@
module Users::CreneauxWizardConcern
extend ActiveSupport::Concern
- # *** Method that outputs the next step for the user to complete its rdv journey ***
- # *** It is used in #to_partial_path to render the matching partial view ***
+ # *** Method that outputs the current step for the user to complete its rdv journey ***
def current_step
if departement.blank?
:address_selection
@@ -23,10 +22,6 @@ def start_date
query_params[:date]&.to_date || super
end
- def to_partial_path
- "search/#{current_step}"
- end
-
def wizard_after_creneau_selection_path(params)
url_helpers = Rails.application.routes.url_helpers
if @prescripteur
@@ -48,7 +43,7 @@ def unique_motifs_by_name_and_location_type
# Retourne une liste d'organisations et leur prochaine dispo, ordonnées par date de prochaine dispo
def next_availability_by_motifs_organisations
@next_availability_by_motifs_organisations ||= matching_motifs.to_h do |motif|
- [motif.organisation, creneaux_search_for(nil, date_range, motif).next_availability]
+ [motif.organisation, creneaux_search_for(nil, motif).next_availability]
end.compact.sort_by(&:last).to_h
end
@@ -72,7 +67,7 @@ def next_availability_by_lieux
return @next_availability_by_lieux if @next_availability_by_lieux
next_availability_by_lieux = Lieu.with_open_slots_for_motifs(matching_motifs).includes(:organisation).to_h do |lieu|
- next_availability = creneaux_search_for(lieu, date_range, matching_motifs.where(organisation: lieu.organisation).first).next_availability
+ next_availability = creneaux_search_for(lieu, matching_motifs.where(organisation: lieu.organisation).first).next_availability
[lieu, next_availability]
end.compact
diff --git a/app/services/invitation_search_context.rb b/app/services/invitation_search_context.rb
index 4761d01b63..411919c971 100644
--- a/app/services/invitation_search_context.rb
+++ b/app/services/invitation_search_context.rb
@@ -35,20 +35,4 @@ def matching_motifs
Motif.available_for_booking.where(organisation_id: @organisation_ids).joins(:organisation)
)
end
-
- def contactable_organisations
- @contactable_organisations ||= Organisation.where(id: @organisation_ids).contactable
- end
-
- def organisations_emails
- contactable_organisations.where.not(email: [nil, ""]).pluck(:email).join(",")
- end
-
- def motif_category_name
- @motif_category_short_name.present? ? MotifCategory.find_by(short_name: @motif_category_short_name)&.name : nil
- end
-
- private
-
- attr_reader :referent_ids, :lieu_id
end
diff --git a/app/services/merge_users_service.rb b/app/services/merge_users_service.rb
index 868d27e719..71206fa72d 100644
--- a/app/services/merge_users_service.rb
+++ b/app/services/merge_users_service.rb
@@ -8,6 +8,7 @@ def initialize(user_target, user_to_merge, attributes_to_merge, organisation)
def perform
User.transaction do
+ merge_annotations if @attributes_to_merge.delete(:annotation_content)
merge_user_attributes
merge_organisations
merge_rdvs
@@ -20,6 +21,14 @@ def perform
private
+ def merge_annotations
+ current_territory = @organisation.territory
+
+ annotation_to_merge = @user_to_merge.annotations.find_by(territory: current_territory)
+ @user_target.annotate!(annotation_to_merge.content, territory: current_territory)
+ annotation_to_merge.destroy!
+ end
+
def merge_user_attributes
@attributes_to_merge.each do |attribute|
@user_target.send("#{attribute}=", @user_to_merge.send(attribute))
diff --git a/app/services/search_context.rb b/app/services/search_context.rb
index 4bcc242727..0335a6ca0b 100644
--- a/app/services/search_context.rb
+++ b/app/services/search_context.rb
@@ -25,7 +25,7 @@ def available_collective_rdvs
end
def creneaux_search
- creneaux_search_for(lieu, date_range, first_matching_motif)
+ creneaux_search_for(lieu, first_matching_motif)
end
def first_matching_motif
@@ -54,9 +54,7 @@ def filter_motifs(available_motifs)
private
- def referent_ids
- raise NoMethodError
- end
+ attr_reader :referent_ids, :lieu_id
def matching_motifs
raise NoMethodError
@@ -74,11 +72,7 @@ def street_ban_id
raise NoMethodError
end
- def lieu_id
- raise NoMethodError
- end
-
- def creneaux_search_for(lieu, date_range, motif)
+ def creneaux_search_for(lieu, motif)
CreneauxSearch::ForUser.new(
user: @user,
motif: motif,
diff --git a/app/services/users/contactable_organisations.rb b/app/services/users/contactable_organisations.rb
new file mode 100644
index 0000000000..8534ffc3cb
--- /dev/null
+++ b/app/services/users/contactable_organisations.rb
@@ -0,0 +1,18 @@
+class Users::ContactableOrganisations
+ def initialize(organisation_ids, motif_category_short_name)
+ @organisation_ids = organisation_ids
+ @motif_category_short_name = motif_category_short_name
+ end
+
+ def organisations
+ @organisations ||= Organisation.where(id: @organisation_ids).contactable
+ end
+
+ def organisations_emails
+ organisations.where.not(email: [nil, ""]).pluck(:email).join(",")
+ end
+
+ def motif_category_name
+ @motif_category_short_name.present? ? MotifCategory.find_by(short_name: @motif_category_short_name)&.name : nil
+ end
+end
diff --git a/app/services/web_invitation_search_context.rb b/app/services/web_invitation_search_context.rb
index fb5820bab2..9eeb4ec47a 100644
--- a/app/services/web_invitation_search_context.rb
+++ b/app/services/web_invitation_search_context.rb
@@ -1,6 +1,6 @@
class WebInvitationSearchContext < InvitationSearchContext
include Users::CreneauxWizardConcern
- attr_reader :errors, :query_params, :address, :latitude, :longitude
+ attr_reader :errors, :query_params, :address, :latitude, :longitude, :organisation_ids, :motif_category_short_name
def initialize(user:, query_params: {})
super
diff --git a/app/services/web_search_context.rb b/app/services/web_search_context.rb
index b8f450c7ca..77fa1d965e 100644
--- a/app/services/web_search_context.rb
+++ b/app/services/web_search_context.rb
@@ -13,7 +13,6 @@ def initialize(user:, query_params: {})
@user_selected_organisation_id = query_params[:user_selected_organisation_id]
@external_organisation_ids = query_params[:external_organisation_ids]
@motif_id = query_params[:motif_id]
- @motif_category_short_name = query_params[:motif_category_short_name]
@motif_name_with_location_type = query_params[:motif_name_with_location_type]
@service_id = query_params[:service_id]
@lieu_id = query_params[:lieu_id]
@@ -68,8 +67,6 @@ def motif_param_present?
private
- attr_reader :referent_ids, :lieu_id
-
def matching_motifs
@matching_motifs ||= filter_motifs(geo_search.available_motifs)
end
diff --git a/app/views/admin/merge_users/new.html.slim b/app/views/admin/merge_users/new.html.slim
index cbe51784ba..9737f40946 100644
--- a/app/views/admin/merge_users/new.html.slim
+++ b/app/views/admin/merge_users/new.html.slim
@@ -27,8 +27,8 @@
td= render "user_selection", user: @merge_users_form.user2, attribute: "user2_id", f: f, other_user_id: @merge_users_form.user1&.id
- @merge_users_form.available_attributes.each do |attribute|
- - value1 = user_merge_attribute_value(@merge_users_form.user1, attribute) if @merge_users_form.user1
- - value2 = user_merge_attribute_value(@merge_users_form.user2, attribute) if @merge_users_form.user2
+ - value1 = user_merge_attribute_value(@merge_users_form.user1, attribute, current_territory) if @merge_users_form.user1
+ - value2 = user_merge_attribute_value(@merge_users_form.user2, attribute, current_territory) if @merge_users_form.user2
tr class=@merge_users_form.attribute_comparison(attribute)
td= User.human_attribute_name(attribute)
diff --git a/app/views/admin/participations/_user_card.html.slim b/app/views/admin/participations/_user_card.html.slim
index 1413ecef4a..4754ac4e18 100644
--- a/app/views/admin/participations/_user_card.html.slim
+++ b/app/views/admin/participations/_user_card.html.slim
@@ -3,11 +3,11 @@
.card-body.p-2
.mb-1
= render "admin/users/identity", user: participation.user
- - notes = participation.user.notes
- - if current_territory.enable_notes_field? && notes.present?
+ - annotation = participation.user.annotation_for(current_territory)
+ - if current_territory.enable_notes_field? && annotation.present?
.participation_notes
i.fa.fa-info-circle.text-primary-blue.mr-2
- = simple_format(notes, {}, wrapper_tag: :span)
+ = simple_format(annotation, {}, wrapper_tag: :span)
div
= render "admin/participations/notifications_email", participation: participation
div
diff --git a/app/views/admin/participations/_user_details.html.slim b/app/views/admin/participations/_user_details.html.slim
index 3ee4fcbd91..9a3edab811 100644
--- a/app/views/admin/participations/_user_details.html.slim
+++ b/app/views/admin/participations/_user_details.html.slim
@@ -11,7 +11,7 @@ div
li= object_attribute_tag(user, :address)
li= object_attribute_tag(user, :email, clickable_user_email(user))
- if current_territory.enable_notes_field?
- li= object_attribute_tag(user, :notes, formatted_user_notes(user))
+ li= object_attribute_tag(user, :annotation_content, formatted_user_annotation(user, current_territory))
- if current_territory.enable_logement_field
li= object_attribute_tag(user, :logement)
- Territory::SOCIAL_FIELD_TOGGLES.each do |toggle, field_name|
diff --git a/app/views/admin/prescription/search_creneau.html.slim b/app/views/admin/prescription/search_creneau.html.slim
index f858cbf2d3..6272bc1566 100644
--- a/app/views/admin/prescription/search_creneau.html.slim
+++ b/app/views/admin/prescription/search_creneau.html.slim
@@ -9,4 +9,4 @@ h1 Nouveau RDV par prescription
|> Pas d'adresse
= link_to("ajouter une addresse", edit_admin_organisation_user_path(current_organisation, @context.user))
section.container
- = render partial: @context.to_partial_path, locals: { context: @context }
+ = render partial: "search/#{@context.current_step}", locals: { context: @context }
diff --git a/app/views/admin/rdvs/_users_table.html.slim b/app/views/admin/rdvs/_users_table.html.slim
index 5331b7c896..22bbc41524 100644
--- a/app/views/admin/rdvs/_users_table.html.slim
+++ b/app/views/admin/rdvs/_users_table.html.slim
@@ -2,7 +2,7 @@
table.table.light-gray-table
thead
tr
- / Fixed width because of user profile notes field that could messed up the table.
+ / Fixed width because of user profile annotation field that could messed up the table.
th style="max-width: 30%" Participant
- if rdv.requires_ants_predemande_number?
th =t("activerecord.attributes.user.ants_pre_demande_number")
diff --git a/app/views/admin/rdvs/print/_user_details.html.slim b/app/views/admin/rdvs/print/_user_details.html.slim
index 568963ede5..2f9d5fb487 100644
--- a/app/views/admin/rdvs/print/_user_details.html.slim
+++ b/app/views/admin/rdvs/print/_user_details.html.slim
@@ -1,6 +1,6 @@
div= object_attribute_tag(user, :address)
- if current_territory.enable_notes_field?
- div= object_attribute_tag(user, :notes)
+ div= object_attribute_tag(user, :annotation_content, user.annotation_for(current_territory).to_s)
- if current_territory.enable_logement_field
div= object_attribute_tag(user, :logement)
- Territory::SOCIAL_FIELD_TOGGLES.each do |toggle, field_name|
diff --git a/app/views/admin/users/_form.html.slim b/app/views/admin/users/_form.html.slim
index d0809b372c..d95ec8e828 100644
--- a/app/views/admin/users/_form.html.slim
+++ b/app/views/admin/users/_form.html.slim
@@ -41,7 +41,7 @@
- unless current_domain == Domain::RDV_MAIRIE
= date_input(f, :birth_date, **input_opts[:relative])
- if current_territory.enable_notes_field?
- = f.input :notes, **input_opts[:relative].deep_merge(input_html: { rows: 6 })
+ = f.input :annotation_content, **input_opts[:relative].deep_merge(input_html: { rows: 6, value: user.annotation_for(current_territory) })
- if user.new_record?
.form-row.mb-2 *div_opts[:relative]
diff --git a/app/views/admin/users/_responsible_form_fields.html.slim b/app/views/admin/users/_responsible_form_fields.html.slim
index b6487275b6..7121889b83 100644
--- a/app/views/admin/users/_responsible_form_fields.html.slim
+++ b/app/views/admin/users/_responsible_form_fields.html.slim
@@ -60,7 +60,7 @@
= f.input :case_number, **input_opts
- if current_territory.enable_notes_field
- = f.input :notes, **input_opts.deep_merge(input_html: { rows: 6 })
+ = f.input :annotation_content, **input_opts.deep_merge(input_html: { rows: 6, value: user.annotation_for(current_territory)} )
- if current_territory.enable_caisse_affiliation_field || current_territory.enable_affiliation_number_field
.form-row
diff --git a/app/views/admin/users/_responsible_information.html.slim b/app/views/admin/users/_responsible_information.html.slim
index 7662636e98..63d51b3a17 100644
--- a/app/views/admin/users/_responsible_information.html.slim
+++ b/app/views/admin/users/_responsible_information.html.slim
@@ -21,7 +21,7 @@ ul.list-unstyled.mb-5
| En attente de confirmation pour #{user.unconfirmed_email}
- if current_territory.enable_notes_field?
- li.mb-2= object_attribute_tag(user, :notes, formatted_user_notes(user))
+ li.mb-2= object_attribute_tag(user, :annotation_content, formatted_user_annotation(user, current_territory))
- if current_territory.any_social_field_enabled?
h4.card-title.mb-3 Informations sociales
diff --git a/app/views/admin/users/show.html.slim b/app/views/admin/users/show.html.slim
index e6b9f8cb00..ab8fee8577 100644
--- a/app/views/admin/users/show.html.slim
+++ b/app/views/admin/users/show.html.slim
@@ -48,7 +48,7 @@
li= object_attribute_tag(@user, :ants_pre_demande_number)
li= object_attribute_tag(@user, :birth_date, birth_date_and_age(@user))
- if current_territory.enable_notes_field?
- li= object_attribute_tag(@user, :notes, formatted_user_notes(@user))
+ li= object_attribute_tag(@user, :annotation_content, formatted_user_annotation(@user, current_territory))
.row.mt-3
.col.text-left
diff --git a/app/views/agents/pages/home.html.slim b/app/views/agents/pages/home.html.slim
new file mode 100644
index 0000000000..a52c2d285f
--- /dev/null
+++ b/app/views/agents/pages/home.html.slim
@@ -0,0 +1,17 @@
+.fr-container.fr-pb-8w
+ h1.fr-h3.fr-pb-2w Bienvenue !
+ p.fr-py-1w Pour commencer, aidez-nous à en savoir plus :
+
+ p.fr-pt-1w.fr-pb-2w
+ | Votre structure utilise déjà #{current_domain.name} et vous souhaitez disposer d’un accès ?
+ br
+ | Vos collègues peuvent vous inviter depuis le menu “Paramètres > Agents > Ajouter un agent”.
+
+ p.fr-py-1w
+ | Votre structure n’utilise pas #{current_domain.name} et vous souhaitez créer un compte ?
+ br
+ | Nous vous invitons à contacter notre équipe. Nous organiserons un temps d’échanges pour vous présenter la solution et créer le compte de votre structure.
+
+ ul.fr-btns-group.fr-btns-group--center.fr-btns-group--inline-md.fr-mb-8w
+ li
+ = link_to "Contacter notre équipe", "https://cal.com/team/rdv-service-public/temps-d-echanges", class: "fr-btn"
diff --git a/app/views/dsfr/rdv_mairie/homepage.html.slim b/app/views/dsfr/rdv_mairie/homepage.html.slim
index 067b1ee4f0..df014efbba 100644
--- a/app/views/dsfr/rdv_mairie/homepage.html.slim
+++ b/app/views/dsfr/rdv_mairie/homepage.html.slim
@@ -1,4 +1,4 @@
-- contact_team_url = "https://cal.com/forms/937585aa-48a4-4efd-a642-961fad79c9c5"
+- contact_team_url = Agents::PagesController::CONTACT_TEAM_URL
.fr-py-8w.rdv-background-color-alt-blue-ecume
.fr-container
@@ -14,7 +14,7 @@
| Éviter les rendez-vous non honorés, gagner du temps au quotidien et améliorer la relation entre les agents et les usagers
ul.fr-btns-group.fr-btns-group--center.fr-btns-group--inline-md.fr-mb-0
li
- = link_to "Contacter notre équipe", contact_team_url, class: "fr-btn fr-mb-0"
+ = link_to "Demander une démo", contact_team_url, class: "fr-btn fr-mb-0"
.fr-container.fr-py-8w
.fr-grid-row.fr-grid-row--gutters.fr-grid-row--center
@@ -417,5 +417,5 @@ hr
h2 Plus de RDV, moins de lapins
p
| Votre agenda vous remerciera.
- .fr-my-4w= link_to "Contacter notre équipe", contact_team_url, class: "fr-btn"
+ .fr-my-4w= link_to "Demander une démo", contact_team_url, class: "fr-btn"
= link_to "Revenir en haut de la page", "#header", class: "fr-link fr-icon-arrow-up-line fr-link--icon-left"
diff --git a/app/views/search/_nothing_to_show_invitation.html.slim b/app/views/search/_nothing_to_show_invitation.html.slim
index 021ee918c2..6571f80ee7 100644
--- a/app/views/search/_nothing_to_show_invitation.html.slim
+++ b/app/views/search/_nothing_to_show_invitation.html.slim
@@ -1,17 +1,19 @@
-- if context.contactable_organisations.present?
- - if context.contactable_organisations.one?
+- contactable_organisations = Users::ContactableOrganisations.new(context.organisation_ids, context.motif_category_short_name)
+
+- if contactable_organisations.organisations.present?
+ - if contactable_organisations.organisations.one?
p Vous pouvez contacter l'organisation pour demander l'ouverture de créneaux.
- else
p Vous pouvez contacter les organisations suivantes pour leur demander d'ouvrir des créneaux.
.row
- - context.contactable_organisations.each do |organisation|
+ - contactable_organisations.organisations.each do |organisation|
= render "nothing_to_show_contactable_organisation", organisation: organisation
p Ou leur envoyer un email en cliquant sur le bouton ci-dessous.
-- email = context.organisations_emails.presence || "support@rdv-insertion.fr"
+- email = contactable_organisations.organisations_emails.presence || "support@rdv-insertion.fr"
= mail_to email,
- subject: "[Problème Invitation. Créneaux Indisponibles, motif : #{context.motif_category_name}]",
- cc: "#{'support@rdv-insertion.fr' unless context.organisations_emails.empty?}",
+ subject: "[Problème Invitation. Créneaux Indisponibles, motif : #{contactable_organisations.motif_category_name}]",
+ cc: "#{'support@rdv-insertion.fr' unless contactable_organisations.organisations_emails.empty?}",
class: "fr-btn fr-btn--icon-left fr-icon-mail-line" do
| Envoyer une demande d'ouverture de créneaux
diff --git a/app/views/search/search_rdv.html.slim b/app/views/search/search_rdv.html.slim
index 138b602eb2..65745866cc 100644
--- a/app/views/search/search_rdv.html.slim
+++ b/app/views/search/search_rdv.html.slim
@@ -9,4 +9,4 @@
- else
.fr-container
section.py-4.fr-col-12.fr-col-lg-10.fr-col-offset-lg-1
- = render @context, context: @context
+ = render partial: "search/#{@context.current_step}", locals: { context: @context }
diff --git a/app/views/super_admins/agents/show.html.slim b/app/views/super_admins/agents/show.html.slim
index c0becefa68..67960bc7f6 100644
--- a/app/views/super_admins/agents/show.html.slim
+++ b/app/views/super_admins/agents/show.html.slim
@@ -7,8 +7,11 @@ header.main-content__header role="banner"
=> link_to "Inviter", invite_super_admins_agent_path(page.resource), method: :post, class: "button", data: { disable_with: "Veuillez patienter…"} if accessible_action?(page.resource, :invite)
- if sign_in_as_allowed?
=> link_to "Se logger en tant que", sign_in_as_super_admins_agent_path(page.resource), class: "button" if accessible_action?(page.resource, :sign_in_as)
- => link_to(t("administrate.actions.edit_resource", name: page.page_title), [:edit, namespace, page.resource], class: "button") if accessible_action?(page.resource, :edit)
+ => link_to("Modifier", [:edit, namespace, page.resource], class: "button") if accessible_action?(page.resource, :edit)
=> link_to("Migrer", new_super_admins_agent_migration_path(agent_id: page.resource.id), class: "button")
+ - if page.resource.roles.none?
+ = link_to("Ouvrir un compte", new_super_admins_compte_path(agent_id: page.resource.id), class: "button")
+
section.main-content__body
dl
- page.attributes.each do |attribute|
diff --git a/app/views/super_admins/comptes/new.html.slim b/app/views/super_admins/comptes/new.html.slim
index 1d74443e7e..1f541d8868 100644
--- a/app/views/super_admins/comptes/new.html.slim
+++ b/app/views/super_admins/comptes/new.html.slim
@@ -12,13 +12,14 @@ section.main-content__body
p
| Ce formulaire vous permet de créer un territoire, une organisation, et un lieu pour un agent qui en sera admin (généralement la personne référente du projet).
p
- | Un motif d'exemple "Mon premier motif" sera créé, sauf s'il s'agit d'une ouverture de compte pour une mairie.
+ | Des motifs "Suivi de dossier" seront créés par défaut
= simple_form_for([namespace, page.resource], html: { class: "form" }) do |f|
= f.simple_fields_for :territory do |ff|
= ff.input :name, label: "Nom du territoire"
= f.simple_fields_for :organisation do |ff|
- = ff.input :name, label: "Nom de la première organisation et du premier lieu"
+ div[style="margin: 8px 0"]
+ = ff.input :name, label: "Nom de la première organisation et du premier lieu"
div[style="margin: 16px 0 8px 0"]
= ff.input :ants_connectable, as: :boolean, label: "Autoriser le branchement au moteur de recherche de l'ANTS", hint: "En cochant cette case, vous permettrez à cette organisation (probablement une mairie) d'apparaitre sur https://rendezvouspasseport.ants.gouv.fr/"
@@ -32,11 +33,14 @@ section.main-content__body
h2[style="margin: 32px 0 16px 0"] Admin de territoire
= f.simple_fields_for :agent do |ff|
- = ff.input :first_name, label: "Prénom"
- = ff.input :last_name, label: "Nom"
- = ff.input :email, label: "Adresse mail", hint: "Une invitation sera envoyée automatiquement"
- - # rubocop:disable Rails/OutputSafety
- = ff.input :service_ids, label: "Service", collection: Service.all, hint: "Si nécessaire, vous pouvez #{link_to('créer un nouveau service', new_super_admins_service_path, target: :blank)}".html_safe
- - # rubocop:enable Rails/OutputSafety
+ - if @agent
+ p = @agent.full_name
+ p = @agent.email
+ = ff.hidden_field :id, value: @agent.id
+ - else
+ = ff.input :first_name, label: "Prénom"
+ = ff.input :last_name, label: "Nom"
+ = ff.input :email, label: "Adresse mail", hint: "Une invitation sera envoyée automatiquement"
+ = ff.input :service_ids, label: "Service", collection: Service.all, hint: sanitize("Si nécessaire, vous pouvez #{link_to('créer un nouveau service', new_super_admins_service_path, target: :blank)}")
= f.submit
diff --git a/app/views/users/relatives/_form_fields.html.slim b/app/views/users/relatives/_form_fields.html.slim
index ef0cb41ac8..fdf2ef2f1f 100644
--- a/app/views/users/relatives/_form_fields.html.slim
+++ b/app/views/users/relatives/_form_fields.html.slim
@@ -1,9 +1,9 @@
-.form-row
- .col-md-6= f.input :first_name
- .col-md-6= f.input :last_name
+.fr-grid-row.fr-grid-row--gutters.fr-mb-5v
+ .fr-col-md-6= f.dsfr_text_field :first_name
+ .fr-col-md-6= f.dsfr_text_field :last_name
- if f.object.ants_pre_demande_number_required
- = f.input :ants_pre_demande_number, required: true, hint: t("simple_form.hints.user.ants_pre_demande_number_html"), input_html: {style: "text-transform: uppercase;"}
+ = f.dsfr_text_field :ants_pre_demande_number, required: true, hint: t("simple_form.hints.user.ants_pre_demande_number_html"), input_html: {style: "text-transform: uppercase;"}
= f.hidden_field :ants_pre_demande_number_required, value: "true"
- = f.input :ants_meeting_point_id, as: :hidden
+ = f.hidden_field :ants_meeting_point_id
- if current_domain != Domain::RDV_MAIRIE
- = f.input :birth_date, as: :string, input_html: { type: "date" }
+ = f.dsfr_text_field :birth_date, type: :date, input_html: { type: "date" }
diff --git a/app/views/users/relatives/edit.html.slim b/app/views/users/relatives/edit.html.slim
index d769e20acc..b9c786a334 100644
--- a/app/views/users/relatives/edit.html.slim
+++ b/app/views/users/relatives/edit.html.slim
@@ -2,12 +2,12 @@
.card
.card-body
- = simple_form_for @form, url: relative_path(@form.user) do |f|
+ = form_for @form, url: relative_path(@form.user), builder: Dsfr::FormBuilder do |f|
= render "model_errors", model: @form, f: f
= render "form_fields", f: f
.d-flex.justify-content-between
- = link_to "Supprimer", relative_path(@form.user), method: :delete, class: "btn btn-outline-danger", data: { confirm: "Confirmez-vous la suppression de ce proche ?"}
+ = link_to "Supprimer", relative_path(@form.user), method: :delete, class: "fr-btn fr-btn--icon-left fr-btn--secondary fr-icon-delete-line", data: { confirm: "Confirmez-vous la suppression de ce proche ?"}
.d-flex.justify-content-end
- = link_to "Annuler", users_informations_path, class: "btn btn-link"
- = f.button :submit, class: "btn"
+ = link_to "Annuler", users_informations_path, class: "fr-btn fr-btn--tertiary fr-mr-2v"
+ = f.button :submit, class: "fr-btn"
diff --git a/app/views/users/relatives/new.html.slim b/app/views/users/relatives/new.html.slim
index 845f70e181..8cab262e93 100644
--- a/app/views/users/relatives/new.html.slim
+++ b/app/views/users/relatives/new.html.slim
@@ -1,10 +1,11 @@
- content_for(:title) do
h1.rdv-color-white Ajouter un proche
-= simple_form_for @form, url: relatives_path, remote: true, data: { modal: true } do |f|
+= form_for @form, url: relatives_path, remote: true, data: { modal: true }, builder: Dsfr::FormBuilder do |f|
= render "model_errors", model: @form, f: f
= render "form_fields", f: f
+ // Note : on laisse le row et le col pour le moment car on va passer la modale au DSFR dans une PR suivante
.row
.col.rdv-text-align-right
- button.btn.btn-link data-dismiss="modal" type="button" Annuler
- = f.button :submit
+ button.fr-btn.fr-btn--secondary.fr-mr-2v data-dismiss="modal" type="button" Annuler
+ = f.button :submit, class: "fr-btn fr-btn--primary"
diff --git a/app/views/welcome/super_admin.html.slim b/app/views/welcome/super_admin.html.slim
index f1e71d4474..d4fc909b26 100644
--- a/app/views/welcome/super_admin.html.slim
+++ b/app/views/welcome/super_admin.html.slim
@@ -1,15 +1,13 @@
.fr-mb-3w
- .row.justify-content-center.pt-6
- .col-3
- = link_to "Accéder à superadmin", "/omniauth/github", class: "btn btn-primary", method: :post
+ .rdv-text-align-center
+ = link_to "Accéder à superadmin", "/omniauth/github", class: "fr-btn", method: :post
- if Rails.env.development?
- .row.justify-content-center.mt-3
- .col-3
- h5 Liens utiles
- ul
- li = link_to "Letter Opener", letter_opener_web_path
- li = link_to "Mailer Previews", "/rails/mailers"
- li = link_to "SMS Previews", "/lapin/sms_preview"
- li = link_to "Routes", "/rails/info/routes"
- li = link_to "Rails properties", "/rails/info/properties"
+ .fr-col-lg-3.fr-col-offset-lg-5.fr-mt-3w
+ h5 Liens utiles
+ ul
+ li = link_to "Letter Opener", letter_opener_web_path
+ li = link_to "Mailer Previews", "/rails/mailers"
+ li = link_to "SMS Previews", "/lapin/sms_preview"
+ li = link_to "Routes", "/rails/info/routes"
+ li = link_to "Rails properties", "/rails/info/properties"
diff --git a/config/anonymizer.yml b/config/anonymizer.yml
index 498fa2d8a6..a09e2e740b 100644
--- a/config/anonymizer.yml
+++ b/config/anonymizer.yml
@@ -72,7 +72,6 @@ tables:
- invitation_limit
- reset_password_sent_at
- invitation_sent_at
- - invitations_count
- invited_by_id
- invited_by_type
- invited_through
@@ -99,7 +98,6 @@ tables:
- microsoft_graph_token
- refresh_microsoft_graph_token
- remember_created_at
- - inclusion_connect_open_id_sub
non_anonymized_column_names:
- reset_password_sent_at
- last_sign_in_at
@@ -111,7 +109,6 @@ tables:
- invitation_limit
- invited_by_type
- invited_by_id
- - invitations_count
- provider
- rdv_notifications_level
- allow_password_change
@@ -246,7 +243,6 @@ tables:
- invitation_limit
- invited_by_type
- invited_by_id
- - invitations_count
- status
- created_by_id
- created_by_type
diff --git a/config/locales/models/user.fr.yml b/config/locales/models/user.fr.yml
index 82de097cd1..fbf871e027 100644
--- a/config/locales/models/user.fr.yml
+++ b/config/locales/models/user.fr.yml
@@ -23,7 +23,7 @@ fr:
notify_by_sms: "Accepte les notifications par SMS"
notify_by_email: "Accepte les notifications par email"
created_through: "Origine du compte"
- notes: Remarques
+ annotation_content: Remarques
ants_pre_demande_number: &ants_pre_demande_number_label Numéro de pré-demande ANTS
user/logements:
sdf: Sans domicile fixe
diff --git a/config/routes.rb b/config/routes.rb
index 9862271b80..fc61ea1de1 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -290,7 +290,7 @@
end
end
authenticated :agent do
- root to: "admin/organisations#index", as: :authenticated_agent_root, defaults: { follow_unique: "1" }
+ root to: "agents/pages#home", as: :authenticated_agent_root
end
scope path: "prescripteur", as: "prescripteur", controller: "prescripteur_rdv_wizard" do
diff --git a/db/migrate/20250227165927_remove_invitations_count_columns.rb b/db/migrate/20250227165927_remove_invitations_count_columns.rb
new file mode 100644
index 0000000000..7da6b14e32
--- /dev/null
+++ b/db/migrate/20250227165927_remove_invitations_count_columns.rb
@@ -0,0 +1,17 @@
+class RemoveInvitationsCountColumns < ActiveRecord::Migration[7.1]
+ disable_ddl_transaction!
+
+ def change
+ safety_assured do
+ remove_index :agents, :invitations_count, algorithm: :concurrently
+ remove_index :users, :invitations_count, algorithm: :concurrently
+
+ remove_column :agents, :invitations_count, :integer, default: 0
+ remove_column :users, :invitations_count, :integer, default: 0
+ remove_column :participations, :invitations_count, :integer, default: 0
+
+ remove_index :agents, :inclusion_connect_open_id_sub, unique: true, where: "inclusion_connect_open_id_sub IS NOT NULL", algorithm: :concurrently
+ remove_column :agents, :inclusion_connect_open_id_sub, :string
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 2e9e9f5e38..485ee60c67 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.1].define(version: 2025_02_25_184048) do
+ActiveRecord::Schema[7.1].define(version: 2025_02_27_165927) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_stat_statements"
enable_extension "pgcrypto"
@@ -209,7 +209,6 @@
t.integer "invitation_limit"
t.string "invited_by_type"
t.bigint "invited_by_id"
- t.integer "invitations_count", default: 0
t.datetime "deleted_at"
t.string "email_original"
t.string "provider", default: "email", null: false
@@ -229,16 +228,13 @@
t.text "refresh_microsoft_graph_token"
t.boolean "outlook_disconnect_in_progress", default: false, null: false
t.datetime "account_deletion_warning_sent_at", comment: "Quand le compte de l'agent est inactif depuis bientôt deux ans, on lui envoie un mail qui le prévient que sont compte sera bientôt supprimé, et qu'il doit se connecter à nouveau s'il souhaite conserver son compte. On enregistre la date d'envoi de cet email ici pour s'assure qu'on lui laisse un délai d'au moins un mois pour réagir.\n"
- t.string "inclusion_connect_open_id_sub"
t.boolean "connected_with_agent_connect", default: false, null: false
t.index ["account_deletion_warning_sent_at"], name: "index_agents_on_account_deletion_warning_sent_at"
t.index ["calendar_uid"], name: "index_agents_on_calendar_uid", unique: true
t.index ["confirmation_token"], name: "index_agents_on_confirmation_token", unique: true
t.index ["email"], name: "index_agents_on_email", unique: true, where: "(email IS NOT NULL)"
t.index ["external_id"], name: "index_agents_on_external_id", unique: true
- t.index ["inclusion_connect_open_id_sub"], name: "index_agents_on_inclusion_connect_open_id_sub", unique: true, where: "(inclusion_connect_open_id_sub IS NOT NULL)"
t.index ["invitation_token"], name: "index_agents_on_invitation_token", unique: true
- t.index ["invitations_count"], name: "index_agents_on_invitations_count"
t.index ["invited_by_id"], name: "index_agents_on_invited_by_id"
t.index ["invited_by_type", "invited_by_id"], name: "index_agents_on_invited_by_type_and_invited_by_id"
t.index ["last_name"], name: "index_agents_on_last_name"
@@ -534,7 +530,6 @@
t.integer "invitation_limit"
t.string "invited_by_type"
t.bigint "invited_by_id"
- t.integer "invitations_count", default: 0
t.enum "status", default: "unknown", null: false, enum_type: "rdv_status"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
@@ -780,7 +775,6 @@
t.integer "invitation_limit"
t.string "invited_by_type"
t.bigint "invited_by_id"
- t.integer "invitations_count", default: 0
t.integer "caisse_affiliation"
t.string "affiliation_number"
t.integer "family_situation"
@@ -815,7 +809,6 @@
t.index ["first_name"], name: "index_users_on_first_name"
t.index ["franceconnect_openid_sub"], name: "index_users_on_franceconnect_openid_sub", where: "(franceconnect_openid_sub IS NOT NULL)"
t.index ["invitation_token"], name: "index_users_on_invitation_token", unique: true
- t.index ["invitations_count"], name: "index_users_on_invitations_count"
t.index ["invited_by_id"], name: "index_users_on_invited_by_id"
t.index ["invited_by_type", "invited_by_id"], name: "index_users_on_invited_by_type_and_invited_by_id"
t.index ["last_name"], name: "index_users_on_last_name"
diff --git a/db/seeds/ccas.rb b/db/seeds/ccas.rb
index c4e193abdb..e4bb8eaf4b 100644
--- a/db/seeds/ccas.rb
+++ b/db/seeds/ccas.rb
@@ -49,3 +49,18 @@
user.skip_confirmation!
user.save!
+
+# Un agent pour tester l'absence d'orga et de services
+agent = Agent.new(
+ email: "bob-sans-orga@demo.rdv-solidarites.fr",
+ uid: "bob-sans-orga@demo.rdv-solidarites.fr",
+ first_name: "Bob",
+ last_name: "Sans Organisation",
+ password: "Rdvservicepublictest1!",
+ services: [],
+ invitation_accepted_at: 1.day.ago,
+ roles_attributes: [],
+ agent_territorial_access_rights_attributes: []
+)
+agent.skip_confirmation!
+agent.save!
diff --git a/db/seeds/rdv_insertion.rb b/db/seeds/rdv_insertion.rb
index 905c2cd91c..30134d54f0 100644
--- a/db/seeds/rdv_insertion.rb
+++ b/db/seeds/rdv_insertion.rb
@@ -341,7 +341,7 @@
# !!! Le lien d'invitation est disponible dans la note des users
# jean.rsavalence@testinvitation.fr et jean.rsaAuxerre@testinvitation.fr
- user.update!(notes: link)
+ user.annotate!(link, territory: organisation.territory)
end
rdv_insertion_logo_base64 = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgaWQ9IkNhbHF1ZV8xIgogICB2aWV3Qm94PSIwIDAgNDgzLjMzIDQ4My4zMyIKICAgdmVyc2lvbj0iMS4xIgogICB3aWR0aD0iNDgzLjMyOTk5IgogICBoZWlnaHQ9IjQ4My4zMjk5OSIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzMSI+CiAgICA8c3R5bGUKICAgICAgIGlkPSJzdHlsZTEiPi5jbHMtMXtmaWxsOiNlYzRjNGM7fS5jbHMtMntmaWxsOiMwODNiNjY7fTwvc3R5bGU+CiAgPC9kZWZzPgogIDxnCiAgICAgaWQ9ImcxOCI+CiAgICA8ZwogICAgICAgaWQ9ImcxOSIKICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xMC45MTcyMTMsLTAuMDAzMDUwMDYpIj4KICAgICAgPHBhdGgKICAgICAgICAgY2xhc3M9ImNscy0yIgogICAgICAgICBkPSJtIDE5My45Nyw0MTEuMzkgOC4wMywtMS45NyBjIDAsMCAxOS4xNiwtNy42OSAyNC41NCwtMzYuNzEgNS4zOCwtMjkuMDEgMzAuNTMsLTQ4LjU2IDU4LjY2LC00MC4yNyAyMC4yNCwtMi44OCA1LjQyLC0zMy45NyAtMjUuNjYsLTI1LjU0IC0yNS42OCw2Ljk3IC0zOC42NCwxMi45NiAtNDguNywxMC4wOCAtMy4zMSwtMC45NiAtNS44MSwtMy42NyAtNi41MSwtNy4wNCAtOC4zOCwtNDAuNyAtMjUuMTcsLTEyMS45NiAtMjcuMTcsLTEyOC45NiAtMi40LC04LjM2IC03LjkzLC0xMC44IC0xMy45MiwtOS42MyAtNy4wMywwLjg5IC03LjQ4LDguMTkgLTcuNjMsMTIuOTEgMCw2LjA2IDQuMDksNTQuMTggNi4yOCw3OS40NyAtMS45MiwtNy45NyAtNi4zOSwtMTUuODkgLTE2LjYsLTE1LjAxIC0xMC40OCwwLjg4IC0xMi4zNSwxMy44MSAtMTIuMTEsMjQuMjUgLTIuMTIsLTguMjIgLTcuNDQsLTE2LjQ5IC0yMC42NiwtMTIuNDkgLTQuNjQsMS4zOSAtNi43OCw4LjE1IC03LjUxLDE2Ljc2IGwgLTAuMzIsLTAuNDcgLTI3LjA0LC00MS4yMyBDIDY1LjIzLDIxNi41OSA3MC41MSwxOTEuMTcgODkuNDYsMTc4Ljc0IEwgMjYyLjQ3LDY1LjI4IGMgMTUuNDMsLTEwLjEzIDM1LjU5LC03LjMxIDQ3LjczLDUuNzkgbCAtNC44OCwxLjIgYyAwLDAgLTE5LjIxLDcuNzIgLTI0LjYxLDM2Ljg3IC01LjM5LDI5LjEzIC0zMC42LDQ4Ljc2IC01OC43OSw0MC40MyAtMjAuMjksMi45IC01LjQ0LDM0LjEyIDI1LjcxLDI1LjY2IDI1Ljc2LC03LjAxIDM4Ljc1LC0xMy4wMyA0OC44NSwtMTAuMTEgMy4zLDAuOTUgNS44LDMuNjYgNi41LDcuMDQgOC4zOSw0MC44NiAyNS4yMywxMjIuNDkgMjcuMjMsMTI5LjUxIDIuNDEsOC40MSA3Ljk1LDEwLjg2IDEzLjk2LDkuNjcgNy4wNSwtMC45IDcuNSwtOC4yMiA3LjY0LC0xMi45NyAwLC02LjA4IC00LjEsLTU0LjQgLTYuMjksLTc5LjggMS45Myw4LjAxIDYuNCwxNS45NSAxNi42NCwxNS4wOCAxMC41MSwtMC44OSAxMi4zOCwtMTMuODcgMTIuMTQsLTI0LjM2IDIuMTIsOC4yNSA3LjQ2LDE2LjU2IDIwLjcyLDEyLjU1IDMuNzEsLTEuMTIgNS44MiwtNS42NyA2LjksLTExLjg1IGwgMC40MSwwLjYzIDYuMjIsOS40OCB2IDAgYyAwLDAgMTguOTYsMjguOTEgMTguOTYsMjguOTEgMTIuNDMsMTguOTQgNy4xNCw0NC4zNyAtMTEuODEsNTYuOCBMIDI0NS4zMSw0MTcuNTUgYyAtMTUuODcsMTAuNDEgLTM2LjM2LDguMTkgLTQ5LjY1LC00LjE5IC0wLjY2LC0wLjYxIC0xLjMsLTEuMjUgLTEuOTIsLTEuOTEgeiIKICAgICAgICAgaWQ9InBhdGgxNSIgLz4KICAgICAgPGcKICAgICAgICAgaWQ9ImcxNyI+CiAgICAgICAgPHBhdGgKICAgICAgICAgICBjbGFzcz0iY2xzLTEiCiAgICAgICAgICAgZD0ibSAyNTIuMzIsMjM4LjY5IC0zOS4wNCwtMzAuMzYgaCA4MS4wOCBsIC0zOS4wNCwzMC4zNiBjIC0wLjg4LDAuNjkgLTIuMTIsMC42OSAtMywwIHoiCiAgICAgICAgICAgaWQ9InBhdGgxNiIgLz4KICAgICAgICA8cGF0aAogICAgICAgICAgIGNsYXNzPSJjbHMtMSIKICAgICAgICAgICBkPSJtIDI5OS45NywyMTMuNDYgdiA0OS45NCBjIDAsNi40IC01LjE5LDExLjU5IC0xMS41OSwxMS41OSBoIC02OS4xMyBjIC02LjQsMCAtMTEuNTksLTUuMTkgLTExLjU5LC0xMS41OSB2IC00OS45NCBjIDAsLTAuMjQgMC4wMiwtMC40OCAwLjA1LC0wLjcyIGwgNDIuMTMsMzIuNzcgYyAyLjMzLDEuODEgNS42LDEuODEgNy45MywwIGwgNDIuMTMsLTMyLjc3IGMgMC4wNCwwLjI0IDAuMDUsMC40OCAwLjA1LDAuNzMgeiIKICAgICAgICAgICBpZD0icGF0aDE3IiAvPgogICAgICA8L2c+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K" # rubocop:disable Layout/LineLength
diff --git a/spec/factories/agent.rb b/spec/factories/agent.rb
index c9917a1967..6ccb6d5a8d 100644
--- a/spec/factories/agent.rb
+++ b/spec/factories/agent.rb
@@ -12,8 +12,17 @@
transient do
service { build(:service) }
+ no_services { false }
+
+ trait :no_services do
+ no_services { true }
+ end
end
after(:build) do |agent, evaluator|
+ next if evaluator.no_services
+ next if agent.agent_services.any?
+ next if agent.services.any?
+
if agent.agent_services.empty? && agent.services.empty?
agent.services = if evaluator.service
[evaluator.service]
diff --git a/spec/factories/user.rb b/spec/factories/user.rb
index 5d5e843577..97fa806761 100644
--- a/spec/factories/user.rb
+++ b/spec/factories/user.rb
@@ -21,7 +21,6 @@
affiliation_number { "39012093812038" }
family_situation { "divorced" }
number_of_children { 12 }
- notes { nil }
logement { :locataire }
responsible { nil }
created_through { "user_sign_up" }
diff --git a/spec/features/agents/account/agent_can_reset_his_password_spec.rb b/spec/features/agents/account/agent_can_reset_his_password_spec.rb
index 25d0b76e6d..db782ac20a 100644
--- a/spec/features/agents/account/agent_can_reset_his_password_spec.rb
+++ b/spec/features/agents/account/agent_can_reset_his_password_spec.rb
@@ -22,7 +22,6 @@
fill_in "Mot de passe", with: "correct H0rse battery! staple"
expect { click_on "Enregistrer" }.to change { agent.reload.encrypted_password }
expect(page).to have_content("Votre mot de passe a été édité avec succès")
- expect(page).to have_link("Vos organisations")
end
it "works when using the user's password reset form" do
@@ -39,6 +38,5 @@
fill_in "Mot de passe", with: "correct H0rse battery! staple"
expect { click_on "Enregistrer" }.to change { agent.reload.encrypted_password }
expect(page).to have_content("Votre mot de passe a été édité avec succès")
- expect(page).to have_link("Vos organisations")
end
end
diff --git a/spec/features/agents/agent_can_list_rdvs_spec.rb b/spec/features/agents/agent_can_list_rdvs_spec.rb
index 363531404d..cf04d5b33d 100644
--- a/spec/features/agents/agent_can_list_rdvs_spec.rb
+++ b/spec/features/agents/agent_can_list_rdvs_spec.rb
@@ -11,6 +11,18 @@ def user_profile_path(user)
login_as(current_agent, scope: :agent)
end
+ it "displays user info for each RDV" do
+ organisation.territory.update!(enable_notes_field: true)
+ motif = create(:motif, service: current_agent.services.first, organisation:)
+ create(:rdv, organisation: organisation, agents: [current_agent], users: [user], motif: motif)
+ user.annotate!("Ma remarque", territory: organisation.territory)
+
+ visit admin_organisation_rdvs_url(organisation, current_agent)
+ expect(page).to have_content(user.full_name)
+ expect(page).to have_content(motif.name)
+ expect(page.html).to include("Ma remarque")
+ end
+
describe "RDV visibility within organisation" do
let!(:agent_from_same_service) { create(:agent, organisations: [organisation], service: current_agent.services.first) }
let!(:agent_from_other_service) { create(:agent, organisations: [organisation]) }
diff --git a/spec/features/agents/agent_can_see_users_rdv_spec.rb b/spec/features/agents/agent_can_see_users_rdv_spec.rb
index 90991ad2b0..ae0f41afe6 100644
--- a/spec/features/agents/agent_can_see_users_rdv_spec.rb
+++ b/spec/features/agents/agent_can_see_users_rdv_spec.rb
@@ -1,27 +1,26 @@
RSpec.describe "can see users' RDV" do
- let!(:organisation) { create(:organisation) }
- let!(:service) { create(:service) }
- let!(:agent) { create(:agent, basic_role_in_organisations: [organisation], service: service) }
- let!(:user) { create(:user, first_name: "Tanguy", last_name: "Laverdure", organisations: [organisation]) }
- let!(:motif) { create(:motif, organisation: organisation, service: service) }
-
- before do
- login_as(agent, scope: :agent)
- visit authenticated_agent_root_path
- end
-
context "with no RDV" do
- before do
- visit admin_organisation_user_path(organisation, user)
- end
+ let!(:organisation) { create(:organisation) }
+ let!(:service) { create(:service) }
+ let!(:agent) { create(:agent, basic_role_in_organisations: [organisation], service: service) }
+ let!(:user) { create(:user, first_name: "Tanguy", last_name: "Laverdure", organisations: [organisation]) }
+ let!(:motif) { create(:motif, organisation: organisation, service: service) }
it do
+ login_as(agent, scope: :agent)
+ visit admin_organisation_user_path(organisation, user)
expect(page).to have_content("À venir\n0 RDV")
expect(page).to have_content("aucun RDV")
end
end
context "with one RDV" do
+ let!(:organisation) { create(:organisation) }
+ let!(:service) { create(:service) }
+ let!(:agent) { create(:agent, basic_role_in_organisations: [organisation], service: service) }
+ let!(:user) { create(:user, first_name: "Tanguy", last_name: "Laverdure", organisations: [organisation]) }
+ let!(:motif) { create(:motif, organisation: organisation, service: service) }
+
let!(:rdv) { create :rdv, :future, users: [user], organisation: organisation, motif: motif, agents: [agent] }
before do
@@ -29,11 +28,12 @@
create(:rdv, :past, status: :excused, users: [user], organisation: organisation, motif: motif, agents: [agent])
create(:rdv, :past, status: :revoked, users: [user], organisation: organisation, motif: motif, agents: [agent])
create(:rdv, :past, status: :noshow, users: [user], organisation: organisation, motif: motif, agents: [agent])
-
- visit admin_organisation_user_path(organisation, user)
end
it do
+ login_as(agent, scope: :agent)
+ visit admin_organisation_user_path(organisation, user)
+
expect(page).to have_content("Excusé\n1 RDV")
expect(page).to have_content("Vu\n1 RDV")
expect(page).to have_content("Annulé par un agent\n1 ")
@@ -45,4 +45,33 @@
expect(page).to have_content("Le #{I18n.l(rdv.starts_at, format: :human)} (durée : #{rdv.duration_in_min} minutes)")
end
end
+
+ describe "displaying annotations" do
+ let!(:territory) { create(:territory, enable_notes_field: true) }
+ let!(:organisation) { create(:organisation, territory:) }
+ let!(:responsible) { create(:user, organisations: [organisation]) }
+ let!(:relative) { create(:user, organisations: [organisation], responsible:) }
+
+ let!(:agent) { create(:agent, basic_role_in_organisations: [organisation]) }
+
+ it "works" do
+ login_as(agent, scope: :agent)
+ visit admin_organisation_user_path(organisation, relative)
+ # On visite pour vérifier que la page ne crash pas en l'absence d'annotation, voir #5133.
+ expect(page).to have_content("Informations de votre proche")
+
+ responsible.annotate!("Ce responsable est très responsable", territory:)
+ relative.annotate!("Ce proche est très proche", territory:)
+
+ # Sur la page du proche, on affiche les remarques du responsable ET du proche
+ visit admin_organisation_user_path(organisation, relative)
+ expect(page).to have_content("Ce responsable est très responsable")
+ expect(page).to have_content("Ce proche est très proche")
+
+ # Sur la page du responsable, on affiche uniquement la remarque du responsable
+ visit admin_organisation_user_path(organisation, responsible)
+ expect(page).to have_content("Ce responsable est très responsable")
+ expect(page).not_to have_content("Ce proche est très proche")
+ end
+ end
end
diff --git a/spec/features/agents/users/agent_can_create_user_spec.rb b/spec/features/agents/users/agent_can_create_user_spec.rb
index 76f4db48bb..17a17cc1b8 100644
--- a/spec/features/agents/users/agent_can_create_user_spec.rb
+++ b/spec/features/agents/users/agent_can_create_user_spec.rb
@@ -1,5 +1,6 @@
RSpec.describe "Agent can create user" do
- let!(:organisation) { create(:organisation, name: "MDS des Champs") }
+ let!(:organisation) { create(:organisation, name: "MDS des Champs", territory: territory) }
+ let!(:territory) { create(:territory, enable_notes_field: true) }
let!(:agent) { create(:agent, basic_role_in_organisations: [organisation]) }
let!(:user) do
create(:user, first_name: "Jean", last_name: "LEGENDE", email: "jean@legende.com", organisations: [organisation])
@@ -18,8 +19,13 @@
it "works" do
fill_in :user_first_name, with: "Marco"
fill_in :user_last_name, with: "Lebreton"
+ fill_in "Remarques", with: "souhaite participer au prochain atelier collectif"
click_button "Créer"
expect_page_title("Marco LEBRETON")
+
+ user = User.last
+ expect(user.annotation_for(organisation.territory)).to eq "souhaite participer au prochain atelier collectif"
+
expect(page).to have_no_content("Inviter")
within("#spec-primary-user-card") { click_link "Modifier" }
fill_in "Email", with: "marco@lebreton.bzh"
diff --git a/spec/features/agents/users/agent_can_merge_users_spec.rb b/spec/features/agents/users/agent_can_merge_users_spec.rb
index d1db8373dd..354c8fd4e6 100644
--- a/spec/features/agents/users/agent_can_merge_users_spec.rb
+++ b/spec/features/agents/users/agent_can_merge_users_spec.rb
@@ -1,6 +1,6 @@
RSpec.describe "Agent can delete user" do
let!(:organisation) { create(:organisation, territory: territory) }
- let!(:territory) { create(:territory, enable_logement_field: true) }
+ let!(:territory) { create(:territory, enable_logement_field: true, enable_notes_field: true) }
let!(:agent) { create(:agent, basic_role_in_organisations: [organisation]) }
let!(:user1) do
create(
@@ -61,4 +61,19 @@
expect(page).to have_content("Les usagers ont été fusionnés")
expect(page).to have_content("aalyah@damn.com")
end
+
+ it "allows to merge annotations" do
+ Annotation.create!(user: user1, territory: territory, content: "Notes 1")
+ Annotation.create!(user: user2, territory: territory, content: "Notes 2")
+
+ login_as(agent, scope: :agent)
+ visit new_admin_organisation_merge_users_path(organisation_id: organisation.id, user1_id: user1.id, user2_id: user2.id)
+ expect(page).to have_content("Notes 1")
+ expect(page).to have_content("Notes 2")
+ choose "Notes 2"
+ click_on "Fusionner"
+ expect(user1.reload.annotations.sole.content).to eq("Notes 2")
+ expect(user2.reload.deleted_at).to be_present
+ expect(user2.reload.annotations).to be_empty
+ end
end
diff --git a/spec/features/agents/users/agent_can_update_user_spec.rb b/spec/features/agents/users/agent_can_update_user_spec.rb
index 6f3398a162..b079958813 100644
--- a/spec/features/agents/users/agent_can_update_user_spec.rb
+++ b/spec/features/agents/users/agent_can_update_user_spec.rb
@@ -11,10 +11,10 @@
login_as(agent, scope: :agent)
visit authenticated_agent_root_path
visit admin_organisation_user_path(organisation, user)
- within("#spec-primary-user-card") { click_link "Modifier" }
end
it "update existing user's email" do
+ within("#spec-primary-user-card") { click_link "Modifier" }
fill_in :user_first_name, with: "jeanne"
fill_in :user_last_name, with: "reynolds"
fill_in "Email", with: "jeanne@reynolds.com"
@@ -33,6 +33,7 @@
let(:territory) { create(:territory, enable_notes_field: false, enable_caisse_affiliation_field: false) }
it "doesn't show them them" do
+ within("#spec-primary-user-card") { click_link "Modifier" }
expect(page).not_to have_content("Remarques")
expect(page).not_to have_content("Caisse d'affiliation")
end
@@ -40,13 +41,30 @@
context "when they are enabled" do
let(:territory) { create(:territory, enable_notes_field: true, enable_caisse_affiliation_field: true) }
+ let!(:annotation_in_current_territory) do
+ Annotation.create!(user: user, territory: territory, content: "Remarques du territoire courant")
+ end
+ let!(:annotation_in_other_territory) do
+ Annotation.create!(user: user, territory: other_territory, content: "Remarques de l'autre territoire")
+ end
+ let(:other_territory) { create(:territory) }
+
+ before do
+ # On ajoute l'usager dans un autre territoire pour vérifier que ses annotations sont mises à jour dans un seul territoire
+ create(:user_profile, user: user, organisation: create(:organisation, territory: other_territory))
+ end
+
+ it "update user annotations" do
+ within("#spec-primary-user-card") { click_link "Modifier" }
+ expect(page).to have_field("Remarques", with: "Remarques du territoire courant")
- it "update user notes" do
fill_in "Remarques", with: "souhaite participer au prochain atelier collectif"
select "MSA", from: "Caisse d'affiliation"
click_button "Enregistrer"
- expect(user.reload.notes).to eq "souhaite participer au prochain atelier collectif"
+ expect(user.reload.annotation_for(territory)).to eq "souhaite participer au prochain atelier collectif"
expect(user.reload.caisse_affiliation).to eq "msa"
+ expect(user.reload.annotation_for(other_territory)).to eq "Remarques de l'autre territoire"
+ expect(page).to have_content("souhaite participer au prochain atelier collectif")
end
end
end
@@ -57,6 +75,7 @@
end
it "add email to existing user" do
+ within("#spec-primary-user-card") { click_link "Modifier" }
fill_in "Email", with: "jean@legende.com"
click_button "Enregistrer"
click_link "Inviter"
@@ -70,6 +89,7 @@
let!(:user) { create(:user, :unconfirmed, organisations: [organisation]) }
it "permet de désactiver et réactiver les préférences de notifications SMS et email" do
+ within("#spec-primary-user-card") { click_link "Modifier" }
expect(page).to have_content("Modifier l'usager")
expect(page).to have_checked_field("Accepte les notifications par email")
expect(page).to have_checked_field("Accepte les notifications par SMS")
diff --git a/spec/form_models/admin/user_form_spec.rb b/spec/form_models/admin/user_form_spec.rb
index dfa44ce61e..e7ffcf5b78 100644
--- a/spec/form_models/admin/user_form_spec.rb
+++ b/spec/form_models/admin/user_form_spec.rb
@@ -17,7 +17,7 @@
it "saves the user" do
expect(user).to receive(:save)
- subject.save
+ subject.save(annotation_content: "", current_territory: organisation.territory)
end
end
@@ -33,7 +33,7 @@
it "does not save the user" do
expect(user).not_to receive(:save)
- subject.save
+ subject.save(annotation_content: "", current_territory: organisation.territory)
end
end
@@ -52,7 +52,7 @@
it "does not save the user" do
expect(user).not_to receive(:save)
- subject.save
+ subject.save(annotation_content: "", current_territory: organisation.territory)
end
end
@@ -70,7 +70,7 @@
it "does not save the user" do
expect(user).not_to receive(:save)
- subject.save
+ subject.save(annotation_content: "", current_territory: organisation.territory)
end
end
@@ -87,7 +87,7 @@
it "saves the user" do
expect(user).to receive(:save)
- subject.save
+ subject.save(annotation_content: "", current_territory: organisation.territory)
end
end
@@ -110,7 +110,7 @@
it "does not save the user" do
expect(user).not_to receive(:save)
- subject.save
+ subject.save(annotation_content: "", current_territory: organisation.territory)
end
end
@@ -130,7 +130,7 @@
it "saves the user" do
expect(user).to receive(:save)
- subject.save
+ subject.save(annotation_content: "", current_territory: organisation.territory)
end
end
diff --git a/spec/models/annotation_spec.rb b/spec/models/annotation_spec.rb
new file mode 100644
index 0000000000..ee604825c8
--- /dev/null
+++ b/spec/models/annotation_spec.rb
@@ -0,0 +1,32 @@
+RSpec.describe Annotation do
+ describe "upsert!" do
+ let!(:organisation) { create(:organisation) }
+ let!(:territory) { organisation.territory }
+ let!(:user) { create(:user, organisations: [organisation]) }
+
+ context "when no annotation already exists for user and territory" do
+ it "creates one when upserting content" do
+ expect { described_class.upsert!("Lorem ipsum", user:, territory:) }.to change(described_class, :count).by(1)
+ end
+
+ it "does nothing when upserting a blank content" do
+ expect { described_class.upsert!(" ", user:, territory:) }.not_to change { described_class.where(user:, territory:).count }
+ end
+ end
+
+ context "when an annotation already exists for user and territory" do
+ before do
+ described_class.create!(content: "Lorem ipsum", user:, territory:)
+ end
+
+ it "updates the existing annotation when upserting content" do
+ annotation = described_class.where(user:, territory:).sole
+ expect { described_class.upsert!("dolor sit amet", user:, territory:) }.to change { annotation.reload.content }.from("Lorem ipsum").to("dolor sit amet")
+ end
+
+ it "deletes the existing annotation when upserting a blank content" do
+ expect { described_class.upsert!(" ", user:, territory:) }.to change { described_class.where(user:, territory:).count }.by(-1)
+ end
+ end
+ end
+end
diff --git a/spec/models/concerns/human_attribute_value_spec.rb b/spec/models/concerns/human_attribute_value_spec.rb
index e934f460fd..953f11e492 100644
--- a/spec/models/concerns/human_attribute_value_spec.rb
+++ b/spec/models/concerns/human_attribute_value_spec.rb
@@ -64,8 +64,8 @@ def self.human_attribute_name(key, _options)
context "string value ends with a dot (.)" do
it "works" do
- expect(User.new(notes: "C'est ainsi.").human_attribute_value(:notes)).to eq("C'est ainsi.")
- expect(User.new(notes: "Hélas...").human_attribute_value(:notes)).to eq("Hélas...")
+ expect(User.new(last_name: "C'est ainsi.").human_attribute_value(:last_name)).to eq("C'est ainsi.")
+ expect(User.new(last_name: "Hélas...").human_attribute_value(:last_name)).to eq("Hélas...")
end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index a3835c507d..4235dc8fd1 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -366,68 +366,38 @@
end
describe "annotations" do
- describe "user creation" do
- it "creates an annotation if needed" do
- organisation = create(:organisation)
- user_with_notes = create(:user, notes: "Lorem ipsum", organisations: [organisation])
- expect(user_with_notes.notes).to eq("Lorem ipsum")
- expect(Annotation.where(user: user_with_notes).sole).to have_attributes(territory: organisation.territory, content: "Lorem ipsum")
-
- expect { create(:user, notes: "", organisations: [organisation]) }.not_to change(Annotation, :count)
- end
+ it "provides the #annotate! helper method" do
+ user = create(:user)
+ territory = create(:territory)
+ expect(Annotation).to receive(:upsert!).with("Ma remarque", user:, territory:)
+ user.annotate!("Ma remarque", territory:)
end
- describe "user update" do
- it "creates an annotation if notes appear" do
- organisation = create(:organisation)
- user = create(:user, notes: nil, organisations: [organisation])
- expect(Annotation.count).to eq(0)
+ it "deletes annotations when leaving the last org of a territory" do
+ territory_a = create(:territory)
+ territory_b = create(:territory)
+ organisation_a_1 = create(:organisation, territory: territory_a)
+ organisation_a_2 = create(:organisation, territory: territory_a)
+ organisation_b_1 = create(:organisation, territory: territory_b)
+ organisation_b_2 = create(:organisation, territory: territory_b)
- expect { user.update!(notes: "Lorem ipsum") }.to change(Annotation, :count).by(1)
- expect(Annotation.where(user: user).sole).to have_attributes(territory: organisation.territory, content: "Lorem ipsum")
- end
+ user = create(:user, organisations: [organisation_a_1, organisation_a_2, organisation_b_1, organisation_b_2])
+ user.annotate!("Infos du territoire A", territory: territory_a)
+ user.annotate!("Infos du territoire B", territory: territory_b)
+ expect(Annotation.where(user: user).count).to eq(2)
- it "updates the annotation if the notes change" do
- organisation = create(:organisation)
- user = create(:user, notes: "Lorem ipsum", organisations: [organisation])
- expect { user.update!(notes: "dolor sit amet") }.to change { Annotation.where(user: user).sole.reload.updated_at }
- expect(Annotation.where(user: user).sole.content).to eq("dolor sit amet")
- end
+ expect { user.organisations.delete(organisation_a_1) }.not_to change(Annotation, :count)
+ expect { user.organisations.delete(organisation_a_2) }.to change(Annotation, :count).by(-1)
- it "deletes the annotation if the notes become blank" do
- organisation = create(:organisation)
- user = create(:user, notes: "Lorem ipsum", organisations: [organisation])
- expect { user.update!(notes: "") }.to change { Annotation.where(user: user).first }.to(nil)
- end
+ expect { user.organisations.delete(organisation_b_1) }.not_to change(Annotation, :count)
+ expect { user.organisations.delete(organisation_b_2) }.to change(Annotation, :count).by(-1)
end
- describe "user anonymization" do
- it "deletes the annotations" do
- organisation = create(:organisation)
- user = create(:user, notes: "Lorem ipsum", organisations: [organisation])
- expect(Annotation.where(user: user).sole.content).to eq("Lorem ipsum")
-
- user.soft_delete
- expect(Annotation.where(user: user)).to be_empty
- end
- end
-
- describe "removing user from organisation" do
- it "removes annotations for the org's territory" do
- territory_a = create(:territory)
- territory_b = create(:territory)
- organisation_a_1 = create(:organisation, territory: territory_a)
- organisation_a_2 = create(:organisation, territory: territory_a)
- organisation_b_1 = create(:organisation, territory: territory_b)
- organisation_b_2 = create(:organisation, territory: territory_b)
-
- user = create(:user, notes: "Lorem ipsum", organisations: [organisation_a_1, organisation_a_2, organisation_b_1, organisation_b_2])
- expect(Annotation.where(user: user).count).to eq(2)
-
- expect { UserProfile.find_by!(user: user, organisation: organisation_a_1).destroy! }.not_to change(Annotation, :count)
- expect { UserProfile.find_by!(user: user, organisation: organisation_a_2).destroy! }.to change(Annotation, :count).by(-1)
- expect(Annotation.where(user: user).sole.territory).to eq(territory_b)
- end
+ it "deletes annotations when soft deleting" do
+ organisation = create(:organisation)
+ user = create(:user, organisations: [organisation])
+ user.annotate!("Ma remarque", territory: organisation.territory)
+ expect { user.soft_delete }.to change { user.annotations.count }.to(0)
end
end
end
diff --git a/spec/services/admin_creates_agent_spec.rb b/spec/services/admin_creates_agent_spec.rb
new file mode 100644
index 0000000000..4437927ff6
--- /dev/null
+++ b/spec/services/admin_creates_agent_spec.rb
@@ -0,0 +1,27 @@
+RSpec.describe AdminCreatesAgent do
+ context "when inviting an agent that doesn't have any services" do
+ let(:agent) do
+ create(:agent, :no_services, organisations: [])
+ end
+ let(:service1) { create(:service) }
+ let(:service2) { create(:service) }
+ let(:organisation) do
+ create(:organisation)
+ end
+ let(:admin) do
+ create(:agent, admin_role_in_organisations: [organisation])
+ end
+
+ it "adds the services to the agent" do
+ described_class.new(
+ agent_params: { email: agent.email, service_ids: [service1.id, service2.id] },
+ current_agent: admin,
+ organisations: [organisation],
+ access_level: :basic
+ ).call
+
+ expect(agent.reload.services).to contain_exactly(service1, service2)
+ expect(agent.organisations).to eq [organisation]
+ end
+ end
+end
diff --git a/spec/services/merge_users_service_spec.rb b/spec/services/merge_users_service_spec.rb
index af943ce24b..444fed6f72 100644
--- a/spec/services/merge_users_service_spec.rb
+++ b/spec/services/merge_users_service_spec.rb
@@ -189,23 +189,62 @@
end
end
- context "both users have notes" do
- before do
- user_target.update(notes: "Sympa")
- user_to_merge.update(notes: "thiquement")
- end
-
+ context "both users have annotations" do
it "preserves target by default" do
+ user_target.annotations.create!(territory: organisation.territory, content: "Sympa")
+ user_to_merge.annotations.create!(territory: organisation.territory, content: "thiquement")
perform
- expect(user_target.notes).to eq("Sympa")
+ expect(user_target.annotation_for(organisation.territory)).to eq("Sympa")
+ end
+
+ context "when merging annotations" do
+ let(:attributes_to_merge) { [:annotation_content] }
+
+ it "overrides annotation from merged user" do
+ user_target.annotations.create!(territory: organisation.territory, content: "Sympa")
+ user_to_merge.annotations.create!(territory: organisation.territory, content: "thiquement")
+ perform
+ expect(user_target.annotation_for(organisation.territory)).to eq("thiquement")
+ end
end
- context "when merging notes" do
- let(:attributes_to_merge) { [:notes] }
+ context "when both users have annotations in two territories" do
+ let(:attributes_to_merge) { [:annotation_content] }
+
+ let(:other_territory) { create(:territory) }
+ let(:organisation_in_other_territory) { create(:organisation, territory: other_territory) }
+
+ let(:current_territory) { organisation.territory }
+
+ before do
+ user_to_merge.organisations << organisation_in_other_territory
+ user_target.organisations << organisation_in_other_territory
+
+ user_to_merge.annotations.create!(territory: current_territory, content: "User to merge, current territory")
+ user_to_merge.annotations.create!(territory: other_territory, content: "User to merge, other territory")
+
+ user_target.annotations.create!(territory: current_territory, content: "User target, current territory")
+ user_target.annotations.create!(territory: other_territory, content: "User target, other territory")
+ end
- it "overrides notes from merged user" do
+ it "preserves the 2 users with their 2 annotations in other territory, merges in current territory" do
perform
- expect(user_target.notes).to eq("thiquement")
+ user_target.reload
+ user_to_merge.reload
+
+ # Dans l'autre territoire, les annotations restent intactes
+ expected_annotations_in_other_territory = [
+ [user_to_merge, "User to merge, other territory"],
+ [user_target, "User target, other territory"],
+ ]
+ expect(Annotation.where(territory: other_territory).map { [_1.user, _1.content] }).to match_array(expected_annotations_in_other_territory)
+
+ # Dans le territoire courant, on ne garde que l'annotation à fusionner, c'est-à-dire
+ # l'annotation sélectionnée, et on l'associe à l'usager qui est gardé (target).
+ expected_annotations_in_current_territory = [
+ [user_target, "User to merge, current territory"],
+ ]
+ expect(Annotation.where(territory: current_territory).map { [_1.user, _1.content] }).to match_array(expected_annotations_in_current_territory)
end
end
end