Merge pull request #6797 from betagouv/feat/6714

ETQ usager, je souhaite restaurer un dossier supprimé
This commit is contained in:
Kara Diaby 2022-01-12 16:04:27 +01:00 committed by GitHub
commit 5dab86d3d6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 172 additions and 60 deletions

View file

@ -14,9 +14,9 @@ module Instructeurs
dossiers = current_instructeur.dossiers.joins(:groupe_instructeur)
@dossiers_count_per_procedure = dossiers.all_state.group('groupe_instructeurs.procedure_id').reorder(nil).count
@dossiers_a_suivre_count_per_procedure = dossiers.without_followers.en_cours.group('groupe_instructeurs.procedure_id').reorder(nil).count
@dossiers_a_suivre_count_per_procedure = dossiers.without_followers.en_cours.visible_by_administration.group('groupe_instructeurs.procedure_id').reorder(nil).count
@dossiers_archived_count_per_procedure = dossiers.archived.group('groupe_instructeurs.procedure_id').count
@dossiers_termines_count_per_procedure = dossiers.termine.not_hidden_by_administration.group('groupe_instructeurs.procedure_id').reorder(nil).count
@dossiers_termines_count_per_procedure = dossiers.termine.visible_by_administration.group('groupe_instructeurs.procedure_id').reorder(nil).count
@dossiers_expirant_count_per_procedure = dossiers.termine_or_en_construction_close_to_expiration.group('groupe_instructeurs.procedure_id').count
groupe_ids = current_instructeur.groupe_instructeurs.pluck(:id)
@ -61,6 +61,7 @@ module Instructeurs
@a_suivre_dossiers = dossiers_visibles
.without_followers
.en_cours
.visible_by_administration
@followed_dossiers = current_instructeur
.followed_dossiers
@ -69,7 +70,7 @@ module Instructeurs
@followed_dossiers_id = @followed_dossiers.pluck(:id)
@termines_dossiers = dossiers_visibles.termine.not_hidden_by_administration
@termines_dossiers = dossiers_visibles.termine.visible_by_administration
@all_state_dossiers = dossiers_visibles.all_state
@archived_dossiers = dossiers_visibles.archived
@expirant_dossiers = dossiers_visibles.termine_or_en_construction_close_to_expiration

View file

@ -5,7 +5,7 @@ module Users
layout 'procedure_context', only: [:identite, :update_identite, :siret, :update_siret]
ACTIONS_ALLOWED_TO_ANY_USER = [:index, :recherche, :new, :transferer_all]
ACTIONS_ALLOWED_TO_OWNER_OR_INVITE = [:show, :demande, :messagerie, :brouillon, :update_brouillon, :modifier, :update, :create_commentaire]
ACTIONS_ALLOWED_TO_OWNER_OR_INVITE = [:show, :demande, :messagerie, :brouillon, :update_brouillon, :modifier, :update, :create_commentaire, :restore]
before_action :ensure_ownership!, except: ACTIONS_ALLOWED_TO_ANY_USER + ACTIONS_ALLOWED_TO_OWNER_OR_INVITE
before_action :ensure_ownership_or_invitation!, only: ACTIONS_ALLOWED_TO_OWNER_OR_INVITE
@ -16,17 +16,18 @@ module Users
before_action :store_user_location!, only: :new
def index
@user_dossiers = current_user.dossiers.includes(:procedure).state_not_termine.order_by_updated_at.page(page)
@dossiers_traites = current_user.dossiers.includes(:procedure).state_termine.not_hidden_by_user.order_by_updated_at.page(page)
@user_dossiers = current_user.dossiers.includes(:procedure).state_not_termine.visible_by_user.order_by_updated_at.page(page)
@dossiers_traites = current_user.dossiers.includes(:procedure).state_termine.visible_by_user.order_by_updated_at.page(page)
@dossiers_invites = current_user.dossiers_invites.includes(:procedure).order_by_updated_at.page(page)
@dossiers_supprimes = current_user.deleted_dossiers.order_by_updated_at.page(page)
@dossiers_supprimes_recemment = current_user.dossiers.hidden_by_user.order_by_updated_at.page(page)
@dossiers_supprimes_definitivement = current_user.deleted_dossiers.order_by_updated_at.page(page)
@dossier_transfers = DossierTransfer
.includes(dossiers: :user)
.with_dossiers
.where(email: current_user.email)
.page(page)
@dossiers_close_to_expiration = current_user.dossiers.close_to_expiration.page(page)
@statut = statut(@user_dossiers, @dossiers_traites, @dossiers_invites, @dossiers_supprimes, @dossier_transfers, @dossiers_close_to_expiration, params[:statut])
@statut = statut(@user_dossiers, @dossiers_traites, @dossiers_invites, @dossiers_supprimes_recemment, @dossiers_supprimes_definitivement, @dossier_transfers, @dossiers_close_to_expiration, params[:statut])
end
def show
@ -287,17 +288,24 @@ module Users
@transfer = DossierTransfer.new(dossiers: current_user.dossiers)
end
def restore
dossier.restore(current_user)
flash.notice = t('users.dossiers.restore')
redirect_to dossiers_path
end
private
# if the status tab is filled, then this tab
# else first filled tab
# else en-cours
def statut(mes_dossiers, dossiers_traites, dossiers_invites, dossiers_supprimes, dossier_transfers, dossiers_close_to_expiration, params_statut)
def statut(mes_dossiers, dossiers_traites, dossiers_invites, dossiers_supprimes_recemment, dossiers_supprimes_definitivement, dossier_transfers, dossiers_close_to_expiration, params_statut)
tabs = {
'en-cours' => mes_dossiers.present?,
'traites' => dossiers_traites.present?,
'dossiers-invites' => dossiers_invites.present?,
'dossiers-supprimes' => dossiers_supprimes.present?,
'dossiers-supprimes-recemment' => dossiers_supprimes_recemment.present?,
'dossiers-supprimes-definitivement' => dossiers_supprimes_definitivement.present?,
'dossiers-transferes' => dossier_transfers.present?,
'dossiers-expirant' => dossiers_close_to_expiration.present?
}

View file

@ -206,9 +206,10 @@ class Dossier < ApplicationRecord
scope :archived, -> { where(archived: true) }
scope :not_archived, -> { where(archived: false) }
scope :hidden_by_user, -> { where.not(hidden_by_user_at: nil) }
scope :hidden_by_administration, -> { where.not(hidden_by_administration_at: nil) }
scope :not_hidden_by_user, -> { where(hidden_by_user_at: nil) }
scope :not_hidden_by_administration, -> { where(hidden_by_administration_at: nil) }
scope :visible_by_user, -> { where(hidden_by_user_at: nil) }
scope :visible_by_administration, -> { where("hidden_by_administration_at IS NULL AND NOT (hidden_by_user_at IS NOT NULL AND state = 'en_construction')") }
scope :order_by_updated_at, -> (order = :desc) { order(updated_at: order) }
scope :order_by_created_at, -> (order = :asc) { order(depose_at: order, created_at: order, id: order) }
@ -237,7 +238,7 @@ class Dossier < ApplicationRecord
end
scope :downloadable_sorted, -> {
state_not_brouillon
.not_hidden_by_administration
.visible_by_administration
.includes(
:user,
:individual,
@ -346,23 +347,26 @@ class Dossier < ApplicationRecord
scope :without_en_construction_expiration_notice_sent, -> { where(en_construction_close_to_expiration_notice_sent_at: nil) }
scope :without_termine_expiration_notice_sent, -> { where(termine_close_to_expiration_notice_sent_at: nil) }
scope :discarded_expired, -> { discarded.where('dossiers.hidden_at < ?', 1.week.ago) }
scope :discarded_by_user_expired, -> { discarded.where('dossiers.hidden_by_user_at < ?', 1.week.ago) }
scope :discarded_by_administration_expired, -> { discarded.where('dossiers.hidden_by_administration_at < ?', 1.week.ago) }
scope :discarded_brouillon_expired, -> do
with_discarded
.discarded
.state_brouillon
.where('hidden_at < ?', 1.week.ago)
.discarded_expired
.or(state_brouillon.discarded_by_user_expired)
end
scope :discarded_en_construction_expired, -> do
with_discarded
.discarded
.state_en_construction
.where('dossiers.hidden_at < ?', 1.week.ago)
.discarded_expired
.or(state_en_construction.discarded_by_user_expired)
end
scope :discarded_termine_expired, -> do
with_discarded
.discarded
.state_termine
.where('dossiers.hidden_at < ?', 1.week.ago)
.discarded_expired
.or(state_termine.discarded_by_user_expired.discarded_by_administration_expired)
end
scope :brouillon_near_procedure_closing_date, -> do
@ -531,6 +535,10 @@ class Dossier < ApplicationRecord
brouillon? || en_construction? || termine?
end
def can_be_hidden_by_user?
en_construction? || termine?
end
def can_be_deleted_by_manager?
kept? && can_be_deleted_by_user?
end
@ -750,16 +758,26 @@ class Dossier < ApplicationRecord
false
end
def discard_and_keep_track!(author, reason)
author_is_user = author.is_a?(User)
author_is_administration = author.is_a?(Instructeur) || author.is_a?(Administrateur) || author.is_a?(SuperAdmin)
def author_is_user(author)
author.is_a?(User)
end
if termine? && author_is_administration
def author_is_administration(author)
author.is_a?(Instructeur) || author.is_a?(Administrateur) || author.is_a?(SuperAdmin)
end
def restore_dossier_and_destroy_deleted_dossier(author)
deleted_dossier&.destroy!
log_dossier_operation(author, :restaurer, self)
end
def discard_and_keep_track!(author, reason)
if termine? && author_is_administration(author)
update(hidden_by_administration_at: Time.zone.now)
end
if termine? && author_is_user
update(hidden_by_user_at: Time.zone.now)
if can_be_hidden_by_user? && author_is_user(author)
update(hidden_by_user_at: Time.zone.now, dossier_transfer_id: nil)
end
user_email = user_deleted? ? nil : user_email_for(:notification)
@ -772,8 +790,9 @@ class Dossier < ApplicationRecord
deleted_dossier = DeletedDossier.create_from_dossier(self, reason)
end
update!(dossier_transfer_id: nil)
discard!
if !(en_construction? && author_is_user(author))
discard!
end
end
end
@ -798,11 +817,19 @@ class Dossier < ApplicationRecord
def restore(author)
if discarded?
transaction do
if hidden_by_administration?
if author_is_administration(author) && hidden_by_administration?
update(hidden_by_administration_at: nil)
elsif undiscard && keep_track_on_deletion?
deleted_dossier&.destroy!
log_dossier_operation(author, :restaurer, self)
end
if undiscard && keep_track_on_deletion?
restore_dossier_and_destroy_deleted_dossier(author)
end
end
elsif author_is_user(author) && hidden_by_user?
transaction do
update(hidden_by_user_at: nil)
if en_construction?
restore_dossier_and_destroy_deleted_dossier(author)
end
end
end

View file

@ -232,7 +232,7 @@ class Instructeur < ApplicationRecord
def dossiers_count_summary(groupe_instructeur_ids)
query = <<~EOF
SELECT
COUNT(DISTINCT dossiers.id) FILTER (where not archived AND dossiers.state in ('en_construction', 'en_instruction') AND follows.id IS NULL) AS a_suivre,
COUNT(DISTINCT dossiers.id) FILTER (where not archived AND NOT (dossiers.hidden_by_user_at IS NOT NULL AND state = 'en_construction') AND dossiers.state in ('en_construction', 'en_instruction') AND follows.id IS NULL) AS a_suivre,
COUNT(DISTINCT dossiers.id) FILTER (where not archived AND dossiers.state in ('en_construction', 'en_instruction') AND follows.instructeur_id = :instructeur_id) AS suivis,
COUNT(DISTINCT dossiers.id) FILTER (where not archived AND dossiers.state in ('accepte', 'refuse', 'sans_suite')) AS traites,
COUNT(DISTINCT dossiers.id) FILTER (where not archived) AS tous,

View file

@ -9,6 +9,7 @@
%tbody
- deleted_dossiers.each do |dossier|
- libelle_demarche = Procedure.find(dossier.procedure_id).libelle
%tr{ data: { 'dossier-id': dossier.dossier_id } }
%td.number-col
%span.icon.folder

View file

@ -0,0 +1,36 @@
- if hidden_dossiers.present?
%table.table.dossiers-table.hoverable
%thead
%tr
%th.number-col Nº dossier
%th Démarche
%th Raison de suppression
%th Date de suppression
%tbody
- hidden_dossiers.each do |dossier|
- libelle_demarche = dossier.procedure.libelle
%tr{ data: { 'dossier-id': dossier.id } }
%td.number-col
%span.icon.folder
= dossier.id
%td
= libelle_demarche
%td.cell-link
= deletion_reason_badge("user_request")
%td
= dossier.updated_at.strftime('%d/%m/%Y')
%td
= link_to restore_dossier_path(dossier.id), method: :patch, class: "button primary" do
Restaurer
= paginate(hidden_dossiers)
- else
.blank-tab
%h2.empty-text Aucun dossier.
%p.empty-text-details
Pour remplir une démarche, contactez votre administration en lui demandant le lien de la démarche.
%br
Celui ci doit ressembler à #{APPLICATION_BASE_URL}/commencer/xxx.

View file

@ -39,11 +39,17 @@
active: @statut == 'dossiers-expirant',
badge: number_with_html_delimiter(@dossiers_close_to_expiration.count))
- if @dossiers_supprimes.present?
= tab_item(t('pluralize.dossiers_supprimes', count: @dossiers_supprimes.count),
dossiers_path(statut: 'dossiers-supprimes'),
active: @statut == 'dossiers-supprimes',
badge: number_with_html_delimiter(@dossiers_supprimes.count))
- if @dossiers_supprimes_recemment.present?
= tab_item(t('pluralize.dossiers_supprimes_recemment', count: @dossiers_supprimes_recemment.count),
dossiers_path(statut: 'dossiers-supprimes-recemment'),
active: @statut == 'dossiers-supprimes-recemment',
badge: number_with_html_delimiter(@dossiers_supprimes_recemment.count))
- if @dossiers_supprimes_definitivement.present?
= tab_item(t('pluralize.dossiers_supprimes_definitivement', count: @dossiers_supprimes_definitivement.count),
dossiers_path(statut: 'dossiers-supprimes-definitivement'),
active: @statut == 'dossiers-supprimes-definitivement',
badge: number_with_html_delimiter(@dossiers_supprimes_definitivement.count))
- if @dossier_transfers.present?
= tab_item(t('pluralize.dossiers_transferes', count: @dossier_transfers.count),
@ -61,8 +67,12 @@
- if @statut == "dossiers-invites"
= render partial: "dossiers_list", locals: { dossiers: @dossiers_invites }
- if @statut == "dossiers-supprimes"
= render partial: "deleted_dossiers_list", locals: { deleted_dossiers: @dossiers_supprimes }
- if @statut == "dossiers-supprimes-recemment"
= render partial: "hidden_dossiers_list", locals: { hidden_dossiers: @dossiers_supprimes_recemment }
- if @statut == "dossiers-supprimes-definitivement"
= render partial: "deleted_dossiers_list", locals: { deleted_dossiers: @dossiers_supprimes_definitivement }
- if @statut == "dossiers-transferes"
= render partial: "transfered_dossiers_list", locals: { dossier_transfers: @dossier_transfers }

View file

@ -364,10 +364,14 @@ en:
zero: guest file
one: guest file
other: guest files
dossiers_supprimes:
zero: deleted file
one: deleted file
other: deleted files
dossiers_supprimes_recemment:
zero: recently deleted file
one: recently deleted file
other: recently deleted files
dossiers_supprimes_definitivement:
zero: permanently deleted file
one: permanently deleted file
other: permanently deleted files
dossiers_transferes:
zero: transfer request
one: transfer request
@ -402,6 +406,7 @@ en:
ask_deletion:
undergoingreview: "Your file is undergoing review. It is no longer possible to delete your file. To cancel the undergoingreview contact the adminitration via the mailbox."
soft_deleted_dossier: "Your file has been successfully deleted from your interface"
restore: "Your file has been successfully restored"
update_brouillon:
draft_saved: "Your draft has been saved."
etablissement:

View file

@ -371,10 +371,14 @@ fr:
zero: dossier invité
one: dossier invité
other: dossiers invités
dossiers_supprimes:
zero: dossier supprimé
one: dossier supprimé
other: dossiers supprimés
dossiers_supprimes_recemment:
zero: dossier supprimé recemment
one: dossier supprimé recemment
other: dossiers supprimés recemment
dossiers_supprimes_definitivement:
zero: dossier supprimé définitivement
one: dossier supprimé définitivement
other: dossiers supprimés définitivement
dossiers_transferes:
zero: demande de transfert
one: demande de transfert
@ -410,6 +414,7 @@ fr:
ask_deletion:
undergoingreview: "Linstruction de votre dossier a commencé, il nest plus possible de supprimer votre dossier. Si vous souhaitez annuler linstruction contactez votre administration par la messagerie de votre dossier."
soft_deleted_dossier: "Votre dossier a bien été supprimé de votre interface"
restore: "Votre dossier a bien été restauré"
update_brouillon:
draft_saved: "Votre brouillon a bien été sauvegardé."
etablissement:

View file

@ -269,6 +269,7 @@ Rails.application.routes.draw do
get 'messagerie'
post 'commentaire' => 'dossiers#create_commentaire'
patch 'delete_dossier'
patch 'restore', to: 'dossiers#restore'
get 'attestation'
get 'transferer', to: 'dossiers#transferer'
end

View file

@ -1028,13 +1028,13 @@ describe Users::DossiersController, type: :controller do
subject
end
it "deletes the dossier" do
it "hide the dossier and create a deleted dossier" do
procedure = dossier.procedure
dossier_id = dossier.id
subject
expect(Dossier.find_by(id: dossier_id)).to eq(nil)
expect(Dossier.find_by(id: dossier_id)).to be_present
expect(Dossier.find_by(id: dossier_id).hidden_by_user_at).to be_present
expect(procedure.deleted_dossiers.count).to eq(1)
expect(procedure.deleted_dossiers.first.dossier_id).to eq(dossier_id)
end
it { is_expected.to redirect_to(dossiers_path) }
@ -1065,6 +1065,21 @@ describe Users::DossiersController, type: :controller do
end
end
describe '#restore' do
before { sign_in(user) }
subject { patch :restore, params: { id: dossier.id } }
context 'when the user want to restore his dossier' do
let!(:dossier) { create(:dossier, :with_individual, state: :accepte, en_construction_at: Time.zone.yesterday.beginning_of_day.utc, hidden_by_user_at: Time.zone.yesterday.beginning_of_day.utc, user: user, autorisation_donnees: true) }
before { subject }
it 'must have hidden_by_user_at nil' do
expect(dossier.reload.hidden_by_user_at).to be_nil
end
end
end
describe '#new' do
let(:procedure) { create(:procedure, :published) }
let(:procedure_id) { procedure.id }

View file

@ -793,6 +793,7 @@ describe Dossier do
describe "#discard_and_keep_track!" do
let(:dossier) { create(:dossier, :en_construction) }
let(:user) { dossier.user }
let(:deleted_dossier) { DeletedDossier.find_by(dossier_id: dossier.id) }
let(:last_operation) { dossier.dossier_operation_logs.last }
let(:reason) { :user_request }
@ -802,7 +803,7 @@ describe Dossier do
allow(DossierMailer).to receive(:notify_deletion_to_administration).and_return(double(deliver_later: nil))
end
subject! { dossier.discard_and_keep_track!(dossier.user, reason) }
subject! { dossier.discard_and_keep_track!(user, reason) }
context 'brouillon' do
let(:dossier) { create(:dossier) }
@ -821,8 +822,9 @@ describe Dossier do
end
context 'en_construction' do
it 'hides the dossier' do
expect(dossier.hidden_at).to be_present
it 'hide the dossier but does not discard' do
expect(dossier.hidden_at).to be_nil
expect(dossier.hidden_by_user_at).to be_present
end
it 'creates a DeletedDossier record' do
@ -872,6 +874,7 @@ describe Dossier do
end
context 'with reason: manager_request' do
let(:user) { dossier.procedure.administrateurs.first }
let(:reason) { :manager_request }
it 'hides the dossier' do
@ -887,13 +890,12 @@ describe Dossier do
context 'with reason: user_removed' do
let(:reason) { :user_removed }
it 'hides the dossier' do
expect(dossier.discarded?).to be_truthy
it 'does not discard the dossier' do
expect(dossier.discarded?).to be_falsy
end
it 'records the operation in the log' do
expect(last_operation.operation).to eq("supprimer")
expect(last_operation.automatic_operation?).to be_falsey
it 'hide the dossier' do
expect(dossier.hidden_by_user_at).to be_present
end
end
end

View file

@ -12,7 +12,8 @@ describe 'users/dossiers/index.html.haml', type: :view do
allow(controller).to receive(:current_user) { user }
assign(:user_dossiers, Kaminari.paginate_array(user_dossiers).page(1))
assign(:dossiers_invites, Kaminari.paginate_array(dossiers_invites).page(1))
assign(:dossiers_supprimes, Kaminari.paginate_array(user_dossiers).page(1))
assign(:dossiers_supprimes_recemment, Kaminari.paginate_array(user_dossiers).page(1))
assign(:dossiers_supprimes_definitivement, Kaminari.paginate_array(user_dossiers).page(1))
assign(:dossiers_traites, Kaminari.paginate_array(user_dossiers).page(1))
assign(:dossier_transfers, Kaminari.paginate_array([]).page(1))
assign(:dossiers_close_to_expiration, Kaminari.paginate_array([]).page(1))
@ -69,7 +70,7 @@ describe 'users/dossiers/index.html.haml', type: :view do
it 'affiche la barre donglets' do
expect(rendered).to have_selector('nav.tabs')
expect(rendered).to have_selector('nav.tabs li', count: 4)
expect(rendered).to have_selector('nav.tabs li', count: 5)
expect(rendered).to have_selector('nav.tabs li.active', count: 1)
end
end