commit
7c75a25552
56 changed files with 581 additions and 402 deletions
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -35,3 +35,10 @@ yarn-debug.log*
|
||||||
# Local Netlify folder
|
# Local Netlify folder
|
||||||
.netlify
|
.netlify
|
||||||
|
|
||||||
|
# Custom locales
|
||||||
|
/config/custom_locales/*
|
||||||
|
!/config/custom_locales/.keep
|
||||||
|
|
||||||
|
# Custom views
|
||||||
|
/app/custom_views/*
|
||||||
|
!/app/custom_views/.keep
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ class ApplicationController < ActionController::Base
|
||||||
before_action :set_active_storage_host
|
before_action :set_active_storage_host
|
||||||
before_action :setup_javascript_settings
|
before_action :setup_javascript_settings
|
||||||
before_action :setup_tracking
|
before_action :setup_tracking
|
||||||
|
before_action :set_customizable_view_path
|
||||||
|
|
||||||
around_action :switch_locale
|
around_action :switch_locale
|
||||||
|
|
||||||
|
@ -359,4 +360,8 @@ class ApplicationController < ActionController::Base
|
||||||
http_accept_language.compatible_language_from(I18n.available_locales)
|
http_accept_language.compatible_language_from(I18n.available_locales)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_customizable_view_path
|
||||||
|
prepend_view_path "app/custom_views"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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!
|
||||||
|
|
0
app/custom_views/.keep
Normal file
0
app/custom_views/.keep
Normal 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
|
||||||
|
|
|
@ -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 })
|
||||||
|
|
|
@ -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})"
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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([
|
||||||
|
|
|
@ -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?
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
|
||||||
|
|
|
@ -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| %>
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
#footer
|
#footer
|
||||||
%p{ class: "copyright col-md-push-#{12-main_container_size} col-md-#{main_container_size} col-lg-push-#{12-main_container_size} col-lg-#{main_container_size} text-muted small" }
|
%p{ class: "copyright col-md-push-#{12-main_container_size} col-md-#{main_container_size} col-lg-push-#{12-main_container_size} col-lg-#{main_container_size} text-muted small" }
|
||||||
= link_to PROVIDER_NAME, PROVIDER_URL
|
= link_to t("links.provider.name"), t("links.provider.url")
|
||||||
= Time.zone.now.year
|
= Time.zone.now.year
|
||||||
\-
|
\-
|
||||||
= link_to 'Nouveautés', DOC_NOUVEAUTES_URL, target: '_blank'
|
= link_to t("links.footer.doc_nouveautes.label"), t("links.footer.doc_nouveautes.url"), title: t("links.footer.doc_nouveautes.title"), target: '_blank'
|
||||||
\-
|
\-
|
||||||
= link_to 'Statistiques', stats_path
|
= link_to t("links.footer.stats.label"), stats_path
|
||||||
\-
|
\-
|
||||||
= link_to 'CGU / Mentions légales', CGU_URL, target: '_blank'
|
= link_to "#{t("links.footer.cgu.label")} / #{t("links.footer.mentions_legales.label")}", t("links.footer.cgu.url"), title: t("links.footer.cgu.title"), target: '_blank'
|
||||||
\-
|
\-
|
||||||
= link_to 'Documentation', DOC_URL, target: '_blank'
|
= link_to t("links.footer.doc.label"), t("links.footer.doc.url"), title: t("links.footer.doc.title"), target: '_blank'
|
||||||
\-
|
\-
|
||||||
= link_to 'FAQ', FAQ_ADMIN_URL, target: '_blank'
|
= link_to t("links.footer.faq_admin.label"), t("links.footer.faq_admin.url"), title: t("links.footer.faq_admin.title"), target: '_blank'
|
||||||
\-
|
\-
|
||||||
= link_to 'Inscription ateliers en ligne', WEBINAIRE_URL, target: '_blank'
|
= link_to t("links.footer.webinaire.label"), t("links.footer.webinaire.url"), title: t("links.footer.webinaire.title"), target: '_blank'
|
||||||
|
|
|
@ -145,7 +145,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td style="word-wrap:break-word;font-size:0px;padding:0px 20px 0px 20px;padding-top:0px;padding-bottom:0px;" align="center">
|
<td style="word-wrap:break-word;font-size:0px;padding:0px 20px 0px 20px;padding-top:0px;padding-bottom:0px;" align="center">
|
||||||
<div class="" style="cursor:auto;color:#55575d;font-family:Helvetica, Arial, sans-serif;font-size:11px;line-height:22px;text-align:center;">
|
<div class="" style="cursor:auto;color:#55575d;font-family:Helvetica, Arial, sans-serif;font-size:11px;line-height:22px;text-align:center;">
|
||||||
<%= "#{APPLICATION_NAME}" %> est un service fourni par <%= PROVIDED_BY %>
|
<%= "#{APPLICATION_NAME}" %> est un service fourni par <%= t("links.provider.provided_by") %>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -6,44 +6,41 @@
|
||||||
%ul.footer-logos
|
%ul.footer-logos
|
||||||
%li.footer-text
|
%li.footer-text
|
||||||
Un service fourni par
|
Un service fourni par
|
||||||
= link_to PROVIDED_BY, PROVIDER_URL, title: PROVIDER_TITLE
|
= link_to t("links.provider.provided_by"), t("links.provider.url"), title: t("links.provider.title")
|
||||||
%br
|
%br
|
||||||
%li
|
%li
|
||||||
= link_to PROVIDER_URL, title: PROVIDER_NAME, 'aria-label': PROVIDER_NAME do
|
= link_to t("links.provider.url"), title: t("links.provider.name"), 'aria-label': t("links.provider.name") do
|
||||||
%span.footer-logo{ role: 'img', 'aria-label': PROVIDER_LOGO_ALT }
|
%span.footer-logo{ role: 'img', 'aria-label': t("links.provider.logo.alt") }
|
||||||
= image_tag PROVIDER_LOGO_SRC, alt: PROVIDER_LOGO_ALT, width: PROVIDER_LOGO_WIDTH, height: PROVIDER_LOGO_HEIGHT, loading: 'lazy'
|
= image_tag t("links.provider.logo.src"), alt: t("links.provider.logo.alt"), width: t("links.provider.logo.width"), height: t("links.provider.logo.height"), loading: 'lazy'
|
||||||
= link_to "https://beta.gouv.fr", title: "le site de Beta.gouv.fr", 'aria-label': 'beta.gouv.fr' do
|
= link_to t("links.footer.betagouv.url"), title: t("links.footer.betagouv.title"), 'aria-label': t("links.footer.betagouv.label") do
|
||||||
%span.footer-logo.footer-logo-beta-gouv-fr{ role: 'img', 'aria-label': 'Logo beta.gouv.fr' }
|
%span.footer-logo.footer-logo-beta-gouv-fr{ role: 'img', 'aria-label': "Logo #{t('links.footer.betagouv.label')}" }
|
||||||
|
|
||||||
%li.footer-column
|
%li.footer-column
|
||||||
%ul.footer-links
|
%ul.footer-links
|
||||||
%li.footer-link
|
%li.footer-link
|
||||||
= link_to "Newsletter", "https://my.sendinblue.com/users/subscribe/js_id/3s2q1/id/1", :title => "Notre newsletter", :class => "footer-link", :target => "_blank", rel: "noopener"
|
= link_to t("links.footer.newsletter.label"), t("links.footer.newsletter.url"), title: t("links.footer.newsletter.title"), class: "footer-link", target: "_blank", rel: "noopener"
|
||||||
%li.footer-link
|
%li.footer-link
|
||||||
-# haml-lint:disable ApplicationNameLinter
|
= link_to t("links.footer.releases.label"), t("links.footer.releases.url"), title: t("links.footer.releases.title"), class: "footer-link"
|
||||||
= link_to "Nouveautés", "https://github.com/betagouv/demarches-simplifiees.fr/releases", :class => "footer-link", :title => "Nos nouveautés"
|
|
||||||
-# haml-lint:enable ApplicationNameLinter
|
|
||||||
%li.footer-link
|
%li.footer-link
|
||||||
= link_to "Statistiques", stats_path, :class => "footer-link"
|
= link_to t("links.footer.stats.label"), stats_path, title: t("links.footer.stats.title"), class: "footer-link"
|
||||||
%li.footer-link
|
%li.footer-link
|
||||||
= link_to "CGU", CGU_URL, :class => "footer-link", :target => "_blank", rel: "noopener noreferrer"
|
= link_to t("links.footer.cgu.label"), t("links.footer.cgu.url"), title: t("links.footer.cgu.title"), class: "footer-link", target: "_blank", rel: "noopener noreferrer"
|
||||||
%li.footer-link
|
%li.footer-link
|
||||||
= link_to "Mentions légales", MENTIONS_LEGALES_URL, :class => "footer-link", :target => "_blank", rel: "noopener noreferrer"
|
= link_to t("links.footer.mentions_legales.label"), t("links.footer.mentions_legales.url"), title: t("links.footer.mentions_legales.title"), class: "footer-link", target: "_blank", rel: "noopener noreferrer"
|
||||||
%li.footer-link
|
%li.footer-link
|
||||||
= link_to "Suivi d’audience et vie privée", suivi_path, :class => "footer-link"
|
= link_to t("links.footer.suivi.label"), suivi_path, title: t("links.footer.suivi.title"), class: "footer-link"
|
||||||
|
|
||||||
%li.footer-column
|
%li.footer-column
|
||||||
%ul.footer-links
|
%ul.footer-links
|
||||||
%li.footer-link
|
%li.footer-link
|
||||||
= contact_link "Contact", class: "footer-link"
|
= contact_link t("links.footer.contact.label"), title: t("links.footer.contact.title"), class: "footer-link"
|
||||||
%li.footer-link
|
%li.footer-link
|
||||||
= link_to "Documentation", DOC_URL, :class => "footer-link", :target => "_blank", rel: "noopener noreferrer"
|
= link_to t("links.footer.doc.label"), t("links.footer.doc.url"), title: t("links.footer.doc.title"), class: "footer-link", target: "_blank", rel: "noopener noreferrer"
|
||||||
%li.footer-link
|
%li.footer-link
|
||||||
= link_to "Documentation de l'API", API_DOC_URL, :class => "footer-link", :target => "_blank", rel: "noopener noreferrer"
|
= link_to t("links.footer.api_doc.label"), t("links.footer.api_doc.url"), title: t("links.footer.api_doc.title"), class: "footer-link", target: "_blank", rel: "noopener noreferrer"
|
||||||
%li.footer-link
|
%li.footer-link
|
||||||
= link_to "FAQ", FAQ_URL, :class => "footer-link", :target => "_blank", rel: "noopener noreferrer"
|
= link_to t("links.footer.faq.label"), t("links.footer.faq.url"), title: t("links.footer.faq.title"), class: "footer-link", target: "_blank", rel: "noopener noreferrer"
|
||||||
%li.footer-link
|
%li.footer-link
|
||||||
= link_to ACCESSIBILITE_URL, :class => "footer-link", :target => "_blank", rel: "noopener noreferrer" do
|
= link_to t("links.footer.accessibilite.label"), t("links.footer.accessibilite.url"), title: t("links.footer.accessibilite.title"), class: "footer-link", target: "_blank", rel: "noopener noreferrer"
|
||||||
Accessibilité : non conforme
|
|
||||||
%li.footer-link
|
%li.footer-link
|
||||||
= link_to "Disponibilité", STATUS_PAGE_URL, :class => "footer-link", :target => "_blank", rel: "noopener noreferrer"
|
= link_to t("links.footer.status_page.label"), t("links.footer.status_page.url"), title: t("links.footer.status_page.title"), class: "footer-link", target: "_blank", rel: "noopener noreferrer"
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
%ul.footer-row.footer-bottom-line.footer-site-links
|
%ul.footer-row.footer-bottom-line.footer-site-links
|
||||||
%li.footer-link-accessibilite>
|
%li.footer-link-accessibilite>
|
||||||
= link_to ACCESSIBILITE_URL, target: "_blank", rel: "noopener noreferrer" do
|
= link_to t("links.footer.accessibilite.label"), t("links.footer.accessibilite.url"), title: t("links.footer.accessibilite.title"), class: "footer-link", target: "_blank", rel: "noopener noreferrer"
|
||||||
Accessibilité : non conforme
|
%li.footer-link-cgu>
|
||||||
%li.footer-link-cgu>= link_to "CGU", CGU_URL, target: "_blank", rel: "noopener noreferrer"
|
= link_to t("links.footer.cgu.label"), t("links.footer.cgu.url"), title: t("links.footer.cgu.title"), class: "footer-link", target: "_blank", rel: "noopener noreferrer"
|
||||||
%li.footer-link-mentions-legales>= link_to "Mentions légales", MENTIONS_LEGALES_URL, target: "_blank", rel: "noopener noreferrer"
|
%li.footer-link-mentions-legales>
|
||||||
%li.footer-link-doc>= link_to 'Documentation', DOC_URL, target: "_blank", rel: "noopener noreferrer"
|
= link_to t("links.footer.mentions_legales.label"), t("links.footer.mentions_legales.url"), title: t("links.footer.mentions_legales.title"), class: "footer-link", target: "_blank", rel: "noopener noreferrer"
|
||||||
%li.footer-link-contact>= contact_link "Contact technique", dossier_id: dossier&.id
|
%li.footer-link-doc>
|
||||||
%li.footer-link-aide>= link_to 'Aide', FAQ_URL, target: "_blank", rel: "noopener noreferrer"
|
= link_to t("links.footer.doc.label"), t("links.footer.doc.url"), title: t("links.footer.doc.title"), class: "footer-link", target: "_blank", rel: "noopener noreferrer"
|
||||||
|
%li.footer-link-contact>
|
||||||
|
= contact_link t("links.footer.contact_technique.label"), dossier_id: dossier&.id, title: t("links.footer.contact_technique.title"), class: "footer-link"
|
||||||
|
%li.footer-link-aide>
|
||||||
|
= link_to t("links.footer.aide.label"), t("links.footer.aide.url"), title: t("links.footer.aide.title"), class: "footer-link", target: "_blank", rel: "noopener noreferrer"
|
||||||
|
|
|
@ -31,6 +31,7 @@ module TPS
|
||||||
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
|
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
|
||||||
config.i18n.default_locale = :fr
|
config.i18n.default_locale = :fr
|
||||||
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}')]
|
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}')]
|
||||||
|
config.i18n.load_path += Dir[Rails.root.join('config', 'custom_locales', '**', '*.{rb,yml}')]
|
||||||
config.i18n.available_locales = [:fr, :en]
|
config.i18n.available_locales = [:fr, :en]
|
||||||
config.i18n.fallbacks = [:fr]
|
config.i18n.fallbacks = [:fr]
|
||||||
|
|
||||||
|
|
0
config/custom_locales/.keep
Normal file
0
config/custom_locales/.keep
Normal file
|
@ -96,16 +96,6 @@ MATOMO_IFRAME_URL="https://matomo.example.org/index.php?module=CoreAdminHome&act
|
||||||
# An URI used to report requests breaking the Content Security Policy
|
# An URI used to report requests breaking the Content Security Policy
|
||||||
# CSP_REPORT_URI="https://myappname.report-uri.com/r/d/csp/reportOnly"
|
# CSP_REPORT_URI="https://myappname.report-uri.com/r/d/csp/reportOnly"
|
||||||
|
|
||||||
# Instance provider
|
|
||||||
# PROVIDED_BY="la DINUM"
|
|
||||||
# PROVIDER_NAME="DINUM"
|
|
||||||
# PROVIDER_TITLE="Direction Interministérielle au Numérique"
|
|
||||||
# PROVIDER_URL="https://numerique.gouv.fr/"
|
|
||||||
# PROVIDER_LOGO_SRC="footer/logo-dinum.png"
|
|
||||||
# PROVIDER_LOGO_ALT="Logo DINUM"
|
|
||||||
# PROVIDER_LOGO_HEIGHT="161"
|
|
||||||
# PROVIDER_LOGO_WIDTH="138"
|
|
||||||
|
|
||||||
# Landing page sections
|
# Landing page sections
|
||||||
# LANDING_TESTIMONIALS_ENABLED="enabled"
|
# LANDING_TESTIMONIALS_ENABLED="enabled"
|
||||||
# LANDING_USERS_ENABLED="enabled"
|
# LANDING_USERS_ENABLED="enabled"
|
||||||
|
|
|
@ -48,5 +48,4 @@ STATUS_PAGE_URL = ENV.fetch("STATUS_PAGE_URL", "https://status.demarches-simplif
|
||||||
DEMANDE_INSCRIPTION_ADMIN_PAGE_URL = ENV.fetch("DEMANDE_INSCRIPTION_ADMIN_PAGE_URL", "https://www.demarches-simplifiees.fr/commencer/demande-d-inscription-a-demarches-simplifiees")
|
DEMANDE_INSCRIPTION_ADMIN_PAGE_URL = ENV.fetch("DEMANDE_INSCRIPTION_ADMIN_PAGE_URL", "https://www.demarches-simplifiees.fr/commencer/demande-d-inscription-a-demarches-simplifiees")
|
||||||
MATOMO_IFRAME_URL = ENV.fetch("MATOMO_IFRAME_URL", "https://#{ENV.fetch('MATOMO_HOST', 'stats.data.gouv.fr')}/index.php?module=CoreAdminHome&action=optOut&language=fr&&fontColor=333333&fontSize=16px&fontFamily=Muli")
|
MATOMO_IFRAME_URL = ENV.fetch("MATOMO_IFRAME_URL", "https://#{ENV.fetch('MATOMO_HOST', 'stats.data.gouv.fr')}/index.php?module=CoreAdminHome&action=optOut&language=fr&&fontColor=333333&fontSize=16px&fontFamily=Muli")
|
||||||
CSP_REPORT_URI = ENV.fetch("CSP_REPORT_URI", "")
|
CSP_REPORT_URI = ENV.fetch("CSP_REPORT_URI", "")
|
||||||
|
|
||||||
# rubocop:enable DS/ApplicationName
|
# rubocop:enable DS/ApplicationName
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
# Instance provider
|
|
||||||
PROVIDED_BY = ENV.fetch("PROVIDED_BY", "la DINUM")
|
|
||||||
PROVIDER_NAME = ENV.fetch("PROVIDER_NAME", "DINUM")
|
|
||||||
PROVIDER_TITLE = ENV.fetch("PROVIDER_TITLE", "Direction Interministérielle au Numérique")
|
|
||||||
PROVIDER_URL = ENV.fetch("PROVIDER_URL", "https://numerique.gouv.fr/")
|
|
||||||
|
|
||||||
PROVIDER_LOGO_SRC = ENV.fetch("PROVIDER_LOGO_SRC", "footer/logo-dinum.png")
|
|
||||||
PROVIDER_LOGO_ALT = ENV.fetch("PROVIDER_LOGO_ALT", "Logo DINUM")
|
|
||||||
PROVIDER_LOGO_HEIGHT = ENV.fetch("PROVIDER_LOGO_HEIGHT", "161")
|
|
||||||
PROVIDER_LOGO_WIDTH = ENV.fetch("PROVIDER_LOGO_WIDTH", "138")
|
|
81
config/locales/links.en.yml
Normal file
81
config/locales/links.en.yml
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
en:
|
||||||
|
links:
|
||||||
|
provider:
|
||||||
|
logo:
|
||||||
|
src: "footer/logo-dinum.png"
|
||||||
|
alt: "Logo DINUM"
|
||||||
|
height: 161
|
||||||
|
width: 138
|
||||||
|
name: "DINUM"
|
||||||
|
provided_by: "la DINUM"
|
||||||
|
title: "Direction Interministérielle au Numérique"
|
||||||
|
url: "https://numerique.gouv.fr"
|
||||||
|
footer:
|
||||||
|
accessibilite:
|
||||||
|
label: "Accessibility: not compliant"
|
||||||
|
title: "Accessibility: not compliant"
|
||||||
|
url: "https://doc.demarches-simplifiees.fr/declaration-daccessibilite"
|
||||||
|
aide:
|
||||||
|
label: "Help"
|
||||||
|
title: "Frequently Asked Questions"
|
||||||
|
url: "https://faq.demarches-simplifiees.fr"
|
||||||
|
api_doc:
|
||||||
|
label: "API Documentation"
|
||||||
|
title: "API Documentation"
|
||||||
|
url: "https://doc.demarches-simplifiees.fr/pour-aller-plus-loin/graphql"
|
||||||
|
betagouv:
|
||||||
|
label: "Beta.gouv.fr"
|
||||||
|
title: "The Beta.gouv.fr website"
|
||||||
|
url: "https://beta.gouv.fr"
|
||||||
|
cgu:
|
||||||
|
label: "ToS"
|
||||||
|
title: "Terms of Service"
|
||||||
|
url: "https://doc.demarches-simplifiees.fr/cgu"
|
||||||
|
contact:
|
||||||
|
label: "Contact"
|
||||||
|
title: "Contact"
|
||||||
|
contact_technique:
|
||||||
|
label: "Technical contact"
|
||||||
|
title: "Technical contact"
|
||||||
|
doc:
|
||||||
|
label: "Documentation"
|
||||||
|
title: "Documentation"
|
||||||
|
url: "https://doc.demarches-simplifiees.fr"
|
||||||
|
doc_nouveautes:
|
||||||
|
label: "News"
|
||||||
|
title: "News"
|
||||||
|
url: "https://doc.demarches-simplifiees.fr/nouveautes"
|
||||||
|
faq:
|
||||||
|
label: "FAQ"
|
||||||
|
title: "Frequently Asked Questions"
|
||||||
|
url: "https://faq.demarches-simplifiees.fr"
|
||||||
|
faq_admin:
|
||||||
|
label: "FAQ"
|
||||||
|
title: "Frequently Asked Questions"
|
||||||
|
url: "https:///faq.demarches-simplifiees.fr/collection/1-administrateur-creation-dun-formulaire"
|
||||||
|
mentions_legales:
|
||||||
|
label: "Legal notices"
|
||||||
|
title: "Legal notices"
|
||||||
|
url: "https//doc.demarches-simplifiees.fr/mentions-legales"
|
||||||
|
newsletter:
|
||||||
|
label: "Newsletter"
|
||||||
|
title: "Notre newsletter"
|
||||||
|
url: "https://my.sendinblue.com/users/subscribe/js_id/3s2q1/id/1"
|
||||||
|
releases:
|
||||||
|
label: "Releases"
|
||||||
|
title: "Releases"
|
||||||
|
url: "https://github.com/betagouv/demarches-simplifiees.fr/releases"
|
||||||
|
stats:
|
||||||
|
label: "Statistics"
|
||||||
|
title: "Statistics"
|
||||||
|
status_page:
|
||||||
|
label: "Disponibility"
|
||||||
|
title: "Disponibility"
|
||||||
|
url: "https://status.demarches-simplifiees.fr"
|
||||||
|
suivi:
|
||||||
|
label: "Audience tracking and privacy"
|
||||||
|
title: "Audience tracking and privacy"
|
||||||
|
webinaire:
|
||||||
|
label: "Online workshop registration"
|
||||||
|
title: "Online workshop registration"
|
||||||
|
url: "https://app.livestorm.co/demarches-simplifiees"
|
81
config/locales/links.fr.yml
Normal file
81
config/locales/links.fr.yml
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
fr:
|
||||||
|
links:
|
||||||
|
provider:
|
||||||
|
logo:
|
||||||
|
src: "footer/logo-dinum.png"
|
||||||
|
alt: "Logo DINUM"
|
||||||
|
height: 161
|
||||||
|
width: 138
|
||||||
|
name: "DINUM"
|
||||||
|
provided_by: "la DINUM"
|
||||||
|
title: "Direction Interministérielle au Numérique"
|
||||||
|
url: "https://numerique.gouv.fr"
|
||||||
|
footer:
|
||||||
|
accessibilite:
|
||||||
|
label: "Accessibilité : non conforme"
|
||||||
|
title: "Accessibilité : non conforme"
|
||||||
|
url: "https://doc.demarches-simplifiees.fr/declaration-daccessibilite"
|
||||||
|
aide:
|
||||||
|
label: "Aide"
|
||||||
|
title: "Foire aux Questions"
|
||||||
|
url: "https://faq.demarches-simplifiees.fr"
|
||||||
|
api_doc:
|
||||||
|
label: "Documentation de l'API"
|
||||||
|
title: "Documentation de l'API"
|
||||||
|
url: "https://doc.demarches-simplifiees.fr/pour-aller-plus-loin/graphql"
|
||||||
|
betagouv:
|
||||||
|
label: "Beta.gouv.fr"
|
||||||
|
title: "Le site de Beta.gouv.fr"
|
||||||
|
url: "https://beta.gouv.fr"
|
||||||
|
cgu:
|
||||||
|
label: "CGU"
|
||||||
|
title: "Conditions Générales d'Utilisation"
|
||||||
|
url: "https://doc.demarches-simplifiees.fr/cgu"
|
||||||
|
contact:
|
||||||
|
label: "Contact"
|
||||||
|
title: "Contact"
|
||||||
|
contact_technique:
|
||||||
|
label: "Contact technique"
|
||||||
|
title: "Contact technique"
|
||||||
|
doc:
|
||||||
|
label: "Documentation"
|
||||||
|
title: "Documentation"
|
||||||
|
url: "https://doc.demarches-simplifiees.fr"
|
||||||
|
doc_nouveautes:
|
||||||
|
label: "Nouveautés"
|
||||||
|
title: "Nouveautés"
|
||||||
|
url: "https://doc.demarches-simplifiees.fr/nouveautes"
|
||||||
|
faq:
|
||||||
|
label: "FAQ"
|
||||||
|
title: "Foire aux Questions"
|
||||||
|
url: "https://faq.demarches-simplifiees.fr"
|
||||||
|
faq_admin:
|
||||||
|
label: "FAQ"
|
||||||
|
title: "Foire aux Questions"
|
||||||
|
url: "https:///faq.demarches-simplifiees.fr/collection/1-administrateur-creation-dun-formulaire"
|
||||||
|
mentions_legales:
|
||||||
|
label: "Mentions légales"
|
||||||
|
title: "Mentions légales"
|
||||||
|
url: "https//doc.demarches-simplifiees.fr/mentions-legales"
|
||||||
|
newsletter:
|
||||||
|
label: "Newsletter"
|
||||||
|
title: "Notre newsletter"
|
||||||
|
url: "https://my.sendinblue.com/users/subscribe/js_id/3s2q1/id/1"
|
||||||
|
releases:
|
||||||
|
label: "Nouveautés"
|
||||||
|
title: "Nos nouveautés"
|
||||||
|
url: "https://github.com/betagouv/demarches-simplifiees.fr/releases"
|
||||||
|
stats:
|
||||||
|
label: "Statistiques"
|
||||||
|
title: "Statistiques"
|
||||||
|
status_page:
|
||||||
|
label: "Disponibilité"
|
||||||
|
title: "Disponibilité"
|
||||||
|
url: "https://status.demarches-simplifiees.fr"
|
||||||
|
suivi:
|
||||||
|
label: "Suivi d'audience et vie privée"
|
||||||
|
title: "Suivi d'audience et vie privée"
|
||||||
|
webinaire:
|
||||||
|
label: "Inscription ateliers en ligne"
|
||||||
|
title: "Inscription ateliers en ligne"
|
||||||
|
url: "https://app.livestorm.co/demarches-simplifiees"
|
36
doc/PRIVACY-POLICY.md
Normal file
36
doc/PRIVACY-POLICY.md
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# Privacy policy documentation
|
||||||
|
|
||||||
|
This document describes various privacy consideration that should be considered when deploying an instance of demarches-simplifiees.fr.
|
||||||
|
## Matomo and or Analytics service
|
||||||
|
|
||||||
|
In order to prevent Matomo to store personnal information, you should set it up with some additional configurations options.
|
||||||
|
|
||||||
|
### Exclude some query parameters from matomo
|
||||||
|
|
||||||
|
* how : [see the matomo doc](https://matomo.org/faq/how-to/faq_81/)
|
||||||
|
* what :
|
||||||
|
We recommend to ignore the following query parameters
|
||||||
|
|
||||||
|
```
|
||||||
|
fbclid
|
||||||
|
*token
|
||||||
|
/.*token/
|
||||||
|
*email*
|
||||||
|
```
|
||||||
|
|
||||||
|
* why : some pages use URL query parameters to transmit the user email address. To avoid these being logged by Matomo, they should be excluded from the logged parameters.
|
||||||
|
|
||||||
|
## Forms data requested by user :
|
||||||
|
|
||||||
|
Depending on your local regulations/laws, **beware** : you can't collect some data, others requires special infrastructure.
|
||||||
|
|
||||||
|
### Risky forms inputs in France :
|
||||||
|
|
||||||
|
* unless your instance is running on a HDS infrastructure, you can't collect any health data. This includes Social Security number, health records, etc. [Source : CNIL](https://www.cnil.fr/fr/quest-ce-ce-quune-donnee-de-sante)
|
||||||
|
* in France, a form can't ask for the race or religion. [Source : INSEE](https://www.insee.fr/fr/information/2108548)
|
||||||
|
|
||||||
|
## Data expirations :
|
||||||
|
|
||||||
|
Data retention **must not exceed 36 months**. Depending on your instance configuration, you should check that all records of the `procedures` table have the column `procedure_expires_when_termine_enabled` set to `true`. Also make sure the default value of `procedures.procedure_expires_when_termine_enabled` is true.
|
||||||
|
|
||||||
|
This flag ensures that processed file will be deleted when expired.
|
35
doc/customization.md
Normal file
35
doc/customization.md
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
# Front end customization
|
||||||
|
|
||||||
|
Do you want to customize your instance ? Here is a step by step guide.
|
||||||
|
|
||||||
|
## Step 1. Understanding
|
||||||
|
|
||||||
|
For your information, you can overide any view in our app by replicating the
|
||||||
|
view structure from `app/views` to `app/custom_views/`.
|
||||||
|
|
||||||
|
You can also overide locales by replicating the locales structure from
|
||||||
|
`config/locales` to `config/custom_locales`.
|
||||||
|
|
||||||
|
## Step 2. Customize the views
|
||||||
|
|
||||||
|
So let's imagine you want to customize the `app/views/root/_footer.html.haml`.
|
||||||
|
Here is how to do:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ mkdir app/custom_views/root
|
||||||
|
$ cp app/views/root/_footer.html.haml app/custom_views/root
|
||||||
|
```
|
||||||
|
|
||||||
|
And _voila!_ You can edit your own template. No need for env var, no need to
|
||||||
|
worry about conflicts.
|
||||||
|
|
||||||
|
## Step 3. Customize the locales
|
||||||
|
|
||||||
|
Now let's imagine you want to customize the `config/locales/links.fr.yml`.
|
||||||
|
Here is how to do:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cp config/locales/links.fr.yml config/custom_locales
|
||||||
|
```
|
||||||
|
|
||||||
|
And _voila!_ You can now edit your own locales.
|
|
@ -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) }
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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) } }
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 'doesn’t send the email' do
|
it 'doesn’t send the email' do
|
||||||
expect(subject.body).to be_blank
|
expect(subject.body).to be_blank
|
||||||
|
|
|
@ -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) }
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 }
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue