Enable user destruction
This commit is contained in:
parent
e240dd6d2c
commit
a4fd629f4a
14 changed files with 126 additions and 63 deletions
|
@ -41,7 +41,7 @@ module Manager
|
|||
|
||||
def export_mail_brouillons
|
||||
dossiers = procedure.dossiers.state_brouillon.includes(:user)
|
||||
emails = dossiers.map { |d| d.user.email }.sort.uniq
|
||||
emails = dossiers.map { |dossier| dossier.user_email_for(:display) }.sort.uniq
|
||||
date = Time.zone.now.strftime('%d-%m-%Y')
|
||||
send_data(emails.join("\n"), :filename => "brouillons-#{procedure.id}-au-#{date}.csv")
|
||||
end
|
||||
|
|
|
@ -54,8 +54,12 @@ module Types
|
|||
end
|
||||
|
||||
def usager
|
||||
if object.user_deleted?
|
||||
{ email: object.user_email_for(:display), id: -1 }
|
||||
else
|
||||
Loaders::Record.for(User).load(object.user_id)
|
||||
end
|
||||
end
|
||||
|
||||
def groupe_instructeur
|
||||
Loaders::Record.for(GroupeInstructeur).load(object.groupe_instructeur_id)
|
||||
|
|
|
@ -13,7 +13,7 @@ class DossierMailer < ApplicationMailer
|
|||
|
||||
subject = "Retrouvez votre brouillon pour la démarche « #{dossier.procedure.libelle} »"
|
||||
|
||||
mail(from: NO_REPLY_EMAIL, to: dossier.user.email, subject: subject) do |format|
|
||||
mail(from: NO_REPLY_EMAIL, to: dossier.user_email_for(:notification), subject: subject) do |format|
|
||||
format.html { render layout: 'mailers/notifications_layout' }
|
||||
end
|
||||
end
|
||||
|
@ -25,7 +25,7 @@ class DossierMailer < ApplicationMailer
|
|||
|
||||
subject = "Nouveau message pour votre dossier nº #{dossier.id} (#{dossier.procedure.libelle})"
|
||||
|
||||
mail(from: NO_REPLY_EMAIL, to: dossier.user.email, subject: subject) do |format|
|
||||
mail(from: NO_REPLY_EMAIL, to: dossier.user_email_for(:notification), subject: subject) do |format|
|
||||
format.html { render layout: 'mailers/notifications_layout' }
|
||||
end
|
||||
end
|
||||
|
@ -49,7 +49,7 @@ class DossierMailer < ApplicationMailer
|
|||
|
||||
subject = "Votre dossier nº #{@dossier.id} est en train d'être réexaminé"
|
||||
|
||||
mail(from: NO_REPLY_EMAIL, to: dossier.user.email, subject: subject) do |format|
|
||||
mail(from: NO_REPLY_EMAIL, to: dossier.user_email_for(:notification), subject: subject) do |format|
|
||||
format.html { render layout: 'mailers/notifications_layout' }
|
||||
end
|
||||
end
|
||||
|
@ -139,7 +139,7 @@ class DossierMailer < ApplicationMailer
|
|||
@subject = "Attention : votre dossier n'est pas déposé."
|
||||
@dossier = dossier
|
||||
|
||||
mail(to: dossier.user.email, subject: @subject)
|
||||
mail(to: dossier.user_email_for(:notification), subject: @subject)
|
||||
end
|
||||
|
||||
protected
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
class NotificationMailer < ApplicationMailer
|
||||
include ActionView::Helpers::SanitizeHelper
|
||||
|
||||
before_action :prevent_delivery_to_deleted_users
|
||||
|
||||
helper ServiceHelper
|
||||
helper MailerHelper
|
||||
|
||||
|
@ -36,8 +38,12 @@ class NotificationMailer < ApplicationMailer
|
|||
|
||||
private
|
||||
|
||||
def prevent_delivery_to_deleted_users
|
||||
!@dossier.user_deleted?
|
||||
end
|
||||
|
||||
def send_notification(dossier, mail_template)
|
||||
email = dossier.user.email
|
||||
email = dossier.user_email_for(:notification)
|
||||
|
||||
subject = mail_template.subject_for_dossier(dossier)
|
||||
body = mail_template.body_for_dossier(dossier)
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
# autorisation_donnees :boolean
|
||||
# brouillon_close_to_expiration_notice_sent_at :datetime
|
||||
# conservation_extension :interval default(0 seconds)
|
||||
# deleted_user_email_never_send :string
|
||||
# en_construction_at :datetime
|
||||
# en_construction_close_to_expiration_notice_sent_at :datetime
|
||||
# en_construction_conservation_extension :interval default(0 seconds)
|
||||
|
@ -137,9 +138,9 @@ class Dossier < ApplicationRecord
|
|||
end
|
||||
|
||||
event :repasser_en_instruction, after: :after_repasser_en_instruction do
|
||||
transitions from: :refuse, to: :en_instruction
|
||||
transitions from: :sans_suite, to: :en_instruction
|
||||
transitions from: :accepte, to: :en_instruction
|
||||
transitions from: :refuse, to: :en_instruction, guard: :can_repasser_en_instruction?
|
||||
transitions from: :sans_suite, to: :en_instruction, guard: :can_repasser_en_instruction?
|
||||
transitions from: :accepte, to: :en_instruction, guard: :can_repasser_en_instruction?
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -249,6 +250,7 @@ class Dossier < ApplicationRecord
|
|||
states = opts[:notify_on_closed] ? [:publiee, :close, :depubliee] : [:publiee, :depubliee]
|
||||
joins(:procedure)
|
||||
.where(procedures: { aasm_state: states })
|
||||
.where.not(user_id: nil)
|
||||
end
|
||||
|
||||
scope :brouillon_close_to_expiration, -> do
|
||||
|
@ -350,6 +352,22 @@ class Dossier < ApplicationRecord
|
|||
validates :individual, presence: true, if: -> { revision.procedure.for_individual? }
|
||||
validates :groupe_instructeur, presence: true, if: -> { !brouillon? }
|
||||
|
||||
def user_deleted?
|
||||
user_id.nil?
|
||||
end
|
||||
|
||||
def user_email_for(use)
|
||||
if user_deleted?
|
||||
if use == :display
|
||||
deleted_user_email_never_send
|
||||
else
|
||||
raise "Can not send email to discarded user"
|
||||
end
|
||||
else
|
||||
user.email
|
||||
end
|
||||
end
|
||||
|
||||
def motivation
|
||||
return nil if !termine?
|
||||
traitements.any? ? traitements.last.motivation : read_attribute(:motivation)
|
||||
|
@ -416,6 +434,10 @@ class Dossier < ApplicationRecord
|
|||
brouillon? && procedure.dossier_can_transition_to_en_construction?
|
||||
end
|
||||
|
||||
def can_repasser_en_instruction?
|
||||
termine? && !user_deleted?
|
||||
end
|
||||
|
||||
def can_be_updated_by_user?
|
||||
brouillon? || en_construction?
|
||||
end
|
||||
|
@ -429,7 +451,7 @@ class Dossier < ApplicationRecord
|
|||
end
|
||||
|
||||
def messagerie_available?
|
||||
!brouillon? && !archived
|
||||
!brouillon? && !user_deleted? && !archived
|
||||
end
|
||||
|
||||
def en_construction_close_to_expiration?
|
||||
|
@ -590,13 +612,18 @@ class Dossier < ApplicationRecord
|
|||
administration_emails.each do |email|
|
||||
DossierMailer.notify_deletion_to_administration(deleted_dossier, email).deliver_later
|
||||
end
|
||||
DossierMailer.notify_deletion_to_user(deleted_dossier, user.email).deliver_later
|
||||
|
||||
if !user_deleted?
|
||||
DossierMailer.notify_deletion_to_user(deleted_dossier, user_email_for(:notification)).deliver_later
|
||||
end
|
||||
|
||||
log_dossier_operation(author, :supprimer, self)
|
||||
elsif termine?
|
||||
deleted_dossier = DeletedDossier.create_from_dossier(self, reason)
|
||||
|
||||
DossierMailer.notify_instructeur_deletion_to_user(deleted_dossier, user.email).deliver_later
|
||||
if !user_deleted?
|
||||
DossierMailer.notify_instructeur_deletion_to_user(deleted_dossier, user_email_for(:notification)).deliver_later
|
||||
end
|
||||
|
||||
log_dossier_operation(author, :supprimer, self)
|
||||
end
|
||||
|
@ -751,7 +778,7 @@ class Dossier < ApplicationRecord
|
|||
def spreadsheet_columns(with_etablissement: false, types_de_champ:, types_de_champ_private:)
|
||||
columns = [
|
||||
['ID', id.to_s],
|
||||
['Email', user.email]
|
||||
['Email', user_email_for(:display)]
|
||||
]
|
||||
|
||||
if procedure.for_individual?
|
||||
|
|
|
@ -170,18 +170,24 @@ class User < ApplicationRecord
|
|||
end
|
||||
|
||||
def can_be_deleted?
|
||||
!administrateur? && !instructeur? && !expert? && dossiers.with_discarded.state_instruction_commencee.empty?
|
||||
!administrateur? && !instructeur? && !expert?
|
||||
end
|
||||
|
||||
def delete_and_keep_track_dossiers(administration)
|
||||
if !can_be_deleted?
|
||||
raise "Cannot delete this user because instruction has started for some dossiers"
|
||||
raise "Cannot delete this user because they are also instructeur, expert or administrateur"
|
||||
end
|
||||
|
||||
dossiers.each do |dossier|
|
||||
Invite.where(dossier: dossiers.with_discarded).destroy_all
|
||||
dossiers.state_en_construction.each do |dossier|
|
||||
dossier.discard_and_keep_track!(administration, :user_removed)
|
||||
end
|
||||
dossiers.with_discarded.destroy_all
|
||||
DossierOperationLog
|
||||
.where(dossier: dossiers.with_discarded.discarded)
|
||||
.where.not(operation: DossierOperationLog.operations.fetch(:supprimer))
|
||||
.destroy_all
|
||||
dossiers.with_discarded.discarded.destroy_all
|
||||
dossiers.update_all(deleted_user_email_never_send: email, user_id: nil)
|
||||
destroy!
|
||||
end
|
||||
|
||||
|
|
|
@ -174,7 +174,7 @@ def add_message(pdf, message)
|
|||
if message.sent_by_system?
|
||||
sender = 'Email automatique'
|
||||
elsif message.sent_by?(@dossier.user)
|
||||
sender = @dossier.user.email
|
||||
sender = @dossier.user_email_for(:display)
|
||||
end
|
||||
|
||||
format_in_2_lines(pdf, "#{sender}, #{try_format_date(message.created_at)}",
|
||||
|
@ -233,7 +233,7 @@ prawn_document(page_size: "A4") do |pdf|
|
|||
format_in_2_columns(pdf, 'Informations France Connect', "Le dossier a été déposé par le compte de #{@dossier.france_connect_information.given_name} #{@dossier.france_connect_information.family_name}, authentifié par France Connect le #{@dossier.france_connect_information.updated_at.strftime('%d/%m/%Y')}")
|
||||
end
|
||||
|
||||
format_in_2_columns(pdf, "Email", @dossier.user.email)
|
||||
format_in_2_columns(pdf, "Email", @dossier.user_email_for(:display))
|
||||
|
||||
if @dossier.individual.present?
|
||||
add_identite_individual(pdf, @dossier.individual)
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
= link_to(instructeur_avis_path(avis.procedure, avis), class: 'cell-link') do
|
||||
%span.icon.folder
|
||||
#{avis.dossier.id}
|
||||
%td= link_to(avis.dossier.user.email, instructeur_avis_path(avis.procedure, avis), class: 'cell-link')
|
||||
%td= link_to(avis.dossier.user_email_for(:display), instructeur_avis_path(avis.procedure, avis), class: 'cell-link')
|
||||
%td= link_to(avis.procedure.libelle, instructeur_avis_path(avis.procedure, avis), class: 'cell-link')
|
||||
= paginate(@avis)
|
||||
- else
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
= link_to(instructeur_avis_path(avis.procedure, avis), class: 'cell-link') do
|
||||
%span.icon.folder
|
||||
#{avis.dossier.id}
|
||||
%td= link_to(avis.dossier.user.email, instructeur_avis_path(avis.procedure, avis), class: 'cell-link')
|
||||
%td= link_to(avis.dossier.user_email_for(:display), instructeur_avis_path(avis.procedure, avis), class: 'cell-link')
|
||||
%td= link_to(avis.procedure.libelle, instructeur_avis_path(avis.procedure, avis), class: 'cell-link')
|
||||
= paginate(@avis)
|
||||
- else
|
||||
|
|
|
@ -99,13 +99,20 @@
|
|||
%h4 Voir l’attestation
|
||||
%p Cette attestation a été envoyée automatiquement au demandeur.
|
||||
|
||||
- if dossier.can_repasser_en_instruction?
|
||||
%li
|
||||
= link_to repasser_en_instruction_instructeur_dossier_path(dossier.procedure, dossier), method: :post, data: { remote:true, confirm: "Voulez vous remettre le dossier #{dossier.id} en instruction ?" } do
|
||||
%span.icon.in-progress
|
||||
.dropdown-description
|
||||
%h4 Repasser en instruction
|
||||
L’usager sera notifié que son dossier est réexaminé.
|
||||
- if dossier.termine?
|
||||
- elsif dossier.user_deleted?
|
||||
%li
|
||||
%span.icon.info
|
||||
.dropdown-description
|
||||
%h4 En attente d‘archivage
|
||||
L'usager a supprimé son compte. Vous pouvez archiver puis supprimer le dossier.
|
||||
|
||||
%li
|
||||
= link_to supprimer_dossier_instructeur_dossier_path(dossier.procedure, dossier), method: :patch, data: { confirm: "Voulez vous vraiment supprimer le dossier #{dossier.id} ? Cette action est irréversible. \nNous vous suggérons de télécharger le dossier au format PDF au préalable." } do
|
||||
%span.icon.delete
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
= link_to(dossier_linked_path(current_instructeur, dossier), class: 'cell-link') do
|
||||
= dossier.id
|
||||
%td= link_to(dossier.procedure.libelle, dossier_linked_path(current_instructeur, dossier), class: 'cell-link')
|
||||
%td= link_to(dossier.user.email, dossier_linked_path(current_instructeur, dossier), class: 'cell-link')
|
||||
%td= link_to(dossier.user_email_for(:display), dossier_linked_path(current_instructeur, dossier), class: 'cell-link')
|
||||
%td.status-col
|
||||
= link_to(dossier_linked_path(current_instructeur, dossier), class: 'cell-link') do
|
||||
= status_badge(dossier.state)
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
class AddDeletedUserEmailNeverSendToDossiers < ActiveRecord::Migration[6.1]
|
||||
def change
|
||||
add_column :dossiers, :deleted_user_email_never_send, :string
|
||||
end
|
||||
end
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2021_04_28_104228) do
|
||||
ActiveRecord::Schema.define(version: 2021_04_28_114228) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
@ -275,6 +275,7 @@ ActiveRecord::Schema.define(version: 2021_04_28_104228) do
|
|||
t.datetime "last_commentaire_updated_at"
|
||||
t.string "api_entreprise_job_exceptions", array: true
|
||||
t.interval "conservation_extension", default: "PT0S"
|
||||
t.string "deleted_user_email_never_send"
|
||||
t.index "to_tsvector('french'::regconfig, (search_terms || private_search_terms))", name: "index_dossiers_on_search_terms_private_search_terms", using: :gin
|
||||
t.index "to_tsvector('french'::regconfig, search_terms)", name: "index_dossiers_on_search_terms", using: :gin
|
||||
t.index ["archived"], name: "index_dossiers_on_archived"
|
||||
|
|
|
@ -263,13 +263,16 @@ describe User, type: :model do
|
|||
|
||||
describe '#can_be_deleted?' do
|
||||
let(:user) { create(:user) }
|
||||
let(:administrateur) { create(:administrateur) }
|
||||
let(:instructeur) { create(:instructeur) }
|
||||
let(:expert) { create(:expert) }
|
||||
|
||||
subject { user.can_be_deleted? }
|
||||
|
||||
context 'when the user has a dossier in instruction' do
|
||||
let!(:dossier) { create(:dossier, :en_instruction, user: user) }
|
||||
|
||||
it { is_expected.to be false }
|
||||
it { is_expected.to be true }
|
||||
end
|
||||
|
||||
context 'when the user has no dossier in instruction' do
|
||||
|
@ -278,19 +281,19 @@ describe User, type: :model do
|
|||
|
||||
context 'when the user is an administrateur' do
|
||||
it 'cannot be deleted' do
|
||||
administrateur = create(:administrateur)
|
||||
user = administrateur.user
|
||||
|
||||
expect(user.can_be_deleted?).to be_falsy
|
||||
expect(administrateur.user.can_be_deleted?).to be_falsy
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the user is an instructeur' do
|
||||
it 'cannot be deleted' do
|
||||
instructeur = create(:instructeur)
|
||||
user = instructeur.user
|
||||
expect(instructeur.user.can_be_deleted?).to be_falsy
|
||||
end
|
||||
end
|
||||
|
||||
expect(user.can_be_deleted?).to be_falsy
|
||||
context 'when the user is an expert' do
|
||||
it 'cannot be deleted' do
|
||||
expect(expert.user.can_be_deleted?).to be_falsy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -299,14 +302,7 @@ describe User, type: :model do
|
|||
let(:super_admin) { create(:super_admin) }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
context 'with a dossier in instruction' do
|
||||
let!(:dossier_en_instruction) { create(:dossier, :en_instruction, user: user) }
|
||||
it 'raises' do
|
||||
expect { user.delete_and_keep_track_dossiers(super_admin) }.to raise_error(RuntimeError)
|
||||
end
|
||||
end
|
||||
|
||||
context 'without a dossier in instruction' do
|
||||
context 'without a dossier with processing strted' do
|
||||
let!(:dossier_en_construction) { create(:dossier, :en_construction, user: user) }
|
||||
let!(:dossier_brouillon) { create(:dossier, user: user) }
|
||||
|
||||
|
@ -321,28 +317,39 @@ describe User, type: :model do
|
|||
end
|
||||
|
||||
context 'with a discarded dossier' do
|
||||
let!(:dossier_cache) do
|
||||
create(:dossier, :en_construction, user: user)
|
||||
end
|
||||
let!(:dossier_from_another_user) do
|
||||
create(:dossier, :en_construction, user: create(:user))
|
||||
end
|
||||
let(:dossier_to_discard) { create(:dossier, :en_construction, user: user) }
|
||||
let!(:dossier_from_another_user) { create(:dossier, :en_construction, user: create(:user)) }
|
||||
|
||||
it "keep track of dossiers and delete user" do
|
||||
dossier_cache.discard_and_keep_track!(super_admin, :user_request)
|
||||
dossier_to_discard.discard_and_keep_track!(super_admin, :user_request)
|
||||
user.delete_and_keep_track_dossiers(super_admin)
|
||||
|
||||
expect(DeletedDossier.find_by(dossier_id: dossier_en_construction)).to be_present
|
||||
expect(DeletedDossier.find_by(dossier_id: dossier_brouillon)).to be_nil
|
||||
expect(Dossier.find_by(id: dossier_from_another_user.id)).to be_present
|
||||
expect(User.find_by(id: user.id)).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "doesn't destroy dossiers of another user" do
|
||||
dossier_cache.discard_and_keep_track!(super_admin, :user_request)
|
||||
context 'with dossiers with processing strted' do
|
||||
let!(:dossier_en_instruction) { create(:dossier, :en_instruction, user: user) }
|
||||
let!(:dossier_termine) { create(:dossier, :accepte, user: user) }
|
||||
|
||||
it "keep track of dossiers and delete user" do
|
||||
user.delete_and_keep_track_dossiers(super_admin)
|
||||
|
||||
expect(Dossier.find_by(id: dossier_from_another_user.id)).to be_present
|
||||
end
|
||||
expect(dossier_en_instruction.reload).to be_present
|
||||
expect(dossier_en_instruction.user).to be_nil
|
||||
expect(dossier_en_instruction.user_email_for(:display)).to eq(user.email)
|
||||
expect { dossier_en_instruction.user_email_for(:notification) }.to raise_error(RuntimeError)
|
||||
|
||||
expect(dossier_termine.reload).to be_present
|
||||
expect(dossier_termine.user).to be_nil
|
||||
expect(dossier_termine.user_email_for(:display)).to eq(user.email)
|
||||
expect { dossier_termine.user_email_for(:notification) }.to raise_error(RuntimeError)
|
||||
|
||||
expect(User.find_by(id: user.id)).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue