Merge pull request #6721 from betagouv/main

2021-12-06-01
This commit is contained in:
mfo 2021-12-06 13:32:56 +01:00 committed by GitHub
commit 37aeae3db0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
63 changed files with 728 additions and 205 deletions

View file

@ -0,0 +1 @@
<svg height="202" viewBox="-.8 -.5 177 202" width="177" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-width="30"><path d="m33.7 64.3c-11.6 12.9-18.7 30-18.7 48.7 0 40.1 32.5 72.7 72.7 72.7 40.1 0 72.7-32.5 72.7-72.7 0-18.7-7.1-35.8-18.7-48.7"/><path d="m87.8 15v98"/></g></svg>

After

Width:  |  Height:  |  Size: 334 B

View file

@ -19,6 +19,10 @@
background-image: image-url("icons/unfollow-folder.svg"); background-image: image-url("icons/unfollow-folder.svg");
} }
&.standby {
background-image: image-url("icons/standby.svg");
}
&.archive { &.archive {
background-image: image-url("icons/archive.svg"); background-image: image-url("icons/archive.svg");
} }

View file

@ -251,7 +251,7 @@ module Administrateurs
end end
def procedure_params def procedure_params
editable_params = [:libelle, :description, :organisation, :direction, :lien_site_web, :cadre_juridique, :deliberation, :notice, :web_hook_url, :declarative_with_state, :logo, :auto_archive_on, :monavis_embed, :api_entreprise_token, :duree_conservation_dossiers_dans_ds] editable_params = [:libelle, :description, :organisation, :direction, :lien_site_web, :cadre_juridique, :deliberation, :notice, :web_hook_url, :declarative_with_state, :logo, :auto_archive_on, :monavis_embed, :api_entreprise_token, :duree_conservation_dossiers_dans_ds, :zone_id]
permited_params = if @procedure&.locked? permited_params = if @procedure&.locked?
params.require(:procedure).permit(*editable_params) params.require(:procedure).permit(*editable_params)
else else

View file

@ -19,6 +19,12 @@ module Instructeurs
end end
end end
def extend_conservation
dossier.update(conservation_extension: dossier.conservation_extension + 1.month)
flash[:notice] = t('views.instructeurs.dossiers.archived_dossier')
redirect_back(fallback_location: instructeur_dossier_path(@dossier.procedure, @dossier))
end
def geo_data def geo_data
send_data dossier.to_feature_collection.to_json, send_data dossier.to_feature_collection.to_json,
type: 'application/json', type: 'application/json',

View file

@ -17,7 +17,7 @@ module Instructeurs
@dossiers_a_suivre_count_per_procedure = dossiers.without_followers.en_cours.group('groupe_instructeurs.procedure_id').reorder(nil).count @dossiers_a_suivre_count_per_procedure = dossiers.without_followers.en_cours.group('groupe_instructeurs.procedure_id').reorder(nil).count
@dossiers_archived_count_per_procedure = dossiers.archived.group('groupe_instructeurs.procedure_id').count @dossiers_archived_count_per_procedure = dossiers.archived.group('groupe_instructeurs.procedure_id').count
@dossiers_termines_count_per_procedure = dossiers.termine.group('groupe_instructeurs.procedure_id').reorder(nil).count @dossiers_termines_count_per_procedure = dossiers.termine.group('groupe_instructeurs.procedure_id').reorder(nil).count
@dossiers_expirant_count_per_procedure = dossiers.termine_or_en_construction_close_to_expiration.group('groupe_instructeurs.procedure_id').count
groupe_ids = current_instructeur.groupe_instructeurs.pluck(:id) groupe_ids = current_instructeur.groupe_instructeurs.pluck(:id)
@followed_dossiers_count_per_procedure = current_instructeur @followed_dossiers_count_per_procedure = current_instructeur
@ -34,6 +34,7 @@ module Instructeurs
'suivis' => @followed_dossiers_count_per_procedure.sum { |_, v| v }, 'suivis' => @followed_dossiers_count_per_procedure.sum { |_, v| v },
'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 },
'archivés' => @dossiers_archived_count_per_procedure.sum { |_, v| v } 'archivés' => @dossiers_archived_count_per_procedure.sum { |_, v| v }
} }
@ -50,9 +51,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 = current_instructeur @a_suivre_count, @suivis_count, @traites_count, @tous_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') .fetch_values('a_suivre', 'suivis', 'traites', 'tous', 'archives', 'expirant')
dossiers_visibles = Dossier dossiers_visibles = Dossier
.where(groupe_instructeur_id: groupe_instructeur_ids) .where(groupe_instructeur_id: groupe_instructeur_ids)
@ -71,6 +72,7 @@ module Instructeurs
@termines_dossiers = dossiers_visibles.termine @termines_dossiers = dossiers_visibles.termine
@all_state_dossiers = dossiers_visibles.all_state @all_state_dossiers = dossiers_visibles.all_state
@archived_dossiers = dossiers_visibles.archived @archived_dossiers = dossiers_visibles.archived
@expirant_dossiers = dossiers_visibles.termine_or_en_construction_close_to_expiration
@dossiers = case statut @dossiers = case statut
when 'a-suivre' when 'a-suivre'
@ -88,6 +90,9 @@ module Instructeurs
when 'archives' when 'archives'
dossiers_count = @archives_count dossiers_count = @archives_count
@archived_dossiers @archived_dossiers
when 'expirant'
dossiers_count = @expirant_count
@expirant_dossiers
end end
notifications = current_instructeur.notifications_for_groupe_instructeurs(groupe_instructeur_ids) notifications = current_instructeur.notifications_for_groupe_instructeurs(groupe_instructeur_ids)

View file

@ -168,9 +168,9 @@ module Users
end end
def extend_conservation def extend_conservation
dossier.update(conservation_extension: dossier.conservation_extension + 1.month) dossier.update(conservation_extension: dossier.conservation_extension + dossier.procedure.duree_conservation_dossiers_dans_ds.months)
flash[:notice] = t('.archived_dossier') flash[:notice] = t('views.users.dossiers.archived_dossier', duree_conservation_dossiers_dans_ds: dossier.procedure.duree_conservation_dossiers_dans_ds)
redirect_to dossier_path(@dossier) redirect_back(fallback_location: dossier_path(@dossier))
end end
def modifier def modifier

View file

@ -34,7 +34,8 @@ class ProcedureDashboard < Administrate::BaseDashboard
closed_mail_template: MailTemplateField, closed_mail_template: MailTemplateField,
refused_mail_template: MailTemplateField, refused_mail_template: MailTemplateField,
without_continuation_mail_template: MailTemplateField, without_continuation_mail_template: MailTemplateField,
attestation_template: AttestationTemplateField attestation_template: AttestationTemplateField,
procedure_expires_when_termine_enabled: Field::Boolean
}.freeze }.freeze
# COLLECTION_ATTRIBUTES # COLLECTION_ATTRIBUTES
@ -79,13 +80,16 @@ class ProcedureDashboard < Administrate::BaseDashboard
:closed_mail_template, :closed_mail_template,
:refused_mail_template, :refused_mail_template,
:without_continuation_mail_template, :without_continuation_mail_template,
:attestation_template :attestation_template,
:procedure_expires_when_termine_enabled
].freeze ].freeze
# FORM_ATTRIBUTES # FORM_ATTRIBUTES
# an array of attributes that will be displayed # an array of attributes that will be displayed
# on the model's form (`new` and `edit`) pages. # on the model's form (`new` and `edit`) pages.
FORM_ATTRIBUTES = [].freeze FORM_ATTRIBUTES = [
:procedure_expires_when_termine_enabled
].freeze
# Overwrite this method to customize how procedures are displayed # Overwrite this method to customize how procedures are displayed
# across all pages of the admin dashboard. # across all pages of the admin dashboard.

View file

@ -91,20 +91,17 @@ class Dossier < ApplicationRecord
def passer_en_construction(instructeur: nil, processed_at: Time.zone.now) def passer_en_construction(instructeur: nil, processed_at: Time.zone.now)
build(state: Dossier.states.fetch(:en_construction), build(state: Dossier.states.fetch(:en_construction),
instructeur_email: instructeur&.email, instructeur_email: instructeur&.email,
process_expired: false,
processed_at: processed_at) processed_at: processed_at)
end end
def passer_en_instruction(instructeur: nil, processed_at: Time.zone.now) def passer_en_instruction(instructeur: nil, processed_at: Time.zone.now)
build(state: Dossier.states.fetch(:en_instruction), build(state: Dossier.states.fetch(:en_instruction),
instructeur_email: instructeur&.email, instructeur_email: instructeur&.email,
process_expired: false,
processed_at: processed_at) processed_at: processed_at)
end end
def accepter_automatiquement(processed_at: Time.zone.now) def accepter_automatiquement(processed_at: Time.zone.now)
build(state: Dossier.states.fetch(:accepte), build(state: Dossier.states.fetch(:accepte),
process_expired: proxy_association.owner.procedure.feature_enabled?(:procedure_process_expired_dossiers_termine),
processed_at: processed_at) processed_at: processed_at)
end end
@ -112,7 +109,6 @@ class Dossier < ApplicationRecord
build(state: Dossier.states.fetch(:accepte), build(state: Dossier.states.fetch(:accepte),
instructeur_email: instructeur&.email, instructeur_email: instructeur&.email,
motivation: motivation, motivation: motivation,
process_expired: proxy_association.owner.procedure.feature_enabled?(:procedure_process_expired_dossiers_termine),
processed_at: processed_at) processed_at: processed_at)
end end
@ -120,7 +116,6 @@ class Dossier < ApplicationRecord
build(state: Dossier.states.fetch(:refuse), build(state: Dossier.states.fetch(:refuse),
instructeur_email: instructeur&.email, instructeur_email: instructeur&.email,
motivation: motivation, motivation: motivation,
process_expired: proxy_association.owner.procedure.feature_enabled?(:procedure_process_expired_dossiers_termine),
processed_at: processed_at) processed_at: processed_at)
end end
@ -128,7 +123,6 @@ class Dossier < ApplicationRecord
build(state: Dossier.states.fetch(:sans_suite), build(state: Dossier.states.fetch(:sans_suite),
instructeur_email: instructeur&.email, instructeur_email: instructeur&.email,
motivation: motivation, motivation: motivation,
process_expired: proxy_association.owner.procedure.feature_enabled?(:procedure_process_expired_dossiers_termine),
processed_at: processed_at) processed_at: processed_at)
end end
end end
@ -300,11 +294,10 @@ class Dossier < ApplicationRecord
scope :interval_en_construction_close_to_expiration, -> do scope :interval_en_construction_close_to_expiration, -> do
state_en_construction.where("dossiers.en_construction_at + dossiers.conservation_extension + (duree_conservation_dossiers_dans_ds * INTERVAL '1 month') - INTERVAL :expires_in < :now", { now: Time.zone.now, expires_in: INTERVAL_BEFORE_EXPIRATION }) state_en_construction.where("dossiers.en_construction_at + dossiers.conservation_extension + (duree_conservation_dossiers_dans_ds * INTERVAL '1 month') - INTERVAL :expires_in < :now", { now: Time.zone.now, expires_in: INTERVAL_BEFORE_EXPIRATION })
end end
scope :interval_en_instruction_close_to_expiration, -> do
state_en_instruction.where("dossiers.en_instruction_at + (duree_conservation_dossiers_dans_ds * INTERVAL '1 month') - INTERVAL :expires_in < :now", { now: Time.zone.now, expires_in: INTERVAL_BEFORE_EXPIRATION })
end
scope :interval_termine_close_to_expiration, -> do scope :interval_termine_close_to_expiration, -> do
state_termine.where(id: Traitement.termine_close_to_expiration.select(:dossier_id).distinct) state_termine
.where(procedures: { procedure_expires_when_termine_enabled: true })
.where("dossiers.processed_at + dossiers.conservation_extension + (duree_conservation_dossiers_dans_ds * INTERVAL '1 month') - INTERVAL :expires_in < :now", { now: Time.zone.now, expires_in: INTERVAL_BEFORE_EXPIRATION })
end end
scope :brouillon_close_to_expiration, -> do scope :brouillon_close_to_expiration, -> do
@ -313,9 +306,6 @@ class Dossier < ApplicationRecord
scope :en_construction_close_to_expiration, -> do scope :en_construction_close_to_expiration, -> do
joins(:procedure).interval_en_construction_close_to_expiration joins(:procedure).interval_en_construction_close_to_expiration
end end
scope :en_instruction_close_to_expiration, -> do
joins(:procedure).interval_en_instruction_close_to_expiration
end
scope :termine_close_to_expiration, -> do scope :termine_close_to_expiration, -> do
joins(:procedure).interval_termine_close_to_expiration joins(:procedure).interval_termine_close_to_expiration
end end
@ -324,7 +314,13 @@ class Dossier < ApplicationRecord
joins(:procedure).scoping do joins(:procedure).scoping do
interval_brouillon_close_to_expiration interval_brouillon_close_to_expiration
.or(interval_en_construction_close_to_expiration) .or(interval_en_construction_close_to_expiration)
.or(interval_en_instruction_close_to_expiration) .or(interval_termine_close_to_expiration)
end
end
scope :termine_or_en_construction_close_to_expiration, -> do
joins(:procedure).scoping do
interval_en_construction_close_to_expiration
.or(interval_termine_close_to_expiration) .or(interval_termine_close_to_expiration)
end end
end end
@ -545,7 +541,11 @@ class Dossier < ApplicationRecord
end end
def expirable? def expirable?
[brouillon?, en_construction?, termine? && procedure.feature_enabled?(:procedure_process_expired_dossiers_termine)].any? [
brouillon?,
en_construction?,
termine? && procedure.procedure_expires_when_termine_enabled
].any?
end end
def approximative_expiration_date_reference def approximative_expiration_date_reference
@ -569,6 +569,7 @@ class Dossier < ApplicationRecord
end end
def close_to_expiration? def close_to_expiration?
return false if en_instruction?
approximative_expiration_date < Time.zone.now approximative_expiration_date < Time.zone.now
end end
@ -646,6 +647,10 @@ class Dossier < ApplicationRecord
parts.join parts.join
end end
def duree_totale_conservation_in_months
procedure.duree_conservation_dossiers_dans_ds + (conservation_extension / 1.month.to_i)
end
def avis_for_instructeur(instructeur) def avis_for_instructeur(instructeur)
if instructeur.dossiers.include?(self) if instructeur.dossiers.include?(self)
avis.order(created_at: :asc) avis.order(created_at: :asc)

View file

@ -237,8 +237,22 @@ 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) AS tous, COUNT(DISTINCT dossiers.id) FILTER (where not archived) AS tous,
COUNT(DISTINCT dossiers.id) FILTER (where archived) AS archives COUNT(DISTINCT dossiers.id) FILTER (where archived) AS archives,
COUNT(DISTINCT dossiers.id) FILTER (where
procedures.procedure_expires_when_termine_enabled
AND (
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
) OR (
dossiers.state in ('en_construction')
AND dossiers.en_construction_at + dossiers.conservation_extension + (duree_conservation_dossiers_dans_ds * INTERVAL '1 month') - INTERVAL :expires_in < :now
)
) AS expirant
FROM "dossiers" FROM "dossiers"
INNER JOIN "procedure_revisions"
ON "procedure_revisions"."id" = "dossiers"."revision_id"
INNER JOIN "procedures"
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
@ -250,7 +264,9 @@ class Instructeur < ApplicationRecord
sanitized_query = ActiveRecord::Base.sanitize_sql([ sanitized_query = ActiveRecord::Base.sanitize_sql([
query, query,
instructeur_id: id, instructeur_id: id,
groupe_instructeur_ids: groupe_instructeur_ids groupe_instructeur_ids: groupe_instructeur_ids,
now: Time.zone.now,
expires_in: Dossier::INTERVAL_BEFORE_EXPIRATION
]) ])
Dossier.connection.select_all(sanitized_query).first Dossier.connection.select_all(sanitized_query).first

View file

@ -34,6 +34,7 @@
# monavis_embed :text # monavis_embed :text
# organisation :string # organisation :string
# path :string not null # path :string not null
# procedure_expires_when_termine_enabled :boolean default(FALSE)
# published_at :datetime # published_at :datetime
# routing_criteria_name :text default("Votre ville") # routing_criteria_name :text default("Votre ville")
# routing_enabled :boolean # routing_enabled :boolean
@ -48,6 +49,7 @@
# parent_procedure_id :bigint # parent_procedure_id :bigint
# published_revision_id :bigint # published_revision_id :bigint
# service_id :bigint # service_id :bigint
# zone_id :bigint
# #
class Procedure < ApplicationRecord class Procedure < ApplicationRecord
@ -85,6 +87,7 @@ class Procedure < ApplicationRecord
belongs_to :parent_procedure, class_name: 'Procedure', optional: true belongs_to :parent_procedure, class_name: 'Procedure', optional: true
belongs_to :canonical_procedure, class_name: 'Procedure', optional: true belongs_to :canonical_procedure, class_name: 'Procedure', optional: true
belongs_to :service, optional: true belongs_to :service, optional: true
belongs_to :zone, optional: true
def active_revision def active_revision
brouillon? ? draft_revision : published_revision brouillon? ? draft_revision : published_revision

View file

@ -6,6 +6,7 @@
# instructeur_email :string # instructeur_email :string
# motivation :string # motivation :string
# process_expired :boolean # process_expired :boolean
# process_expired_migrated :boolean default(FALSE)
# processed_at :datetime # processed_at :datetime
# state :string # state :string
# dossier_id :bigint # dossier_id :bigint

13
app/models/zone.rb Normal file
View file

@ -0,0 +1,13 @@
# == Schema Information
#
# Table name: zones
#
# id :bigint not null, primary key
# acronym :string
# label :string
# created_at :datetime not null
# updated_at :datetime not null
#
class Zone < ApplicationRecord
validates :acronym, presence: true, uniqueness: true
end

View file

@ -95,7 +95,6 @@ class ExpiredDossiersDeletionService
deleted_dossier_ids << dossier.id deleted_dossier_ids << dossier.id
end end
end end
user_notifications.each do |(email, dossier_ids)| user_notifications.each do |(email, dossier_ids)|
dossier_ids = dossier_ids.intersection(deleted_dossier_ids) dossier_ids = dossier_ids.intersection(deleted_dossier_ids)
if dossier_ids.present? if dossier_ids.present?

View file

@ -13,6 +13,11 @@
%span.mandatory * %span.mandatory *
= f.text_area :description, rows: '6', placeholder: 'Description de la démarche, destinataires, etc. ', class: 'form-control' = f.text_area :description, rows: '6', placeholder: 'Description de la démarche, destinataires, etc. ', class: 'form-control'
= f.label :zone do
= t('zone', scope: 'activerecord.attributes.procedure')
%span.mandatory *
= f.collection_select :zone_id, Zone.order(:label), :id, :label, prompt: true
%h3.header-subsection Logo de la démarche %h3.header-subsection Logo de la démarche
= image_upload_and_render f, @procedure.logo = image_upload_and_render f, @procedure.logo

View file

@ -0,0 +1,31 @@
-# small expires mention
- if dossier.expirable?
%p.expires_at.mb-2
%small
= t("shared.dossiers.header.expires_at.#{dossier.state}", date: safe_expiration_date(dossier), duree_conservation_totale: dossier.duree_totale_conservation_in_months)
- if dossier.conservation_extension.positive?
= t('instructeurs.dossiers.header.banner.expiration_date_extended')
-# big banner warning
- if dossier.close_to_expiration?
.card.warning.mb-3
.card-title= t('instructeurs.dossiers.header.banner.title')
%p
- if dossier.brouillon?
= t('instructeurs.dossiers.header.banner.states.brouillon')
- elsif dossier.en_construction?
= t('instructeurs.dossiers.header.banner.states.en_construction')
- elsif dossier.termine?
= t('instructeurs.dossiers.header.banner.states.termine')
- if dossier.expiration_can_be_extended?
%br
= button_to repousser_expiration_instructeur_dossier_path(dossier.procedure, dossier), class: 'button mt-2', id: 'test-instructeur-repousser-expiration' do
%span.icon.standby
= t('instructeurs.dossiers.header.banner.button_delay_expiration')
- else
%p.expires_at_en_instruction
%small= t("shared.dossiers.header.expires_at.en_instruction")

View file

@ -9,9 +9,12 @@
= dossier.procedure.libelle.truncate_words(10) = dossier.procedure.libelle.truncate_words(10)
%li %li
= "Dossier nº #{dossier.id}" = "Dossier nº #{dossier.id}"
.header-actions .header-actions
= render partial: 'instructeurs/dossiers/header_actions', locals: { dossier: dossier } = render partial: 'instructeurs/dossiers/header_actions', locals: { dossier: dossier }
= render(partial: 'instructeurs/dossiers/expiration_banner', locals: {dossier: dossier})
%ul.tabs %ul.tabs
- notifications_summary = current_instructeur.notifications_for_dossier(dossier) - notifications_summary = current_instructeur.notifications_for_dossier(dossier)

View file

@ -27,7 +27,8 @@
dossier_id: dossier.id, dossier_id: dossier.id,
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? }
.state-button .state-button

View file

@ -1,9 +1,14 @@
- if Dossier::TERMINE.include?(state) - if close_to_expiration || Dossier::TERMINE.include?(state)
.dropdown.user-dossier-actions .dropdown.user-dossier-actions
%button.button.dropdown-button{ 'aria-expanded' => 'false', 'aria-controls' => 'actions-menu' } %button.button.dropdown-button{ 'aria-expanded' => 'false', 'aria-controls' => 'actions-menu' }
Actions Actions
#actions-menu.dropdown-content.fade-in-down #actions-menu.dropdown-content.fade-in-down
%ul.dropdown-items %ul.dropdown-items
- if close_to_expiration
%li
= link_to repousser_expiration_instructeur_dossier_path(procedure_id, dossier_id), method: :post do
%span.icon.standby
.dropdown-description= t('instructeurs.dossiers.header.banner.button_delay_expiration')
- if archived - if archived
%li %li
= link_to unarchive_instructeur_dossier_path(procedure_id, dossier_id), method: :patch do = link_to unarchive_instructeur_dossier_path(procedure_id, dossier_id), method: :patch do

View file

@ -17,30 +17,3 @@
| |
= link_to 'contacter les usagers (brouillon)', email_usagers_instructeur_procedure_path(procedure), class: 'header-link' = link_to 'contacter les usagers (brouillon)', email_usagers_instructeur_procedure_path(procedure), class: 'header-link'
%ul.tabs
= tab_item('à suivre',
instructeur_procedure_path(procedure, statut: 'a-suivre'),
active: statut == 'a-suivre',
badge: number_with_html_delimiter(a_suivre_count))
= tab_item(t('pluralize.followed', count: suivis_count),
instructeur_procedure_path(procedure, statut: 'suivis'),
active: statut == 'suivis',
badge: number_with_html_delimiter(suivis_count),
notification: has_en_cours_notifications)
= tab_item(t('pluralize.processed', count: traites_count),
instructeur_procedure_path(procedure, statut: 'traites'),
active: statut == 'traites',
badge: number_with_html_delimiter(traites_count),
notification: has_termine_notifications)
= tab_item('au total',
instructeur_procedure_path(procedure, statut: 'tous'),
active: statut == 'tous',
badge: number_with_html_delimiter(tous_count))
= tab_item(t('pluralize.archived', count: archives_count),
instructeur_procedure_path(procedure, statut: 'archives'),
active: statut == 'archives',
badge: number_with_html_delimiter(archives_count))

View file

@ -44,6 +44,17 @@
= number_with_html_delimiter(dossier_count) = number_with_html_delimiter(dossier_count)
.stats-legend .stats-legend
= t('pluralize.case', count: dossier_count) = t('pluralize.case', count: dossier_count)
- if p.procedure_expires_when_termine_enabled
%li
%object
= link_to(instructeur_procedure_path(p, statut: 'expirant')) do
- expirant_count = dossiers_expirant_count_per_procedure[p.id] || 0
.stats-number
= number_with_html_delimiter(expirant_count)
.stats-legend
= t('pluralize.dossiers_close_to_expiration', count: expirant_count)
%li %li
%object %object
= link_to(instructeur_procedure_path(p, statut: 'archives')) do = link_to(instructeur_procedure_path(p, statut: 'archives')) do

View file

@ -0,0 +1,34 @@
%ul.tabs.mt-3
= tab_item('à suivre',
instructeur_procedure_path(procedure, statut: 'a-suivre'),
active: statut == 'a-suivre',
badge: number_with_html_delimiter(a_suivre_count))
= tab_item(t('pluralize.followed', count: suivis_count),
instructeur_procedure_path(procedure, statut: 'suivis'),
active: statut == 'suivis',
badge: number_with_html_delimiter(suivis_count),
notification: has_en_cours_notifications)
= tab_item(t('pluralize.processed', count: traites_count),
instructeur_procedure_path(procedure, statut: 'traites'),
active: statut == 'traites',
badge: number_with_html_delimiter(traites_count),
notification: has_termine_notifications)
= tab_item('au total',
instructeur_procedure_path(procedure, statut: 'tous'),
active: statut == 'tous',
badge: number_with_html_delimiter(tous_count))
- if procedure.procedure_expires_when_termine_enabled
= tab_item(t('pluralize.dossiers_close_to_expiration', count: expirant_count),
instructeur_procedure_path(procedure, statut: 'expirant'),
active: statut == 'expirant',
badge: number_with_html_delimiter(expirant_count))
= tab_item(t('pluralize.archived', count: archives_count),
instructeur_procedure_path(procedure, statut: 'archives'),
active: statut == 'archives',
badge: number_with_html_delimiter(archives_count))

View file

@ -12,6 +12,7 @@
dossiers_a_suivre_count_per_procedure: @dossiers_a_suivre_count_per_procedure, dossiers_a_suivre_count_per_procedure: @dossiers_a_suivre_count_per_procedure,
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,
followed_dossiers_count_per_procedure: @followed_dossiers_count_per_procedure, followed_dossiers_count_per_procedure: @followed_dossiers_count_per_procedure,
procedure_ids_en_cours_with_notifications: @procedure_ids_en_cours_with_notifications, procedure_ids_en_cours_with_notifications: @procedure_ids_en_cours_with_notifications,
procedure_ids_termines_with_notifications: @procedure_ids_termines_with_notifications } procedure_ids_termines_with_notifications: @procedure_ids_termines_with_notifications }

View file

@ -7,19 +7,21 @@
.procedure-logo{ style: "background-image: url(#{@procedure.logo_url})", .procedure-logo{ style: "background-image: url(#{@procedure.logo_url})",
role: 'img', 'aria-label': "logo de la démarche #{@procedure.libelle}" } role: 'img', 'aria-label': "logo de la démarche #{@procedure.libelle}" }
= render partial: 'header', locals: { procedure: @procedure, = render partial: 'header', locals: { procedure: @procedure, statut: @statut }
.procedure-actions
= render partial: "download_dossiers", locals: { procedure: @procedure, exports: @exports, dossier_count: @tous_count + @archives_count }
.container.flex= render partial: "tabs", locals: { procedure: @procedure,
statut: @statut, statut: @statut,
a_suivre_count: @a_suivre_count, a_suivre_count: @a_suivre_count,
suivis_count: @suivis_count, suivis_count: @suivis_count,
traites_count: @traites_count, traites_count: @traites_count,
tous_count: @tous_count, tous_count: @tous_count,
archives_count: @archives_count, archives_count: @archives_count,
expirant_count: @expirant_count,
has_en_cours_notifications: @has_en_cours_notifications, has_en_cours_notifications: @has_en_cours_notifications,
has_termine_notifications: @has_termine_notifications } has_termine_notifications: @has_termine_notifications }
.procedure-actions
= render partial: "download_dossiers", locals: { procedure: @procedure, exports: @exports, dossier_count: @tous_count + @archives_count }
.container .container
- if @statut == 'a-suivre' - if @statut == 'a-suivre'
%p.explication-onglet Aucun instructeur nest affecté au suivi de ces dossiers. Soyez le premier ! %p.explication-onglet Aucun instructeur nest affecté au suivi de ces dossiers. Soyez le premier !
@ -42,6 +44,9 @@
= link_to deleted_dossiers_instructeur_procedure_path(@procedure) do = link_to deleted_dossiers_instructeur_procedure_path(@procedure) do
%span.icon.delete %span.icon.delete
Afficher les dossiers supprimés Afficher les dossiers supprimés
- if @statut == 'expirant'
%p.explication-onglet Les dossiers n'expireront pas avant la période de conservation des données.
- if @filtered_sorted_paginated_ids.present? || @current_filters.count > 0 - if @filtered_sorted_paginated_ids.present? || @current_filters.count > 0
- pagination = paginate @filtered_sorted_paginated_ids - pagination = paginate @filtered_sorted_paginated_ids
@ -124,7 +129,8 @@
dossier_id: p.dossier_id, dossier_id: p.dossier_id,
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' }
= pagination = pagination
- else - else

View file

@ -81,7 +81,8 @@
dossier_id: p.dossier_id, dossier_id: p.dossier_id,
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: nil }
- else - else
%td %td

View file

@ -1,25 +0,0 @@
-# small expires mention
- if dossier.expirable?
%p.expires_at
%small= t("shared.dossiers.header.expires_at.#{dossier.state}", date: safe_expiration_date(dossier))
-# big banner warning
- if dossier.close_to_expiration?
.card.warning.mt-2.mb-3
.card-title= t('shared.dossiers.header.banner.title')
%p
- if dossier.brouillon?
= t('shared.dossiers.header.banner.states.brouillon')
- elsif dossier.en_construction?
= t('shared.dossiers.header.banner.states.en_construction')
- elsif dossier.termine?
= t('shared.dossiers.header.banner.states.termine')
- if dossier.expiration_can_be_extended?
%br
= button_to t('shared.dossiers.header.banner.button_delay_expiration'), users_dossier_repousser_expiration_path(dossier), class: 'button secondary mt-2'
- else
%p.expires_at_en_instruction
%small= t("shared.dossiers.header.expires_at.en_instruction")

View file

@ -6,7 +6,7 @@
= t('views.users.dossiers.show.header.dossier_number', dossier_id: dossier.id) = t('views.users.dossiers.show.header.dossier_number', dossier_id: dossier.id)
= t('views.users.dossiers.show.header.created_date', date_du_dossier: I18n.l(dossier.created_at)) = t('views.users.dossiers.show.header.created_date', date_du_dossier: I18n.l(dossier.created_at))
= render(partial: 'shared/dossiers/expiration_banner', locals: {dossier: dossier}) = render(partial: 'users/dossiers/expiration_banner', locals: {dossier: dossier})
.header-actions .header-actions
- if current_user.owns?(dossier) - if current_user.owns?(dossier)

View file

@ -20,8 +20,8 @@
= link_to(url_for_dossier(dossier), class: 'cell-link') do = link_to(url_for_dossier(dossier), class: 'cell-link') do
= procedure_libelle(dossier.procedure) = procedure_libelle(dossier.procedure)
- if dossiers.present? - if dossiers.present?
%td.cell-link %td
= demandeur_dossier(dossier) %span.cell-link= demandeur_dossier(dossier)
%td.status-col %td.status-col
= status_badge(dossier.state) = status_badge(dossier.state)
%td.updated-at-col.cell-link %td.updated-at-col.cell-link

View file

@ -0,0 +1,27 @@
-# small expires mention
- if dossier.expirable?
%p.expires_at.mb-2
%small= t("shared.dossiers.header.expires_at.#{dossier.state}", date: safe_expiration_date(dossier), duree_conservation_totale: dossier.duree_totale_conservation_in_months)
-# big banner warning
- if dossier.close_to_expiration?
.card.warning.mb-3
.card-title= t('users.dossiers.header.banner.title')
%p
- if dossier.brouillon?
= t('users.dossiers.header.banner.states.brouillon')
- elsif dossier.en_construction?
= t('users.dossiers.header.banner.states.en_construction')
- elsif dossier.termine?
= t('users.dossiers.header.banner.states.termine')
- if dossier.expiration_can_be_extended?
%br
= button_to users_dossier_repousser_expiration_path(dossier), class: 'button mt-2', id: 'test-user-repousser-expiration' do
%span.icon.standby
= t('users.dossiers.header.banner.button_delay_expiration', duree_conservation_dossiers_dans_ds: dossier.procedure.duree_conservation_dossiers_dans_ds)
- else
%p.expires_at_en_instruction
%small= t("shared.dossiers.header.expires_at.en_instruction")

View file

@ -32,6 +32,12 @@
active: @statut == 'dossiers-invites', active: @statut == 'dossiers-invites',
badge: number_with_html_delimiter(@dossiers_invites.count)) badge: number_with_html_delimiter(@dossiers_invites.count))
- if @dossiers_close_to_expiration.count > 0
= tab_item(t('pluralize.dossiers_close_to_expiration', count: @dossiers_close_to_expiration.count),
dossiers_path(statut: 'dossiers-expirant'),
active: @statut == 'dossiers-expirant',
badge: number_with_html_delimiter(@dossiers_close_to_expiration.count))
- if @dossiers_supprimes.present? - if @dossiers_supprimes.present?
= tab_item(t('pluralize.dossiers_supprimes', count: @dossiers_supprimes.count), = tab_item(t('pluralize.dossiers_supprimes', count: @dossiers_supprimes.count),
dossiers_path(statut: 'dossiers-supprimes'), dossiers_path(statut: 'dossiers-supprimes'),
@ -44,12 +50,6 @@
active: @statut == 'dossiers-transferes', active: @statut == 'dossiers-transferes',
badge: number_with_html_delimiter(@dossier_transfers.count)) badge: number_with_html_delimiter(@dossier_transfers.count))
- if @dossiers_close_to_expiration.count > 0
= tab_item(t('pluralize.dossiers_close_to_expiration', count: @dossiers_close_to_expiration.count),
dossiers_path(statut: 'dossiers-expirant'),
active: @statut == 'dossiers-expirant',
badge: number_with_html_delimiter(@dossiers_close_to_expiration.count))
.container .container
- if @statut == "en-cours" - if @statut == "en-cours"
= render partial: "dossiers_list", locals: { dossiers: @user_dossiers } = render partial: "dossiers_list", locals: { dossiers: @user_dossiers }

View file

@ -10,7 +10,7 @@
- if dossier.en_construction_at.present? - if dossier.en_construction_at.present?
= t('views.users.dossiers.show.header.submit_date', date_du_dossier: I18n.l(dossier.en_construction_at)) = t('views.users.dossiers.show.header.submit_date', date_du_dossier: I18n.l(dossier.en_construction_at))
= render(partial: 'shared/dossiers/expiration_banner', locals: {dossier: dossier}) = render(partial: 'users/dossiers/expiration_banner', locals: {dossier: dossier})
- if current_user.owns?(dossier) - if current_user.owns?(dossier)

View file

@ -134,11 +134,13 @@ en:
edit_identity: "Edit identity data" edit_identity: "Edit identity data"
instructeurs: instructeurs:
dossiers: dossiers:
archived_dossier: "This file will be kept for an additional month"
deleted_by_user: "File deleted by user" deleted_by_user: "File deleted by user"
avis: avis:
introduction_file_explaination: "File attached to the request for advice" introduction_file_explaination: "File attached to the request for advice"
users: users:
dossiers: dossiers:
archived_dossier: "Your file will be kept %{duree_conservation_dossiers_dans_ds} more months"
autosave: autosave:
autosave_draft: Your draft is automatically saved. autosave_draft: Your draft is automatically saved.
more_infos: More informations more_infos: More informations
@ -382,8 +384,6 @@ en:
ask_deletion: ask_deletion:
undergoingreview: "Your file is undergoing review. It is no longer possible to delete your file. To cancel the undergoingreview contact the adminitration via the mailbox." undergoingreview: "Your file is undergoing review. It is no longer possible to delete your file. To cancel the undergoingreview contact the adminitration via the mailbox."
deleted_dossier: "Your file has been successfully deleted" deleted_dossier: "Your file has been successfully deleted"
extend_conservation:
archived_dossier: "Your file will be archived for an additional month"
update_brouillon: update_brouillon:
draft_saved: "Your draft has been saved." draft_saved: "Your draft has been saved."
etablissement: etablissement:

View file

@ -130,11 +130,13 @@ fr:
edit_identity: "Modifier lidentité" edit_identity: "Modifier lidentité"
instructeurs: instructeurs:
dossiers: dossiers:
archived_dossier: "Le dossier sera conservé 1 mois supplémentaire"
deleted_by_user: "Dossier supprimé par l'usager" deleted_by_user: "Dossier supprimé par l'usager"
avis: avis:
introduction_file_explaination: "Fichier joint à la demande davis" introduction_file_explaination: "Fichier joint à la demande davis"
users: users:
dossiers: dossiers:
archived_dossier: "Votre dossier sera conservé %{duree_conservation_dossiers_dans_ds} mois supplémentaire"
autosave: autosave:
autosave_draft: Votre brouillon est automatiquement enregistré. autosave_draft: Votre brouillon est automatiquement enregistré.
more_infos: En savoir plus more_infos: En savoir plus
@ -248,6 +250,8 @@ fr:
<< : *default_attributes << : *default_attributes
super_admin: super_admin:
<< : *default_attributes << : *default_attributes
procedure:
zone: Organisme qui met en œuvre la démarche
errors: errors:
messages: messages:
not_a_phone: 'Numéro de téléphone invalide' not_a_phone: 'Numéro de téléphone invalide'
@ -363,9 +367,9 @@ fr:
one: demande de transfert one: demande de transfert
other: demandes de transfert other: demandes de transfert
dossiers_close_to_expiration: dossiers_close_to_expiration:
zero: dossier expirant zero: expirant
one: dossier expirant one: expirant
other: dossiers expirant other: expirant
dossier_trouve: dossier_trouve:
zero: 0 dossier trouvé zero: 0 dossier trouvé
one: 1 dossier trouvé one: 1 dossier trouvé
@ -387,13 +391,12 @@ fr:
test_procedure: "Ce dossier est déposé sur une démarche en test. Toute modification de la démarche par ladministrateur (ajout d'un champ, publication de la démarche...) entraînera sa suppression." test_procedure: "Ce dossier est déposé sur une démarche en test. Toute modification de la démarche par ladministrateur (ajout d'un champ, publication de la démarche...) entraînera sa suppression."
no_access: "Vous navez pas accès à ce dossier" no_access: "Vous navez pas accès à ce dossier"
no_longer_editable: "Votre dossier ne peut plus être modifié" no_longer_editable: "Votre dossier ne peut plus être modifié"
create_commentaire: create_commentaire:
message_send: "Votre message a bien été envoyé à linstructeur en charge de votre dossier." message_send: "Votre message a bien été envoyé à linstructeur en charge de votre dossier."
ask_deletion: ask_deletion:
undergoingreview: "Linstruction de votre dossier a commencé, il nest plus possible de supprimer votre dossier. Si vous souhaitez annuler linstruction contactez votre administration par la messagerie de votre dossier." undergoingreview: "Linstruction de votre dossier a commencé, il nest plus possible de supprimer votre dossier. Si vous souhaitez annuler linstruction contactez votre administration par la messagerie de votre dossier."
deleted_dossier: "Votre dossier a bien été supprimé." deleted_dossier: "Votre dossier a bien été supprimé."
extend_conservation:
archived_dossier: "Votre dossier sera conservé un mois supplémentaire"
update_brouillon: update_brouillon:
draft_saved: "Votre brouillon a bien été sauvegardé." draft_saved: "Votre brouillon a bien été sauvegardé."
etablissement: etablissement:

View file

@ -9,19 +9,12 @@ en:
code_postal_notice: It is usually composed of 5 digits. code_postal_notice: It is usually composed of 5 digits.
header: header:
expires_at: expires_at:
brouillon: "Expires at %{date}" brouillon: "Expires at %{date} (%{duree_conservation_totale} months after the creation of this file)"
en_construction: "Expires at %{date}" en_construction: "Expires at %{date} (%{duree_conservation_totale} months after the last la edition of this file)"
en_instruction: "This file is being instructed, the administration will answer as soon as possible" en_instruction: "This file is being instructed, the administration will answer as soon as possible"
accepte: "Expires at %{date}" accepte: "Expires at %{date} (%{duree_conservation_totale} months after the acceptation of this file)"
refuse: "Expires at %{date}" refuse: "Expires at %{date} (%{duree_conservation_totale} months after the rejection of this file)"
sans_suite: "Expires at %{date}" sans_suite: "Expires at %{date} (%{duree_conservation_totale} months after this file had been closed)"
banner:
title: Your file will expire
states:
brouillon: Your file is still in draft and will soon expire. So it will be deleted soon without being instructed. If you want to pursue your procedure you can submit it now. Otherwise you are able to delay its expiration by clicking on the underneath button.
en_construction: Your file is pending for instruction. The maximum delay is 6 months, but your can extend the duration by a month by clicking on the underneath button.
termine: Your file had been processed and will soon expire.So it will be deleted soon. If you want to keep it, your can dowload a PDF file of it.
button_delay_expiration: "Delay deletion"
champs: champs:
cnaf: cnaf:
show: show:

View file

@ -9,19 +9,13 @@ fr:
code_postal_notice: Il est généralement composé de 5 chiffres. code_postal_notice: Il est généralement composé de 5 chiffres.
header: header:
expires_at: expires_at:
brouillon: "Expirera le %{date}" brouillon: "Expirera le %{date} (%{duree_conservation_totale} mois après la création du dossier)"
en_construction: "Expirera le %{date}" en_construction: "Expirera le %{date} (%{duree_conservation_totale} mois après la dernière date d'édition)"
en_instruction: "Ce dossier est en instruction, il n'expirera pas" en_instruction: "Ce dossier est en instruction, il n'expirera pas"
accepte: "Expirera le %{date}" accepte: "Expirera le %{date} (%{duree_conservation_totale} mois après l'acceptation du dossier)"
refuse: "Expirera le %{date}" refuse: "Expirera le %{date} (%{duree_conservation_totale} mois après le refus du dossier)"
sans_suite: "Expirera le %{date}" sans_suite: "Expirera le %{date} (%{duree_conservation_totale} mois après que le dossier aie été classé sans suite)"
banner:
title: Votre dossier va expirer
states:
brouillon: Votre dossier est en brouillon, mais va bientôt expirer. Cela signifie quil va bientôt être supprimé sans avoir été déposé. Si vous souhaitez le conserver afin de poursuivre la démarche, vous pouvez le conserver un mois de plus en cliquant sur le bouton ci-dessous.
en_construction: Votre dossier est en attente de prise en charge par l'administration. Le delais de prise en charge maximale est de 6 mois. Vous pouvez toutefois entendre cette durée d'un mois en cliquant sur le bouton suivant.
termine: Le traitement de votre dossier est terminé, mais il va bientôt expirer. Cela signifie quil va bientôt être supprimé. Si vous souhaitez conserver une trace, vous pouvez le télécharger au format PDF.
button_delay_expiration: "Repousser sa suppression"
champs: champs:
cnaf: cnaf:

View file

@ -18,6 +18,8 @@ fr:
archive_pending_html: Archive en cours de création<br>(demandée il y a %{created_period}) archive_pending_html: Archive en cours de création<br>(demandée il y a %{created_period})
archive_ready_html: Télécharger larchive<br>(demandée il y a %{generated_period}) archive_ready_html: Télécharger larchive<br>(demandée il y a %{generated_period})
dossiers: dossiers:
extend_conservation:
archived_dossier: "Le dossier sera conservé 1 mois supplémentaire"
decisions_rendues_block: decisions_rendues_block:
without_email: without_email:
en_construction: Le %{processed_at} ce dossier a été passé en construction en_construction: Le %{processed_at} ce dossier a été passé en construction

View file

@ -0,0 +1,12 @@
en:
instructeurs:
dossiers:
header:
banner:
expiration_date_extended: " the expiration date had already been extended"
title: This file will expire
states:
brouillon: "" # not applicable, instructeur does not see brouillons
en_construction: This file is pending for instruction. The maximum delay is 6 months, you can extend the duration by a month by clicking on the underneath button.
termine: This file had been processed and will soon expire. So it will be deleted soon. If you want to keep it, you can dowload a PDF file of it.
button_delay_expiration: "Keep for one more month"

View file

@ -0,0 +1,12 @@
fr:
instructeurs:
dossiers:
header:
banner:
expiration_date_extended: " la date de conservation a déjà été etendue"
title: Ce dossier va expirer
states:
brouillon: "" # not applicable, instructeur does not see brouillons
en_construction: Ce dossier est en attente de prise en charge. Vous pouvez toutefois entendre cette durée d'un mois en cliquant sur le bouton suivant.
termine: Le traitement de ce dossier est terminé, mais il va bientôt expirer. Cela signifie quil va bientôt être supprimé. Si vous souhaitez conserver une trace, vous pouvez le télécharger au format PDF.
button_delay_expiration: "Conserver un mois de plus"

View file

@ -0,0 +1,11 @@
en:
users:
dossiers:
header:
banner:
title: Your file will expire
states:
brouillon: Your file is still in draft and will soon expire. So it will be deleted soon without being instructed. If you want to pursue your procedure you can submit it now. Otherwise you are able to delay its expiration by clicking on the underneath button.
en_construction: Your file is pending for instruction. The maximum delay is 6 months, but you can extend the duration by a month by clicking on the underneath button.
termine: Your file had been processed and will soon expire. So it will be deleted soon. If you want to keep it, you can dowload a PDF file of it.
button_delay_expiration: "Keep for %{duree_conservation_dossiers_dans_ds} more months"

View file

@ -0,0 +1,11 @@
fr:
users:
dossiers:
header:
banner:
title: Votre dossier va expirer
states:
brouillon: Votre dossier est en brouillon, mais va bientôt expirer. Cela signifie quil va bientôt être supprimé sans avoir été déposé. Si vous souhaitez le conserver afin de poursuivre la démarche, vous pouvez le conserver un mois de plus en cliquant sur le bouton ci-dessous.
en_construction: Votre dossier est en attente de prise en charge par l'administration. Le delais de prise en charge maximale est de 6 mois. Vous pouvez toutefois entendre cette durée d'un mois en cliquant sur le bouton suivant.
termine: Le traitement de votre dossier est terminé, mais il va bientôt expirer. Cela signifie quil va bientôt être supprimé. Si vous souhaitez conserver une trace, vous pouvez le télécharger au format PDF.
button_delay_expiration: "Conserver %{duree_conservation_dossiers_dans_ds} mois supplémentaires "

View file

@ -10,7 +10,7 @@ Rails.application.routes.draw do
# #
namespace :manager do namespace :manager do
resources :procedures, only: [:index, :show] do resources :procedures, only: [:index, :show, :edit, :update] do
post 'whitelist', on: :member post 'whitelist', on: :member
post 'draft', on: :member post 'draft', on: :member
post 'discard', on: :member post 'discard', on: :member
@ -361,7 +361,7 @@ Rails.application.routes.draw do
resources :dossiers, only: [:show], param: :dossier_id do resources :dossiers, only: [:show], param: :dossier_id do
member do member do
resources :commentaires, only: [:destroy] resources :commentaires, only: [:destroy]
post 'repousser-expiration' => 'dossiers#extend_conservation'
get 'attestation' get 'attestation'
get 'geo_data' get 'geo_data'
get 'apercu_attestation' get 'apercu_attestation'

37
config/zones.yml Normal file
View file

@ -0,0 +1,37 @@
ministeres:
- MAA:
label: "Ministère de l'Agriculture et de l'Alimentation"
- MC:
label: "Ministère de la Culture"
- MAS:
label: "Ministère des Solidarités et de la Santé"
- MTEI:
label: "Ministère du Travail"
- MEAE:
label: "Ministère de l'Europe et des Affaires étrangères"
- MEF:
label: "Ministère de l'Économie, des Finances et de la Relance"
- MJS:
label: "Ministère de la Jeunesse et des Sports"
- EN:
label: "Ministère de l'Éducation nationale, de la Jeunesse et des Sports"
- ESR:
label: "Ministère de l'Enseignement supérieur, de la Recherche et de l'Innovation"
- MI:
label: "Ministère de l'Intérieur"
- MInArm:
label: "Ministère des Armées"
- MJ:
label: "Ministère de la Justice"
- MTES:
label: "Ministère de la Transition écologique"
- MCTRCT:
label: "Ministère de la Cohésion des territoires et des Relations avec les collectivités territoriales"
- SPM:
label: "Premier ministre"
- MER:
label: "Ministère de la Mer"
- MTFP:
label: "Ministère de la Transformation et de la Fonction publiques"
- OM:
label: "Ministère des Outre-mer"

View file

@ -0,0 +1,17 @@
class MoveProcessExpireToProcedures < ActiveRecord::Migration[6.1]
include Database::MigrationHelpers
disable_ddl_transaction!
def up
add_column :procedures, :procedure_expires_when_termine_enabled, :boolean, default: false
add_column :traitements, :process_expired_migrated, :boolean, default: false
add_concurrent_index :procedures, :procedure_expires_when_termine_enabled
end
def down
remove_index :procedures, name: :index_procedures_on_process_expired
remove_column :traitements, :process_expired_migrated
remove_column :procedures, :procedure_expires_when_termine_enabled
end
end

View file

@ -0,0 +1,10 @@
class CreateZones < ActiveRecord::Migration[6.1]
def change
create_table :zones do |t|
t.string :acronym, null: false, index: { unique: true }
t.string :label
t.timestamps
end
end
end

View file

@ -0,0 +1,5 @@
class AddZoneToProcedures < ActiveRecord::Migration[6.1]
def change
add_reference :procedures, :zone, null: true, foreign_key: true
end
end

View file

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

View file

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2021_11_26_150915) do ActiveRecord::Schema.define(version: 2021_12_01_135804) 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"
@ -320,12 +320,12 @@ ActiveRecord::Schema.define(version: 2021_11_26_150915) do
t.interval "conservation_extension", default: "PT0S" t.interval "conservation_extension", default: "PT0S"
t.string "deleted_user_email_never_send" t.string "deleted_user_email_never_send"
t.datetime "declarative_triggered_at" t.datetime "declarative_triggered_at"
t.index "to_tsvector('french'::regconfig, (search_terms || private_search_terms))", name: "index_dossiers_on_search_terms_private_search_terms", using: :gin
t.index "to_tsvector('french'::regconfig, search_terms)", name: "index_dossiers_on_search_terms", using: :gin
t.bigint "dossier_transfer_id" t.bigint "dossier_transfer_id"
t.datetime "identity_updated_at" t.datetime "identity_updated_at"
t.datetime "depose_at" t.datetime "depose_at"
t.datetime "hidden_by_user_at" t.datetime "hidden_by_user_at"
t.index "to_tsvector('french'::regconfig, (search_terms || private_search_terms))", name: "index_dossiers_on_search_terms_private_search_terms", using: :gin
t.index "to_tsvector('french'::regconfig, search_terms)", name: "index_dossiers_on_search_terms", using: :gin
t.index ["archived"], name: "index_dossiers_on_archived" t.index ["archived"], name: "index_dossiers_on_archived"
t.index ["dossier_transfer_id"], name: "index_dossiers_on_dossier_transfer_id" t.index ["dossier_transfer_id"], name: "index_dossiers_on_dossier_transfer_id"
t.index ["groupe_instructeur_id"], name: "index_dossiers_on_groupe_instructeur_id" t.index ["groupe_instructeur_id"], name: "index_dossiers_on_groupe_instructeur_id"
@ -568,7 +568,7 @@ ActiveRecord::Schema.define(version: 2021_11_26_150915) 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"=>[]}, null: false t.jsonb "filters", default: {"tous"=>[], "suivis"=>[], "traites"=>[], "a-suivre"=>[], "archives"=>[], "expirant"=>[]}, 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
@ -638,16 +638,20 @@ ActiveRecord::Schema.define(version: 2021_11_26_150915) do
t.text "api_particulier_scopes", default: [], array: true t.text "api_particulier_scopes", default: [], array: true
t.jsonb "api_particulier_sources", default: {} t.jsonb "api_particulier_sources", default: {}
t.boolean "instructeurs_self_management_enabled" t.boolean "instructeurs_self_management_enabled"
t.index ["api_particulier_sources"], name: "index_procedures_on_api_particulier_sources", using: :gin
t.boolean "routing_enabled" t.boolean "routing_enabled"
t.boolean "procedure_expires_when_termine_enabled", default: false
t.bigint "zone_id"
t.index ["api_particulier_sources"], name: "index_procedures_on_api_particulier_sources", using: :gin
t.index ["declarative_with_state"], name: "index_procedures_on_declarative_with_state" t.index ["declarative_with_state"], name: "index_procedures_on_declarative_with_state"
t.index ["draft_revision_id"], name: "index_procedures_on_draft_revision_id" t.index ["draft_revision_id"], name: "index_procedures_on_draft_revision_id"
t.index ["hidden_at"], name: "index_procedures_on_hidden_at" t.index ["hidden_at"], name: "index_procedures_on_hidden_at"
t.index ["parent_procedure_id"], name: "index_procedures_on_parent_procedure_id" t.index ["parent_procedure_id"], name: "index_procedures_on_parent_procedure_id"
t.index ["path", "closed_at", "hidden_at", "unpublished_at"], name: "procedure_path_uniqueness", unique: true t.index ["path", "closed_at", "hidden_at", "unpublished_at"], name: "procedure_path_uniqueness", unique: true
t.index ["path", "closed_at", "hidden_at"], name: "index_procedures_on_path_and_closed_at_and_hidden_at", unique: true t.index ["path", "closed_at", "hidden_at"], name: "index_procedures_on_path_and_closed_at_and_hidden_at", unique: true
t.index ["procedure_expires_when_termine_enabled"], name: "index_procedures_on_procedure_expires_when_termine_enabled"
t.index ["published_revision_id"], name: "index_procedures_on_published_revision_id" t.index ["published_revision_id"], name: "index_procedures_on_published_revision_id"
t.index ["service_id"], name: "index_procedures_on_service_id" t.index ["service_id"], name: "index_procedures_on_service_id"
t.index ["zone_id"], name: "index_procedures_on_zone_id"
end end
create_table "received_mails", id: :serial, force: :cascade do |t| create_table "received_mails", id: :serial, force: :cascade do |t|
@ -735,6 +739,7 @@ ActiveRecord::Schema.define(version: 2021_11_26_150915) do
t.datetime "processed_at" t.datetime "processed_at"
t.string "instructeur_email" t.string "instructeur_email"
t.boolean "process_expired" t.boolean "process_expired"
t.boolean "process_expired_migrated", default: false
t.index ["dossier_id"], name: "index_traitements_on_dossier_id" t.index ["dossier_id"], name: "index_traitements_on_dossier_id"
t.index ["process_expired"], name: "index_traitements_on_process_expired" t.index ["process_expired"], name: "index_traitements_on_process_expired"
end end
@ -823,6 +828,14 @@ ActiveRecord::Schema.define(version: 2021_11_26_150915) do
t.index ["procedure_id"], name: "index_without_continuation_mails_on_procedure_id" t.index ["procedure_id"], name: "index_without_continuation_mails_on_procedure_id"
end end
create_table "zones", force: :cascade do |t|
t.string "acronym", null: false
t.string "label"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["acronym"], name: "index_zones_on_acronym", unique: true
end
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
add_foreign_key "archives_groupe_instructeurs", "archives" add_foreign_key "archives_groupe_instructeurs", "archives"
add_foreign_key "archives_groupe_instructeurs", "groupe_instructeurs" add_foreign_key "archives_groupe_instructeurs", "groupe_instructeurs"
@ -858,6 +871,7 @@ ActiveRecord::Schema.define(version: 2021_11_26_150915) do
add_foreign_key "procedures", "procedure_revisions", column: "draft_revision_id" add_foreign_key "procedures", "procedure_revisions", column: "draft_revision_id"
add_foreign_key "procedures", "procedure_revisions", column: "published_revision_id" add_foreign_key "procedures", "procedure_revisions", column: "published_revision_id"
add_foreign_key "procedures", "services" add_foreign_key "procedures", "services"
add_foreign_key "procedures", "zones"
add_foreign_key "received_mails", "procedures" add_foreign_key "received_mails", "procedures"
add_foreign_key "refused_mails", "procedures" add_foreign_key "refused_mails", "procedures"
add_foreign_key "services", "administrateurs" add_foreign_key "services", "administrateurs"

View file

@ -0,0 +1,17 @@
namespace :after_party do
desc 'Deployment task: populate_zones'
task populate_zones: :environment do
puts "Running deploy task 'populate_zones'"
Zone.create!(acronym: 'COLLECTIVITE', label: 'Collectivité territoriale')
config = Psych.safe_load(File.read(Rails.root.join("config", "zones.yml")))
config["ministeres"].each do |ministere|
acronym = ministere.keys.first
Zone.create!(acronym: acronym, label: ministere["label"])
end
# Update task as completed. If you remove the line below, the task will
# run with every deploy (or every time you call after_party:run).
AfterParty::TaskRecord
.create version: AfterParty::TaskRecorder.new(__FILE__).timestamp
end
end

View file

@ -0,0 +1,21 @@
namespace :after_party do
desc 'Deployment task: move_traitement_process_expired_to_procedure'
task move_traitement_process_expired_to_procedure: :environment do
procedures = Procedure.joins(dossiers: :traitements).where(dossiers: { traitements: { process_expired: true, process_expired_migrated: false } })
progress = ProgressReport.new(procedures.count)
procedures.group(:id).find_each do |procedure|
ActiveRecord::Base.transaction do
puts "update traitements from dossier_ids: #{procedure.dossiers.ids}"
Traitement.where(id: procedure.dossiers.ids).update_all(process_expired_migrated: true)
procedure.update(procedure_expires_when_termine_enabled: true)
progress.inc
end
end
progress.finish
AfterParty::TaskRecord
.create version: AfterParty::TaskRecorder.new(__FILE__).timestamp
end
end

View file

@ -7,6 +7,7 @@ describe Administrateurs::ProceduresController, type: :controller do
let(:description) { 'Description de test' } let(:description) { 'Description de test' }
let(:organisation) { 'Organisation de test' } let(:organisation) { 'Organisation de test' }
let(:direction) { 'Direction de test' } let(:direction) { 'Direction de test' }
let(:ministere) { create(:zone) }
let(:cadre_juridique) { 'cadre juridique' } let(:cadre_juridique) { 'cadre juridique' }
let(:duree_conservation_dossiers_dans_ds) { 3 } let(:duree_conservation_dossiers_dans_ds) { 3 }
let(:monavis_embed) { nil } let(:monavis_embed) { nil }
@ -30,6 +31,7 @@ describe Administrateurs::ProceduresController, type: :controller do
description: description, description: description,
organisation: organisation, organisation: organisation,
direction: direction, direction: direction,
ministere: ministere,
cadre_juridique: cadre_juridique, cadre_juridique: cadre_juridique,
duree_conservation_dossiers_dans_ds: duree_conservation_dossiers_dans_ds, duree_conservation_dossiers_dans_ds: duree_conservation_dossiers_dans_ds,
monavis_embed: monavis_embed, monavis_embed: monavis_embed,

View file

@ -804,4 +804,23 @@ describe Instructeurs::DossiersController, type: :controller do
end end
end end
end end
describe '#extend_conservation' do
subject { post :extend_conservation, params: { procedure_id: procedure.id, dossier_id: dossier.id } }
context 'when user logged in' do
it 'works' do
expect(subject).to redirect_to(instructeur_dossier_path(procedure, dossier))
end
it 'extends conservation_extension by 1 month' do
subject
expect(dossier.reload.conservation_extension).to eq(1.month)
end
it 'flashed notice success' do
subject
expect(flash[:notice]).to eq(I18n.t('views.instructeurs.dossiers.archived_dossier'))
end
end
end
end end

View file

@ -1161,4 +1161,33 @@ describe Users::DossiersController, type: :controller do
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
end end
end end
describe '#extend_conservation' do
let(:procedure) { create(:procedure, duree_conservation_dossiers_dans_ds: 3) }
let(:dossier) { create(:dossier, procedure: procedure, user: user) }
subject { post :extend_conservation, params: { dossier_id: dossier.id } }
context 'when user logged in' do
before { sign_in(user) }
it 'works' do
expect(subject).to redirect_to(dossier_path(dossier))
end
it 'extends conservation_extension by duree_conservation_dossiers_dans_ds' do
subject
expect(dossier.reload.conservation_extension).to eq(procedure.duree_conservation_dossiers_dans_ds.months)
end
it 'flashed notice success' do
subject
expect(flash[:notice]).to eq(I18n.t('views.users.dossiers.archived_dossier', duree_conservation_dossiers_dans_ds: procedure.duree_conservation_dossiers_dans_ds))
end
end
context 'when not logged in' do
it 'fails' do
subject
expect { expect(response).to redirect_to(new_user_session_path) }
end
end
end
end end

View file

@ -135,10 +135,12 @@ FactoryBot.define do
if processed_at.present? if processed_at.present?
dossier.en_construction_at ||= processed_at - 2.minutes dossier.en_construction_at ||= processed_at - 2.minutes
dossier.en_instruction_at ||= processed_at - 1.minute dossier.en_instruction_at ||= processed_at - 1.minute
dossier.processed_at = processed_at
dossier.traitements.accepter(motivation: evaluator.motivation, processed_at: processed_at) dossier.traitements.accepter(motivation: evaluator.motivation, processed_at: processed_at)
else else
dossier.en_construction_at ||= dossier.created_at + 1.minute dossier.en_construction_at ||= dossier.created_at + 1.minute
dossier.en_instruction_at ||= dossier.en_construction_at + 1.minute dossier.en_instruction_at ||= dossier.en_construction_at + 1.minute
dossier.processed_at = dossier.en_instruction_at + 1.minute
dossier.traitements.accepter(motivation: evaluator.motivation, processed_at: dossier.en_instruction_at + 1.minute) dossier.traitements.accepter(motivation: evaluator.motivation, processed_at: dossier.en_instruction_at + 1.minute)
end end
dossier.save! dossier.save!

View file

@ -12,6 +12,7 @@ FactoryBot.define do
ask_birthday { false } ask_birthday { false }
lien_site_web { "https://mon-site.gouv" } lien_site_web { "https://mon-site.gouv" }
path { SecureRandom.uuid } path { SecureRandom.uuid }
association :zone
groupe_instructeurs { [association(:groupe_instructeur, :default, procedure: instance, strategy: :build)] } groupe_instructeurs { [association(:groupe_instructeur, :default, procedure: instance, strategy: :build)] }
administrateurs { administrateur.present? ? [administrateur] : [association(:administrateur)] } administrateurs { administrateur.present? ? [administrateur] : [association(:administrateur)] }

6
spec/factories/zone.rb Normal file
View file

@ -0,0 +1,6 @@
FactoryBot.define do
factory :zone do
sequence(:acronym) { |n| "MA#{n}" }
sequence(:label) { |n| "Ministère de l'Education Populaire #{n}" }
end
end

View file

@ -0,0 +1,11 @@
describe '20211116140232_populate_zones' do
let(:rake_task) { Rake::Task['after_party:populate_zones'] }
subject(:run_task) do
rake_task.invoke
end
it 'populates zones' do
run_task
expect(Zone.find_by(acronym: 'SPM').label).to eq "Premier ministre"
end
end

View file

@ -107,26 +107,8 @@ describe Dossier do
is_expected.to include(long_expired_dossier) is_expected.to include(long_expired_dossier)
end end
end end
end context 'when .termine_or_en_construction_close_to_expiration' do
subject { Dossier.termine_or_en_construction_close_to_expiration }
describe 'en_instruction_close_to_expiration' do
let(:procedure) { create(:procedure, :published, duree_conservation_dossiers_dans_ds: 6) }
let!(:young_dossier) { create(:dossier, procedure: procedure) }
let!(:expiring_dossier) { create(:dossier, :en_instruction, en_instruction_at: 175.days.ago, procedure: procedure) }
let!(:just_expired_dossier) { create(:dossier, :en_instruction, en_instruction_at: (6.months + 1.hour + 10.seconds).ago, procedure: procedure) }
let!(:long_expired_dossier) { create(:dossier, :en_instruction, en_instruction_at: 1.year.ago, procedure: procedure) }
subject { Dossier.en_instruction_close_to_expiration }
it do
is_expected.not_to include(young_dossier)
is_expected.to include(expiring_dossier)
is_expected.to include(just_expired_dossier)
is_expected.to include(long_expired_dossier)
end
context 'when .close_to_expiration' do
subject { Dossier.close_to_expiration }
it do it do
is_expected.not_to include(young_dossier) is_expected.not_to include(young_dossier)
is_expected.to include(expiring_dossier) is_expected.to include(expiring_dossier)
@ -137,11 +119,11 @@ describe Dossier do
end end
describe 'termine_close_to_expiration' do describe 'termine_close_to_expiration' do
let(:procedure) { create(:procedure, :published, duree_conservation_dossiers_dans_ds: 6) } let(:procedure) { create(:procedure, :published, duree_conservation_dossiers_dans_ds: 6, procedure_expires_when_termine_enabled: true) }
let!(:young_dossier) { create(:dossier, :accepte, procedure: procedure, traitements: [build(:traitement, :accepte)]) } let!(:young_dossier) { create(:dossier, state: :accepte, procedure: procedure, processed_at: 2.days.ago) }
let!(:expiring_dossier) { create(:dossier, :accepte, procedure: procedure, traitements: [build(:traitement, :accepte, processed_at: 175.days.ago)]) } let!(:expiring_dossier) { create(:dossier, state: :accepte, procedure: procedure, processed_at: 175.days.ago) }
let!(:just_expired_dossier) { create(:dossier, :accepte, procedure: procedure, traitements: [build(:traitement, :accepte, processed_at: (6.months + 1.hour + 10.seconds).ago)]) } let!(:just_expired_dossier) { create(:dossier, state: :accepte, procedure: procedure, processed_at: (6.months + 1.hour + 10.seconds).ago) }
let!(:long_expired_dossier) { create(:dossier, :accepte, procedure: procedure, traitements: [build(:traitement, :accepte, processed_at: 1.year.ago)]) } let!(:long_expired_dossier) { create(:dossier, state: :accepte, procedure: procedure, processed_at: 1.year.ago) }
subject { Dossier.termine_close_to_expiration } subject { Dossier.termine_close_to_expiration }
@ -161,6 +143,16 @@ describe Dossier do
is_expected.to include(long_expired_dossier) is_expected.to include(long_expired_dossier)
end end
end end
context 'when .close_to_expiration' do
subject { Dossier.termine_or_en_construction_close_to_expiration }
it do
is_expected.not_to include(young_dossier)
is_expected.to include(expiring_dossier)
is_expected.to include(just_expired_dossier)
is_expected.to include(long_expired_dossier)
end
end
end end
describe 'with_notifications' do describe 'with_notifications' do

View file

@ -594,7 +594,7 @@ describe Instructeur, type: :model do
describe "#dossiers_count_summary" do describe "#dossiers_count_summary" do
let(:instructeur_2) { create(:instructeur) } let(:instructeur_2) { create(:instructeur) }
let(:instructeur_3) { create(:instructeur) } let(:instructeur_3) { create(:instructeur) }
let(:procedure) { create(:procedure, instructeurs: [instructeur_2, instructeur_3]) } let(:procedure) { create(:procedure, instructeurs: [instructeur_2, instructeur_3], procedure_expires_when_termine_enabled: true) }
let(:gi_1) { procedure.groupe_instructeurs.first } let(:gi_1) { procedure.groupe_instructeurs.first }
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') }
@ -614,6 +614,7 @@ describe Instructeur, type: :model do
it { expect(subject['traites']).to eq(0) } it { expect(subject['traites']).to eq(0) }
it { expect(subject['tous']).to eq(0) } it { expect(subject['tous']).to eq(0) }
it { expect(subject['archives']).to eq(0) } it { expect(subject['archives']).to eq(0) }
it { expect(subject['expirant']).to eq(0) }
end end
context 'with a new brouillon dossier' do context 'with a new brouillon dossier' do
@ -624,6 +625,7 @@ describe Instructeur, type: :model do
it { expect(subject['traites']).to eq(0) } it { expect(subject['traites']).to eq(0) }
it { expect(subject['tous']).to eq(0) } it { expect(subject['tous']).to eq(0) }
it { expect(subject['archives']).to eq(0) } it { expect(subject['archives']).to eq(0) }
it { expect(subject['expirant']).to eq(0) }
end end
context 'with a new dossier without follower' do context 'with a new dossier without follower' do
@ -634,6 +636,7 @@ describe Instructeur, type: :model do
it { expect(subject['traites']).to eq(0) } it { expect(subject['traites']).to eq(0) }
it { expect(subject['tous']).to eq(1) } it { expect(subject['tous']).to eq(1) }
it { expect(subject['archives']).to eq(0) } it { expect(subject['archives']).to eq(0) }
it { expect(subject['expirant']).to eq(0) }
context 'and dossiers without follower on each of the others groups' do context 'and dossiers without follower on each of the others groups' do
let!(:new_unfollow_dossier_on_gi_2) { create(:dossier, :en_instruction, groupe_instructeur: gi_2) } let!(:new_unfollow_dossier_on_gi_2) { create(:dossier, :en_instruction, groupe_instructeur: gi_2) }
@ -658,6 +661,7 @@ describe Instructeur, type: :model do
it { expect(subject['traites']).to eq(0) } it { expect(subject['traites']).to eq(0) }
it { expect(subject['tous']).to eq(1) } it { expect(subject['tous']).to eq(1) }
it { expect(subject['archives']).to eq(0) } it { expect(subject['archives']).to eq(0) }
it { expect(subject['expirant']).to eq(0) }
context 'and another one follows the same dossier' do context 'and another one follows the same dossier' do
before do before do
@ -669,6 +673,7 @@ describe Instructeur, type: :model do
it { expect(subject['traites']).to eq(0) } it { expect(subject['traites']).to eq(0) }
it { expect(subject['tous']).to eq(1) } it { expect(subject['tous']).to eq(1) }
it { expect(subject['archives']).to eq(0) } it { expect(subject['archives']).to eq(0) }
it { expect(subject['expirant']).to eq(0) }
end end
context 'and dossier with a follower on each of the others groups' do context 'and dossier with a follower on each of the others groups' do
@ -692,6 +697,7 @@ describe Instructeur, type: :model do
it { expect(subject['a_suivre']).to eq(1) } it { expect(subject['a_suivre']).to eq(1) }
it { expect(subject['suivis']).to eq(0) } it { expect(subject['suivis']).to eq(0) }
it { expect(subject['tous']).to eq(1) } it { expect(subject['tous']).to eq(1) }
it { expect(subject['expirant']).to eq(0) }
end end
end end
@ -703,6 +709,7 @@ describe Instructeur, type: :model do
it { expect(subject['traites']).to eq(1) } it { expect(subject['traites']).to eq(1) }
it { expect(subject['tous']).to eq(1) } it { expect(subject['tous']).to eq(1) }
it { expect(subject['archives']).to eq(0) } it { expect(subject['archives']).to eq(0) }
it { expect(subject['expirant']).to eq(0) }
context 'and terminer dossiers on each of the others groups' do context 'and terminer dossiers on each of the others groups' do
let!(:termine_dossier_on_gi_2) { create(:dossier, :accepte, groupe_instructeur: gi_2) } let!(:termine_dossier_on_gi_2) { create(:dossier, :accepte, groupe_instructeur: gi_2) }
@ -715,6 +722,7 @@ describe Instructeur, type: :model do
it { expect(subject['traites']).to eq(2) } it { expect(subject['traites']).to eq(2) }
it { expect(subject['tous']).to eq(2) } it { expect(subject['tous']).to eq(2) }
it { expect(subject['archives']).to eq(0) } it { expect(subject['archives']).to eq(0) }
it { expect(subject['expirant']).to eq(0) }
end end
end end
@ -726,6 +734,7 @@ describe Instructeur, type: :model do
it { expect(subject['traites']).to eq(0) } it { expect(subject['traites']).to eq(0) }
it { expect(subject['tous']).to eq(0) } it { expect(subject['tous']).to eq(0) }
it { expect(subject['archives']).to eq(1) } it { expect(subject['archives']).to eq(1) }
it { expect(subject['expirant']).to eq(0) }
context 'and terminer dossiers on each of the others groups' do context 'and terminer dossiers on each of the others groups' do
let!(:archives_dossier_on_gi_2) { create(:dossier, :en_instruction, groupe_instructeur: gi_2, archived: true) } let!(:archives_dossier_on_gi_2) { create(:dossier, :en_instruction, groupe_instructeur: gi_2, archived: true) }
@ -734,6 +743,19 @@ describe Instructeur, type: :model do
it { expect(subject['archives']).to eq(2) } it { expect(subject['archives']).to eq(2) }
end end
end end
context 'with an expirants dossier' do
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) }
before { subject }
it { expect(subject['a_suivre']).to eq(1) }
it { expect(subject['suivis']).to eq(0) }
it { expect(subject['traites']).to eq(1) }
it { expect(subject['tous']).to eq(2) }
it { expect(subject['archives']).to eq(0) }
it { expect(subject['expirant']).to eq(2) }
end
end end
end end

View file

@ -2,8 +2,9 @@ describe ExpiredDossiersDeletionService do
let(:warning_period) { 1.month + 5.days } let(:warning_period) { 1.month + 5.days }
let(:conservation_par_defaut) { 3.months } let(:conservation_par_defaut) { 3.months }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:procedure) { create(:procedure, :published) } let(:procedure_opts) { {} }
let(:procedure_2) { create(:procedure, :published) } let(:procedure) { create(:procedure, :published, procedure_opts) }
let(:procedure_2) { create(:procedure, :published, procedure_opts) }
let(:reference_date) { Date.parse("March 8") } let(:reference_date) { Date.parse("March 8") }
describe '#process_expired_dossiers_brouillon' do describe '#process_expired_dossiers_brouillon' do
@ -273,19 +274,18 @@ describe ExpiredDossiersDeletionService do
describe '#send_termine_expiration_notices' do describe '#send_termine_expiration_notices' do
before { Timecop.freeze(reference_date) } before { Timecop.freeze(reference_date) }
after { Timecop.return } after { Timecop.return }
let(:procedure_opts) do
before do {
Flipper.enable(:procedure_process_expired_dossiers_termine, procedure) procedure_expires_when_termine_enabled: true
Flipper.enable(:procedure_process_expired_dossiers_termine, procedure_2) }
end end
before do before do
allow(DossierMailer).to receive(:notify_near_deletion_to_user).and_call_original allow(DossierMailer).to receive(:notify_near_deletion_to_user).and_call_original
allow(DossierMailer).to receive(:notify_near_deletion_to_administration).and_call_original allow(DossierMailer).to receive(:notify_near_deletion_to_administration).and_call_original
end end
context 'with a single dossier' do context 'with a single dossier' do
let!(:dossier) { create(:dossier, :accepte, :followed, procedure: procedure, processed_at: processed_at) } let!(:dossier) { create(:dossier, :followed, state: :accepte, procedure: procedure, processed_at: processed_at) }
before { ExpiredDossiersDeletionService.send_termine_expiration_notices } before { ExpiredDossiersDeletionService.send_termine_expiration_notices }
@ -311,8 +311,8 @@ describe ExpiredDossiersDeletionService do
end end
context 'with 2 dossiers to notice' do context 'with 2 dossiers to notice' do
let!(:dossier_1) { create(:dossier, :accepte, procedure: procedure, user: user, processed_at: (conservation_par_defaut - 2.weeks + 1.day).ago) } let!(:dossier_1) { create(:dossier, state: :accepte, procedure: procedure, user: user, processed_at: (conservation_par_defaut - 2.weeks + 1.day).ago) }
let!(:dossier_2) { create(:dossier, :accepte, procedure: procedure_2, user: user, processed_at: (conservation_par_defaut - 2.weeks + 1.day).ago) } let!(:dossier_2) { create(:dossier, state: :accepte, procedure: procedure_2, user: user, processed_at: (conservation_par_defaut - 2.weeks + 1.day).ago) }
let!(:instructeur) { create(:instructeur) } let!(:instructeur) { create(:instructeur) }
@ -331,7 +331,7 @@ describe ExpiredDossiersDeletionService do
context 'when an instructeur is also administrateur' do context 'when an instructeur is also administrateur' do
let!(:administrateur) { procedure.administrateurs.first } let!(:administrateur) { procedure.administrateurs.first }
let!(:dossier) { create(:dossier, :accepte, procedure: procedure, processed_at: (conservation_par_defaut - 2.weeks + 1.day).ago) } let!(:dossier) { create(:dossier, state: :accepte, procedure: procedure, processed_at: (conservation_par_defaut - 2.weeks + 1.day).ago) }
before do before do
administrateur.instructeur.followed_dossiers << dossier administrateur.instructeur.followed_dossiers << dossier
@ -348,9 +348,10 @@ describe ExpiredDossiersDeletionService do
before { Timecop.freeze(reference_date) } before { Timecop.freeze(reference_date) }
after { Timecop.return } after { Timecop.return }
before do let(:procedure_opts) do
Flipper.enable(:procedure_process_expired_dossiers_termine, procedure) {
Flipper.enable(:procedure_process_expired_dossiers_termine, procedure_2) procedure_expires_when_termine_enabled: true
}
end end
before do before do
@ -359,7 +360,7 @@ describe ExpiredDossiersDeletionService do
end end
context 'with a single dossier' do context 'with a single dossier' do
let!(:dossier) { create(:dossier, :accepte, :followed, procedure: procedure, termine_close_to_expiration_notice_sent_at: notice_sent_at) } let!(:dossier) { create(:dossier, :followed, :accepte, procedure: procedure, termine_close_to_expiration_notice_sent_at: notice_sent_at) }
let(:deleted_dossier) { DeletedDossier.find_by(dossier_id: dossier.id) } let(:deleted_dossier) { DeletedDossier.find_by(dossier_id: dossier.id) }
before { ExpiredDossiersDeletionService.delete_expired_termine_and_notify } before { ExpiredDossiersDeletionService.delete_expired_termine_and_notify }

View file

@ -166,7 +166,7 @@ describe 'The user' do
end end
scenario 'extends dossier experation date more than one time, ', js: true do scenario 'extends dossier experation date more than one time, ', js: true do
Flipper.enable(:procedure_process_expired_dossiers_termine) simple_procedure.update(procedure_expires_when_termine_enabled: true)
allow(simple_procedure).to receive(:feature_enabled?).with(:procedure_process_expired_dossiers_termine).and_return(true) allow(simple_procedure).to receive(:feature_enabled?).with(:procedure_process_expired_dossiers_termine).and_return(true)
user_old_dossier = create(:dossier, user_old_dossier = create(:dossier,
procedure: simple_procedure, procedure: simple_procedure,
@ -176,15 +176,14 @@ describe 'The user' do
visit brouillon_dossier_path(user_old_dossier) visit brouillon_dossier_path(user_old_dossier)
expect(page).to have_css('.card-title', text: 'Votre dossier va expirer', visible: true) expect(page).to have_css('.card-title', text: 'Votre dossier va expirer', visible: true)
click_on "Repousser sa suppression" find('#test-user-repousser-expiration').click
expect(page).not_to have_button("Repousser sa suppression") expect(page).not_to have_selector('#test-user-repousser-expiration')
Timecop.freeze(1.month.from_now) do Timecop.freeze(simple_procedure.duree_conservation_dossiers_dans_ds.month.from_now) do
visit brouillon_dossier_path(user_old_dossier) visit brouillon_dossier_path(user_old_dossier)
expect(page).to have_css('.card-title', text: 'Votre dossier va expirer', visible: true) expect(page).to have_css('.card-title', text: 'Votre dossier va expirer', visible: true)
click_on "Repousser sa suppression" find('#test-user-repousser-expiration').click
expect(page).not_to have_button("Repousser sa suppression") expect(page).not_to have_selector('#test-user-repousser-expiration')
end end
end end

View file

@ -0,0 +1,66 @@
describe 'instructeur/dossiers/expiration_banner.html.haml', type: :view do
include DossierHelper
let(:duree_conservation_dossiers_dans_ds) { 3 }
let(:dossier) do
create(:dossier, state, attributes.merge(
id: 1,
state: state,
procedure: create(:procedure, procedure_expires_when_termine_enabled: expiration_enabled, duree_conservation_dossiers_dans_ds: duree_conservation_dossiers_dans_ds)
))
end
let(:i18n_key_state) { state }
subject do
render('instructeurs/dossiers/expiration_banner.html.haml',
dossier: dossier,
current_user: build(:user))
end
context 'with procedure having procedure_expires_when_termine_enabled not enabled' do
let(:expiration_enabled) { false }
let(:attributes) { { processed_at: 6.months.ago } }
let(:state) { :accepte }
it 'render estimated expiration date' do
expect(subject).not_to have_selector('.expires_at')
end
end
context 'with procedure having procedure_expires_when_termine_enabled enabled' do
let(:expiration_enabled) { true }
context 'with dossier.en_construction?' do
let(:attributes) { { en_construction_at: 6.months.ago } }
let(:state) { :en_construction }
it 'render estimated expiration date' do
expect(subject).to have_selector('.expires_at',
text: I18n.t("shared.dossiers.header.expires_at.#{i18n_key_state}",
date: safe_expiration_date(dossier),
duree_conservation_totale: duree_conservation_dossiers_dans_ds))
end
end
context 'with dossier.en_instruction?' do
let(:state) { :en_instruction }
let(:attributes) { {} }
it 'render estimated expiration date' do
expect(subject).to have_selector('p.expires_at_en_instruction',
text: I18n.t("shared.dossiers.header.expires_at.#{i18n_key_state}"))
end
end
context 'with dossier.en_processed_at?' do
let(:state) { :accepte }
let(:attributes) { {} }
it 'render estimated expiration date' do
allow(dossier).to receive(:processed_at).and_return(6.months.ago)
expect(subject).to have_selector('.expires_at',
text: I18n.t("shared.dossiers.header.expires_at.#{i18n_key_state}",
date: safe_expiration_date(dossier),
duree_conservation_totale: duree_conservation_dossiers_dans_ds))
end
end
end
end

View file

@ -0,0 +1,30 @@
describe 'instructeurs/procedures/_list.html.haml', type: :view do
let(:procedure) { create(:procedure, id: 1, procedure_expires_when_termine_enabled: expiration_enabled) }
subject do
render('instructeurs/procedures/list.html.haml',
p: procedure,
dossiers_count_per_procedure: 5,
dossiers_a_suivre_count_per_procedure: 2,
dossiers_archived_count_per_procedure: 1,
dossiers_termines_count_per_procedure: 1,
dossiers_expirant_count_per_procedure: 0,
followed_dossiers_count_per_procedure: 0,
procedure_ids_en_cours_with_notifications: [],
procedure_ids_termines_with_notifications: [])
end
context 'when procedure_expires_when_termine_enabled is true' do
let(:expiration_enabled) { true }
it 'contains link to expiring dossiers within procedure' do
expect(subject).to have_selector(%Q(a[href="#{instructeur_procedure_path(procedure, statut: 'expirant')}"]), count: 1)
end
end
context 'when procedure_expires_when_termine_enabled is false' do
let(:expiration_enabled) { false }
it 'does not contain link to expiring dossiers within procedure' do
expect(subject).to have_selector(%Q(a[href="#{instructeur_procedure_path(procedure, statut: 'expirant')}"]), count: 0)
end
end
end

View file

@ -0,0 +1,33 @@
describe 'instructeurs/procedures/_tabs.html.haml', type: :view do
let(:procedure) { create(:procedure, id: 1, procedure_expires_when_termine_enabled: expiration_enabled) }
before { allow(view).to receive(:current_instructeur).and_return(create(:instructeur)) }
subject do
render('instructeurs/procedures/tabs.html.haml',
procedure: procedure,
statut: 'tous',
a_suivre_count: 0,
suivis_count: 0,
traites_count: 0,
tous_count: 0,
archives_count: 0,
expirant_count: 0,
has_en_cours_notifications: false,
has_termine_notifications: false)
end
context 'when procedure_expires_when_termine_enabled is true' do
let(:expiration_enabled) { true }
it 'contains link to expiring dossiers within procedure' do
expect(subject).to have_selector(%Q(a[href="#{instructeur_procedure_path(procedure, statut: 'expirant')}"]), count: 1)
end
end
context 'when procedure_expires_when_termine_enabled is false' do
let(:expiration_enabled) { false }
it 'does not contain link to expiring dossiers within procedure' do
expect(subject).to have_selector(%Q(a[href="#{instructeur_procedure_path(procedure, statut: 'expirant')}"]), count: 0)
end
end
end

View file

@ -1,15 +1,23 @@
describe 'shared/dossiers/expiration_banner.html.haml', type: :view do describe 'users/dossiers/expiration_banner.html.haml', type: :view do
include DossierHelper include DossierHelper
let(:dossier) { build(:dossier, state, attributes.merge(id: 1, state: state)) } let(:duree_conservation_dossiers_dans_ds) { 3 }
let(:dossier) do
create(:dossier, state, attributes.merge(
id: 1,
state: state,
conservation_extension: 1,
procedure: create(:procedure, procedure_expires_when_termine_enabled: expiration_enabled, duree_conservation_dossiers_dans_ds: duree_conservation_dossiers_dans_ds)
))
end
let(:i18n_key_state) { state } let(:i18n_key_state) { state }
subject do subject do
render('shared/dossiers/expiration_banner.html.haml', render('users/dossiers/expiration_banner.html.haml',
dossier: dossier, dossier: dossier,
current_user: build(:user)) current_user: build(:user))
end end
context 'with procedure having procedure_process_expired_dossiers_termine not enabled' do context 'with procedure having procedure_expires_when_termine_enabled not enabled' do
before { allow(dossier.procedure).to receive(:feature_enabled?).with(:procedure_process_expired_dossiers_termine).and_return(false) } let(:expiration_enabled) { false }
let(:attributes) { { processed_at: 6.months.ago } } let(:attributes) { { processed_at: 6.months.ago } }
let(:state) { :accepte } let(:state) { :accepte }
@ -18,8 +26,8 @@ describe 'shared/dossiers/expiration_banner.html.haml', type: :view do
end end
end end
context 'with procedure having procedure_process_expired_dossiers_termine enabled' do context 'with procedure having procedure_expires_when_termine_enabled enabled' do
before { allow(dossier.procedure).to receive(:feature_enabled?).with(:procedure_process_expired_dossiers_termine).and_return(true) } let(:expiration_enabled) { true }
context 'with dossier.brouillon?' do context 'with dossier.brouillon?' do
let(:attributes) { { created_at: 6.months.ago } } let(:attributes) { { created_at: 6.months.ago } }
@ -28,7 +36,8 @@ describe 'shared/dossiers/expiration_banner.html.haml', type: :view do
it 'render estimated expiration date' do it 'render estimated expiration date' do
expect(subject).to have_selector('.expires_at', expect(subject).to have_selector('.expires_at',
text: I18n.t("shared.dossiers.header.expires_at.#{i18n_key_state}", text: I18n.t("shared.dossiers.header.expires_at.#{i18n_key_state}",
date: safe_expiration_date(dossier))) date: safe_expiration_date(dossier),
duree_conservation_totale: duree_conservation_dossiers_dans_ds))
end end
end end
@ -39,7 +48,8 @@ describe 'shared/dossiers/expiration_banner.html.haml', type: :view do
it 'render estimated expiration date' do it 'render estimated expiration date' do
expect(subject).to have_selector('.expires_at', expect(subject).to have_selector('.expires_at',
text: I18n.t("shared.dossiers.header.expires_at.#{i18n_key_state}", text: I18n.t("shared.dossiers.header.expires_at.#{i18n_key_state}",
date: safe_expiration_date(dossier))) date: safe_expiration_date(dossier),
duree_conservation_totale: duree_conservation_dossiers_dans_ds))
end end
end end
@ -61,7 +71,8 @@ describe 'shared/dossiers/expiration_banner.html.haml', type: :view do
allow(dossier).to receive(:processed_at).and_return(6.months.ago) allow(dossier).to receive(:processed_at).and_return(6.months.ago)
expect(subject).to have_selector('.expires_at', expect(subject).to have_selector('.expires_at',
text: I18n.t("shared.dossiers.header.expires_at.#{i18n_key_state}", text: I18n.t("shared.dossiers.header.expires_at.#{i18n_key_state}",
date: safe_expiration_date(dossier))) date: safe_expiration_date(dossier),
duree_conservation_totale: duree_conservation_dossiers_dans_ds))
end end
end end
end end