Merge pull request #6843 from betagouv/feat/6828

ETQ instructeur, je veux pouvoir restaurer un dossier supprimé
This commit is contained in:
Kara Diaby 2022-02-01 12:20:42 +01:00 committed by GitHub
commit 64d75360d0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 102 additions and 21 deletions

View file

@ -18,6 +18,7 @@ module Instructeurs
@dossiers_archived_count_per_procedure = dossiers.archived.group('groupe_instructeurs.procedure_id').count @dossiers_archived_count_per_procedure = dossiers.archived.group('groupe_instructeurs.procedure_id').count
@dossiers_termines_count_per_procedure = dossiers.termine.visible_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 @dossiers_expirant_count_per_procedure = dossiers.termine_or_en_construction_close_to_expiration.group('groupe_instructeurs.procedure_id').count
@dossiers_supprimes_recemment_count_per_procedure = dossiers.hidden_by_administration.group('groupe_instructeurs.procedure_id').reorder(nil).count
groupe_ids = current_instructeur.groupe_instructeurs.pluck(:id) groupe_ids = current_instructeur.groupe_instructeurs.pluck(:id)
@followed_dossiers_count_per_procedure = current_instructeur @followed_dossiers_count_per_procedure = current_instructeur
@ -35,7 +36,8 @@ module Instructeurs
'traités' => @dossiers_termines_count_per_procedure.sum { |_, v| v }, 'traités' => @dossiers_termines_count_per_procedure.sum { |_, v| v },
'dossiers' => @dossiers_count_per_procedure.sum { |_, v| v }, 'dossiers' => @dossiers_count_per_procedure.sum { |_, v| v },
'expirant' => @dossiers_expirant_count_per_procedure.sum { |_, v| v }, 'expirant' => @dossiers_expirant_count_per_procedure.sum { |_, v| v },
'archivés' => @dossiers_archived_count_per_procedure.sum { |_, v| v } 'archivés' => @dossiers_archived_count_per_procedure.sum { |_, v| v },
'supprimes_recemment' => @dossiers_supprimes_recemment_count_per_procedure.sum { |_, v| v }
} }
@procedure_ids_en_cours_with_notifications = current_instructeur.procedure_ids_with_notifications(:en_cours) @procedure_ids_en_cours_with_notifications = current_instructeur.procedure_ids_with_notifications(:en_cours)
@ -51,9 +53,9 @@ module Instructeurs
@current_filters = current_filters @current_filters = current_filters
@displayed_fields_options, @displayed_fields_selected = procedure_presentation.displayed_fields_for_select @displayed_fields_options, @displayed_fields_selected = procedure_presentation.displayed_fields_for_select
@a_suivre_count, @suivis_count, @traites_count, @tous_count, @archives_count, @expirant_count = current_instructeur @a_suivre_count, @suivis_count, @traites_count, @tous_count, @supprimes_recemment_count, @archives_count, @expirant_count = current_instructeur
.dossiers_count_summary(groupe_instructeur_ids) .dossiers_count_summary(groupe_instructeur_ids)
.fetch_values('a_suivre', 'suivis', 'traites', 'tous', 'archives', 'expirant') .fetch_values('a_suivre', 'suivis', 'traites', 'tous', 'supprimes_recemment', 'archives', 'expirant')
dossiers_visibles = Dossier dossiers_visibles = Dossier
.where(groupe_instructeur_id: groupe_instructeur_ids) .where(groupe_instructeur_id: groupe_instructeur_ids)
@ -72,6 +74,7 @@ module Instructeurs
@termines_dossiers = dossiers_visibles.termine.visible_by_administration @termines_dossiers = dossiers_visibles.termine.visible_by_administration
@all_state_dossiers = dossiers_visibles.all_state.visible_by_administration @all_state_dossiers = dossiers_visibles.all_state.visible_by_administration
@supprimes_recemment_dossiers = dossiers_visibles.termine.hidden_by_administration
@archived_dossiers = dossiers_visibles.archived @archived_dossiers = dossiers_visibles.archived
@expirant_dossiers = dossiers_visibles.termine_or_en_construction_close_to_expiration @expirant_dossiers = dossiers_visibles.termine_or_en_construction_close_to_expiration
@ -88,6 +91,9 @@ module Instructeurs
when 'tous' when 'tous'
dossiers_count = @tous_count dossiers_count = @tous_count
@all_state_dossiers @all_state_dossiers
when 'supprimes_recemment'
dossiers_count = @supprimes_recemment_count
@supprimes_recemment_dossiers
when 'archives' when 'archives'
dossiers_count = @archives_count dossiers_count = @archives_count
@archived_dossiers @archived_dossiers
@ -282,6 +288,13 @@ module Instructeurs
) )
end end
def restore
dossier = current_instructeur.dossiers.find(params[:dossier_id])
dossier.restore(current_instructeur)
flash.notice = t('instructeurs.dossiers.restore')
redirect_to instructeur_procedure_path(procedure)
end
private private
def assign_to_params def assign_to_params

View file

@ -815,6 +815,10 @@ class Dossier < ApplicationRecord
restore_dossier_and_destroy_deleted_dossier(author) restore_dossier_and_destroy_deleted_dossier(author)
end end
end end
elsif author_is_administration(author) && hidden_by_administration?
transaction do
update(hidden_by_administration_at: nil)
end
end end
end end

View file

@ -236,6 +236,7 @@ class Instructeur < ApplicationRecord
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 ('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 AND dossiers.state in ('accepte', 'refuse', 'sans_suite')) AS traites,
COUNT(DISTINCT dossiers.id) FILTER (where not archived AND NOT (dossiers.hidden_by_user_at IS NOT NULL AND state = 'en_construction')) AS tous, COUNT(DISTINCT dossiers.id) FILTER (where not archived AND NOT (dossiers.hidden_by_user_at IS NOT NULL AND state = 'en_construction')) AS tous,
COUNT(DISTINCT dossiers.id) FILTER (where not archived AND (dossiers.hidden_by_administration_at IS NOT NULL AND dossiers.state in ('accepte', 'refuse', 'sans_suite') )) AS supprimes_recemment,
COUNT(DISTINCT dossiers.id) FILTER (where archived) AS archives, COUNT(DISTINCT dossiers.id) FILTER (where archived) AS archives,
COUNT(DISTINCT dossiers.id) FILTER (where COUNT(DISTINCT dossiers.id) FILTER (where
procedures.procedure_expires_when_termine_enabled procedures.procedure_expires_when_termine_enabled
@ -256,7 +257,6 @@ class Instructeur < ApplicationRecord
ON follows.dossier_id = dossiers.id ON follows.dossier_id = dossiers.id
AND follows.unfollowed_at IS NULL AND follows.unfollowed_at IS NULL
WHERE "dossiers"."hidden_at" IS NULL WHERE "dossiers"."hidden_at" IS NULL
AND "dossiers"."hidden_by_administration_at" IS NULL
AND "dossiers"."state" != 'brouillon' AND "dossiers"."state" != 'brouillon'
AND "dossiers"."groupe_instructeur_id" in (:groupe_instructeur_ids) AND "dossiers"."groupe_instructeur_id" in (:groupe_instructeur_ids)
EOF EOF

View file

@ -28,7 +28,8 @@
state: dossier.state, state: dossier.state,
archived: dossier.archived, archived: dossier.archived,
dossier_is_followed: current_instructeur&.follow?(dossier), dossier_is_followed: current_instructeur&.follow?(dossier),
close_to_expiration: dossier.close_to_expiration? } close_to_expiration: dossier.close_to_expiration?,
supprimes_recemment: dossier.hidden_by_administration? }
.state-button .state-button

View file

@ -21,7 +21,13 @@
%span.icon.archive %span.icon.archive
.dropdown-description .dropdown-description
Archiver le dossier Archiver le dossier
- if supprimes_recemment
%li.danger
= link_to restore_instructeur_dossier_path(procedure_id, dossier_id), method: :patch, data: { confirm: "Voulez vous vraiment restaurer le dossier #{dossier_id}" } do
%span.icon.reply
.dropdown-description
= t('views.instructeurs.dossiers.restore')
- else
%li.danger %li.danger
= link_to supprimer_dossier_instructeur_dossier_path(procedure_id, dossier_id), 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 = link_to supprimer_dossier_instructeur_dossier_path(procedure_id, dossier_id), 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 %span.icon.delete

View file

@ -45,6 +45,15 @@
.stats-legend .stats-legend
= t('pluralize.case', count: dossier_count) = t('pluralize.case', count: dossier_count)
%li
%object
= link_to(instructeur_procedure_path(p, statut: 'supprimes_recemment')) do
- dossier_count = dossiers_supprimes_recemment_count_per_procedure[p.id] || 0
.stats-number
= number_with_html_delimiter(dossier_count)
.stats-legend
= t('pluralize.dossiers_supprimes_recemment', count: dossier_count)
- if p.procedure_expires_when_termine_enabled - if p.procedure_expires_when_termine_enabled
%li %li
%object %object

View file

@ -22,6 +22,11 @@
active: statut == 'tous', active: statut == 'tous',
badge: number_with_html_delimiter(tous_count)) badge: number_with_html_delimiter(tous_count))
= tab_item(t('pluralize.dossiers_supprimes_recemment', count: supprimes_recemment_count),
instructeur_procedure_path(procedure, statut: 'supprimes_recemment'),
active: statut == 'supprimes_recemment',
badge: number_with_html_delimiter(supprimes_recemment_count))
- if procedure.procedure_expires_when_termine_enabled - if procedure.procedure_expires_when_termine_enabled
= tab_item(t('pluralize.dossiers_close_to_expiration', count: expirant_count), = tab_item(t('pluralize.dossiers_close_to_expiration', count: expirant_count),
instructeur_procedure_path(procedure, statut: 'expirant'), instructeur_procedure_path(procedure, statut: 'expirant'),

View file

@ -13,6 +13,7 @@
dossiers_archived_count_per_procedure: @dossiers_archived_count_per_procedure, dossiers_archived_count_per_procedure: @dossiers_archived_count_per_procedure,
dossiers_termines_count_per_procedure: @dossiers_termines_count_per_procedure, dossiers_termines_count_per_procedure: @dossiers_termines_count_per_procedure,
dossiers_expirant_count_per_procedure: @dossiers_expirant_count_per_procedure, dossiers_expirant_count_per_procedure: @dossiers_expirant_count_per_procedure,
dossiers_supprimes_recemment_count_per_procedure: @dossiers_supprimes_recemment_count_per_procedure,
followed_dossiers_count_per_procedure: @followed_dossiers_count_per_procedure, followed_dossiers_count_per_procedure: @followed_dossiers_count_per_procedure,
procedure_ids_en_cours_with_notifications: @procedure_ids_en_cours_with_notifications, procedure_ids_en_cours_with_notifications: @procedure_ids_en_cours_with_notifications,
procedure_ids_termines_with_notifications: @procedure_ids_termines_with_notifications } procedure_ids_termines_with_notifications: @procedure_ids_termines_with_notifications }

View file

@ -17,6 +17,7 @@
suivis_count: @suivis_count, suivis_count: @suivis_count,
traites_count: @traites_count, traites_count: @traites_count,
tous_count: @tous_count, tous_count: @tous_count,
supprimes_recemment_count: @supprimes_recemment_count,
archives_count: @archives_count, archives_count: @archives_count,
expirant_count: @expirant_count, expirant_count: @expirant_count,
has_en_cours_notifications: @has_en_cours_notifications, has_en_cours_notifications: @has_en_cours_notifications,
@ -31,6 +32,8 @@
%p.explication-onglet Les dossiers dans cet onglet sont terminés : ils ont été acceptés, refusés ou classés sans suite. %p.explication-onglet Les dossiers dans cet onglet sont terminés : ils ont été acceptés, refusés ou classés sans suite.
- if @statut == 'tous' - if @statut == 'tous'
%p.explication-onglet Tous les dossiers qui ont été déposés sur cette démarche, quel que soit le statut. %p.explication-onglet Tous les dossiers qui ont été déposés sur cette démarche, quel que soit le statut.
- if @statut == 'supprimes_recemment'
%p.explication-onglet Tous les dossiers terminés et supprimés par les instructeurs sur cette démarche
- if @statut == 'archives' - if @statut == 'archives'
%p.explication-onglet %p.explication-onglet
Les dossiers de cet onglet sont archivés : vous ne pouvez plus y répondre, et les demandeurs ne peuvent plus les modifier. Les dossiers de cet onglet sont archivés : vous ne pouvez plus y répondre, et les demandeurs ne peuvent plus les modifier.
@ -135,7 +138,8 @@
state: p.state, state: p.state,
archived: p.archived, archived: p.archived,
dossier_is_followed: @followed_dossiers_id.include?(p.dossier_id), dossier_is_followed: @followed_dossiers_id.include?(p.dossier_id),
close_to_expiration: @statut == 'expirant' } close_to_expiration: @statut == 'expirant',
supprimes_recemment: @statut == 'supprimes_recemment' }
= pagination = pagination
- else - else

View file

@ -140,6 +140,7 @@ en:
archived_dossier: "This file will be kept for an additional month" archived_dossier: "This file will be kept for an additional month"
delete_dossier: "Delete file" delete_dossier: "Delete file"
deleted_by_user: "File deleted by user" deleted_by_user: "File deleted by user"
restore: "Restore the file"
avis: avis:
introduction_file_explaination: "File attached to the request for advice" introduction_file_explaination: "File attached to the request for advice"
users: users:
@ -419,6 +420,7 @@ en:
dossiers: dossiers:
deleted_by_instructeur: "The folder has been deleted" deleted_by_instructeur: "The folder has been deleted"
impossible_deletion: "Unable to delete : the folder is not processed" impossible_deletion: "Unable to delete : the folder is not processed"
restore: "The folder has been restored"
france_connect: france_connect:
particulier: particulier:
password_confirmation: password_confirmation:

View file

@ -137,6 +137,7 @@ fr:
archived_dossier: "Le dossier sera conservé 1 mois supplémentaire" archived_dossier: "Le dossier sera conservé 1 mois supplémentaire"
delete_dossier: "Supprimer le dossier" delete_dossier: "Supprimer le dossier"
deleted_by_user: "Dossier supprimé par l'usager" deleted_by_user: "Dossier supprimé par l'usager"
restore: "Restaurer le dossier"
avis: avis:
introduction_file_explaination: "Fichier joint à la demande davis" introduction_file_explaination: "Fichier joint à la demande davis"
users: users:
@ -372,13 +373,13 @@ fr:
one: dossier invité one: dossier invité
other: dossiers invités other: dossiers invités
dossiers_supprimes_recemment: dossiers_supprimes_recemment:
zero: dossier supprimé recemment zero: supprimé recemment
one: dossier supprimé recemment one: supprimé recemment
other: dossiers supprimés recemment other: supprimés recemment
dossiers_supprimes_definitivement: dossiers_supprimes_definitivement:
zero: dossier supprimé définitivement zero: supprimé définitivement
one: dossier supprimé définitivement one: supprimé définitivement
other: dossiers supprimés définitivement other: supprimés définitivement
dossiers_transferes: dossiers_transferes:
zero: demande de transfert zero: demande de transfert
one: demande de transfert one: demande de transfert
@ -427,6 +428,7 @@ fr:
dossiers: dossiers:
deleted_by_instructeur: "Le dossier a bien été supprimé de votre interface" deleted_by_instructeur: "Le dossier a bien été supprimé de votre interface"
impossible_deletion: "Supression impossible : le dossier n'est pas traité" impossible_deletion: "Supression impossible : le dossier n'est pas traité"
restore: "Le dossier a bien été restauré"
administrateurs: administrateurs:
procedures: procedures:
show: show:

View file

@ -386,6 +386,7 @@ Rails.application.routes.draw do
post 'avis' => 'dossiers#create_avis' post 'avis' => 'dossiers#create_avis'
get 'print' => 'dossiers#print' get 'print' => 'dossiers#print'
get 'telecharger_pjs' => 'dossiers#telecharger_pjs' get 'telecharger_pjs' => 'dossiers#telecharger_pjs'
patch 'restore'
end end
end end

View file

@ -0,0 +1,10 @@
class AddSupprimesRecemmentToProcedurePresentations < ActiveRecord::Migration[6.1]
def up
ProcedurePresentation.update_all(%Q(filters = filters || '{"supprimes_recemment": []}'))
change_column_default :procedure_presentations, :filters, { "tous" => [], "suivis" => [], "traites" => [], "a-suivre" => [], "archives" => [], "supprimes_recemment" => [], "expirant": [] }
end
def down
change_column_default :procedure_presentations, :filters, { "tous" => [], "suivis" => [], "traites" => [], "a-suivre" => [], "archives" => [], "expirant": [] }
end
end

View file

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2021_12_02_133139) do ActiveRecord::Schema.define(version: 2022_01_27_135056) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -569,7 +569,7 @@ ActiveRecord::Schema.define(version: 2021_12_02_133139) do
create_table "procedure_presentations", id: :serial, force: :cascade do |t| create_table "procedure_presentations", id: :serial, force: :cascade do |t|
t.integer "assign_to_id" t.integer "assign_to_id"
t.jsonb "sort", default: {"order"=>"desc", "table"=>"notifications", "column"=>"notifications"}, null: false t.jsonb "sort", default: {"order"=>"desc", "table"=>"notifications", "column"=>"notifications"}, null: false
t.jsonb "filters", default: {"tous"=>[], "suivis"=>[], "traites"=>[], "a-suivre"=>[], "archives"=>[], "expirant"=>[]}, null: false t.jsonb "filters", default: {"tous"=>[], "suivis"=>[], "traites"=>[], "a-suivre"=>[], "archives"=>[], "expirant"=>[], "supprimes_recemment"=>[]}, null: false
t.datetime "created_at" t.datetime "created_at"
t.datetime "updated_at" t.datetime "updated_at"
t.jsonb "displayed_fields", default: [{"label"=>"Demandeur", "table"=>"user", "column"=>"email"}], null: false t.jsonb "displayed_fields", default: [{"label"=>"Demandeur", "table"=>"user", "column"=>"email"}], null: false

View file

@ -839,4 +839,25 @@ describe Instructeurs::DossiersController, type: :controller do
end end
end end
end end
describe '#restore' do
let(:instructeur) { create(:instructeur) }
let!(:gi_p1_1) { GroupeInstructeur.create(label: '1', procedure: procedure) }
let!(:procedure) { create(:procedure, :published, instructeurs: [instructeur]) }
let!(:dossier) { create(:dossier, state: 'accepte', procedure: procedure, groupe_instructeur: procedure.groupe_instructeurs.first, hidden_by_administration_at: 1.hour.ago) }
before do
sign_in(instructeur.user)
instructeur.groupe_instructeurs << gi_p1_1
patch :restore,
params: {
procedure_id: procedure.id,
dossier_id: dossier.id
}
end
it "puts hidden_by_administration_at to nil" do
expect(dossier.reload.hidden_by_administration_at).to eq(nil)
end
end
end end

View file

@ -8,6 +8,7 @@ describe 'instructeurs/procedures/_list.html.haml', type: :view do
dossiers_a_suivre_count_per_procedure: 2, dossiers_a_suivre_count_per_procedure: 2,
dossiers_archived_count_per_procedure: 1, dossiers_archived_count_per_procedure: 1,
dossiers_termines_count_per_procedure: 1, dossiers_termines_count_per_procedure: 1,
dossiers_supprimes_recemment_count_per_procedure: 0,
dossiers_expirant_count_per_procedure: 0, dossiers_expirant_count_per_procedure: 0,
followed_dossiers_count_per_procedure: 0, followed_dossiers_count_per_procedure: 0,
procedure_ids_en_cours_with_notifications: [], procedure_ids_en_cours_with_notifications: [],

View file

@ -11,6 +11,7 @@ describe 'instructeurs/procedures/_tabs.html.haml', type: :view do
suivis_count: 0, suivis_count: 0,
traites_count: 0, traites_count: 0,
tous_count: 0, tous_count: 0,
supprimes_recemment_count: 0,
archives_count: 0, archives_count: 0,
expirant_count: 0, expirant_count: 0,
has_en_cours_notifications: false, has_en_cours_notifications: false,