commit
ce3809a998
27 changed files with 173 additions and 66 deletions
|
@ -18,6 +18,7 @@ module Instructeurs
|
||||||
@dossiers_archived_count_per_procedure = dossiers.archived.group('groupe_instructeurs.procedure_id').count
|
@dossiers_archived_count_per_procedure = dossiers.archived.group('groupe_instructeurs.procedure_id').count
|
||||||
@dossiers_termines_count_per_procedure = dossiers.termine.visible_by_administration.group('groupe_instructeurs.procedure_id').reorder(nil).count
|
@dossiers_termines_count_per_procedure = dossiers.termine.visible_by_administration.group('groupe_instructeurs.procedure_id').reorder(nil).count
|
||||||
@dossiers_expirant_count_per_procedure = dossiers.termine_or_en_construction_close_to_expiration.group('groupe_instructeurs.procedure_id').count
|
@dossiers_expirant_count_per_procedure = dossiers.termine_or_en_construction_close_to_expiration.group('groupe_instructeurs.procedure_id').count
|
||||||
|
@dossiers_supprimes_recemment_count_per_procedure = dossiers.hidden_by_administration.group('groupe_instructeurs.procedure_id').reorder(nil).count
|
||||||
groupe_ids = current_instructeur.groupe_instructeurs.pluck(:id)
|
groupe_ids = current_instructeur.groupe_instructeurs.pluck(:id)
|
||||||
|
|
||||||
@followed_dossiers_count_per_procedure = current_instructeur
|
@followed_dossiers_count_per_procedure = current_instructeur
|
||||||
|
@ -35,7 +36,8 @@ module Instructeurs
|
||||||
'traités' => @dossiers_termines_count_per_procedure.sum { |_, v| v },
|
'traités' => @dossiers_termines_count_per_procedure.sum { |_, v| v },
|
||||||
'dossiers' => @dossiers_count_per_procedure.sum { |_, v| v },
|
'dossiers' => @dossiers_count_per_procedure.sum { |_, v| v },
|
||||||
'expirant' => @dossiers_expirant_count_per_procedure.sum { |_, v| v },
|
'expirant' => @dossiers_expirant_count_per_procedure.sum { |_, v| v },
|
||||||
'archivés' => @dossiers_archived_count_per_procedure.sum { |_, v| v }
|
'archivés' => @dossiers_archived_count_per_procedure.sum { |_, v| v },
|
||||||
|
'supprimes_recemment' => @dossiers_supprimes_recemment_count_per_procedure.sum { |_, v| v }
|
||||||
}
|
}
|
||||||
|
|
||||||
@procedure_ids_en_cours_with_notifications = current_instructeur.procedure_ids_with_notifications(:en_cours)
|
@procedure_ids_en_cours_with_notifications = current_instructeur.procedure_ids_with_notifications(:en_cours)
|
||||||
|
@ -51,9 +53,9 @@ module Instructeurs
|
||||||
@current_filters = current_filters
|
@current_filters = current_filters
|
||||||
@displayed_fields_options, @displayed_fields_selected = procedure_presentation.displayed_fields_for_select
|
@displayed_fields_options, @displayed_fields_selected = procedure_presentation.displayed_fields_for_select
|
||||||
|
|
||||||
@a_suivre_count, @suivis_count, @traites_count, @tous_count, @archives_count, @expirant_count = current_instructeur
|
@a_suivre_count, @suivis_count, @traites_count, @tous_count, @supprimes_recemment_count, @archives_count, @expirant_count = current_instructeur
|
||||||
.dossiers_count_summary(groupe_instructeur_ids)
|
.dossiers_count_summary(groupe_instructeur_ids)
|
||||||
.fetch_values('a_suivre', 'suivis', 'traites', 'tous', 'archives', 'expirant')
|
.fetch_values('a_suivre', 'suivis', 'traites', 'tous', 'supprimes_recemment', 'archives', 'expirant')
|
||||||
|
|
||||||
dossiers_visibles = Dossier
|
dossiers_visibles = Dossier
|
||||||
.where(groupe_instructeur_id: groupe_instructeur_ids)
|
.where(groupe_instructeur_id: groupe_instructeur_ids)
|
||||||
|
@ -72,6 +74,7 @@ module Instructeurs
|
||||||
|
|
||||||
@termines_dossiers = dossiers_visibles.termine.visible_by_administration
|
@termines_dossiers = dossiers_visibles.termine.visible_by_administration
|
||||||
@all_state_dossiers = dossiers_visibles.all_state.visible_by_administration
|
@all_state_dossiers = dossiers_visibles.all_state.visible_by_administration
|
||||||
|
@supprimes_recemment_dossiers = dossiers_visibles.termine.hidden_by_administration
|
||||||
@archived_dossiers = dossiers_visibles.archived
|
@archived_dossiers = dossiers_visibles.archived
|
||||||
@expirant_dossiers = dossiers_visibles.termine_or_en_construction_close_to_expiration
|
@expirant_dossiers = dossiers_visibles.termine_or_en_construction_close_to_expiration
|
||||||
|
|
||||||
|
@ -88,6 +91,9 @@ module Instructeurs
|
||||||
when 'tous'
|
when 'tous'
|
||||||
dossiers_count = @tous_count
|
dossiers_count = @tous_count
|
||||||
@all_state_dossiers
|
@all_state_dossiers
|
||||||
|
when 'supprimes_recemment'
|
||||||
|
dossiers_count = @supprimes_recemment_count
|
||||||
|
@supprimes_recemment_dossiers
|
||||||
when 'archives'
|
when 'archives'
|
||||||
dossiers_count = @archives_count
|
dossiers_count = @archives_count
|
||||||
@archived_dossiers
|
@archived_dossiers
|
||||||
|
@ -282,6 +288,13 @@ module Instructeurs
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def restore
|
||||||
|
dossier = current_instructeur.dossiers.find(params[:dossier_id])
|
||||||
|
dossier.restore(current_instructeur)
|
||||||
|
flash.notice = t('instructeurs.dossiers.restore')
|
||||||
|
redirect_to instructeur_procedure_path(procedure)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def assign_to_params
|
def assign_to_params
|
||||||
|
|
|
@ -815,6 +815,10 @@ class Dossier < ApplicationRecord
|
||||||
restore_dossier_and_destroy_deleted_dossier(author)
|
restore_dossier_and_destroy_deleted_dossier(author)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
elsif author_is_administration(author) && hidden_by_administration?
|
||||||
|
transaction do
|
||||||
|
update(hidden_by_administration_at: nil)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -236,6 +236,7 @@ class Instructeur < ApplicationRecord
|
||||||
COUNT(DISTINCT dossiers.id) FILTER (where not archived AND dossiers.state in ('en_construction', 'en_instruction') AND follows.instructeur_id = :instructeur_id) AS suivis,
|
COUNT(DISTINCT dossiers.id) FILTER (where not archived AND dossiers.state in ('en_construction', 'en_instruction') AND follows.instructeur_id = :instructeur_id) AS suivis,
|
||||||
COUNT(DISTINCT dossiers.id) FILTER (where not archived AND dossiers.state in ('accepte', 'refuse', 'sans_suite')) AS traites,
|
COUNT(DISTINCT dossiers.id) FILTER (where not archived AND dossiers.state in ('accepte', 'refuse', 'sans_suite')) AS traites,
|
||||||
COUNT(DISTINCT dossiers.id) FILTER (where not archived AND NOT (dossiers.hidden_by_user_at IS NOT NULL AND state = 'en_construction')) AS tous,
|
COUNT(DISTINCT dossiers.id) FILTER (where not archived AND NOT (dossiers.hidden_by_user_at IS NOT NULL AND state = 'en_construction')) AS tous,
|
||||||
|
COUNT(DISTINCT dossiers.id) FILTER (where not archived AND (dossiers.hidden_by_administration_at IS NOT NULL AND dossiers.state in ('accepte', 'refuse', 'sans_suite') )) AS supprimes_recemment,
|
||||||
COUNT(DISTINCT dossiers.id) FILTER (where archived) AS archives,
|
COUNT(DISTINCT dossiers.id) FILTER (where archived) AS archives,
|
||||||
COUNT(DISTINCT dossiers.id) FILTER (where
|
COUNT(DISTINCT dossiers.id) FILTER (where
|
||||||
procedures.procedure_expires_when_termine_enabled
|
procedures.procedure_expires_when_termine_enabled
|
||||||
|
@ -256,7 +257,6 @@ class Instructeur < ApplicationRecord
|
||||||
ON follows.dossier_id = dossiers.id
|
ON follows.dossier_id = dossiers.id
|
||||||
AND follows.unfollowed_at IS NULL
|
AND follows.unfollowed_at IS NULL
|
||||||
WHERE "dossiers"."hidden_at" IS NULL
|
WHERE "dossiers"."hidden_at" IS NULL
|
||||||
AND "dossiers"."hidden_by_administration_at" IS NULL
|
|
||||||
AND "dossiers"."state" != 'brouillon'
|
AND "dossiers"."state" != 'brouillon'
|
||||||
AND "dossiers"."groupe_instructeur_id" in (:groupe_instructeur_ids)
|
AND "dossiers"."groupe_instructeur_id" in (:groupe_instructeur_ids)
|
||||||
EOF
|
EOF
|
||||||
|
|
|
@ -245,9 +245,13 @@ class Procedure < ApplicationRecord
|
||||||
validates :description, presence: true, allow_blank: false, allow_nil: false
|
validates :description, presence: true, allow_blank: false, allow_nil: false
|
||||||
validates :administrateurs, presence: true
|
validates :administrateurs, presence: true
|
||||||
validates :lien_site_web, presence: true, if: :publiee?
|
validates :lien_site_web, presence: true, if: :publiee?
|
||||||
validates :draft_revision,
|
validates :draft_types_de_champ,
|
||||||
'revisions/no_empty_repetition': true,
|
'types_de_champ/no_empty_repetition': true,
|
||||||
'revisions/no_empty_drop_down': true,
|
'types_de_champ/no_empty_drop_down': true,
|
||||||
|
if: :validate_for_publication?
|
||||||
|
validates :draft_types_de_champ_private,
|
||||||
|
'types_de_champ/no_empty_repetition': true,
|
||||||
|
'types_de_champ/no_empty_drop_down': true,
|
||||||
if: :validate_for_publication?
|
if: :validate_for_publication?
|
||||||
validate :check_juridique
|
validate :check_juridique
|
||||||
validates :path, presence: true, format: { with: /\A[a-z0-9_\-]{3,200}\z/ }, uniqueness: { scope: [:path, :closed_at, :hidden_at, :unpublished_at], case_sensitive: false }
|
validates :path, presence: true, format: { with: /\A[a-z0-9_\-]{3,200}\z/ }, uniqueness: { scope: [:path, :closed_at, :hidden_at, :unpublished_at], case_sensitive: false }
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
class Revisions::NoEmptyDropDownValidator < ActiveModel::EachValidator
|
class TypesDeChamp::NoEmptyDropDownValidator < ActiveModel::EachValidator
|
||||||
def validate_each(procedure, attribute, revision)
|
def validate_each(procedure, attribute, types_de_champ)
|
||||||
return if revision.nil?
|
types_de_champ.filter(&:drop_down_list?).each do |drop_down|
|
||||||
|
|
||||||
tdcs = revision.types_de_champ + revision.types_de_champ_private
|
|
||||||
drop_downs = tdcs.filter(&:drop_down_list?)
|
|
||||||
drop_downs.each do |drop_down|
|
|
||||||
validate_drop_down_not_empty(procedure, attribute, drop_down)
|
validate_drop_down_not_empty(procedure, attribute, drop_down)
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -1,10 +1,6 @@
|
||||||
class Revisions::NoEmptyRepetitionValidator < ActiveModel::EachValidator
|
class TypesDeChamp::NoEmptyRepetitionValidator < ActiveModel::EachValidator
|
||||||
def validate_each(procedure, attribute, revision)
|
def validate_each(procedure, attribute, types_de_champ)
|
||||||
return if revision.nil?
|
types_de_champ.filter(&:repetition?).each do |repetition|
|
||||||
|
|
||||||
revision_tdcs = revision.types_de_champ + revision.types_de_champ_private
|
|
||||||
repetitions = revision_tdcs.filter(&:repetition?)
|
|
||||||
repetitions.each do |repetition|
|
|
||||||
validate_repetition_not_empty(procedure, attribute, repetition)
|
validate_repetition_not_empty(procedure, attribute, repetition)
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -53,7 +53,7 @@
|
||||||
|
|
||||||
- if !@procedure.locked? || @procedure.feature_enabled?(:procedure_revisions)
|
- if !@procedure.locked? || @procedure.feature_enabled?(:procedure_revisions)
|
||||||
- @procedure.validate(:publication)
|
- @procedure.validate(:publication)
|
||||||
- error_messages = @procedure.errors.messages_for(:draft_revision).to_sentence
|
- error_messages = @procedure.errors.messages_for(:draft_types_de_champ).to_sentence
|
||||||
|
|
||||||
= link_to champs_admin_procedure_path(@procedure), class: 'card-admin', title: error_messages do
|
= link_to champs_admin_procedure_path(@procedure), class: 'card-admin', title: error_messages do
|
||||||
- if @procedure.draft_types_de_champ.count == 0
|
- if @procedure.draft_types_de_champ.count == 0
|
||||||
|
@ -171,11 +171,18 @@
|
||||||
%p.button Modifier
|
%p.button Modifier
|
||||||
|
|
||||||
- if !@procedure.locked? || @procedure.feature_enabled?(:procedure_revisions)
|
- if !@procedure.locked? || @procedure.feature_enabled?(:procedure_revisions)
|
||||||
= link_to annotations_admin_procedure_path(@procedure), class: 'card-admin' do
|
- @procedure.validate(:publication)
|
||||||
|
- error_messages = @procedure.errors.messages_for(:draft_types_de_champ_private).to_sentence
|
||||||
|
|
||||||
|
= link_to annotations_admin_procedure_path(@procedure), class: 'card-admin', title: error_messages do
|
||||||
- if @procedure.draft_types_de_champ_private.present?
|
- if @procedure.draft_types_de_champ_private.present?
|
||||||
%div
|
%div
|
||||||
%span.icon.accept
|
%span.icon.accept
|
||||||
%p.card-admin-status-accept Validé
|
%p.card-admin-status-accept Validé
|
||||||
|
- elsif error_messages.present?
|
||||||
|
%div
|
||||||
|
%span.icon.refuse
|
||||||
|
%p.card-admin-status-error À modifier
|
||||||
- else
|
- else
|
||||||
%div
|
%div
|
||||||
%span.icon.clock
|
%span.icon.clock
|
||||||
|
|
|
@ -28,7 +28,8 @@
|
||||||
state: dossier.state,
|
state: dossier.state,
|
||||||
archived: dossier.archived,
|
archived: dossier.archived,
|
||||||
dossier_is_followed: current_instructeur&.follow?(dossier),
|
dossier_is_followed: current_instructeur&.follow?(dossier),
|
||||||
close_to_expiration: dossier.close_to_expiration? }
|
close_to_expiration: dossier.close_to_expiration?,
|
||||||
|
supprimes_recemment: dossier.hidden_by_administration? }
|
||||||
|
|
||||||
|
|
||||||
.state-button
|
.state-button
|
||||||
|
|
|
@ -21,7 +21,13 @@
|
||||||
%span.icon.archive
|
%span.icon.archive
|
||||||
.dropdown-description
|
.dropdown-description
|
||||||
Archiver le dossier
|
Archiver le dossier
|
||||||
|
- if supprimes_recemment
|
||||||
|
%li.danger
|
||||||
|
= link_to restore_instructeur_dossier_path(procedure_id, dossier_id), method: :patch, data: { confirm: "Voulez vous vraiment restaurer le dossier #{dossier_id}" } do
|
||||||
|
%span.icon.reply
|
||||||
|
.dropdown-description
|
||||||
|
= t('views.instructeurs.dossiers.restore')
|
||||||
|
- else
|
||||||
%li.danger
|
%li.danger
|
||||||
= link_to supprimer_dossier_instructeur_dossier_path(procedure_id, dossier_id), method: :patch, data: { confirm: "Voulez vous vraiment supprimer le dossier #{dossier_id} ? Cette action est irréversible. \nNous vous suggérons de télécharger le dossier au format PDF au préalable." } do
|
= link_to supprimer_dossier_instructeur_dossier_path(procedure_id, dossier_id), method: :patch, data: { confirm: "Voulez vous vraiment supprimer le dossier #{dossier_id} ? Cette action est irréversible. \nNous vous suggérons de télécharger le dossier au format PDF au préalable." } do
|
||||||
%span.icon.delete
|
%span.icon.delete
|
||||||
|
|
|
@ -45,6 +45,15 @@
|
||||||
.stats-legend
|
.stats-legend
|
||||||
= t('pluralize.case', count: dossier_count)
|
= t('pluralize.case', count: dossier_count)
|
||||||
|
|
||||||
|
%li
|
||||||
|
%object
|
||||||
|
= link_to(instructeur_procedure_path(p, statut: 'supprimes_recemment')) do
|
||||||
|
- dossier_count = dossiers_supprimes_recemment_count_per_procedure[p.id] || 0
|
||||||
|
.stats-number
|
||||||
|
= number_with_html_delimiter(dossier_count)
|
||||||
|
.stats-legend
|
||||||
|
= t('pluralize.dossiers_supprimes_recemment', count: dossier_count)
|
||||||
|
|
||||||
- if p.procedure_expires_when_termine_enabled
|
- if p.procedure_expires_when_termine_enabled
|
||||||
%li
|
%li
|
||||||
%object
|
%object
|
||||||
|
|
|
@ -22,6 +22,11 @@
|
||||||
active: statut == 'tous',
|
active: statut == 'tous',
|
||||||
badge: number_with_html_delimiter(tous_count))
|
badge: number_with_html_delimiter(tous_count))
|
||||||
|
|
||||||
|
= tab_item(t('pluralize.dossiers_supprimes_recemment', count: supprimes_recemment_count),
|
||||||
|
instructeur_procedure_path(procedure, statut: 'supprimes_recemment'),
|
||||||
|
active: statut == 'supprimes_recemment',
|
||||||
|
badge: number_with_html_delimiter(supprimes_recemment_count))
|
||||||
|
|
||||||
- if procedure.procedure_expires_when_termine_enabled
|
- if procedure.procedure_expires_when_termine_enabled
|
||||||
= tab_item(t('pluralize.dossiers_close_to_expiration', count: expirant_count),
|
= tab_item(t('pluralize.dossiers_close_to_expiration', count: expirant_count),
|
||||||
instructeur_procedure_path(procedure, statut: 'expirant'),
|
instructeur_procedure_path(procedure, statut: 'expirant'),
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
dossiers_archived_count_per_procedure: @dossiers_archived_count_per_procedure,
|
dossiers_archived_count_per_procedure: @dossiers_archived_count_per_procedure,
|
||||||
dossiers_termines_count_per_procedure: @dossiers_termines_count_per_procedure,
|
dossiers_termines_count_per_procedure: @dossiers_termines_count_per_procedure,
|
||||||
dossiers_expirant_count_per_procedure: @dossiers_expirant_count_per_procedure,
|
dossiers_expirant_count_per_procedure: @dossiers_expirant_count_per_procedure,
|
||||||
|
dossiers_supprimes_recemment_count_per_procedure: @dossiers_supprimes_recemment_count_per_procedure,
|
||||||
followed_dossiers_count_per_procedure: @followed_dossiers_count_per_procedure,
|
followed_dossiers_count_per_procedure: @followed_dossiers_count_per_procedure,
|
||||||
procedure_ids_en_cours_with_notifications: @procedure_ids_en_cours_with_notifications,
|
procedure_ids_en_cours_with_notifications: @procedure_ids_en_cours_with_notifications,
|
||||||
procedure_ids_termines_with_notifications: @procedure_ids_termines_with_notifications }
|
procedure_ids_termines_with_notifications: @procedure_ids_termines_with_notifications }
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
suivis_count: @suivis_count,
|
suivis_count: @suivis_count,
|
||||||
traites_count: @traites_count,
|
traites_count: @traites_count,
|
||||||
tous_count: @tous_count,
|
tous_count: @tous_count,
|
||||||
|
supprimes_recemment_count: @supprimes_recemment_count,
|
||||||
archives_count: @archives_count,
|
archives_count: @archives_count,
|
||||||
expirant_count: @expirant_count,
|
expirant_count: @expirant_count,
|
||||||
has_en_cours_notifications: @has_en_cours_notifications,
|
has_en_cours_notifications: @has_en_cours_notifications,
|
||||||
|
@ -31,6 +32,8 @@
|
||||||
%p.explication-onglet Les dossiers dans cet onglet sont terminés : ils ont été acceptés, refusés ou classés sans suite.
|
%p.explication-onglet Les dossiers dans cet onglet sont terminés : ils ont été acceptés, refusés ou classés sans suite.
|
||||||
- if @statut == 'tous'
|
- if @statut == 'tous'
|
||||||
%p.explication-onglet Tous les dossiers qui ont été déposés sur cette démarche, quel que soit le statut.
|
%p.explication-onglet Tous les dossiers qui ont été déposés sur cette démarche, quel que soit le statut.
|
||||||
|
- if @statut == 'supprimes_recemment'
|
||||||
|
%p.explication-onglet Tous les dossiers terminés et supprimés par les instructeurs sur cette démarche
|
||||||
- if @statut == 'archives'
|
- if @statut == 'archives'
|
||||||
%p.explication-onglet
|
%p.explication-onglet
|
||||||
Les dossiers de cet onglet sont archivés : vous ne pouvez plus y répondre, et les demandeurs ne peuvent plus les modifier.
|
Les dossiers de cet onglet sont archivés : vous ne pouvez plus y répondre, et les demandeurs ne peuvent plus les modifier.
|
||||||
|
@ -135,7 +138,8 @@
|
||||||
state: p.state,
|
state: p.state,
|
||||||
archived: p.archived,
|
archived: p.archived,
|
||||||
dossier_is_followed: @followed_dossiers_id.include?(p.dossier_id),
|
dossier_is_followed: @followed_dossiers_id.include?(p.dossier_id),
|
||||||
close_to_expiration: @statut == 'expirant' }
|
close_to_expiration: @statut == 'expirant',
|
||||||
|
supprimes_recemment: @statut == 'supprimes_recemment' }
|
||||||
|
|
||||||
= pagination
|
= pagination
|
||||||
- else
|
- else
|
||||||
|
|
|
@ -25,7 +25,7 @@ as defined by the routes in the `admin/` namespace
|
||||||
|
|
||||||
<%= link_to "Delayed Jobs", manager_delayed_job_path, class: "navigation__link" %>
|
<%= link_to "Delayed Jobs", manager_delayed_job_path, class: "navigation__link" %>
|
||||||
<%= link_to "Features", manager_flipper_path, class: "navigation__link" %>
|
<%= link_to "Features", manager_flipper_path, class: "navigation__link" %>
|
||||||
<% if Rails.env.production? && ENV['SENDINBLUE_ENABLED'] == 'enabled'%>
|
<% if ENV["SENDINBLUE_ENABLED"] == "enabled" && ENV["SAML_IDP_ENABLED"] == "enabled" %>
|
||||||
<%= link_to "Sendinblue", ENV.fetch("SENDINBLUE_LOGIN_URL"), class: "navigation__link", target: '_blank' %>
|
<%= link_to "Sendinblue", ENV.fetch("SENDINBLUE_LOGIN_URL"), class: "navigation__link", target: '_blank' %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
@ -16,11 +16,6 @@ SECRET_KEY_BASE="05a2d479d8e412198dabd08ef0eee9d6e180f5cbb48661a35fd1cae287f0a93
|
||||||
# Secret key for One-Time-Password codes, used for 2-factors authentication
|
# Secret key for One-Time-Password codes, used for 2-factors authentication
|
||||||
OTP_SECRET_KEY=""
|
OTP_SECRET_KEY=""
|
||||||
|
|
||||||
# SAML IdP
|
|
||||||
|
|
||||||
# SAML_IDP_CERTIFICATE="billybop"
|
|
||||||
# SAML_IDP_SECRET_KEY="-----BEGIN RSA PRIVATE KEY-----\nblabla+blabla\n-----END RSA PRIVATE KEY-----\n"
|
|
||||||
|
|
||||||
# Database credentials
|
# Database credentials
|
||||||
DB_DATABASE="tps_development"
|
DB_DATABASE="tps_development"
|
||||||
DB_HOST="localhost"
|
DB_HOST="localhost"
|
||||||
|
@ -41,6 +36,11 @@ FOG_OPENSTACK_URL=""
|
||||||
FOG_OPENSTACK_REGION=""
|
FOG_OPENSTACK_REGION=""
|
||||||
DS_PROXY_URL=""
|
DS_PROXY_URL=""
|
||||||
|
|
||||||
|
# SAML Identity provider
|
||||||
|
SAML_IDP_ENABLED="disabled"
|
||||||
|
SAML_IDP_CERTIFICATE=""
|
||||||
|
SAML_IDP_SECRET_KEY="-----BEGIN RSA PRIVATE KEY-----\nblabla+blabla\n-----END RSA PRIVATE KEY-----\n"
|
||||||
|
|
||||||
# External service: authentication through France Connect
|
# External service: authentication through France Connect
|
||||||
FC_PARTICULIER_ID=""
|
FC_PARTICULIER_ID=""
|
||||||
FC_PARTICULIER_SECRET=""
|
FC_PARTICULIER_SECRET=""
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# The certificate and secret key are not fetched from secrets.yml because there is a problem to set a secret key from a multiline env var"
|
# The certificate and secret key are not fetched from secrets.yml because there is a problem to set a secret key from a multiline env var"
|
||||||
# So we fetch env var directly here
|
# So we fetch env var directly here
|
||||||
|
|
||||||
if Rails.env.production?
|
if ENV['SAML_IDP_ENABLED'] == 'enabled'
|
||||||
SamlIdp.config.x509_certificate = ENV.fetch("SAML_IDP_CERTIFICATE")
|
SamlIdp.config.x509_certificate = ENV.fetch("SAML_IDP_CERTIFICATE")
|
||||||
SamlIdp.config.secret_key = ENV.fetch("SAML_IDP_SECRET_KEY")
|
SamlIdp.config.secret_key = ENV.fetch("SAML_IDP_SECRET_KEY")
|
||||||
end
|
end
|
||||||
|
|
|
@ -140,6 +140,7 @@ en:
|
||||||
archived_dossier: "This file will be kept for an additional month"
|
archived_dossier: "This file will be kept for an additional month"
|
||||||
delete_dossier: "Delete file"
|
delete_dossier: "Delete file"
|
||||||
deleted_by_user: "File deleted by user"
|
deleted_by_user: "File deleted by user"
|
||||||
|
restore: "Restore the file"
|
||||||
avis:
|
avis:
|
||||||
introduction_file_explaination: "File attached to the request for advice"
|
introduction_file_explaination: "File attached to the request for advice"
|
||||||
users:
|
users:
|
||||||
|
@ -419,6 +420,7 @@ en:
|
||||||
dossiers:
|
dossiers:
|
||||||
deleted_by_instructeur: "The folder has been deleted"
|
deleted_by_instructeur: "The folder has been deleted"
|
||||||
impossible_deletion: "Unable to delete : the folder is not processed"
|
impossible_deletion: "Unable to delete : the folder is not processed"
|
||||||
|
restore: "The folder has been restored"
|
||||||
france_connect:
|
france_connect:
|
||||||
particulier:
|
particulier:
|
||||||
password_confirmation:
|
password_confirmation:
|
||||||
|
|
|
@ -137,6 +137,7 @@ fr:
|
||||||
archived_dossier: "Le dossier sera conservé 1 mois supplémentaire"
|
archived_dossier: "Le dossier sera conservé 1 mois supplémentaire"
|
||||||
delete_dossier: "Supprimer le dossier"
|
delete_dossier: "Supprimer le dossier"
|
||||||
deleted_by_user: "Dossier supprimé par l'usager"
|
deleted_by_user: "Dossier supprimé par l'usager"
|
||||||
|
restore: "Restaurer le dossier"
|
||||||
avis:
|
avis:
|
||||||
introduction_file_explaination: "Fichier joint à la demande d’avis"
|
introduction_file_explaination: "Fichier joint à la demande d’avis"
|
||||||
users:
|
users:
|
||||||
|
@ -327,8 +328,8 @@ fr:
|
||||||
connexion: "Erreur lors de la connexion à France Connect."
|
connexion: "Erreur lors de la connexion à France Connect."
|
||||||
forbidden_html: "Seul-e-s les usagers peuvent se connecter via France Connect. En tant qu’instructeur ou administrateur, nous vous invitons à <a href='%{reset_link}'>réininitialiser votre mot de passe</a>."
|
forbidden_html: "Seul-e-s les usagers peuvent se connecter via France Connect. En tant qu’instructeur ou administrateur, nous vous invitons à <a href='%{reset_link}'>réininitialiser votre mot de passe</a>."
|
||||||
procedure_archived: "Cette démarche en ligne a été close, il n’est plus possible de déposer de dossier."
|
procedure_archived: "Cette démarche en ligne a été close, il n’est plus possible de déposer de dossier."
|
||||||
empty_repetition: 'Le bloc répétable « %{value} » doit comporter au moins un champ'
|
empty_repetition: '« %{value} » doit comporter au moins un champ répétable'
|
||||||
empty_drop_down: 'La liste de choix « %{value} » doit comporter au moins un choix sélectionnable'
|
empty_drop_down: '« %{value} » doit comporter au moins un choix sélectionnable'
|
||||||
# procedure_not_draft: "Cette démarche n’est maintenant plus en brouillon."
|
# procedure_not_draft: "Cette démarche n’est maintenant plus en brouillon."
|
||||||
cadastres_empty:
|
cadastres_empty:
|
||||||
one: "Aucune parcelle cadastrale sur la zone sélectionnée"
|
one: "Aucune parcelle cadastrale sur la zone sélectionnée"
|
||||||
|
@ -372,13 +373,13 @@ fr:
|
||||||
one: dossier invité
|
one: dossier invité
|
||||||
other: dossiers invités
|
other: dossiers invités
|
||||||
dossiers_supprimes_recemment:
|
dossiers_supprimes_recemment:
|
||||||
zero: dossier supprimé recemment
|
zero: supprimé recemment
|
||||||
one: dossier supprimé recemment
|
one: supprimé recemment
|
||||||
other: dossiers supprimés recemment
|
other: supprimés recemment
|
||||||
dossiers_supprimes_definitivement:
|
dossiers_supprimes_definitivement:
|
||||||
zero: dossier supprimé définitivement
|
zero: supprimé définitivement
|
||||||
one: dossier supprimé définitivement
|
one: supprimé définitivement
|
||||||
other: dossiers supprimés définitivement
|
other: supprimés définitivement
|
||||||
dossiers_transferes:
|
dossiers_transferes:
|
||||||
zero: demande de transfert
|
zero: demande de transfert
|
||||||
one: demande de transfert
|
one: demande de transfert
|
||||||
|
@ -427,6 +428,7 @@ fr:
|
||||||
dossiers:
|
dossiers:
|
||||||
deleted_by_instructeur: "Le dossier a bien été supprimé de votre interface"
|
deleted_by_instructeur: "Le dossier a bien été supprimé de votre interface"
|
||||||
impossible_deletion: "Supression impossible : le dossier n'est pas traité"
|
impossible_deletion: "Supression impossible : le dossier n'est pas traité"
|
||||||
|
restore: "Le dossier a bien été restauré"
|
||||||
administrateurs:
|
administrateurs:
|
||||||
procedures:
|
procedures:
|
||||||
show:
|
show:
|
||||||
|
|
|
@ -23,5 +23,7 @@ fr:
|
||||||
attributes:
|
attributes:
|
||||||
api_particulier_token:
|
api_particulier_token:
|
||||||
invalid: 'n’a pas le bon format'
|
invalid: 'n’a pas le bon format'
|
||||||
draft_revision:
|
draft_types_de_champ:
|
||||||
format: '%{message}'
|
format: 'Le champ %{message}'
|
||||||
|
draft_types_de_champ_private:
|
||||||
|
format: 'L’annotation privée %{message}'
|
||||||
|
|
|
@ -386,6 +386,7 @@ Rails.application.routes.draw do
|
||||||
post 'avis' => 'dossiers#create_avis'
|
post 'avis' => 'dossiers#create_avis'
|
||||||
get 'print' => 'dossiers#print'
|
get 'print' => 'dossiers#print'
|
||||||
get 'telecharger_pjs' => 'dossiers#telecharger_pjs'
|
get 'telecharger_pjs' => 'dossiers#telecharger_pjs'
|
||||||
|
patch 'restore'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
class AddSupprimesRecemmentToProcedurePresentations < ActiveRecord::Migration[6.1]
|
||||||
|
def up
|
||||||
|
ProcedurePresentation.update_all(%Q(filters = filters || '{"supprimes_recemment": []}'))
|
||||||
|
change_column_default :procedure_presentations, :filters, { "tous" => [], "suivis" => [], "traites" => [], "a-suivre" => [], "archives" => [], "supprimes_recemment" => [], "expirant": [] }
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
change_column_default :procedure_presentations, :filters, { "tous" => [], "suivis" => [], "traites" => [], "a-suivre" => [], "archives" => [], "expirant": [] }
|
||||||
|
end
|
||||||
|
end
|
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 2021_12_02_133139) do
|
ActiveRecord::Schema.define(version: 2022_01_27_135056) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
@ -569,7 +569,7 @@ ActiveRecord::Schema.define(version: 2021_12_02_133139) do
|
||||||
create_table "procedure_presentations", id: :serial, force: :cascade do |t|
|
create_table "procedure_presentations", id: :serial, force: :cascade do |t|
|
||||||
t.integer "assign_to_id"
|
t.integer "assign_to_id"
|
||||||
t.jsonb "sort", default: {"order"=>"desc", "table"=>"notifications", "column"=>"notifications"}, null: false
|
t.jsonb "sort", default: {"order"=>"desc", "table"=>"notifications", "column"=>"notifications"}, null: false
|
||||||
t.jsonb "filters", default: {"tous"=>[], "suivis"=>[], "traites"=>[], "a-suivre"=>[], "archives"=>[], "expirant"=>[]}, null: false
|
t.jsonb "filters", default: {"tous"=>[], "suivis"=>[], "traites"=>[], "a-suivre"=>[], "archives"=>[], "expirant"=>[], "supprimes_recemment"=>[]}, null: false
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.jsonb "displayed_fields", default: [{"label"=>"Demandeur", "table"=>"user", "column"=>"email"}], null: false
|
t.jsonb "displayed_fields", default: [{"label"=>"Demandeur", "table"=>"user", "column"=>"email"}], null: false
|
||||||
|
|
|
@ -839,4 +839,25 @@ describe Instructeurs::DossiersController, type: :controller do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#restore' do
|
||||||
|
let(:instructeur) { create(:instructeur) }
|
||||||
|
let!(:gi_p1_1) { GroupeInstructeur.create(label: '1', procedure: procedure) }
|
||||||
|
let!(:procedure) { create(:procedure, :published, instructeurs: [instructeur]) }
|
||||||
|
let!(:dossier) { create(:dossier, state: 'accepte', procedure: procedure, groupe_instructeur: procedure.groupe_instructeurs.first, hidden_by_administration_at: 1.hour.ago) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in(instructeur.user)
|
||||||
|
instructeur.groupe_instructeurs << gi_p1_1
|
||||||
|
patch :restore,
|
||||||
|
params: {
|
||||||
|
procedure_id: procedure.id,
|
||||||
|
dossier_id: dossier.id
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it "puts hidden_by_administration_at to nil" do
|
||||||
|
expect(dossier.reload.hidden_by_administration_at).to eq(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -299,20 +299,20 @@ describe Procedure do
|
||||||
it_behaves_like 'duree de conservation'
|
it_behaves_like 'duree de conservation'
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'draft_revision' do
|
describe 'draft_types_de_champ validations' do
|
||||||
let(:repetition) { build(:type_de_champ_repetition, libelle: 'Enfants') }
|
let(:repetition) { build(:type_de_champ_repetition, libelle: 'Enfants') }
|
||||||
let(:text_field) { build(:type_de_champ_text) }
|
let(:text_field) { build(:type_de_champ_text) }
|
||||||
let(:invalid_repetition_error_message) { 'Le bloc répétable « Enfants » doit comporter au moins un champ' }
|
let(:invalid_repetition_error_message) { 'Le champ « Enfants » doit comporter au moins un champ répétable' }
|
||||||
|
|
||||||
let(:drop_down) { build(:type_de_champ_drop_down_list, :without_selectable_values, libelle: 'Civilité') }
|
let(:drop_down) { build(:type_de_champ_drop_down_list, :without_selectable_values, libelle: 'Civilité') }
|
||||||
let(:invalid_drop_down_error_message) { 'La liste de choix « Civilité » doit comporter au moins un choix sélectionnable' }
|
let(:invalid_drop_down_error_message) { 'Le champ « Civilité » doit comporter au moins un choix sélectionnable' }
|
||||||
|
|
||||||
let(:procedure) { create(:procedure, types_de_champ: [repetition, drop_down]) }
|
let(:procedure) { create(:procedure, types_de_champ: [repetition, drop_down]) }
|
||||||
|
|
||||||
context 'on a draft procedure' do
|
context 'on a draft procedure' do
|
||||||
it 'doesn’t validate the draft revision' do
|
it 'doesn’t validate the types de champs' do
|
||||||
procedure.validate
|
procedure.validate
|
||||||
expect(procedure.errors[:draft_revision]).not_to be_present
|
expect(procedure.errors[:draft_types_de_champ]).not_to be_present
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -321,35 +321,52 @@ describe Procedure do
|
||||||
|
|
||||||
it 'validates that no repetition type de champ is empty' do
|
it 'validates that no repetition type de champ is empty' do
|
||||||
procedure.validate
|
procedure.validate
|
||||||
expect(procedure.errors.full_messages_for(:draft_revision)).to include(invalid_repetition_error_message)
|
expect(procedure.errors.full_messages_for(:draft_types_de_champ)).to include(invalid_repetition_error_message)
|
||||||
|
|
||||||
text_field.revision = repetition.revision
|
text_field.revision = repetition.revision
|
||||||
text_field.order_place = repetition.types_de_champ.size
|
text_field.order_place = repetition.types_de_champ.size
|
||||||
procedure.draft_revision.types_de_champ.find(&:repetition?).types_de_champ << text_field
|
procedure.draft_types_de_champ.find(&:repetition?).types_de_champ << text_field
|
||||||
|
|
||||||
procedure.validate
|
procedure.validate
|
||||||
expect(procedure.errors.full_messages_for(:draft_revision)).not_to include(invalid_repetition_error_message)
|
expect(procedure.errors.full_messages_for(:draft_types_de_champ)).not_to include(invalid_repetition_error_message)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'validates that no drop-down type de champ is empty' do
|
it 'validates that no drop-down type de champ is empty' do
|
||||||
procedure.validate
|
procedure.validate
|
||||||
expect(procedure.errors.full_messages_for(:draft_revision)).to include(invalid_drop_down_error_message)
|
expect(procedure.errors.full_messages_for(:draft_types_de_champ)).to include(invalid_drop_down_error_message)
|
||||||
|
|
||||||
drop_down.update!(drop_down_list_value: "--title--\r\nsome value")
|
drop_down.update!(drop_down_list_value: "--title--\r\nsome value")
|
||||||
procedure.reload.validate
|
procedure.reload.validate
|
||||||
expect(procedure.errors.full_messages_for(:draft_revision)).not_to include(invalid_drop_down_error_message)
|
expect(procedure.errors.full_messages_for(:draft_types_de_champ)).not_to include(invalid_drop_down_error_message)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when validating for publication' do
|
context 'when validating for publication' do
|
||||||
it 'validates that no repetition type de champ is empty' do
|
it 'validates that no repetition type de champ is empty' do
|
||||||
procedure.validate(:publication)
|
procedure.validate(:publication)
|
||||||
expect(procedure.errors.full_messages_for(:draft_revision)).to include(invalid_repetition_error_message)
|
expect(procedure.errors.full_messages_for(:draft_types_de_champ)).to include(invalid_repetition_error_message)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'validates that no drop-down type de champ is empty' do
|
it 'validates that no drop-down type de champ is empty' do
|
||||||
procedure.validate(:publication)
|
procedure.validate(:publication)
|
||||||
expect(procedure.errors.full_messages_for(:draft_revision)).to include(invalid_drop_down_error_message)
|
expect(procedure.errors.full_messages_for(:draft_types_de_champ)).to include(invalid_drop_down_error_message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the champ is private' do
|
||||||
|
let(:procedure) { create(:procedure, types_de_champ_private: [repetition, drop_down]) }
|
||||||
|
|
||||||
|
let(:invalid_repetition_error_message) { 'L’annotation privée « Enfants » doit comporter au moins un champ répétable' }
|
||||||
|
let(:invalid_drop_down_error_message) { 'L’annotation privée « Civilité » doit comporter au moins un choix sélectionnable' }
|
||||||
|
|
||||||
|
it 'validates that no repetition type de champ is empty' do
|
||||||
|
procedure.validate(:publication)
|
||||||
|
expect(procedure.errors.full_messages_for(:draft_types_de_champ_private)).to include(invalid_repetition_error_message)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'validates that no drop-down type de champ is empty' do
|
||||||
|
procedure.validate(:publication)
|
||||||
|
expect(procedure.errors.full_messages_for(:draft_types_de_champ_private)).to include(invalid_drop_down_error_message)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -47,19 +47,23 @@ describe 'Publishing a procedure', js: true do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the procedure has invalid champs' do
|
context 'when the procedure has invalid champs' do
|
||||||
let(:empty_repetition) { build(:type_de_champ_repetition, types_de_champ: []) }
|
let(:empty_repetition) { build(:type_de_champ_repetition, types_de_champ: [], libelle: 'Enfants') }
|
||||||
|
let(:empty_drop_down) { build(:type_de_champ_drop_down_list, :without_selectable_values, libelle: 'Civilité') }
|
||||||
|
|
||||||
let!(:procedure) do
|
let!(:procedure) do
|
||||||
create(:procedure,
|
create(:procedure,
|
||||||
:with_path,
|
:with_path,
|
||||||
:with_service,
|
:with_service,
|
||||||
instructeurs: instructeurs,
|
instructeurs: instructeurs,
|
||||||
administrateur: administrateur,
|
administrateur: administrateur,
|
||||||
types_de_champ: [empty_repetition])
|
types_de_champ: [empty_repetition],
|
||||||
|
types_de_champ_private: [empty_drop_down])
|
||||||
end
|
end
|
||||||
|
|
||||||
scenario 'an error message prevents the publication' do
|
scenario 'an error message prevents the publication' do
|
||||||
expect(page).to have_content('Des problèmes empêchent la publication de la démarche')
|
expect(page).to have_content('Des problèmes empêchent la publication de la démarche')
|
||||||
expect(page).to have_content("Le bloc répétable « #{empty_repetition.libelle} » doit comporter au moins un champ")
|
expect(page).to have_content("Le champ « Enfants » doit comporter au moins un champ répétable")
|
||||||
|
expect(page).to have_content("L’annotation privée « Civilité » doit comporter au moins un choix sélectionnable")
|
||||||
|
|
||||||
expect(find_field('procedure_path').value).to eq procedure.path
|
expect(find_field('procedure_path').value).to eq procedure.path
|
||||||
fill_in 'lien_site_web', with: 'http://some.website'
|
fill_in 'lien_site_web', with: 'http://some.website'
|
||||||
|
|
|
@ -8,6 +8,7 @@ describe 'instructeurs/procedures/_list.html.haml', type: :view do
|
||||||
dossiers_a_suivre_count_per_procedure: 2,
|
dossiers_a_suivre_count_per_procedure: 2,
|
||||||
dossiers_archived_count_per_procedure: 1,
|
dossiers_archived_count_per_procedure: 1,
|
||||||
dossiers_termines_count_per_procedure: 1,
|
dossiers_termines_count_per_procedure: 1,
|
||||||
|
dossiers_supprimes_recemment_count_per_procedure: 0,
|
||||||
dossiers_expirant_count_per_procedure: 0,
|
dossiers_expirant_count_per_procedure: 0,
|
||||||
followed_dossiers_count_per_procedure: 0,
|
followed_dossiers_count_per_procedure: 0,
|
||||||
procedure_ids_en_cours_with_notifications: [],
|
procedure_ids_en_cours_with_notifications: [],
|
||||||
|
|
|
@ -11,6 +11,7 @@ describe 'instructeurs/procedures/_tabs.html.haml', type: :view do
|
||||||
suivis_count: 0,
|
suivis_count: 0,
|
||||||
traites_count: 0,
|
traites_count: 0,
|
||||||
tous_count: 0,
|
tous_count: 0,
|
||||||
|
supprimes_recemment_count: 0,
|
||||||
archives_count: 0,
|
archives_count: 0,
|
||||||
expirant_count: 0,
|
expirant_count: 0,
|
||||||
has_en_cours_notifications: false,
|
has_en_cours_notifications: false,
|
||||||
|
|
Loading…
Reference in a new issue