Merge pull request #7014 from tchak/feat-deleted-dossiers-scopes

Ne plus utiliser Discarded sur les Dossiers et utiliser les bons scopes
This commit is contained in:
Paul Chavard 2022-03-10 16:28:54 +01:00 committed by GitHub
commit 0b4fa6f3c9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 298 additions and 345 deletions

View file

@ -63,7 +63,7 @@ module Administrateurs
end end
def destroy def destroy
if !groupe_instructeur.dossiers.with_discarded.empty? if !groupe_instructeur.dossiers.empty?
flash[:alert] = "Impossible de supprimer un groupe avec des dossiers. Il faut le réaffecter avant" flash[:alert] = "Impossible de supprimer un groupe avec des dossiers. Il faut le réaffecter avant"
elsif procedure.groupe_instructeurs.one? elsif procedure.groupe_instructeurs.one?
flash[:alert] = "Suppression impossible : il doit y avoir au moins un groupe instructeur sur chaque procédure" flash[:alert] = "Suppression impossible : il doit y avoir au moins un groupe instructeur sur chaque procédure"
@ -95,7 +95,7 @@ module Administrateurs
def reaffecter def reaffecter
target_group = procedure.groupe_instructeurs.find(params[:target_group]) target_group = procedure.groupe_instructeurs.find(params[:target_group])
reaffecter_bulk_messages(target_group) reaffecter_bulk_messages(target_group)
groupe_instructeur.dossiers.with_discarded.find_each do |dossier| groupe_instructeur.dossiers.find_each do |dossier|
dossier.assign_to_groupe_instructeur(target_group, current_administrateur) dossier.assign_to_groupe_instructeur(target_group, current_administrateur)
end end

View file

@ -228,7 +228,7 @@ module Instructeurs
def delete_dossier def delete_dossier
if dossier.termine? if dossier.termine?
dossier.discard_and_keep_track!(current_instructeur, :instructeur_request) dossier.hide_and_keep_track!(current_instructeur, :instructeur_request)
flash.notice = t('instructeurs.dossiers.deleted_by_instructeur') flash.notice = t('instructeurs.dossiers.deleted_by_instructeur')
redirect_to instructeur_procedure_path(procedure) redirect_to instructeur_procedure_path(procedure)
else else
@ -242,6 +242,7 @@ module Instructeurs
def dossier def dossier
@dossier ||= current_instructeur @dossier ||= current_instructeur
.dossiers .dossiers
.visible_by_administration
.includes(champs: :type_de_champ) .includes(champs: :type_de_champ)
.find(params[:dossier_id]) .find(params[:dossier_id])
end end

View file

@ -13,19 +13,21 @@ module Instructeurs
.order(closed_at: :desc, unpublished_at: :desc, published_at: :desc, created_at: :desc) .order(closed_at: :desc, unpublished_at: :desc, published_at: :desc, created_at: :desc)
dossiers = current_instructeur.dossiers.joins(:groupe_instructeur) dossiers = current_instructeur.dossiers.joins(:groupe_instructeur)
@dossiers_count_per_procedure = dossiers.all_state.visible_by_administration.group('groupe_instructeurs.procedure_id').reorder(nil).count dossiers_visibles = dossiers.visible_by_administration
@dossiers_a_suivre_count_per_procedure = dossiers.without_followers.en_cours.visible_by_administration.group('groupe_instructeurs.procedure_id').reorder(nil).count @dossiers_count_per_procedure = dossiers_visibles.all_state.group('groupe_instructeurs.procedure_id').reorder(nil).count
@dossiers_archived_count_per_procedure = dossiers.archived.group('groupe_instructeurs.procedure_id').count @dossiers_a_suivre_count_per_procedure = dossiers_visibles.without_followers.en_cours.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_archived_count_per_procedure = dossiers_visibles.archived.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_termines_count_per_procedure = dossiers_visibles.termine.group('groupe_instructeurs.procedure_id').reorder(nil).count
@dossiers_expirant_count_per_procedure = dossiers_visibles.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 @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
.followed_dossiers .followed_dossiers
.joins(:groupe_instructeur) .joins(:groupe_instructeur)
.en_cours .en_cours
.where(groupe_instructeur_id: groupe_ids) .where(groupe_instructeur_id: groupe_ids)
.visible_by_administration
.group('groupe_instructeurs.procedure_id') .group('groupe_instructeurs.procedure_id')
.reorder(nil) .reorder(nil)
.count .count
@ -56,27 +58,27 @@ module Instructeurs
@a_suivre_count, @suivis_count, @traites_count, @tous_count, @supprimes_recemment_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', 'supprimes_recemment', 'archives', 'expirant') .fetch_values('a_suivre', 'suivis', 'traites', 'tous', 'supprimes_recemment', 'archives', 'expirant')
@can_download_dossiers = (@tous_count + @archives_count) > 0
dossiers_visibles = Dossier dossiers = Dossier.where(groupe_instructeur_id: groupe_instructeur_ids)
.where(groupe_instructeur_id: groupe_instructeur_ids) dossiers_visibles = dossiers.visible_by_administration
@a_suivre_dossiers = dossiers_visibles @a_suivre_dossiers = dossiers_visibles
.without_followers .without_followers
.en_cours .en_cours
.visible_by_administration
@followed_dossiers = current_instructeur @followed_dossiers = current_instructeur
.followed_dossiers .followed_dossiers
.where(groupe_instructeur_id: groupe_instructeur_ids)
.en_cours .en_cours
.merge(dossiers_visibles)
@followed_dossiers_id = @followed_dossiers.pluck(:id) @followed_dossiers_id = @followed_dossiers.pluck(:id)
@termines_dossiers = dossiers_visibles.termine.visible_by_administration @termines_dossiers = dossiers_visibles.termine
@all_state_dossiers = dossiers_visibles.all_state.visible_by_administration @all_state_dossiers = dossiers_visibles.all_state
@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
@supprimes_recemment_dossiers = dossiers.hidden_by_administration.termine
@dossiers = case statut @dossiers = case statut
when 'a-suivre' when 'a-suivre'
@ -178,10 +180,10 @@ module Instructeurs
.groupe_instructeurs .groupe_instructeurs
.where(procedure: procedure) .where(procedure: procedure)
@dossier_count = current_instructeur @can_download_dossiers = current_instructeur
.dossiers_count_summary(groupe_instructeur_ids) .dossiers
.fetch_values('tous', 'archives') .visible_by_administration
.sum .exists?(groupe_instructeur_id: groupe_instructeur_ids)
export = Export.find_or_create_export(export_format, time_span_type, groupe_instructeurs) export = Export.find_or_create_export(export_format, time_span_type, groupe_instructeurs)

View file

@ -4,7 +4,7 @@ class InvitesController < ApplicationController
def create def create
email = params[:invite_email].downcase email = params[:invite_email].downcase
dossier = current_user.dossiers.find(params[:dossier_id]) dossier = current_user.dossiers.visible_by_user.find(params[:dossier_id])
invite = Invite.create( invite = Invite.create(
dossier: dossier, dossier: dossier,

View file

@ -9,10 +9,10 @@ module Manager
def scoped_resource def scoped_resource
if unfiltered_list? if unfiltered_list?
# Don't display discarded dossiers in the unfiltered list… # Don't display discarded dossiers in the unfiltered list…
Dossier.kept Dossier.visible_by_administration
else else
# … but allow them to be searched and displayed. # … but allow them to be searched and displayed.
Dossier.with_discarded Dossier
end end
end end

View file

@ -5,10 +5,12 @@ module Users
layout 'procedure_context', only: [:identite, :update_identite, :siret, :update_siret] layout 'procedure_context', only: [:identite, :update_identite, :siret, :update_siret]
ACTIONS_ALLOWED_TO_ANY_USER = [:index, :recherche, :new, :transferer_all] 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, :restore] ACTIONS_ALLOWED_TO_OWNER_OR_INVITE = [:show, :demande, :messagerie, :brouillon, :update_brouillon, :modifier, :update, :create_commentaire]
ACTIONS_ALLOWED_TO_OWNER_OR_INVITE_HIDDEN = [:restore]
before_action :ensure_ownership!, except: ACTIONS_ALLOWED_TO_ANY_USER + ACTIONS_ALLOWED_TO_OWNER_OR_INVITE before_action :ensure_ownership!, except: ACTIONS_ALLOWED_TO_ANY_USER + ACTIONS_ALLOWED_TO_OWNER_OR_INVITE + ACTIONS_ALLOWED_TO_OWNER_OR_INVITE_HIDDEN
before_action :ensure_ownership_or_invitation!, only: ACTIONS_ALLOWED_TO_OWNER_OR_INVITE before_action :ensure_ownership_or_invitation!, only: ACTIONS_ALLOWED_TO_OWNER_OR_INVITE
before_action :ensure_ownership_or_invitation_hidden!, only: ACTIONS_ALLOWED_TO_OWNER_OR_INVITE_HIDDEN
before_action :ensure_dossier_can_be_updated, only: [:update_identite, :update_brouillon, :modifier, :update] before_action :ensure_dossier_can_be_updated, only: [:update_identite, :update_brouillon, :modifier, :update]
before_action :forbid_invite_submission!, only: [:update_brouillon] before_action :forbid_invite_submission!, only: [:update_brouillon]
before_action :forbid_closed_submission!, only: [:update_brouillon] before_action :forbid_closed_submission!, only: [:update_brouillon]
@ -16,17 +18,20 @@ module Users
before_action :store_user_location!, only: :new before_action :store_user_location!, only: :new
def index def index
@user_dossiers = current_user.dossiers.includes(:procedure).state_not_termine.visible_by_user.order_by_updated_at.page(page) dossiers = Dossier.includes(:procedure).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_visibles = dossiers.visible_by_user
@dossiers_invites = current_user.dossiers_invites.includes(:procedure).order_by_updated_at.page(page)
@dossiers_supprimes_recemment = current_user.dossiers.hidden_by_user.order_by_updated_at.page(page) @user_dossiers = current_user.dossiers.state_not_termine.merge(dossiers_visibles)
@dossiers_traites = current_user.dossiers.state_termine.merge(dossiers_visibles)
@dossiers_close_to_expiration = current_user.dossiers.close_to_expiration.merge(dossiers_visibles)
@dossiers_invites = current_user.dossiers_invites.merge(dossiers_visibles)
@dossiers_supprimes_recemment = current_user.dossiers.hidden_by_user.merge(dossiers)
@dossiers_supprimes_definitivement = current_user.deleted_dossiers.order_by_updated_at.page(page) @dossiers_supprimes_definitivement = current_user.deleted_dossiers.order_by_updated_at.page(page)
@dossier_transfers = DossierTransfer @dossier_transfers = DossierTransfer
.includes(dossiers: :user) .includes(dossiers: :user)
.with_dossiers .with_dossiers
.where(email: current_user.email) .where(email: current_user.email)
.page(page) .page(page)
@dossiers_close_to_expiration = current_user.dossiers.close_to_expiration.page(page)
@statut = statut(@user_dossiers, @dossiers_traites, @dossiers_invites, @dossiers_supprimes_recemment, @dossiers_supprimes_definitivement, @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 end
@ -214,9 +219,8 @@ module Users
end end
def delete_dossier def delete_dossier
dossier = current_user.dossiers.includes(:user, procedure: :administrateurs).find(params[:id])
if dossier.can_be_deleted_by_user? if dossier.can_be_deleted_by_user?
dossier.discard_and_keep_track!(current_user, :user_request) dossier.hide_and_keep_track!(current_user, :user_request)
flash.notice = t('users.dossiers.ask_deletion.soft_deleted_dossier') flash.notice = t('users.dossiers.ask_deletion.soft_deleted_dossier')
redirect_to dossiers_path redirect_to dossiers_path
else else
@ -277,7 +281,7 @@ module Users
def dossier_for_help def dossier_for_help
dossier_id = params[:id] || params[:dossier_id] dossier_id = params[:id] || params[:dossier_id]
@dossier || (dossier_id.present? && Dossier.find_by(id: dossier_id.to_i)) @dossier || (dossier_id.present? && Dossier.visible_by_user.find_by(id: dossier_id.to_i))
end end
def transferer def transferer
@ -289,7 +293,7 @@ module Users
end end
def restore def restore
dossier.restore(current_user) hidden_dossier.restore(current_user)
flash.notice = t('users.dossiers.restore') flash.notice = t('users.dossiers.restore')
redirect_to dossiers_path redirect_to dossiers_path
end end
@ -355,11 +359,15 @@ module Users
end end
def dossier def dossier
@dossier ||= Dossier.find(params[:id] || params[:dossier_id]) @dossier ||= Dossier.visible_by_user.find(params[:id] || params[:dossier_id])
end
def hidden_dossier
@hidden_dossier ||= Dossier.hidden_by_user.find(params[:id] || params[:dossier_id])
end end
def dossier_with_champs def dossier_with_champs
Dossier.with_champs.find(params[:id]) Dossier.with_champs.visible_by_user.find(params[:id])
end end
def should_change_groupe_instructeur? def should_change_groupe_instructeur?
@ -436,6 +444,12 @@ module Users
end end
end end
def ensure_ownership_or_invitation_hidden!
if !current_user.owns_or_invite?(hidden_dossier)
forbidden!
end
end
def forbid_invite_submission! def forbid_invite_submission!
if passage_en_construction? && !current_user.owns?(dossier) if passage_en_construction? && !current_user.owns?(dossier)
forbidden! forbidden!

View file

@ -15,7 +15,8 @@ class DossierDashboard < Administrate::BaseDashboard
text_summary: Field::String.with_options(searchable: false), text_summary: Field::String.with_options(searchable: false),
created_at: Field::DateTime, created_at: Field::DateTime,
updated_at: Field::DateTime, updated_at: Field::DateTime,
hidden_at: Field::DateTime, hidden_by_user_at: Field::DateTime,
hidden_by_administration_at: Field::DateTime,
champs: ChampCollectionField champs: ChampCollectionField
}.freeze }.freeze
@ -41,7 +42,8 @@ class DossierDashboard < Administrate::BaseDashboard
:champs, :champs,
:created_at, :created_at,
:updated_at, :updated_at,
:hidden_at :hidden_by_user_at,
:hidden_by_administration_at
].freeze ].freeze
# FORM_ATTRIBUTES # FORM_ATTRIBUTES

View file

@ -20,9 +20,9 @@ module Types
def dossier(number:) def dossier(number:)
if context.internal_use? if context.internal_use?
Dossier.state_not_brouillon.with_discarded.for_api_v2.find(number)
else
Dossier.state_not_brouillon.for_api_v2.find(number) Dossier.state_not_brouillon.for_api_v2.find(number)
else
Dossier.visible_by_administration.for_api_v2.find(number)
end end
rescue => e rescue => e
raise GraphQL::ExecutionError.new(e.message, extensions: { code: :not_found }) raise GraphQL::ExecutionError.new(e.message, extensions: { code: :not_found })

View file

@ -5,7 +5,7 @@ class AvisMailer < ApplicationMailer
layout 'mailers/layout' layout 'mailers/layout'
def avis_invitation(avis) def avis_invitation(avis)
if avis.dossier.present? if avis.dossier.visible_by_administration?
@avis = avis @avis = avis
email = @avis.expert&.email email = @avis.expert&.email
subject = "Donnez votre avis sur le dossier nº #{@avis.dossier.id} (#{@avis.dossier.procedure.libelle})" subject = "Donnez votre avis sur le dossier nº #{@avis.dossier.id} (#{@avis.dossier.procedure.libelle})"

View file

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

View file

@ -20,7 +20,7 @@
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champ < ApplicationRecord class Champ < ApplicationRecord
belongs_to :dossier, -> { with_discarded }, inverse_of: false, touch: true, optional: false belongs_to :dossier, inverse_of: false, touch: true, optional: false
belongs_to :type_de_champ, inverse_of: :champ, optional: false belongs_to :type_de_champ, inverse_of: :champ, optional: false
belongs_to :parent, class_name: 'Champ', optional: true belongs_to :parent, class_name: 'Champ', optional: true
has_one_attached :piece_justificative_file has_one_attached :piece_justificative_file

View file

@ -30,6 +30,8 @@ class DeletedDossier < ApplicationRecord
} }
def self.create_from_dossier(dossier, reason) def self.create_from_dossier(dossier, reason)
return if !dossier.log_operations?
# We have some bad data because of partially deleted dossiers in the past. # We have some bad data because of partially deleted dossiers in the past.
# For now use find_or_create_by! to avoid errors. # For now use find_or_create_by! to avoid errors.
create_with( create_with(

View file

@ -42,10 +42,6 @@ class Dossier < ApplicationRecord
include DossierFilteringConcern include DossierFilteringConcern
include DossierRebaseConcern include DossierRebaseConcern
include Discard::Model
self.discard_column = :hidden_at
default_scope -> { kept }
enum state: { enum state: {
brouillon: 'brouillon', brouillon: 'brouillon',
en_construction: 'en_construction', en_construction: 'en_construction',
@ -200,6 +196,7 @@ class Dossier < ApplicationRecord
scope :state_brouillon, -> { where(state: states.fetch(:brouillon)) } scope :state_brouillon, -> { where(state: states.fetch(:brouillon)) }
scope :state_not_brouillon, -> { where.not(state: states.fetch(:brouillon)) } scope :state_not_brouillon, -> { where.not(state: states.fetch(:brouillon)) }
scope :state_en_construction, -> { where(state: states.fetch(:en_construction)) } 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_en_instruction, -> { where(state: states.fetch(:en_instruction)) }
scope :state_en_construction_ou_instruction, -> { where(state: EN_CONSTRUCTION_OU_INSTRUCTION) } scope :state_en_construction_ou_instruction, -> { where(state: EN_CONSTRUCTION_OU_INSTRUCTION) }
scope :state_instruction_commencee, -> { where(state: INSTRUCTION_COMMENCEE) } scope :state_instruction_commencee, -> { where(state: INSTRUCTION_COMMENCEE) }
@ -211,7 +208,11 @@ class Dossier < ApplicationRecord
scope :hidden_by_user, -> { where.not(hidden_by_user_at: nil) } scope :hidden_by_user, -> { where.not(hidden_by_user_at: nil) }
scope :hidden_by_administration, -> { where.not(hidden_by_administration_at: nil) } scope :hidden_by_administration, -> { where.not(hidden_by_administration_at: nil) }
scope :visible_by_user, -> { where(hidden_by_user_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 dossiers.state = 'en_construction')") } scope :visible_by_administration, -> {
state_not_brouillon
.where(hidden_by_administration_at: nil)
.merge(visible_by_user.or(state_not_en_construction))
}
scope :order_by_updated_at, -> (order = :desc) { order(updated_at: order) } 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) } scope :order_by_created_at, -> (order = :asc) { order(depose_at: order, created_at: order, id: order) }
@ -349,27 +350,11 @@ class Dossier < ApplicationRecord
scope :without_en_construction_expiration_notice_sent, -> { where(en_construction_close_to_expiration_notice_sent_at: nil) } 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 :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 :deleted_by_user_expired, -> { where('dossiers.hidden_by_user_at < ?', 1.week.ago) }
scope :discarded_by_user_expired, -> { discarded.where('dossiers.hidden_by_user_at < ?', 1.week.ago) } scope :deleted_by_administration_expired, -> { where('dossiers.hidden_by_administration_at < ?', 1.week.ago) }
scope :discarded_by_administration_expired, -> { discarded.where('dossiers.hidden_by_administration_at < ?', 1.week.ago) } scope :en_brouillon_expired_to_delete, -> { state_brouillon.deleted_by_user_expired }
scope :discarded_brouillon_expired, -> do scope :en_construction_expired_to_delete, -> { state_en_construction.deleted_by_user_expired }
with_discarded scope :termine_expired_to_delete, -> { state_termine.deleted_by_user_expired.deleted_by_administration_expired }
.state_brouillon
.discarded_expired
.or(state_brouillon.discarded_by_user_expired)
end
scope :discarded_en_construction_expired, -> do
with_discarded
.state_en_construction
.discarded_expired
.or(state_en_construction.discarded_by_user_expired)
end
scope :discarded_termine_expired, -> do
with_discarded
.state_termine
.discarded_expired
.or(state_termine.discarded_by_user_expired.discarded_by_administration_expired)
end
scope :brouillon_near_procedure_closing_date, -> do scope :brouillon_near_procedure_closing_date, -> do
# select users who have submitted dossier for the given 'procedures.id' # select users who have submitted dossier for the given 'procedures.id'
@ -537,8 +522,8 @@ class Dossier < ApplicationRecord
brouillon? || en_construction? || termine? brouillon? || en_construction? || termine?
end end
def can_be_hidden_by_user? def can_be_deleted_by_administration?(reason)
en_construction? || termine? termine? || reason == :procedure_removed
end end
def messagerie_available? def messagerie_available?
@ -686,10 +671,6 @@ class Dossier < ApplicationRecord
!procedure.brouillon? && !brouillon? !procedure.brouillon? && !brouillon?
end end
def keep_track_on_deletion?
!procedure.brouillon? && !brouillon?
end
def hidden_by_user? def hidden_by_user?
hidden_by_user_at.present? hidden_by_user_at.present?
end end
@ -698,8 +679,16 @@ class Dossier < ApplicationRecord
hidden_by_administration_at.present? hidden_by_administration_at.present?
end end
def deleted_by_instructeur_and_user? def hidden_for_administration?
termine? && hidden_by_administration? && hidden_by_user? hidden_by_administration? || (hidden_by_user? && en_construction?) || brouillon?
end
def visible_by_administration?
!hidden_for_administration?
end
def hidden_for_administration_and_user?
hidden_for_administration? && hidden_by_user?
end end
def expose_legacy_carto_api? def expose_legacy_carto_api?
@ -738,11 +727,9 @@ class Dossier < ApplicationRecord
def expired_keep_track_and_destroy! def expired_keep_track_and_destroy!
transaction do transaction do
if keep_track_on_deletion? DeletedDossier.create_from_dossier(self, :expired)
DeletedDossier.create_from_dossier(self, :expired) dossier_operation_logs.destroy_all
dossier_operation_logs.destroy_all log_automatic_dossier_operation(:supprimer, self)
log_automatic_dossier_operation(:supprimer, self)
end
destroy! destroy!
end end
true true
@ -758,37 +745,20 @@ class Dossier < ApplicationRecord
author.is_a?(Instructeur) || author.is_a?(Administrateur) || author.is_a?(SuperAdmin) author.is_a?(Instructeur) || author.is_a?(Administrateur) || author.is_a?(SuperAdmin)
end end
def restore_dossier_and_destroy_deleted_dossier(author) def hide_and_keep_track!(author, reason)
if deleted_dossier.present?
deleted_dossier&.destroy!
end
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, hidden_by_reason: reason)
end
if can_be_hidden_by_user? && author_is_user(author)
update(hidden_by_user_at: Time.zone.now, dossier_transfer_id: nil, hidden_by_reason: reason)
end
transaction do transaction do
if deleted_by_instructeur_and_user? || en_construction? || brouillon? if author_is_administration(author) && can_be_deleted_by_administration?(reason)
if keep_track_on_deletion? update(hidden_by_administration_at: Time.zone.now, hidden_by_reason: reason)
log_dossier_operation(author, :supprimer, self) elsif author_is_user(author) && can_be_deleted_by_user?
end update(hidden_by_user_at: Time.zone.now, dossier_transfer_id: nil, hidden_by_reason: reason)
else
if !(en_construction? && author_is_user(author)) raise "Unauthorized dossier hide attempt Dossier##{id} by #{author} for reason #{reason}"
discard!
end
end end
log_dossier_operation(author, :supprimer, self)
end end
if en_construction? if en_construction? && !hidden_by_administration?
update(hidden_by_reason: reason)
administration_emails = followers_instructeurs.present? ? followers_instructeurs.map(&:email) : procedure.administrateurs.map(&:email) administration_emails = followers_instructeurs.present? ? followers_instructeurs.map(&:email) : procedure.administrateurs.map(&:email)
administration_emails.each do |email| administration_emails.each do |email|
DossierMailer.notify_en_construction_deletion_to_administration(self, email).deliver_later DossierMailer.notify_en_construction_deletion_to_administration(self, email).deliver_later
@ -797,30 +767,18 @@ class Dossier < ApplicationRecord
end end
def restore(author) def restore(author)
if discarded? transaction do
transaction do if author_is_administration(author)
if author_is_administration(author) && hidden_by_administration?
update(hidden_by_administration_at: nil)
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)
!hidden_by_administration? && update(hidden_by_reason: nil)
if en_construction?
restore_dossier_and_destroy_deleted_dossier(author)
end
end
elsif author_is_administration(author) && hidden_by_administration?
transaction do
update(hidden_by_administration_at: nil) update(hidden_by_administration_at: nil)
!hidden_by_user? && update(hidden_by_reason: nil) elsif author_is_user(author)
update(hidden_by_user_at: nil)
end end
if !hidden_by_user? && !hidden_by_administration?
update(hidden_by_reason: nil)
end
log_dossier_operation(author, :restaurer, self)
end end
end end
@ -1152,19 +1110,16 @@ class Dossier < ApplicationRecord
def purge_discarded def purge_discarded
transaction do transaction do
if keep_track_on_deletion? DeletedDossier.create_from_dossier(self, hidden_by_reason)
DeletedDossier.create_from_dossier(self, hidden_by_reason)
end
dossier_operation_logs.not_deletion.destroy_all dossier_operation_logs.not_deletion.destroy_all
destroy destroy
end end
end end
def self.purge_discarded def self.purge_discarded
discarded_brouillon_expired.find_each(&:purge_discarded) en_brouillon_expired_to_delete.find_each(&:purge_discarded)
discarded_en_construction_expired.find_each(&:purge_discarded) en_construction_expired_to_delete.find_each(&:purge_discarded)
discarded_termine_expired.find_each(&:purge_discarded) termine_expired_to_delete.find_each(&:purge_discarded)
end end
private private
@ -1243,7 +1198,7 @@ class Dossier < ApplicationRecord
followers_instructeurs.each do |instructeur| followers_instructeurs.each do |instructeur|
if instructeur.groupe_instructeurs.exclude?(groupe_instructeur) if instructeur.groupe_instructeurs.exclude?(groupe_instructeur)
instructeur.unfollow(self) instructeur.unfollow(self)
if kept? if visible_by_administration?
DossierMailer.notify_groupe_instructeur_changed(instructeur, self).deliver_later DossierMailer.notify_groupe_instructeur_changed(instructeur, self).deliver_later
end end
end end

View file

@ -37,8 +37,9 @@ class DossierOperationLog < ApplicationRecord
belongs_to :bill_signature, optional: true belongs_to :bill_signature, optional: true
scope :not_deletion, -> { where.not(operation: operations.fetch(:supprimer)) } scope :not_deletion, -> { where.not(operation: operations.fetch(:supprimer)) }
scope :discarded_en_construction_expired, -> { where(dossier: Dossier.discarded_en_construction_expired).not_deletion } scope :brouillon_expired, -> { where(dossier: Dossier.brouillon_expired).not_deletion }
scope :discarded_termine_expired, -> { where(dossier: Dossier.discarded_termine_expired).not_deletion } scope :en_construction_expired, -> { where(dossier: Dossier.en_construction_expired).not_deletion }
scope :termine_expired, -> { where(dossier: Dossier.termine_expired).not_deletion }
def self.create_and_serialize(params) def self.create_and_serialize(params)
dossier = params.fetch(:dossier) dossier = params.fetch(:dossier)

View file

@ -14,7 +14,7 @@ class DossierTransfer < ApplicationRecord
scope :pending, -> { where('created_at > ?', (Time.zone.now - EXPIRATION_LIMIT)) } scope :pending, -> { where('created_at > ?', (Time.zone.now - EXPIRATION_LIMIT)) }
scope :stale, -> { where('created_at < ?', (Time.zone.now - EXPIRATION_LIMIT)) } scope :stale, -> { where('created_at < ?', (Time.zone.now - EXPIRATION_LIMIT)) }
scope :with_dossiers, -> { joins(:dossiers) } scope :with_dossiers, -> { joins(:dossiers).merge(Dossier.visible_by_user) }
after_create_commit :send_notification after_create_commit :send_notification
@ -48,7 +48,7 @@ class DossierTransfer < ApplicationRecord
def destroy_and_nullify def destroy_and_nullify
transaction do transaction do
# Rails cascading is not working with default scopes. Doing nullify cascade manually. # Rails cascading is not working with default scopes. Doing nullify cascade manually.
dossiers.with_discarded.update_all(dossier_transfer_id: nil) dossiers.update_all(dossier_transfer_id: nil)
destroy destroy
end end
end end
@ -56,7 +56,7 @@ class DossierTransfer < ApplicationRecord
def self.destroy_stale def self.destroy_stale
transaction do transaction do
# Rails cascading is not working with default scopes. Doing nullify cascade manually. # Rails cascading is not working with default scopes. Doing nullify cascade manually.
Dossier.with_discarded.where(transfer: stale).update_all(dossier_transfer_id: nil) Dossier.where(transfer: stale).update_all(dossier_transfer_id: nil)
stale.destroy_all stale.destroy_all
end end
end end

View file

@ -231,14 +231,13 @@ class Instructeur < ApplicationRecord
def dossiers_count_summary(groupe_instructeur_ids) def dossiers_count_summary(groupe_instructeur_ids)
query = <<~EOF query = <<~EOF
SELECT SELECT
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 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 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 ('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 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 not archived AND NOT (dossiers.hidden_by_user_at IS NOT NULL AND state = 'en_construction') AND NOT (dossiers.hidden_by_administration_at IS NOT NULL)) AS tous, COUNT(DISTINCT dossiers.id) FILTER (where dossiers.hidden_by_administration_at IS NULL AND not archived) 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 dossiers.hidden_by_administration_at IS NULL AND archived) AS archives,
COUNT(DISTINCT dossiers.id) FILTER (where 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 COUNT(DISTINCT dossiers.id) FILTER (where dossiers.hidden_by_administration_at IS NULL AND procedures.procedure_expires_when_termine_enabled
procedures.procedure_expires_when_termine_enabled
AND ( AND (
dossiers.state in ('accepte', 'refuse', 'sans_suite') 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 AND dossiers.processed_at + dossiers.conservation_extension + (procedures.duree_conservation_dossiers_dans_ds * INTERVAL '1 month') - INTERVAL :expires_in < :now
@ -247,17 +246,17 @@ class Instructeur < ApplicationRecord
AND dossiers.en_construction_at + dossiers.conservation_extension + (duree_conservation_dossiers_dans_ds * INTERVAL '1 month') - INTERVAL :expires_in < :now AND dossiers.en_construction_at + dossiers.conservation_extension + (duree_conservation_dossiers_dans_ds * INTERVAL '1 month') - INTERVAL :expires_in < :now
) )
) AS expirant ) AS expirant
FROM "dossiers" FROM dossiers
INNER JOIN "procedure_revisions" INNER JOIN procedure_revisions
ON "procedure_revisions"."id" = "dossiers"."revision_id" ON procedure_revisions.id = dossiers.revision_id
INNER JOIN "procedures" INNER JOIN procedures
ON "procedures"."id" = "procedure_revisions"."procedure_id" ON procedures.id = procedure_revisions.procedure_id
LEFT OUTER JOIN follows LEFT OUTER JOIN follows
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.state != 'brouillon'
AND "dossiers"."state" != 'brouillon' AND dossiers.groupe_instructeur_id in (:groupe_instructeur_ids)
AND "dossiers"."groupe_instructeur_id" in (:groupe_instructeur_ids) AND (dossiers.hidden_by_user_at IS NULL OR dossiers.state != 'en_construction')
EOF EOF
sanitized_query = ActiveRecord::Base.sanitize_sql([ sanitized_query = ActiveRecord::Base.sanitize_sql([

View file

@ -26,14 +26,9 @@ class Invite < ApplicationRecord
validates :email, format: { with: Devise.email_regexp, message: "n'est pas valide" }, allow_nil: true validates :email, format: { with: Devise.email_regexp, message: "n'est pas valide" }, allow_nil: true
# #1619 When an administrateur deletes a `Procedure`, its `hidden_at` field, and scope :with_dossiers, -> { joins(:dossier).merge(Dossier.visible_by_user) }
# the `hidden_at` field of its `Dossier`s, get set, effectively removing the Procedure
# and Dossier from their respective `default_scope`s.
# Therefore, we also remove `Invite`s for such effectively deleted `Dossier`s
# from their default scope.
scope :kept, -> { joins(:dossier).merge(Dossier.kept) }
default_scope { kept } default_scope { with_dossiers }
def send_notification def send_notification
if self.user.present? if self.user.present?

View file

@ -682,15 +682,15 @@ class Procedure < ApplicationRecord
close! close!
end end
dossiers.termine.visible_by_administration.each do |dossier| dossiers.visible_by_administration.each do |dossier|
dossier.discard_and_keep_track!(author, :procedure_removed) dossier.hide_and_keep_track!(author, :procedure_removed)
end end
discard! discard!
end end
def purge_discarded def purge_discarded
if !dossiers.with_discarded.exists? if dossiers.empty?
destroy destroy
end end
end end

View file

@ -190,20 +190,34 @@ class User < ApplicationRecord
end end
transaction do transaction do
Invite.where(dossier: dossiers.with_discarded).destroy_all # delete invites
dossiers.state_en_construction.each do |dossier| Invite.where(dossier: dossiers).destroy_all
dossier.discard_and_keep_track!(administration, :user_removed)
# delete dossiers brouillon
dossiers.state_brouillon.each do |dossier|
dossier.hide_and_keep_track!(dossier.user, :user_removed)
end
dossiers.state_brouillon.find_each(&:purge_discarded)
# delete dossiers en_construction
dossiers.state_en_construction.each do |dossier|
dossier.hide_and_keep_track!(dossier.user, :user_removed)
end
dossiers.state_en_construction.find_each(&:purge_discarded)
# delete dossiers terminé
dossiers.state_termine.each do |dossier|
dossier.hide_and_keep_track!(dossier.user, :user_removed)
end end
DossierOperationLog.where(dossier: dossiers.with_discarded.discarded).not_deletion.destroy_all
dossiers.with_discarded.discarded.destroy_all
dossiers.update_all(deleted_user_email_never_send: email, user_id: nil, dossier_transfer_id: nil) dossiers.update_all(deleted_user_email_never_send: email, user_id: nil, dossier_transfer_id: nil)
destroy! destroy!
end end
end end
def merge(old_user) def merge(old_user)
transaction do transaction do
old_user.dossiers.with_discarded.update_all(user_id: id) old_user.dossiers.update_all(user_id: id)
old_user.invites.update_all(user_id: id) old_user.invites.update_all(user_id: id)
old_user.merge_logs.update_all(user_id: id) old_user.merge_logs.update_all(user_id: id)

View file

@ -18,7 +18,7 @@ class DossierSearchService
def self.dossier_by_exact_id(dossiers, search_terms) def self.dossier_by_exact_id(dossiers, search_terms)
id = search_terms.to_i id = search_terms.to_i
if id != 0 && id_compatible?(id) # Sometimes instructeur is searching dossiers with a big number (ex: SIRET), ActiveRecord can't deal with them and throws ActiveModel::RangeError. id_compatible? prevents this. if id != 0 && id_compatible?(id) # Sometimes instructeur is searching dossiers with a big number (ex: SIRET), ActiveRecord can't deal with them and throws ActiveModel::RangeError. id_compatible? prevents this.
dossiers.where(id: id).ids dossiers.visible_by_administration.where(id: id).ids
else else
[] []
end end
@ -29,6 +29,7 @@ class DossierSearchService
ts_query = "to_tsquery('french', #{Dossier.connection.quote(to_tsquery(search_terms))})" ts_query = "to_tsquery('french', #{Dossier.connection.quote(to_tsquery(search_terms))})"
dossiers dossiers
.visible_by_administration
.where("#{ts_vector} @@ #{ts_query}") .where("#{ts_vector} @@ #{ts_query}")
.order(Arel.sql("COALESCE(ts_rank(#{ts_vector}, #{ts_query}), 0) DESC")) .order(Arel.sql("COALESCE(ts_rank(#{ts_vector}, #{ts_query}), 0) DESC"))
.pluck('id') .pluck('id')
@ -40,6 +41,7 @@ class DossierSearchService
ts_query = "to_tsquery('french', #{Dossier.connection.quote(to_tsquery(search_terms))})" ts_query = "to_tsquery('french', #{Dossier.connection.quote(to_tsquery(search_terms))})"
dossiers dossiers
.visible_by_user
.where("#{ts_vector} @@ #{ts_query}") .where("#{ts_vector} @@ #{ts_query}")
.order(Arel.sql("COALESCE(ts_rank(#{ts_vector}, #{ts_query}), 0) DESC")) .order(Arel.sql("COALESCE(ts_rank(#{ts_vector}, #{ts_query}), 0) DESC"))
end end
@ -47,7 +49,7 @@ class DossierSearchService
def self.dossier_by_exact_id_for_user(search_terms, user) def self.dossier_by_exact_id_for_user(search_terms, user)
id = search_terms.to_i id = search_terms.to_i
if id != 0 && id_compatible?(id) # Sometimes user is searching dossiers with a big number (ex: SIRET), ActiveRecord can't deal with them and throws ActiveModel::RangeError. id_compatible? prevents this. if id != 0 && id_compatible?(id) # Sometimes user is searching dossiers with a big number (ex: SIRET), ActiveRecord can't deal with them and throws ActiveModel::RangeError. id_compatible? prevents this.
Dossier.where(id: user.dossiers.where(id: id) + user.dossiers_invites.where(id: id)).distinct Dossier.where(id: user.dossiers.visible_by_user.where(id: id) + user.dossiers_invites.visible_by_user.where(id: id)).distinct
else else
Dossier.none Dossier.none
end end

View file

@ -1,21 +1,20 @@
- if dossier_count > 0 %span.dropdown
%span.dropdown %button.button.dropdown-button{ 'aria-expanded' => 'false', 'aria-controls' => 'download-menu' }
%button.button.dropdown-button{ 'aria-expanded' => 'false', 'aria-controls' => 'download-menu' } Télécharger tous les dossiers
Télécharger tous les dossiers #download-menu.dropdown-content.fade-in-down{ style: 'width: 450px' }
#download-menu.dropdown-content.fade-in-down{ style: 'width: 450px' } %ul.dropdown-items
%ul.dropdown-items - exports_list(exports).each do |(format, time_span_type, export)|
- exports_list(exports).each do |(format, time_span_type, export)| %li
%li - if export.nil?
- if export.nil? = link_to t("#{time_span_type}_#{format}_html", scope: [:instructeurs, :procedure, :export_stale]), download_export_instructeur_procedure_path(procedure, time_span_type: time_span_type, export_format: format), remote: true
= link_to t("#{time_span_type}_#{format}_html", scope: [:instructeurs, :procedure, :export_stale]), download_export_instructeur_procedure_path(procedure, time_span_type: time_span_type, export_format: format), remote: true - elsif export.ready?
- elsif export.ready? = link_to t("export_#{time_span_type}_ready_html", export_time: time_ago_in_words(export.updated_at), export_format: ".#{format}", scope: [:instructeurs, :procedure]), export.file.service_url, target: "_blank", rel: "noopener"
= link_to t("export_#{time_span_type}_ready_html", export_time: time_ago_in_words(export.updated_at), export_format: ".#{format}", scope: [:instructeurs, :procedure]), export.file.service_url, target: "_blank", rel: "noopener" - if export.old?
- if export.old? = button_to download_export_instructeur_procedure_path(procedure, export_format: format, time_span_type: time_span_type, force_export: true), class: "button small", style: "padding-right: 2px", title: t("#{time_span_type}_short", export_format: ".#{format}", scope: [:instructeurs, :procedure, :export_stale]), remote: true, method: :get, params: { export_format: format, time_span_type: time_span_type, force_export: true } do
= button_to download_export_instructeur_procedure_path(procedure, export_format: format, time_span_type: time_span_type, force_export: true), class: "button small", style: "padding-right: 2px", title: t("#{time_span_type}_short", export_format: ".#{format}", scope: [:instructeurs, :procedure, :export_stale]), remote: true, method: :get, params: { export_format: format, time_span_type: time_span_type, force_export: true } do .icon.retry
.icon.retry - else
- else %span{ 'data-export-poll-url': download_export_instructeur_procedure_path(procedure, export_format: format, no_progress_notification: true) }
%span{ 'data-export-poll-url': download_export_instructeur_procedure_path(procedure, export_format: format, no_progress_notification: true) } = t("export_#{time_span_type}_pending_html", export_time: time_ago_in_words(export.created_at), export_format: ".#{format}", scope: [:instructeurs, :procedure])
= t("export_#{time_span_type}_pending_html", export_time: time_ago_in_words(export.created_at), export_format: ".#{format}", scope: [:instructeurs, :procedure]) - if procedure.feature_enabled?(:archive_zip_globale)
- if procedure.feature_enabled?(:archive_zip_globale) %li
%li = link_to t(:download_archive, scope: [:instructeurs, :procedure]), instructeur_archives_path(procedure)
= link_to t(:download_archive, scope: [:instructeurs, :procedure]), instructeur_archives_path(procedure)

View file

@ -1,4 +1,6 @@
<%= render_to_element('.procedure-actions', partial: "download_dossiers", locals: { procedure: @procedure, exports: @exports, dossier_count: @dossier_count }) %> <% if @can_download_dossiers %>
<%= render_to_element('.procedure-actions', partial: "download_dossiers", locals: { procedure: @procedure, exports: @exports }) %>
<% end %>
<% @exports.each do |format, exports| %> <% @exports.each do |format, exports| %>
<% exports.each do |time_span_type, export| %> <% exports.each do |time_span_type, export| %>

View file

@ -10,7 +10,8 @@
= render partial: 'header', locals: { procedure: @procedure, statut: @statut } = render partial: 'header', locals: { procedure: @procedure, statut: @statut }
.procedure-actions .procedure-actions
= render partial: "download_dossiers", locals: { procedure: @procedure, exports: @exports, dossier_count: @tous_count + @archives_count } - if @can_download_dossiers
= render partial: "download_dossiers", locals: { procedure: @procedure, exports: @exports }
.container.flex= render partial: "tabs", locals: { procedure: @procedure, .container.flex= render partial: "tabs", locals: { procedure: @procedure,
statut: @statut, statut: @statut,
a_suivre_count: @a_suivre_count, a_suivre_count: @a_suivre_count,

View file

@ -22,7 +22,7 @@ as well as a link to its edit page.
<header class="main-content__header" role="banner"> <header class="main-content__header" role="banner">
<h1 class="main-content__page-title"> <h1 class="main-content__page-title">
<%= content_for(:title) %> <%= content_for(:title) %>
<% if dossier.discarded? %> <% if dossier.hidden_for_administration? %>
(Supprimé) (Supprimé)
<% end %> <% end %>
</h1> </h1>

View file

@ -139,11 +139,6 @@ describe Administrateurs::GroupeInstructeursController, type: :controller do
let!(:gi_1_2) { procedure.groupe_instructeurs.create(label: 'groupe instructeur 2') } let!(:gi_1_2) { procedure.groupe_instructeurs.create(label: 'groupe instructeur 2') }
let!(:gi_1_3) { procedure.groupe_instructeurs.create(label: 'groupe instructeur 3') } let!(:gi_1_3) { procedure.groupe_instructeurs.create(label: 'groupe instructeur 3') }
let!(:dossier12) { create(:dossier, :en_construction, :with_individual, procedure: procedure, groupe_instructeur: gi_1_1) } let!(:dossier12) { create(:dossier, :en_construction, :with_individual, procedure: procedure, groupe_instructeur: gi_1_1) }
let!(:dossier_discarded) do
dossier = create(:dossier, :en_construction, :with_individual, procedure: procedure, groupe_instructeur: gi_1_1)
dossier.discard!
dossier
end
let!(:instructeur) { create(:instructeur) } let!(:instructeur) { create(:instructeur) }
let!(:bulk_message) { BulkMessage.create(dossier_count: 2, dossier_state: "brouillon", body: "hello", sent_at: Time.zone.now, groupe_instructeurs: [gi_1_1, gi_1_3], instructeur: instructeur) } let!(:bulk_message) { BulkMessage.create(dossier_count: 2, dossier_state: "brouillon", body: "hello", sent_at: Time.zone.now, groupe_instructeurs: [gi_1_1, gi_1_3], instructeur: instructeur) }
@ -160,8 +155,6 @@ describe Administrateurs::GroupeInstructeursController, type: :controller do
end end
it { expect(response).to redirect_to(admin_procedure_groupe_instructeurs_path(procedure)) } it { expect(response).to redirect_to(admin_procedure_groupe_instructeurs_path(procedure)) }
it { expect(gi_1_1.dossiers.with_discarded.count).to be(0) }
it { expect(gi_1_2.dossiers.with_discarded.count).to be(2) }
it { expect(gi_1_2.dossiers.last.id).to be(dossier12.id) } it { expect(gi_1_2.dossiers.last.id).to be(dossier12.id) }
it { expect(dossier12.groupe_instructeur.id).to be(gi_1_2.id) } it { expect(dossier12.groupe_instructeur.id).to be(gi_1_2.id) }
it { expect(bulk_message.groupe_instructeurs).to contain_exactly(gi_1_2, gi_1_3) } it { expect(bulk_message.groupe_instructeurs).to contain_exactly(gi_1_2, gi_1_3) }

View file

@ -387,12 +387,8 @@ describe Administrateurs::ProceduresController, type: :controller do
before { subject } before { subject }
it 'discard the procedure' do
expect(procedure.reload.discarded?).to be_truthy
end
it 'deletes associated dossiers' do it 'deletes associated dossiers' do
expect(procedure.dossiers.with_discarded.count).to eq(0) expect(procedure.dossiers.count).to eq(0)
end end
it 'redirects to the procedure drafts page' do it 'redirects to the procedure drafts page' do
@ -414,7 +410,7 @@ describe Administrateurs::ProceduresController, type: :controller do
it do it do
expect(procedure.reload.close?).to be_truthy expect(procedure.reload.close?).to be_truthy
expect(procedure.discarded?).to be_truthy expect(procedure.discarded?).to be_truthy
expect(dossier.reload.kept?).to be_truthy expect(dossier.reload.visible_by_administration?).to be_falsy
end end
end end
@ -441,7 +437,7 @@ describe Administrateurs::ProceduresController, type: :controller do
it do it do
expect(procedure.reload.discarded?).to be_truthy expect(procedure.reload.discarded?).to be_truthy
expect(dossier.reload.kept?).to be_truthy expect(dossier.reload.visible_by_administration?).to be_falsy
end end
end end

View file

@ -778,8 +778,8 @@ describe Instructeurs::DossiersController, type: :controller do
expect(DeletedDossier.where(dossier_id: dossier.id).count).to eq(0) expect(DeletedDossier.where(dossier_id: dossier.id).count).to eq(0)
end end
it 'discard the dossier' do it 'is not visible by administration' do
expect(dossier.reload.hidden_at).not_to eq(nil) expect(dossier.reload.visible_by_administration?).to be_falsy
end end
end end
@ -789,9 +789,9 @@ describe Instructeurs::DossiersController, type: :controller do
subject subject
end end
it 'does not deletes previous logs and does not add a suppression log' do it 'does not deletes previous logs and adds a suppression log' do
expect(DossierOperationLog.where(dossier_id: dossier.id).count).to eq(2) expect(DossierOperationLog.where(dossier_id: dossier.id).count).to eq(3)
expect(DossierOperationLog.where(dossier_id: dossier.id).last.operation).not_to eq('supprimer') expect(DossierOperationLog.where(dossier_id: dossier.id).last.operation).to eq('supprimer')
end end
it 'add a record into deleted_dossiers table' do it 'add a record into deleted_dossiers table' do
@ -845,8 +845,8 @@ describe Instructeurs::DossiersController, type: :controller do
describe '#restore' do describe '#restore' do
let(:instructeur) { create(:instructeur) } let(:instructeur) { create(:instructeur) }
let!(:gi_p1_1) { GroupeInstructeur.create(label: '1', procedure: procedure) } let!(:gi_p1_1) { GroupeInstructeur.create(label: '1', procedure: procedure) }
let!(:procedure) { create(:procedure, :published, instructeurs: [instructeur]) } let!(:procedure) { create(:procedure, :published, :for_individual, instructeurs: [instructeur]) }
let!(:dossier) { create(:dossier, state: 'accepte', procedure: procedure, groupe_instructeur: procedure.groupe_instructeurs.first, hidden_by_administration_at: 1.hour.ago) } let!(:dossier) { create(:dossier, :accepte, :with_individual, procedure: procedure, groupe_instructeur: procedure.groupe_instructeurs.first, hidden_by_administration_at: 1.hour.ago) }
before do before do
sign_in(instructeur.user) sign_in(instructeur.user)

View file

@ -76,7 +76,7 @@ describe Instructeurs::ProceduresController, type: :controller do
end end
context "with dossiers" do context "with dossiers" do
let(:procedure) { create(:procedure, :published) } let(:procedure) { create(:procedure, :published, :expirable) }
let(:dossier) { create(:dossier, state: state, procedure: procedure) } let(:dossier) { create(:dossier, state: state, procedure: procedure) }
before do before do
@ -93,37 +93,54 @@ describe Instructeurs::ProceduresController, type: :controller do
it { expect(assigns(:dossiers_archived_count_per_procedure)[procedure.id]).to eq(nil) } it { expect(assigns(:dossiers_archived_count_per_procedure)[procedure.id]).to eq(nil) }
it { expect(assigns(:followed_dossiers_count_per_procedure)[procedure.id]).to eq(nil) } it { expect(assigns(:followed_dossiers_count_per_procedure)[procedure.id]).to eq(nil) }
it { expect(assigns(:dossiers_termines_count_per_procedure)[procedure.id]).to eq(nil) } it { expect(assigns(:dossiers_termines_count_per_procedure)[procedure.id]).to eq(nil) }
it { expect(assigns(:dossiers_expirant_count_per_procedure)[procedure.id]).to eq(nil) }
it { expect(assigns(:all_dossiers_counts)['à suivre']).to eq(0) } it { expect(assigns(:all_dossiers_counts)['à suivre']).to eq(0) }
it { expect(assigns(:all_dossiers_counts)['suivis']).to eq(0) } it { expect(assigns(:all_dossiers_counts)['suivis']).to eq(0) }
it { expect(assigns(:all_dossiers_counts)['traités']).to eq(0) } it { expect(assigns(:all_dossiers_counts)['traités']).to eq(0) }
it { expect(assigns(:all_dossiers_counts)['dossiers']).to eq(0) } it { expect(assigns(:all_dossiers_counts)['dossiers']).to eq(0) }
it { expect(assigns(:all_dossiers_counts)['archivés']).to eq(0) } it { expect(assigns(:all_dossiers_counts)['archivés']).to eq(0) }
it { expect(assigns(:all_dossiers_counts)['expirant']).to eq(0) }
end end
context "with not draft state on multiple procedures" do context "with not draft state on multiple procedures" do
let(:procedure2) { create(:procedure, :published) } let(:procedure2) { create(:procedure, :published, :expirable) }
let(:state) { Dossier.states.fetch(:en_construction) } let(:state) { Dossier.states.fetch(:en_construction) }
before do before do
create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_construction)) create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_construction))
create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_construction), hidden_by_user_at: 1.hour.ago) create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_construction), hidden_by_user_at: 1.hour.ago)
create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_instruction)) create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_instruction))
create(:dossier, procedure: procedure, state: Dossier.states.fetch(:sans_suite), archived: true) create(:dossier, procedure: procedure, state: Dossier.states.fetch(:sans_suite), archived: true)
create(:dossier, procedure: procedure, state: Dossier.states.fetch(:sans_suite), archived: true,
hidden_by_administration_at: 1.day.ago)
instructeur.groupe_instructeurs << procedure2.defaut_groupe_instructeur instructeur.groupe_instructeurs << procedure2.defaut_groupe_instructeur
create(:dossier, :followed, procedure: procedure2, state: Dossier.states.fetch(:en_construction)) create(:dossier, :followed, procedure: procedure2, state: Dossier.states.fetch(:en_construction))
create(:dossier, procedure: procedure2, state: Dossier.states.fetch(:accepte)) create(:dossier, procedure: procedure2, state: Dossier.states.fetch(:accepte))
instructeur.followed_dossiers << create(:dossier, procedure: procedure2, state: Dossier.states.fetch(:en_instruction)) instructeur.followed_dossiers << create(:dossier, procedure: procedure2, state: Dossier.states.fetch(:en_instruction))
create(:dossier, procedure: procedure,
state: Dossier.states.fetch(:sans_suite),
processed_at: 8.months.ago) # counted as expirable
create(:dossier, procedure: procedure,
state: Dossier.states.fetch(:sans_suite),
processed_at: 8.months.ago,
hidden_by_administration_at: 1.day.ago) # not counted as expirable since its removed by instructeur
create(:dossier, procedure: procedure,
state: Dossier.states.fetch(:sans_suite),
processed_at: 8.months.ago,
hidden_by_user_at: 1.day.ago) # counted as expirable because even if user remove it, instructeur see it
subject subject
end end
it { expect(assigns(:dossiers_count_per_procedure)[procedure.id]).to eq(3) } it { expect(assigns(:dossiers_count_per_procedure)[procedure.id]).to eq(5) }
it { expect(assigns(:dossiers_a_suivre_count_per_procedure)[procedure.id]).to eq(3) } it { expect(assigns(:dossiers_a_suivre_count_per_procedure)[procedure.id]).to eq(3) }
it { expect(assigns(:followed_dossiers_count_per_procedure)[procedure.id]).to eq(nil) } it { expect(assigns(:followed_dossiers_count_per_procedure)[procedure.id]).to eq(nil) }
it { expect(assigns(:dossiers_archived_count_per_procedure)[procedure.id]).to eq(1) } it { expect(assigns(:dossiers_archived_count_per_procedure)[procedure.id]).to eq(1) }
it { expect(assigns(:dossiers_termines_count_per_procedure)[procedure.id]).to eq(nil) } it { expect(assigns(:dossiers_termines_count_per_procedure)[procedure.id]).to eq(2) }
it { expect(assigns(:dossiers_expirant_count_per_procedure)[procedure.id]).to eq(2) }
it { expect(assigns(:dossiers_count_per_procedure)[procedure2.id]).to eq(3) } it { expect(assigns(:dossiers_count_per_procedure)[procedure2.id]).to eq(3) }
it { expect(assigns(:dossiers_a_suivre_count_per_procedure)[procedure2.id]).to eq(nil) } it { expect(assigns(:dossiers_a_suivre_count_per_procedure)[procedure2.id]).to eq(nil) }
@ -133,9 +150,10 @@ describe Instructeurs::ProceduresController, type: :controller do
it { expect(assigns(:all_dossiers_counts)['à suivre']).to eq(3 + 0) } it { expect(assigns(:all_dossiers_counts)['à suivre']).to eq(3 + 0) }
it { expect(assigns(:all_dossiers_counts)['suivis']).to eq(0 + 1) } it { expect(assigns(:all_dossiers_counts)['suivis']).to eq(0 + 1) }
it { expect(assigns(:all_dossiers_counts)['traités']).to eq(0 + 1) } it { expect(assigns(:all_dossiers_counts)['traités']).to eq(2 + 1) }
it { expect(assigns(:all_dossiers_counts)['dossiers']).to eq(3 + 3) } it { expect(assigns(:all_dossiers_counts)['dossiers']).to eq(5 + 3) }
it { expect(assigns(:all_dossiers_counts)['archivés']).to eq(1 + 0) } it { expect(assigns(:all_dossiers_counts)['archivés']).to eq(1 + 0) }
it { expect(assigns(:all_dossiers_counts)['expirant']).to eq(2 + 0) }
end end
end end
@ -205,7 +223,7 @@ describe Instructeurs::ProceduresController, type: :controller do
describe "#show" do describe "#show" do
let(:instructeur) { create(:instructeur) } let(:instructeur) { create(:instructeur) }
let!(:procedure) { create(:procedure, instructeurs: [instructeur]) } let!(:procedure) { create(:procedure, :expirable, instructeurs: [instructeur]) }
let!(:gi_2) { procedure.groupe_instructeurs.create(label: '2') } let!(:gi_2) { procedure.groupe_instructeurs.create(label: '2') }
let!(:gi_3) { procedure.groupe_instructeurs.create(label: '3') } let!(:gi_3) { procedure.groupe_instructeurs.create(label: '3') }
let(:statut) { nil } let(:statut) { nil }
@ -323,6 +341,7 @@ describe Instructeurs::ProceduresController, type: :controller do
context 'with an archived dossier' do context 'with an archived dossier' do
let!(:archived_dossier) { create(:dossier, :en_instruction, procedure: procedure, archived: true) } let!(:archived_dossier) { create(:dossier, :en_instruction, procedure: procedure, archived: true) }
let!(:archived_dossier_deleted) { create(:dossier, :en_instruction, procedure: procedure, archived: true, hidden_by_administration_at: 2.days.ago) }
before { subject } before { subject }
@ -342,6 +361,16 @@ describe Instructeurs::ProceduresController, type: :controller do
end end
end end
context 'with an expirants dossier' do
let!(:expiring_dossier_termine_deleted) { create(:dossier, :accepte, procedure: procedure, processed_at: 175.days.ago, hidden_by_administration_at: 2.days.ago) }
let!(:expiring_dossier_termine) { create(:dossier, :accepte, procedure: procedure, processed_at: 175.days.ago) }
let!(:expiring_dossier_en_construction) { create(:dossier, :en_construction, procedure: procedure, en_construction_at: 175.days.ago) }
before { subject }
it { expect(assigns(:expirant_dossiers)).to match_array([expiring_dossier_termine, expiring_dossier_en_construction]) }
end
describe 'statut' do describe 'statut' do
let!(:a_suivre__dossier) { Timecop.freeze(1.day.ago) { create(:dossier, :en_instruction, procedure: procedure) } } let!(:a_suivre__dossier) { Timecop.freeze(1.day.ago) { create(:dossier, :en_instruction, procedure: procedure) } }
let!(:new_followed_dossier) { Timecop.freeze(2.days.ago) { create(:dossier, :en_instruction, procedure: procedure) } } let!(:new_followed_dossier) { Timecop.freeze(2.days.ago) { create(:dossier, :en_instruction, procedure: procedure) } }

View file

@ -1069,15 +1069,6 @@ describe Users::DossiersController, type: :controller do
it_behaves_like "the dossier can not be deleted" it_behaves_like "the dossier can not be deleted"
it { is_expected.to redirect_to(root_path) } it { is_expected.to redirect_to(root_path) }
end 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 end
describe '#restore' do describe '#restore' do

View file

@ -6,5 +6,17 @@ FactoryBot.define do
deleted_at { Time.zone.now } deleted_at { Time.zone.now }
association :procedure, :published association :procedure, :published
transient do
dossier { nil }
end
after(:build) do |deleted_dossier, evaluator|
if evaluator.dossier
deleted_dossier.dossier_id = evaluator.dossier.id
deleted_dossier.state = evaluator.dossier.state
deleted_dossier.procedure = evaluator.dossier.procedure
end
end
end end
end end

View file

@ -81,7 +81,9 @@ FactoryBot.define do
trait :with_logo do trait :with_logo do
logo { Rack::Test::UploadedFile.new('spec/fixtures/files/logo_test_procedure.png', 'image/png') } logo { Rack::Test::UploadedFile.new('spec/fixtures/files/logo_test_procedure.png', 'image/png') }
end end
trait :expirable do
procedure_expires_when_termine_enabled { true }
end
trait :with_path do trait :with_path do
path { generate(:published_path) } path { generate(:published_path) }
end end

View file

@ -7,7 +7,7 @@ RSpec.describe Cron::DiscardedDossiersDeletionJob, type: :job do
# hack to add passer_en_instruction and supprimer to dossier.dossier_operation_logs # hack to add passer_en_instruction and supprimer to dossier.dossier_operation_logs
dossier.send(:log_dossier_operation, instructeur, :passer_en_instruction, dossier) dossier.send(:log_dossier_operation, instructeur, :passer_en_instruction, dossier)
dossier.send(:log_dossier_operation, instructeur, :supprimer, dossier) dossier.send(:log_dossier_operation, instructeur, :supprimer, dossier)
dossier.update_column(:hidden_at, hidden_at) dossier.update_columns(hidden_by_user_at: hidden_at, hidden_by_administration_at: hidden_at)
dossier.update_column(:hidden_by_reason, "user_request") dossier.update_column(:hidden_by_reason, "user_request")
Cron::DiscardedDossiersDeletionJob.perform_now Cron::DiscardedDossiersDeletionJob.perform_now

View file

@ -2,7 +2,7 @@ RSpec.describe AvisMailer, type: :mailer do
describe '.avis_invitation' do describe '.avis_invitation' do
let(:claimant) { create(:instructeur) } let(:claimant) { create(:instructeur) }
let(:expert) { create(:expert) } let(:expert) { create(:expert) }
let(:dossier) { create(:dossier) } let(:dossier) { create(:dossier, :en_construction) }
let(:experts_procedure) { create(:experts_procedure, expert: expert, procedure: dossier.procedure) } let(:experts_procedure) { create(:experts_procedure, expert: expert, procedure: dossier.procedure) }
let(:avis) { create(:avis, dossier: dossier, claimant: claimant, experts_procedure: experts_procedure, introduction: 'intro') } let(:avis) { create(:avis, dossier: dossier, claimant: claimant, experts_procedure: experts_procedure, introduction: 'intro') }
@ -18,7 +18,7 @@ RSpec.describe AvisMailer, type: :mailer do
end end
context 'when the dossier has been deleted before the avis was sent' do context 'when the dossier has been deleted before the avis was sent' do
before { dossier.update(hidden_at: Time.zone.now) } before { dossier.update(hidden_by_user_at: 1.hour.ago) }
it 'doesnt send the email' do it 'doesnt send the email' do
expect(subject.body).to be_blank expect(subject.body).to be_blank

View file

@ -117,9 +117,10 @@ RSpec.describe DossierMailer, type: :mailer do
end end
describe '.notify_automatic_deletion_to_user' do describe '.notify_automatic_deletion_to_user' do
let(:deleted_dossier) { create(:deleted_dossier, dossier: dossier, reason: :expired) }
describe 'en_construction' do describe 'en_construction' do
let(:dossier) { create(:dossier, :en_construction) } let(:dossier) { create(:dossier, :en_construction) }
let(:deleted_dossier) { DeletedDossier.create_from_dossier(dossier, :expired) }
subject { described_class.notify_automatic_deletion_to_user([deleted_dossier], dossier.user.email) } subject { described_class.notify_automatic_deletion_to_user([deleted_dossier], dossier.user.email) }
@ -132,7 +133,6 @@ RSpec.describe DossierMailer, type: :mailer do
describe 'termine' do describe 'termine' do
let(:dossier) { create(:dossier, :accepte) } let(:dossier) { create(:dossier, :accepte) }
let(:deleted_dossier) { DeletedDossier.create_from_dossier(dossier, :expired) }
subject { described_class.notify_automatic_deletion_to_user([deleted_dossier], dossier.user.email) } subject { described_class.notify_automatic_deletion_to_user([deleted_dossier], dossier.user.email) }
@ -145,8 +145,8 @@ RSpec.describe DossierMailer, type: :mailer do
end end
describe '.notify_automatic_deletion_to_administration' do describe '.notify_automatic_deletion_to_administration' do
let(:dossier) { create(:dossier) } let(:dossier) { create(:dossier, :en_construction) }
let(:deleted_dossier) { DeletedDossier.create_from_dossier(dossier, :expired) } let(:deleted_dossier) { create(:deleted_dossier, dossier: dossier, reason: :expired) }
subject { described_class.notify_automatic_deletion_to_administration([deleted_dossier], dossier.user.email) } subject { described_class.notify_automatic_deletion_to_administration([deleted_dossier], dossier.user.email) }

View file

@ -6,7 +6,6 @@ describe Dossier do
describe 'scopes' do describe 'scopes' do
describe '.default_scope' do describe '.default_scope' do
let!(:dossier) { create(:dossier) } let!(:dossier) { create(:dossier) }
let!(:discarded_dossier) { create(:dossier, :discarded) }
subject { Dossier.all } subject { Dossier.all }
@ -791,7 +790,7 @@ describe Dossier do
end end
end end
describe "#discard_and_keep_track!" do describe "#hide_and_keep_track!" do
let(:dossier) { create(:dossier, :en_construction) } let(:dossier) { create(:dossier, :en_construction) }
let(:user) { dossier.user } let(:user) { dossier.user }
let(:last_operation) { dossier.dossier_operation_logs.last } let(:last_operation) { dossier.dossier_operation_logs.last }
@ -801,17 +800,17 @@ describe Dossier do
allow(DossierMailer).to receive(:notify_deletion_to_administration).and_return(double(deliver_later: nil)) allow(DossierMailer).to receive(:notify_deletion_to_administration).and_return(double(deliver_later: nil))
end end
subject! { dossier.discard_and_keep_track!(user, reason) } subject! { dossier.hide_and_keep_track!(user, reason) }
context 'brouillon' do context 'brouillon' do
let(:dossier) { create(:dossier) } let(:dossier) { create(:dossier) }
it 'hides the dossier' do it 'hide the dossier' do
expect(dossier.discarded?).to be_truthy expect(dossier.reload.hidden_by_user_at).to be_present
end end
it 'do not records the operation in the log' do it 'does not records operation in the log' do
expect(last_operation).to be_nil expect(dossier.reload.dossier_operation_logs.last).to eq(nil)
end end
end end
@ -842,30 +841,16 @@ describe Dossier do
end end
end end
context 'with reason: manager_request' do
let(:user) { dossier.procedure.administrateurs.first }
let(:reason) { :manager_request }
it 'hides the dossier' do
expect(dossier.discarded?).to be_truthy
end
it 'records the operation in the log' do
expect(last_operation.operation).to eq("supprimer")
expect(last_operation.automatic_operation?).to be_falsey
end
end
context 'with reason: user_removed' do context 'with reason: user_removed' do
let(:reason) { :user_removed } let(:reason) { :user_removed }
it 'does not discard the dossier' do
expect(dossier.discarded?).to be_falsy
end
it 'hide the dossier' do it 'hide the dossier' do
expect(dossier.hidden_by_user_at).to be_present expect(dossier.hidden_by_user_at).to be_present
end end
it 'write the good reason to hidden_by_reason' do
expect(dossier.hidden_by_reason).to eq("user_removed")
end
end end
end end
@ -876,10 +861,6 @@ describe Dossier do
it 'affect the right deletion reason to the dossier' do it 'affect the right deletion reason to the dossier' do
expect(dossier.hidden_by_reason).to eq("user_request") expect(dossier.hidden_by_reason).to eq("user_request")
end end
it 'discard the dossier' do
expect(dossier.discarded?).to be_truthy
end
end end
end end
@ -1308,7 +1289,7 @@ describe Dossier do
end end
end end
describe 'discarded_brouillon_expired and discarded_en_construction_expired' do describe 'brouillon_expired and en_construction_expired' do
let(:administrateur) { create(:administrateur) } let(:administrateur) { create(:administrateur) }
let(:user) { administrateur.user } let(:user) { administrateur.user }
let(:reason) { DeletedDossier.reasons.fetch(:user_request) } let(:reason) { DeletedDossier.reasons.fetch(:user_request) }
@ -1316,24 +1297,25 @@ describe Dossier do
before do before do
create(:dossier, user: user) create(:dossier, user: user)
create(:dossier, :en_construction, user: user) create(:dossier, :en_construction, user: user)
create(:dossier, user: user).discard_and_keep_track!(user, reason) create(:dossier, user: user).hide_and_keep_track!(user, reason)
create(:dossier, :en_construction, user: user).discard_and_keep_track!(user, reason) create(:dossier, :en_construction, user: user).hide_and_keep_track!(user, reason)
Timecop.travel(2.months.ago) do Timecop.travel(2.months.ago) do
create(:dossier, user: user).discard_and_keep_track!(user, reason) create(:dossier, user: user).hide_and_keep_track!(user, reason)
create(:dossier, :en_construction, user: user).discard_and_keep_track!(user, reason) create(:dossier, :en_construction, user: user).hide_and_keep_track!(user, reason)
create(:dossier, user: user).procedure.discard_and_keep_track!(administrateur) create(:dossier, user: user).procedure.discard_and_keep_track!(administrateur)
create(:dossier, :en_construction, user: user).procedure.discard_and_keep_track!(administrateur) create(:dossier, :en_construction, user: user).procedure.discard_and_keep_track!(administrateur)
end end
Timecop.travel(1.week.ago) do Timecop.travel(1.week.ago) do
create(:dossier, user: user).discard_and_keep_track!(user, reason) create(:dossier, user: user).hide_and_keep_track!(user, reason)
create(:dossier, :en_construction, user: user).discard_and_keep_track!(user, reason) create(:dossier, :en_construction, user: user).hide_and_keep_track!(user, reason)
end end
end end
it { expect(Dossier.discarded_brouillon_expired.count).to eq(2) } it { expect(Dossier.en_brouillon_expired_to_delete.count).to eq(2) }
it { expect(Dossier.discarded_en_construction_expired.count).to eq(0) } it { expect(Dossier.en_construction_expired_to_delete.count).to eq(2) }
end end
describe "discarded procedure dossier should be able to access it's procedure" do describe "discarded procedure dossier should be able to access it's procedure" do
@ -1529,42 +1511,6 @@ describe Dossier do
expect(dossier.destroy).to be_truthy expect(dossier.destroy).to be_truthy
expect(transfer.reload).not_to be_nil expect(transfer.reload).not_to be_nil
end end
context 'discarded' do
context 'en_construction' do
let(:dossier) { create(:dossier, :en_construction) }
before do
create(:avis, dossier: dossier)
Timecop.travel(2.weeks.ago) do
dossier.discard!
end
dossier.reload
end
it "can destroy dossier with avis" do
Avis.discarded_en_construction_expired.destroy_all
expect(dossier.destroy).to be_truthy
end
end
context 'termine' do
let(:dossier) { create(:dossier, :accepte) }
before do
create(:avis, dossier: dossier)
Timecop.travel(2.weeks.ago) do
dossier.discard!
end
dossier.reload
end
it "can destroy dossier with avis" do
Avis.discarded_termine_expired.destroy_all
expect(dossier.destroy).to be_truthy
end
end
end
end end
describe "#spreadsheet_columns" do describe "#spreadsheet_columns" do

View file

@ -40,8 +40,8 @@ RSpec.describe DossierTransfer, type: :model do
it { expect(DossierTransfer.with_dossiers.count).to eq(1) } it { expect(DossierTransfer.with_dossiers.count).to eq(1) }
context "when dossier discarded" do context "when dossier deleted" do
before { dossier.discard! } before { dossier.update(hidden_by_user_at: 1.hour.ago) }
it { expect(DossierTransfer.with_dossiers.count).to eq(0) } it { expect(DossierTransfer.with_dossiers.count).to eq(0) }
end end
@ -51,10 +51,10 @@ RSpec.describe DossierTransfer, type: :model do
describe '#destroy_and_nullify' do describe '#destroy_and_nullify' do
let(:transfer) { create(:dossier_transfer) } let(:transfer) { create(:dossier_transfer) }
let(:dossier) { create(:dossier, user: user, transfer: transfer) } let(:dossier) { create(:dossier, user: user, transfer: transfer) }
let(:discarded_dossier) { create(:dossier, user: user, transfer: dossier.transfer) } let(:deleted_dossier) { create(:dossier, user: user, transfer: dossier.transfer) }
before do before do
discarded_dossier.discard! deleted_dossier.update(hidden_by_user_at: 1.hour.ago)
end end
it 'nullify transfer relationship on dossier' do it 'nullify transfer relationship on dossier' do
@ -67,10 +67,10 @@ RSpec.describe DossierTransfer, type: :model do
describe '#destroy_stale' do describe '#destroy_stale' do
let(:transfer) { create(:dossier_transfer, created_at: 1.month.ago) } let(:transfer) { create(:dossier_transfer, created_at: 1.month.ago) }
let(:dossier) { create(:dossier, user: user, transfer: transfer) } let(:dossier) { create(:dossier, user: user, transfer: transfer) }
let(:discarded_dossier) { create(:dossier, user: user, transfer: dossier.transfer) } let(:deleted_dossier) { create(:dossier, user: user, transfer: dossier.transfer) }
before do before do
discarded_dossier.discard! deleted_dossier.update(hidden_by_user_at: 1.hour.ago)
end end
it 'nullify the transfer on discarded dossier' do it 'nullify the transfer on discarded dossier' do

View file

@ -721,6 +721,7 @@ describe Instructeur, type: :model do
end end
context 'with an expirants dossier' do context 'with an expirants dossier' do
let!(:expiring_dossier_termine_deleted) { create(:dossier, :accepte, procedure: procedure, processed_at: 175.days.ago, hidden_by_administration_at: 2.days.ago) }
let!(:expiring_dossier_termine) { create(:dossier, :accepte, procedure: procedure, processed_at: 175.days.ago) } let!(:expiring_dossier_termine) { create(:dossier, :accepte, procedure: procedure, processed_at: 175.days.ago) }
let!(:expiring_dossier_en_construction) { create(:dossier, :en_construction, en_construction_at: 175.days.ago, procedure: procedure) } let!(:expiring_dossier_en_construction) { create(:dossier, :en_construction, en_construction_at: 175.days.ago, procedure: procedure) }
before { subject } before { subject }

View file

@ -56,18 +56,18 @@ describe Invite do
end end
describe "#default_scope" do describe "#default_scope" do
let(:dossier) { create(:dossier, hidden_at: hidden_at) } let!(:dossier) { create(:dossier, hidden_by_user_at: hidden_by_user_at) }
let!(:invite) { create(:invite, email: "email@totor.com", dossier: dossier) } let!(:invite) { create(:invite, email: "email@totor.com", dossier: dossier) }
context "when dossier is not discarded" do context "when dossier is not discarded" do
let(:hidden_at) { nil } let(:hidden_by_user_at) { nil }
it { expect(Invite.count).to eq(1) } it { expect(Invite.count).to eq(1) }
it { expect(Invite.all).to include(invite) } it { expect(Invite.all).to include(invite) }
end end
context "when dossier is discarded" do context "when dossier is discarded" do
let(:hidden_at) { 1.day.ago } let(:hidden_by_user_at) { 1.hour.ago }
it { expect(Invite.count).to eq(0) } it { expect(Invite.count).to eq(0) }
end end

View file

@ -310,21 +310,21 @@ describe User, type: :model do
it "keep track of dossiers and delete user" do it "keep track of dossiers and delete user" do
user.delete_and_keep_track_dossiers(super_admin) user.delete_and_keep_track_dossiers(super_admin)
expect(DeletedDossier.find_by(dossier_id: dossier_en_construction)).to be_nil expect(DeletedDossier.find_by(dossier_id: dossier_en_construction)).to be_present
expect(DeletedDossier.find_by(dossier_id: dossier_brouillon)).to be_nil expect(DeletedDossier.find_by(dossier_id: dossier_brouillon)).to be_nil
expect(User.find_by(id: user.id)).to be_nil expect(User.find_by(id: user.id)).to be_nil
end end
end end
context 'with a discarded dossier' do context 'with a deleted dossier' do
let(:dossier_to_discard) { create(:dossier, :en_construction, user: user) } let(:dossier_to_delete) { create(:dossier, :en_construction, user: user) }
let!(:dossier_from_another_user) { create(:dossier, :en_construction, user: create(:user)) } let!(:dossier_from_another_user) { create(:dossier, :en_construction, user: create(:user)) }
it "keep track of dossiers and delete user" do it "keep track of dossiers and delete user" do
dossier_to_discard.discard_and_keep_track!(super_admin, :user_request) dossier_to_delete.hide_and_keep_track!(user, :user_request)
user.delete_and_keep_track_dossiers(super_admin) user.delete_and_keep_track_dossiers(super_admin)
expect(DeletedDossier.find_by(dossier_id: dossier_en_construction)).to be_nil expect(DeletedDossier.find_by(dossier_id: dossier_en_construction)).to be_present
expect(DeletedDossier.find_by(dossier_id: dossier_brouillon)).to be_nil 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(Dossier.find_by(id: dossier_from_another_user.id)).to be_present
expect(User.find_by(id: user.id)).to be_nil expect(User.find_by(id: user.id)).to be_nil
@ -426,14 +426,14 @@ describe User, type: :model do
context 'and the old account has some stuff' do context 'and the old account has some stuff' do
let!(:dossier) { create(:dossier, user: old_user) } let!(:dossier) { create(:dossier, user: old_user) }
let!(:hidden_dossier) { create(:dossier, user: old_user, hidden_at: Time.zone.now) } let!(:hidden_dossier) { create(:dossier, user: old_user, hidden_by_user_at: 1.hour.ago) }
let!(:invite) { create(:invite, user: old_user) } let!(:invite) { create(:invite, user: old_user) }
let!(:merge_log) { MergeLog.create(user: old_user, from_user_id: 1, from_user_email: 'a') } let!(:merge_log) { MergeLog.create(user: old_user, from_user_id: 1, from_user_email: 'a') }
it 'transfers the dossier' do it 'transfers the dossier' do
subject subject
expect(targeted_user.dossiers.with_discarded).to contain_exactly(dossier, hidden_dossier) expect(targeted_user.dossiers).to contain_exactly(dossier, hidden_dossier)
expect(targeted_user.invites).to match([invite]) expect(targeted_user.invites).to match([invite])
expect(targeted_user.merge_logs.first).to eq(merge_log) expect(targeted_user.merge_logs.first).to eq(merge_log)

View file

@ -1,16 +1,10 @@
describe 'instructeurs/procedures/_download_dossiers.html.haml', type: :view do describe 'instructeurs/procedures/_download_dossiers.html.haml', type: :view do
let(:current_instructeur) { create(:instructeur) } let(:current_instructeur) { create(:instructeur) }
let(:procedure) { create(:procedure) } let(:procedure) { create(:procedure) }
let(:dossier_count) { 0 }
subject { render 'instructeurs/procedures/download_dossiers.html.haml', procedure: procedure, dossier_count: dossier_count, exports: {} } subject { render 'instructeurs/procedures/download_dossiers.html.haml', procedure: procedure, exports: {} }
context "when procedure has 0 dossier" do
it { is_expected.not_to include("Télécharger tous les dossiers") }
end
context "when procedure has at least 1 dossier" do context "when procedure has at least 1 dossier" do
let(:dossier_count) { 1 }
it { is_expected.to include("Télécharger tous les dossiers") } it { is_expected.to include("Télécharger tous les dossiers") }
context "With zip archive enabled" do context "With zip archive enabled" do