Skip to content

Commit

Permalink
hide expired dossiers instead of delete them
Browse files Browse the repository at this point in the history
  • Loading branch information
lisa-durand committed Jun 11, 2024
1 parent 256b707 commit a163d1c
Show file tree
Hide file tree
Showing 23 changed files with 231 additions and 132 deletions.
9 changes: 8 additions & 1 deletion app/controllers/users/dossiers_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def index
@user_dossiers = current_user.dossiers.state_not_termine.merge(@dossiers_visibles)
@dossiers_traites = current_user.dossiers.state_termine.merge(@dossiers_visibles)
@dossiers_invites = current_user.dossiers_invites.merge(@dossiers_visibles)
@dossiers_supprimes_recemment = current_user.dossiers.hidden_by_user.merge(ordered_dossiers)
@dossiers_supprimes_recemment = (current_user.dossiers.hidden_by_user.or(current_user.dossiers.hidden_by_automatic)).merge(ordered_dossiers)
@dossier_transferes = @dossiers_visibles.where(dossier_transfer_id: DossierTransfer.for_email(current_user.email))
@dossiers_close_to_expiration = current_user.dossiers.close_to_expiration.merge(@dossiers_visibles)
@dossiers_supprimes_definitivement = deleted_dossiers
Expand Down Expand Up @@ -251,6 +251,11 @@ def submit_brouillon

def extend_conservation
dossier.extend_conservation(dossier.procedure.duree_conservation_dossiers_dans_ds.months)

if dossier.hidden_at.present?
dossier.update!(hidden_at: nil, hidden_by_reason: nil)
end

flash[:notice] = t('views.users.dossiers.archived_dossier', duree_conservation_dossiers_dans_ds: dossier.procedure.duree_conservation_dossiers_dans_ds)
redirect_back(fallback_location: dossier_path(@dossier))
end
Expand Down Expand Up @@ -526,6 +531,8 @@ def dossier_scope
Dossier.visible_by_user.or(Dossier.for_procedure_preview).or(Dossier.for_editing_fork)
elsif action_name == 'restore'
Dossier.hidden_by_user
elsif action_name == 'extend_conservation'
Dossier.visible_by_user.or(Dossier.hidden_by_automatic)
else
Dossier.visible_by_user
end
Expand Down
22 changes: 11 additions & 11 deletions app/mailers/dossier_mailer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -129,32 +129,32 @@ def notify_en_construction_deletion_to_administration(dossier, to_email)
mail(to: to_email, subject: @subject)
end

def notify_deletion_to_administration(deleted_dossier, to_email)
def notify_deletion_to_administration(hidden_dossier, to_email)
configure_defaults_for_email(to_email)

@subject = default_i18n_subject(dossier_id: deleted_dossier.dossier_id)
@deleted_dossier = deleted_dossier
@subject = default_i18n_subject(dossier_id: hidden_dossier.id)
@hidden_dossier = hidden_dossier

Check warning on line 136 in app/mailers/dossier_mailer.rb

View check run for this annotation

Codecov / codecov/patch

app/mailers/dossier_mailer.rb#L135-L136

Added lines #L135 - L136 were not covered by tests

mail(to: to_email, subject: @subject)
end

def notify_automatic_deletion_to_user(deleted_dossiers, to_email)
def notify_automatic_deletion_to_user(hidden_dossiers, to_email)
configure_defaults_for_email(to_email)

I18n.with_locale(deleted_dossiers.first.user_locale) do
@state = deleted_dossiers.first.state
@subject = default_i18n_subject(count: deleted_dossiers.size)
@deleted_dossiers = deleted_dossiers
I18n.with_locale(hidden_dossiers.first.user_locale) do
@state = hidden_dossiers.first.state
@subject = default_i18n_subject(count: hidden_dossiers.size)
@hidden_dossiers = hidden_dossiers

Check warning on line 147 in app/mailers/dossier_mailer.rb

View check run for this annotation

Codecov / codecov/patch

app/mailers/dossier_mailer.rb#L144-L147

Added lines #L144 - L147 were not covered by tests

mail(to: to_email, subject: @subject)
end
end

def notify_automatic_deletion_to_administration(deleted_dossiers, to_email)
def notify_automatic_deletion_to_administration(hidden_dossiers, to_email)
configure_defaults_for_email(to_email)

@subject = default_i18n_subject(count: deleted_dossiers.size)
@deleted_dossiers = deleted_dossiers
@subject = default_i18n_subject(count: hidden_dossiers.size)
@hidden_dossiers = hidden_dossiers

Check warning on line 157 in app/mailers/dossier_mailer.rb

View check run for this annotation

Codecov / codecov/patch

app/mailers/dossier_mailer.rb#L156-L157

Added lines #L156 - L157 were not covered by tests

mail(to: to_email, subject: @subject)
end
Expand Down
39 changes: 24 additions & 15 deletions app/models/dossier.rb
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ def classer_sans_suite(motivation: nil, instructeur: nil, processed_at: Time.zon
scope :state_en_construction, -> { where(state: states.fetch(:en_construction)) }
scope :state_not_en_construction, -> { where.not(state: states.fetch(:en_construction)) }
scope :state_en_instruction, -> { where(state: states.fetch(:en_instruction)) }
scope :state_not_en_instruction, -> { where.not(state: states.fetch(:en_instruction)) }
scope :state_en_construction_ou_instruction, -> { where(state: EN_CONSTRUCTION_OU_INSTRUCTION) }
scope :state_instruction_commencee, -> { where(state: INSTRUCTION_COMMENCEE) }
scope :state_termine, -> { where(state: TERMINE) }
Expand All @@ -220,11 +221,13 @@ def classer_sans_suite(motivation: nil, instructeur: nil, processed_at: Time.zon
scope :not_archived, -> { where(archived: false) }
scope :prefilled, -> { where(prefilled: true) }
scope :hidden_by_user, -> { where.not(hidden_by_user_at: nil) }
scope :hidden_by_automatic, -> { where.not(hidden_at: nil).where(hidden_by_reason: 'expired') }
scope :hidden_by_administration, -> { where.not(hidden_by_administration_at: nil) }
scope :visible_by_user, -> { where(for_procedure_preview: false).where(hidden_by_user_at: nil, editing_fork_origin_id: nil) }
scope :visible_by_user, -> { where(for_procedure_preview: false).where(hidden_by_user_at: nil, editing_fork_origin_id: nil).where(hidden_at: nil) }
scope :visible_by_administration, -> {
state_not_brouillon
.where(hidden_by_administration_at: nil)
.where(hidden_at: nil)
.merge(visible_by_user.or(state_not_en_construction))
}
scope :visible_by_user_or_administration, -> { visible_by_user.or(visible_by_administration) }
Expand Down Expand Up @@ -370,10 +373,12 @@ def classer_sans_suite(motivation: nil, instructeur: nil, processed_at: Time.zon
scope :without_termine_expiration_notice_sent, -> { where(termine_close_to_expiration_notice_sent_at: nil) }

scope :deleted_by_user_expired, -> { where('dossiers.hidden_by_user_at < ?', 1.week.ago) }
scope :deleted_by_automatic_expired, -> { where('dossiers.hidden_at < ?', 1.week.ago) }
scope :deleted_by_administration_expired, -> { where('dossiers.hidden_by_administration_at < ?', 1.week.ago) }
scope :en_brouillon_expired_to_delete, -> { state_brouillon.deleted_by_user_expired }
scope :en_construction_expired_to_delete, -> { state_en_construction.deleted_by_user_expired }
scope :termine_expired_to_delete, -> { state_termine.deleted_by_user_expired.deleted_by_administration_expired }
scope :not_en_instruction_expired_to_delete, -> { state_not_en_instruction.deleted_by_automatic_expired }

scope :brouillon_near_procedure_closing_date, -> do
# select users who have submitted dossier for the given 'procedures.id'
Expand Down Expand Up @@ -420,7 +425,7 @@ def classer_sans_suite(motivation: nil, instructeur: nil, processed_at: Time.zon
when 'tous'
visible_by_administration.all_state
when 'supprimes_recemment'
hidden_by_administration.state_termine
hidden_by_administration.state_termine.or(hidden_by_automatic)
when 'archives'
visible_by_administration.archived
when 'expirant'
Expand Down Expand Up @@ -595,6 +600,10 @@ def can_be_deleted_by_administration?(reason)
termine? || reason == :procedure_removed
end

def can_be_deleted_by_automatic?(reason)
reason == :expired
end

def can_terminer_automatiquement_by_sva_svr?
sva_svr_decision_triggered_at.nil? && !pending_correction? && (sva_svr_decision_on.today? || sva_svr_decision_on.past?)
end
Expand Down Expand Up @@ -640,7 +649,12 @@ def expiration_notification_date

def close_to_expiration?
return false if en_instruction?
expiration_notification_date < Time.zone.now
expiration_notification_date < Time.zone.now && expiration_notification_date > Expired::REMAINING_WEEKS_BEFORE_EXPIRATION.weeks.ago
end

def has_expired?
return false if en_instruction?
expiration_notification_date < Time.zone.now && expiration_notification_date < Expired::REMAINING_WEEKS_BEFORE_EXPIRATION.weeks.ago
end

def after_notification_expiration_date
Expand Down Expand Up @@ -815,18 +829,6 @@ def build_attestation
end
end

def expired_keep_track_and_destroy!
transaction do
DeletedDossier.create_from_dossier(self, :expired)
log_automatic_dossier_operation(:supprimer, self)
dossier_operation_logs.purge_discarded
destroy!
end
true
rescue
false
end

def author_is_user(author)
author.is_a?(User)
end
Expand All @@ -835,12 +837,18 @@ def author_is_administration(author)
author.is_a?(Instructeur) || author.is_a?(Administrateur) || author.is_a?(SuperAdmin)
end

def author_is_automatic(author)
author == :automatic
end

def hide_and_keep_track!(author, reason)
transaction do
if author_is_administration(author) && can_be_deleted_by_administration?(reason)
update(hidden_by_administration_at: Time.zone.now, hidden_by_reason: reason)
elsif author_is_user(author) && can_be_deleted_by_user?
update(hidden_by_user_at: Time.zone.now, dossier_transfer_id: nil, hidden_by_reason: reason)
elsif author_is_automatic(author) && can_be_deleted_by_automatic?(reason)
update(hidden_at: Time.zone.now, hidden_by_reason: reason)
else
raise "Unauthorized dossier hide attempt Dossier##{id} by #{author} for reason #{reason}"
end
Expand Down Expand Up @@ -1086,6 +1094,7 @@ def self.purge_discarded
en_brouillon_expired_to_delete.find_each(&:purge_discarded)
en_construction_expired_to_delete.find_each(&:purge_discarded)
termine_expired_to_delete.find_each(&:purge_discarded)
not_en_instruction_expired_to_delete.find_each(&:purge_discarded)
end

def skip_user_notification_email?
Expand Down
5 changes: 5 additions & 0 deletions app/models/dossier_operation_log.rb
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ def self.create_and_serialize(params)
def self.serialize_author(author)
if author.nil?
nil
elsif author == :automatic
{
id: "Automatic",
email: CONTACT_EMAIL
}.as_json
else
{
id: serialize_author_id(author),
Expand Down
16 changes: 8 additions & 8 deletions app/models/instructeur.rb
Original file line number Diff line number Diff line change
Expand Up @@ -227,18 +227,18 @@ def flipper_id
def dossiers_count_summary(groupe_instructeur_ids)
query = <<~EOF
SELECT
COUNT(DISTINCT dossiers.id) FILTER (where dossiers.hidden_by_administration_at IS NULL AND not archived AND dossiers.state in ('en_construction', 'en_instruction') AND follows.id IS NULL) AS a_suivre,
COUNT(DISTINCT dossiers.id) FILTER (where dossiers.hidden_by_administration_at IS NULL AND not archived AND dossiers.state in ('en_construction', 'en_instruction') AND follows.instructeur_id = :instructeur_id) AS suivis,
COUNT(DISTINCT dossiers.id) FILTER (where dossiers.hidden_by_administration_at IS NULL AND not archived AND dossiers.state in ('accepte', 'refuse', 'sans_suite')) AS traites,
COUNT(DISTINCT dossiers.id) FILTER (where dossiers.hidden_by_administration_at IS NULL AND not archived) AS tous,
COUNT(DISTINCT dossiers.id) FILTER (where dossiers.hidden_by_administration_at IS NULL AND archived) AS archives,
COUNT(DISTINCT dossiers.id) FILTER (where dossiers.hidden_by_administration_at IS NOT NULL AND not archived AND dossiers.state in ('accepte', 'refuse', 'sans_suite')) AS supprimes_recemment,
COUNT(DISTINCT dossiers.id) FILTER (where dossiers.hidden_by_administration_at IS NULL AND procedures.procedure_expires_when_termine_enabled
COUNT(DISTINCT dossiers.id) FILTER (where dossiers.hidden_by_administration_at IS NULL AND dossiers.hidden_at IS NULL AND not archived AND dossiers.state in ('en_construction', 'en_instruction') AND follows.id IS NULL) AS a_suivre,
COUNT(DISTINCT dossiers.id) FILTER (where dossiers.hidden_by_administration_at IS NULL AND dossiers.hidden_at IS NULL AND not archived AND dossiers.state in ('en_construction', 'en_instruction') AND follows.instructeur_id = :instructeur_id) AS suivis,
COUNT(DISTINCT dossiers.id) FILTER (where dossiers.hidden_by_administration_at IS NULL AND dossiers.hidden_at IS NULL AND not archived AND dossiers.state in ('accepte', 'refuse', 'sans_suite')) AS traites,
COUNT(DISTINCT dossiers.id) FILTER (where dossiers.hidden_by_administration_at IS NULL AND dossiers.hidden_at IS NULL AND not archived) AS tous,
COUNT(DISTINCT dossiers.id) FILTER (where dossiers.hidden_by_administration_at IS NULL AND dossiers.hidden_at IS NULL AND archived) AS archives,
COUNT(DISTINCT dossiers.id) FILTER (where dossiers.hidden_by_administration_at IS NOT NULL AND not archived OR dossiers.hidden_at IS NOT NULL) AS supprimes_recemment,
COUNT(DISTINCT dossiers.id) FILTER (where dossiers.hidden_by_administration_at IS NULL AND dossiers.hidden_at IS NULL AND procedures.procedure_expires_when_termine_enabled
AND (
dossiers.state in ('accepte', 'refuse', 'sans_suite')
AND dossiers.processed_at + dossiers.conservation_extension + (procedures.duree_conservation_dossiers_dans_ds * INTERVAL '1 month') - INTERVAL :expires_in < :now
) OR (
dossiers.state in ('en_construction')
dossiers.state in ('en_construction') AND dossiers.hidden_at IS NULL
AND dossiers.en_construction_at + dossiers.conservation_extension + (duree_conservation_dossiers_dans_ds * INTERVAL '1 month') - INTERVAL :expires_in < :now
)
) AS expirant
Expand Down
15 changes: 7 additions & 8 deletions app/services/expired/dossiers_deletion_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,27 +93,26 @@ def delete_expired_and_notify(dossiers_to_remove, notify_on_closed_procedures_to
administration_notifications = group_by_fonctionnaire_email(dossiers_to_remove)
.map { |(email, dossiers)| [email, dossiers.map(&:id)] }

deleted_dossier_ids = []
hidden_dossier_ids = []
dossiers_to_remove.find_each do |dossier|
if dossier.expired_keep_track_and_destroy!
deleted_dossier_ids << dossier.id
end
dossier.hide_and_keep_track!(:automatic, :expired)
hidden_dossier_ids << dossier.id
end
user_notifications.each do |(email, dossier_ids)|
dossier_ids = dossier_ids.intersection(deleted_dossier_ids)
dossier_ids = dossier_ids.intersection(hidden_dossier_ids)
if dossier_ids.present?
mail = DossierMailer.notify_automatic_deletion_to_user(
DeletedDossier.where(dossier_id: dossier_ids).to_a,
Dossier.where(id: dossier_ids).to_a,
email
)
send_with_delay(mail)
end
end
administration_notifications.each do |(email, dossier_ids)|
dossier_ids = dossier_ids.intersection(deleted_dossier_ids)
dossier_ids = dossier_ids.intersection(hidden_dossier_ids)
if dossier_ids.present?
mail = DossierMailer.notify_automatic_deletion_to_administration(
DeletedDossier.where(dossier_id: dossier_ids).to_a,
Dossier.where(id: dossier_ids).to_a,
email
)
send_with_delay(mail)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
%p= t(:hello, scope: [:views, :shared, :greetings])

%p
= t('.header', count: @deleted_dossiers.size)
= t('.header', count: @hidden_dossiers.size)
%ul
- @deleted_dossiers.each do |d|
%li n° #{d.dossier_id} (#{d.procedure.libelle})
- @hidden_dossiers.each do |d|
%li n° #{d.id} (#{d.procedure.libelle})

= render partial: "layouts/mailers/signature"
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
%p= t(:hello, scope: [:views, :shared, :greetings])

%p
= t('.header', count: @deleted_dossiers.size)
= t('.header', count: @hidden_dossiers.size)
%ul
- @deleted_dossiers.each do |d|
%li N° #{d.dossier_id} (#{d.procedure.libelle})
- @hidden_dossiers.each do |d|
%li N° #{d.id} (#{d.procedure.libelle})

%p
%strong= t('.account_active', count: @deleted_dossiers.size)
%strong= t('.account_active', count: @hidden_dossiers.size)

- if @state == Dossier.states.fetch(:en_construction)
%p= t('.footer_en_construction', count: @deleted_dossiers.size, remaining_weeks_before_expiration: distance_of_time_in_words(Expired::REMAINING_WEEKS_BEFORE_EXPIRATION.weeks))
%p= t('.footer_en_construction', count: @hidden_dossiers.size, remaining_weeks_before_expiration: distance_of_time_in_words(Expired::REMAINING_WEEKS_BEFORE_EXPIRATION.weeks))

= render partial: "layouts/mailers/signature"
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
%p= t(:hello, scope: [:views, :shared, :greetings])

%p
= t('.body', dossier_id: @deleted_dossier.dossier_id, procedure: @deleted_dossier.procedure.libelle)
= t('.body', dossier_id: @hidden_dossier.id, procedure: @hidden_dossier.procedure.libelle)

= render partial: "layouts/mailers/signature"
5 changes: 3 additions & 2 deletions app/views/instructeurs/dossiers/_expiration_banner.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
- if dossier.conservation_extension.positive?
= t('instructeurs.dossiers.header.banner.expiration_date_extended')

- if dossier.close_to_expiration?
= render Dsfr::CalloutComponent.new(title: t('instructeurs.dossiers.header.banner.title'), theme: :warning) do |c|
- if dossier.close_to_expiration? || dossier.has_expired?
- title = dossier.has_expired? ? 'title_expired' : 'title'
= render Dsfr::CalloutComponent.new(title: t("instructeurs.dossiers.header.banner.#{title}"), theme: :warning) do |c|
- c.with_body do
- if dossier.brouillon?
= t('instructeurs.dossiers.header.banner.states.brouillon')
Expand Down
2 changes: 2 additions & 0 deletions app/views/users/dossiers/_deleted_dossiers_list.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
= t('views.users.dossiers.dossiers_list.n_dossier')
= dossier.dossier_id

= status_badge(dossier.state, 'fr-mb-1w')
%br
%span.fr-badge.fr-badge--sm.fr-badge--warning
= t('views.users.dossiers.dossiers_list.deleted_badge')

Expand Down
25 changes: 20 additions & 5 deletions app/views/users/dossiers/_dossiers_list.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@

- if dossier.hidden_by_user?
%p.fr-icon--sm.fr-icon-delete-line
= t('views.users.dossiers.dossiers_list.deleted', date: l(dossier.hidden_by_user_at.to_date))
= t('views.users.dossiers.dossiers_list.deleted_by_user', date: l(dossier.hidden_by_user_at.to_date))
- elsif dossier.hidden_at?
%p.fr-icon--sm.fr-icon-delete-line
= t('views.users.dossiers.dossiers_list.deleted_by_automatic', date: l(dossier.hidden_at.to_date))
- else
%p.fr-icon--sm.fr-icon-edit-box-line
- if dossier.depose_at.present?
Expand All @@ -40,11 +43,13 @@
= t('views.users.dossiers.dossiers_list.n_dossier')
= number_with_html_delimiter(dossier.id)

= status_badge_user(dossier, 'fr-mb-1w')

- if @statut == "dossiers-supprimes-recemment"
%br
%span.fr-badge.fr-badge--sm.fr-badge--warning
= t('views.users.dossiers.dossiers_list.deleted_badge')
- else
= status_badge_user(dossier, 'fr-mb-1w')


- if dossier.pending_correction?
%br
Expand Down Expand Up @@ -103,8 +108,18 @@

- if @statut == "dossiers-supprimes-recemment"
.flex.justify-end
= link_to restore_dossier_path(dossier.id), method: :patch, class: "fr-btn fr-btn--sm" do
Restaurer
- if dossier.hidden_at.blank?
= link_to restore_dossier_path(dossier.id), method: :patch, class: "fr-btn fr-btn--sm" do
Restaurer

- else
- if dossier.expiration_can_be_extended?
= button_to users_dossier_repousser_expiration_path(dossier), class: 'fr-btn fr-btn--sm' do
Restaurer et étendre la conservation


- else
= render(partial: 'users/dossiers/show/print_dossier', locals: { dossier: dossier })

= paginate dossiers, views_prefix: 'shared'

Expand Down
5 changes: 3 additions & 2 deletions app/views/users/dossiers/_expiration_banner.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
%p.expires_at
%small= t("shared.dossiers.header.expires_at.#{dossier.state}", date: safe_expiration_date(dossier), duree_conservation_totale: dossier.duree_totale_conservation_in_months)

- if dossier.close_to_expiration?
= render Dsfr::CalloutComponent.new(title: t('users.dossiers.header.banner.title'), theme: :warning) do |c|
- if dossier.close_to_expiration? || dossier.has_expired?
- title = dossier.has_expired? ? 'title_expired' : 'title'
= render Dsfr::CalloutComponent.new(title: t("users.dossiers.header.banner.#{title}"), theme: :warning) do |c|
- c.with_body do
- if dossier.brouillon?
= t('users.dossiers.header.banner.states.brouillon')
Expand Down
2 changes: 2 additions & 0 deletions config/locales/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,8 @@ fr:
updated_at: modifié le %{date}
shared_with: Dossier partagé par %{owner} avec
deleted: Supprimé le %{date}
deleted_by_user: Supprimé le %{date} par l'usager
deleted_by_automatic: Supprimé le %{date} automatiquement du à la date d'expiration
deleted_badge: Supprimé
dossier_action:
edit_dossier: "Modifier le dossier"
Expand Down
Loading

0 comments on commit a163d1c

Please sign in to comment.