Merge pull request #6789 from betagouv/main

2022-01-03-02
This commit is contained in:
Kara Diaby 2022-01-03 14:57:44 +01:00 committed by GitHub
commit 6324def470
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 158 additions and 69 deletions

View file

@ -12,13 +12,13 @@ module Experts
DONNES_STATUS = 'donnes'
def index
avis = current_expert.avis.includes(dossier: [groupe_instructeur: :procedure])
avis = current_expert.avis.includes(dossier: [groupe_instructeur: :procedure]).not_hidden_by_administration
@avis_by_procedure = avis.to_a.group_by(&:procedure)
end
def procedure
@procedure = Procedure.find(params[:procedure_id])
expert_avis = current_expert.avis.includes(:dossier).where(dossiers: { groupe_instructeur: GroupeInstructeur.where(procedure: @procedure.id) })
expert_avis = current_expert.avis.includes(:dossier).not_hidden_by_administration.where(dossiers: { groupe_instructeur: GroupeInstructeur.where(procedure: @procedure.id) })
@avis_a_donner = expert_avis.without_answer
@avis_donnes = expert_avis.with_answer

View file

@ -140,9 +140,6 @@ module Instructeurs
def repasser_en_instruction
begin
if dossier.hidden_by_user_at.present?
dossier.update!(hidden_by_user_at: nil)
end
flash.notice = "Le dossier #{dossier.id} a été repassé en instruction."
dossier.repasser_en_instruction!(instructeur: current_instructeur)
rescue AASM::InvalidTransition => e
@ -232,10 +229,10 @@ module Instructeurs
def delete_dossier
if dossier.termine?
dossier.discard_and_keep_track!(current_instructeur, :instructeur_request)
flash.notice = 'Le dossier a bien été supprimé'
flash.notice = t('instructeurs.dossiers.deleted_by_instructeur')
redirect_to instructeur_procedure_path(procedure)
else
flash.alert = "Suppression impossible : le dossier nest pas traité"
flash.alert = t('instructeurs.dossiers.impossible_deletion')
redirect_back(fallback_location: instructeur_procedures_url)
end
end

View file

@ -16,7 +16,7 @@ module Instructeurs
@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_archived_count_per_procedure = dossiers.archived.group('groupe_instructeurs.procedure_id').count
@dossiers_termines_count_per_procedure = dossiers.termine.group('groupe_instructeurs.procedure_id').reorder(nil).count
@dossiers_termines_count_per_procedure = dossiers.termine.not_hidden_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)
@ -69,7 +69,7 @@ module Instructeurs
@followed_dossiers_id = @followed_dossiers.pluck(:id)
@termines_dossiers = dossiers_visibles.termine
@termines_dossiers = dossiers_visibles.termine.not_hidden_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

@ -212,16 +212,15 @@ module Users
end
end
def ask_deletion
def delete_dossier
dossier = current_user.dossiers.includes(:user, procedure: :administrateurs).find(params[:id])
if dossier.can_be_deleted_by_user?
dossier.discard_and_keep_track!(current_user, :user_request)
flash.notice = t('.soft_deleted_dossier')
flash.notice = t('users.dossiers.ask_deletion.soft_deleted_dossier')
redirect_to dossiers_path
else
flash.notice = t('.undergoingreview')
redirect_to dossier_path(dossier)
flash.alert = t('users.dossiers.ask_deletion.undergoingreview')
redirect_to dossiers_path
end
end
@ -288,13 +287,6 @@ module Users
@transfer = DossierTransfer.new(dossiers: current_user.dossiers)
end
def hide_dossier
dossier = current_user.dossiers.includes(:user, procedure: :administrateurs).find(params[:id])
dossier.update(hidden_by_user_at: Time.zone.now)
flash.notice = t('users.dossiers.ask_deletion.soft_deleted_dossier')
redirect_to dossiers_path
end
private
# if the status tab is filled, then this tab

View file

@ -50,7 +50,7 @@ class Avis < ApplicationRecord
scope :updated_since?, -> (date) { where('avis.updated_at > ?', date) }
scope :discarded_termine_expired, -> { unscope(:joins).where(dossier: Dossier.discarded_termine_expired) }
scope :discarded_en_construction_expired, -> { unscope(:joins).where(dossier: Dossier.discarded_en_construction_expired) }
scope :not_hidden_by_administration, -> { where(dossiers: { hidden_by_administration_at: nil }) }
# The form allows subtmitting avis requests to several emails at once,
# hence this virtual attribute.
attr_accessor :emails

View file

@ -16,6 +16,7 @@
# en_instruction_at :datetime
# groupe_instructeur_updated_at :datetime
# hidden_at :datetime
# hidden_by_administration_at :datetime
# hidden_by_user_at :datetime
# identity_updated_at :datetime
# last_avis_updated_at :datetime
@ -205,7 +206,9 @@ class Dossier < ApplicationRecord
scope :archived, -> { where(archived: true) }
scope :not_archived, -> { where(archived: false) }
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 :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) }
@ -234,6 +237,7 @@ class Dossier < ApplicationRecord
end
scope :downloadable_sorted, -> {
state_not_brouillon
.not_hidden_by_administration
.includes(
:user,
:individual,
@ -524,7 +528,7 @@ class Dossier < ApplicationRecord
end
def can_be_deleted_by_user?
brouillon? || en_construction?
brouillon? || en_construction? || termine?
end
def can_be_deleted_by_manager?
@ -680,6 +684,22 @@ class Dossier < ApplicationRecord
!procedure.brouillon? && !brouillon?
end
def hidden_by_user?
hidden_by_user_at.present?
end
def hidden_by_administration?
hidden_by_administration_at.present?
end
def deleted_by_instructeur_and_user?
termine? && hidden_by_administration? && hidden_by_user?
end
def can_be_restored_by_manager?
hidden_by_administration? || discarded? && !procedure.discarded?
end
def expose_legacy_carto_api?
procedure.expose_legacy_carto_api?
end
@ -731,10 +751,22 @@ class Dossier < ApplicationRecord
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)
if termine? && author_is_administration
update(hidden_by_administration_at: Time.zone.now)
end
if termine? && author_is_user
update(hidden_by_user_at: Time.zone.now)
end
user_email = user_deleted? ? nil : user_email_for(:notification)
deleted_dossier = nil
transaction do
if deleted_by_instructeur_and_user? || en_construction? || brouillon?
if keep_track_on_deletion?
log_dossier_operation(author, :supprimer, self)
deleted_dossier = DeletedDossier.create_from_dossier(self, reason)
@ -743,6 +775,7 @@ class Dossier < ApplicationRecord
update!(dossier_transfer_id: nil)
discard!
end
end
if deleted_dossier.present?
if en_construction?
@ -765,7 +798,9 @@ class Dossier < ApplicationRecord
def restore(author)
if discarded?
transaction do
if undiscard && keep_track_on_deletion?
if 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
@ -773,12 +808,6 @@ class Dossier < ApplicationRecord
end
end
def restore_if_discarded_with_procedure(author)
if deleted_dossier&.procedure_removed?
restore(author)
end
end
def attestation_activated?
termine? && procedure.attestation_template&.activated?
end
@ -838,6 +867,7 @@ class Dossier < ApplicationRecord
create_missing_traitemets
self.hidden_by_user_at = nil
self.archived = false
self.termine_close_to_expiration_notice_sent_at = nil
self.conservation_extension = 0.days
@ -1120,10 +1150,6 @@ class Dossier < ApplicationRecord
end
end
def hidden_by_user?
self.hidden_by_user_at.present?
end
private
def create_missing_traitemets

View file

@ -29,7 +29,7 @@ class Expert < ApplicationRecord
@avis_summary
else
query = <<~EOF
COUNT(*) FILTER (where answer IS NULL) AS unanswered,
COUNT(*) FILTER (where answer IS NULL AND dossiers.hidden_by_administration_at IS NULL) AS unanswered,
COUNT(*) AS total
EOF
result = avis.select(query)[0]

View file

@ -256,6 +256,7 @@ class Instructeur < ApplicationRecord
ON follows.dossier_id = dossiers.id
AND follows.unfollowed_at IS NULL
WHERE "dossiers"."hidden_at" IS NULL
AND "dossiers"."hidden_by_administration_at" IS NULL
AND "dossiers"."state" != 'brouillon'
AND "dossiers"."groupe_instructeur_id" in (:groupe_instructeur_ids)
EOF

View file

@ -673,7 +673,10 @@ class Procedure < ApplicationRecord
def restore(author)
if discarded? && undiscard
dossiers.with_discarded.discarded.find_each do |dossier|
dossier.restore_if_discarded_with_procedure(author)
dossier.restore(author)
end
dossiers.hidden_by_administration.find_each do |dossier|
dossier.restore(author)
end
end
end

View file

@ -119,4 +119,3 @@
.dropdown-description
%h4 Supprimer le dossier
Lusager sera notifié que son dossier est supprimé.

View file

@ -26,7 +26,7 @@
= 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
.dropdown-description
Supprimer le dossier
= t('views.instructeurs.dossiers.delete_dossier')
- elsif Dossier::EN_CONSTRUCTION_OU_INSTRUCTION.include?(state)
- if dossier_is_followed

View file

@ -33,7 +33,7 @@ as well as a link to its edit page.
<% end %>
<% if dossier.can_be_deleted_by_manager? %>
<%= link_to 'Supprimer le dossier', discard_manager_dossier_path(dossier), method: :post, class: 'button', data: { confirm: "Confirmez vous la suppression du dossier ?" } %>
<% elsif dossier.discarded? && !dossier.procedure.discarded? %>
<% elsif dossier.can_be_restored_by_manager? %>
<%= link_to 'Restaurer le dossier', restore_manager_dossier_path(dossier), method: :post, class: 'button', data: { confirm: "Confirmez vous la restauration du dossier ?" } %>
<% end %>
</div>

View file

@ -2,8 +2,7 @@
- has_delete_action = dossier.can_be_deleted_by_user?
- has_new_dossier_action = dossier.procedure.accepts_new_dossiers?
- has_transfer_action = dossier.user == current_user
- has_hide_action = dossier.termine? && dossier.hidden_by_user_at.nil?
- has_actions = has_edit_action || has_delete_action || has_new_dossier_action || has_transfer_action || has_hide_action
- has_actions = has_edit_action || has_delete_action || has_new_dossier_action || has_transfer_action
- if has_actions
.dropdown.user-dossier-actions
@ -41,14 +40,8 @@
- if has_delete_action
%li.danger
= link_to ask_deletion_dossier_path(dossier), method: :post, data: { disable: true, confirm: "En continuant, vous allez supprimer ce dossier ainsi que les informations quil contient. Toute suppression entraîne lannulation de la démarche en cours.\n\nConfirmer la suppression ?" } do
= link_to delete_dossier_dossier_path(dossier), method: :patch, data: { disable: true, confirm: "En continuant, vous allez supprimer ce dossier ainsi que les informations quil contient. Toute suppression entraîne lannulation de la démarche en cours.\n\nConfirmer la suppression ?" } do
%span.icon.delete
.dropdown-description
= t('views.users.dossiers.dossier_action.delete_dossier')
- if has_hide_action
%li
= link_to hide_dossier_dossier_path(dossier), method: :patch do
%span.icon.delete
.dropdown-description
= t('views.users.dossiers.dossier_action.hide_dossier')

View file

@ -135,6 +135,7 @@ en:
instructeurs:
dossiers:
archived_dossier: "This file will be kept for an additional month"
delete_dossier: "Delete file"
deleted_by_user: "File deleted by user"
avis:
introduction_file_explaination: "File attached to the request for advice"
@ -198,7 +199,6 @@ en:
edit_dossier: "Edit the file"
start_other_dossier: "Start an other file"
delete_dossier: "Delete the file"
hide_dossier: "Delete from your screen"
transfer_dossier: "Transfer the file"
edit_draft: "Edit the draft"
actions: "Actions"
@ -407,6 +407,10 @@ en:
identity_saved: "Identity data is registred"
attestation:
no_longer_available: "The certificate is no longer available on this file."
instructeurs:
dossiers:
deleted_by_instructeur: "The folder has been deleted"
impossible_deletion: "Unable to delete : the folder is not processed"
france_connect:
particulier:
password_confirmation:

View file

@ -132,6 +132,7 @@ fr:
instructeurs:
dossiers:
archived_dossier: "Le dossier sera conservé 1 mois supplémentaire"
delete_dossier: "Supprimer le dossier"
deleted_by_user: "Dossier supprimé par l'usager"
avis:
introduction_file_explaination: "Fichier joint à la demande davis"
@ -195,7 +196,6 @@ fr:
edit_dossier: "Modifier le dossier"
start_other_dossier: "Commencer un autre dossier"
delete_dossier: "Supprimer le dossier"
hide_dossier: "Supprimer de votre interface"
transfer_dossier: "Transferer le dossier"
edit_draft: "Modifier le brouillon"
actions: "Actions"
@ -415,6 +415,10 @@ fr:
identity_saved: "Identité enregistrée"
attestation:
no_longer_available: "Lattestation n'est plus disponible sur ce dossier."
instructeurs:
dossiers:
deleted_by_instructeur: "Le dossier a bien été supprimé de votre interface"
impossible_deletion: "Supression impossible : le dossier n'est pas traité"
administrateurs:
procedures:
show:

View file

@ -268,8 +268,7 @@ Rails.application.routes.draw do
get 'demande'
get 'messagerie'
post 'commentaire' => 'dossiers#create_commentaire'
post 'ask_deletion'
patch 'hide_dossier'
patch 'delete_dossier'
get 'attestation'
get 'transferer', to: 'dossiers#transferer'
end

View file

@ -0,0 +1,5 @@
class AddHiddenByAdministrationToDossiers < ActiveRecord::Migration[6.1]
def change
add_column :dossiers, :hidden_by_administration_at, :datetime
end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2021_12_02_135804) do
ActiveRecord::Schema.define(version: 2021_12_02_133139) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -326,6 +326,7 @@ ActiveRecord::Schema.define(version: 2021_12_02_135804) do
t.datetime "hidden_by_user_at"
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.datetime "hidden_by_administration_at"
t.index ["archived"], name: "index_dossiers_on_archived"
t.index ["dossier_transfer_id"], name: "index_dossiers_on_dossier_transfer_id"
t.index ["groupe_instructeur_id"], name: "index_dossiers_on_groupe_instructeur_id"

View file

@ -762,9 +762,10 @@ describe Instructeurs::DossiersController, type: :controller do
end
end
context 'when the instructeur want to delete a dossier with a decision' do
context 'when the instructeur want to delete a dossier with a decision and already hidden by user' do
before do
dossier.accepter!(instructeur: instructeur, motivation: "le dossier est correct")
dossier.update!(hidden_by_user_at: Time.zone.now.beginning_of_day.utc)
allow(DossierMailer).to receive(:notify_instructeur_deletion_to_user).and_return(double(deliver_later: nil))
subject
end
@ -790,6 +791,31 @@ describe Instructeurs::DossiersController, type: :controller do
end
end
context 'when the instructeur want to delete a dossier with a decision and not hidden by user' do
before do
dossier.accepter!(instructeur: instructeur, motivation: "le dossier est correct")
allow(DossierMailer).to receive(:notify_instructeur_deletion_to_user).and_return(double(deliver_later: nil))
subject
end
it 'does not deletes previous logs and does not add a suppression log' do
expect(DossierOperationLog.where(dossier_id: dossier.id).count).to eq(2)
expect(DossierOperationLog.where(dossier_id: dossier.id).last.operation).not_to eq('supprimer')
end
it 'does not send an email to the user' do
expect(DossierMailer).not_to have_received(:notify_instructeur_deletion_to_user).with(DeletedDossier.where(dossier_id: dossier.id).first, dossier.user.email)
end
it 'add a record into deleted_dossiers table' do
expect(DeletedDossier.where(dossier_id: dossier.id).count).to eq(0)
end
it 'does not discard the dossier' do
expect(dossier.reload.hidden_at).to eq(nil)
end
end
context 'when the instructeur want to delete a dossier without a decision' do
before do
subject

View file

@ -1000,10 +1000,10 @@ describe Users::DossiersController, type: :controller do
end
end
describe '#ask_deletion' do
describe '#delete_dossier' do
before { sign_in(user) }
subject { post :ask_deletion, params: { id: dossier.id } }
subject { patch :delete_dossier, params: { id: dossier.id } }
shared_examples_for "the dossier can not be deleted" do
it "doesnt notify the deletion" do
@ -1043,7 +1043,7 @@ describe Users::DossiersController, type: :controller do
let(:dossier) { create(:dossier, :en_instruction, user: user, autorisation_donnees: true) }
it_behaves_like "the dossier can not be deleted"
it { is_expected.to redirect_to(dossier_path(dossier)) }
it { is_expected.to redirect_to(dossiers_path) }
end
end
@ -1054,6 +1054,15 @@ describe Users::DossiersController, type: :controller do
it_behaves_like "the dossier can not be deleted"
it { is_expected.to redirect_to(root_path) }
end
context 'when the dossier is already deleted by instructeur' do
let!(:dossier) { create(:dossier, :with_individual, state: :accepte, en_construction_at: Time.zone.yesterday.beginning_of_day.utc, user: user, autorisation_donnees: true, hidden_by_administration_at: Time.zone.now.beginning_of_day.utc) }
before { subject }
it 'discard the dossier' do
expect(dossier.reload.hidden_at).to be_present
end
end
end
describe '#new' do

View file

@ -62,9 +62,9 @@ describe 'user access to the list of their dossiers' do
describe 'deletion' do
it 'should have links to delete dossiers' do
expect(page).to have_link(nil, href: ask_deletion_dossier_path(dossier_brouillon))
expect(page).to have_link(nil, href: ask_deletion_dossier_path(dossier_en_construction))
expect(page).not_to have_link(nil, href: ask_deletion_dossier_path(dossier_en_instruction))
expect(page).to have_link(nil, href: delete_dossier_dossier_path(dossier_brouillon))
expect(page).to have_link(nil, href: delete_dossier_dossier_path(dossier_en_construction))
expect(page).not_to have_link(nil, href: delete_dossier_dossier_path(dossier_en_instruction))
end
context 'when user clicks on delete button', js: true do

View file

@ -0,0 +1,30 @@
describe 'experts/avis/index.html.haml', type: :view do
let!(:expert) { create(:expert) }
let!(:claimant) { create(:instructeur) }
let!(:procedure) { create(:procedure) }
let!(:avis) { create(:avis, claimant: claimant, experts_procedure: experts_procedure) }
let!(:experts_procedure) { create(:experts_procedure, expert: expert, procedure: procedure) }
before do
allow(view).to receive(:current_expert).and_return(avis.expert)
assign(:dossier, avis.dossier)
allow(view).to receive(:current_expert).and_return(avis.expert)
end
subject { render }
context 'when the dossier is deleted by instructeur' do
before do
avis.dossier.update!(state: "accepte", hidden_by_administration_at: Time.zone.now.beginning_of_day.utc)
assign(:avis_by_procedure, avis.expert.avis.includes(dossier: [groupe_instructeur: :procedure]).where(dossiers: { hidden_by_administration_at: nil }).to_a.group_by(&:procedure))
end
it { is_expected.not_to have_text("avis à donner") }
end
context 'when the dossier is not deleted by instructeur' do
before do
assign(:avis_by_procedure, avis.expert.avis.includes(dossier: [groupe_instructeur: :procedure]).where(dossiers: { hidden_by_administration_at: nil }).to_a.group_by(&:procedure))
end
it { is_expected.to have_text("avis à donner") }
end
end

View file

@ -6,12 +6,12 @@ describe 'users/dossiers/dossier_actions.html.haml', type: :view do
subject { render 'users/dossiers/dossier_actions.html.haml', dossier: dossier, current_user: user }
it { is_expected.to have_link('Commencer un autre dossier', href: commencer_url(path: procedure.path)) }
it { is_expected.to have_link('Supprimer le dossier', href: ask_deletion_dossier_path(dossier)) }
it { is_expected.to have_link('Supprimer le dossier', href: delete_dossier_dossier_path(dossier)) }
it { is_expected.to have_link('Transferer le dossier', href: transferer_dossier_path(dossier)) }
context 'when the dossier cannot be deleted' do
context 'when the dossier is termine' do
let(:dossier) { create(:dossier, :accepte, procedure: procedure) }
it { is_expected.not_to have_link('Supprimer le dossier') }
it { is_expected.to have_link('Supprimer le dossier') }
end
context 'when the procedure is closed' do