diff --git a/app/helpers/dossier_helper.rb b/app/helpers/dossier_helper.rb index 1891b8485..8a2582100 100644 --- a/app/helpers/dossier_helper.rb +++ b/app/helpers/dossier_helper.rb @@ -98,6 +98,11 @@ module DossierHelper end end + def safe_expiration_date(dossier) + date = dossier.expiration_date.presence || dossier.approximative_expiration_date + l(date, format: '%d/%m/%Y') + end + def annuaire_link(siren) base_url = "https://annuaire-entreprises.data.gouv.fr" return base_url if siren.blank? diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 0117f1459..42d51a228 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -539,20 +539,32 @@ class Dossier < ApplicationRecord !brouillon? && !user_deleted? && !archived end - def close_to_expiration_at + def expirable? + [brouillon?, en_construction?, termine?].any? + end + + def approximative_expiration_date_reference if brouillon? created_at elsif en_construction? en_construction_at - elsif en_instruction? - en_instruction_at - else + elsif termine? processed_at - end + conservation_extension + procedure.duree_conservation_dossiers_dans_ds.months - REMAINING_WEEKS_BEFORE_EXPIRATION.weeks + else + fail "approximative_expiration_date_reference should not be called in state #{self.state}" + end end - def duration_after_notice - MONTHS_AFTER_EXPIRATION.month + DAYS_AFTER_EXPIRATION.days + def approximative_expiration_date + [ + approximative_expiration_date_reference, + conservation_extension, + procedure.duree_conservation_dossiers_dans_ds.months + ].sum - REMAINING_WEEKS_BEFORE_EXPIRATION.weeks + end + + def close_to_expiration? + approximative_expiration_date < Time.zone.now end def expiration_date @@ -565,12 +577,8 @@ class Dossier < ApplicationRecord end end - def close_to_expiration? - !en_instruction? && close_to_expiration_at < Time.zone.now - end - - def close_to_expiration_notice_sent? - expiration_date.present? + def duration_after_notice + MONTHS_AFTER_EXPIRATION.month + DAYS_AFTER_EXPIRATION.days end def expiration_can_be_extended? diff --git a/app/views/shared/dossiers/_expiration_banner.html.haml b/app/views/shared/dossiers/_expiration_banner.html.haml new file mode 100644 index 000000000..b01fbea34 --- /dev/null +++ b/app/views/shared/dossiers/_expiration_banner.html.haml @@ -0,0 +1,17 @@ +- if dossier.expirable? && dossier.close_to_expiration? + .card.warning.mt-2.mb-3 + .card-title Votre dossier va expirer + %p + - if dossier.brouillon? + Votre dossier est en brouillon, mais va bientôt expirer. Cela signifie qu’il 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. + - elsif dossier.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. + - elsif dossier.termine? + Le traitement de votre dossier est terminé, mais il va bientôt expirer. Cela signifie qu’il va bientôt être supprimé. + Si vous souhaitez conserver une trace, vous pouvez le télécharger au format PDF. + + - if dossier.expiration_can_be_extended? + %br + = button_to 'Repousser sa suppression', users_dossier_repousser_expiration_path(dossier), class: 'button secondary mt-2' diff --git a/app/views/shared/dossiers/_header.html.haml b/app/views/shared/dossiers/_header.html.haml index aad250c4b..056dceff1 100644 --- a/app/views/shared/dossiers/_header.html.haml +++ b/app/views/shared/dossiers/_header.html.haml @@ -1,6 +1,14 @@ -%h1 - = procedure_libelle(dossier.procedure) += status_badge(dossier.state) +.title-container + %span.icon.folder + %h1= procedure_libelle(dossier.procedure) + %h2 + = 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)) + + = render(partial: 'shared/dossiers/short_expires_message', locals: {dossier: dossier}) + + .header-actions + - if current_user.owns?(dossier) + = render partial: 'invites/dropdown', locals: { dossier: dossier } -.dossier-form-actions - - if current_user.owns?(dossier) - = render partial: 'invites/dropdown', locals: { dossier: dossier } diff --git a/app/views/shared/dossiers/_short_expires_message.html.haml b/app/views/shared/dossiers/_short_expires_message.html.haml new file mode 100644 index 000000000..466c3f880 --- /dev/null +++ b/app/views/shared/dossiers/_short_expires_message.html.haml @@ -0,0 +1,9 @@ +- if dossier.expirable? + %p.expires_at + %small= t("shared.dossiers.header.expires_at.#{dossier.state}", date: safe_expiration_date(dossier)) +- else + %p.expires_at_en_instruction + %small= t("shared.dossiers.header.expires_at.en_instruction") + + += render(partial: 'shared/dossiers/expiration_banner', locals: {dossier: dossier}) diff --git a/app/views/users/dossiers/show/_header.html.haml b/app/views/users/dossiers/show/_header.html.haml index 46516aed8..2985d024f 100644 --- a/app/views/users/dossiers/show/_header.html.haml +++ b/app/views/users/dossiers/show/_header.html.haml @@ -10,6 +10,9 @@ - if dossier.en_construction_at.present? = t('views.users.dossiers.show.header.submit_date', date_du_dossier: I18n.l(dossier.en_construction_at)) + = render(partial: 'shared/dossiers/short_expires_message', locals: {dossier: dossier}) + + - if current_user.owns?(dossier) .header-actions = render partial: 'invites/dropdown', locals: { dossier: dossier } @@ -22,25 +25,6 @@ %li = link_to t('views.users.dossiers.show.header.print_dossier'), dossier_path(dossier, format: :pdf), target: "_blank", rel: "noopener", class: "menu-item menu-link" - - if dossier.close_to_expiration? - .card.warning - .card-title Votre dossier va expirer - %p - - if dossier.brouillon? - Votre dossier est en brouillon, mais va bientôt expirer. Cela signifie qu’il 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. - - elsif dossier.termine? - Le traitement de votre dossier est terminé, mais il va bientôt expirer. Cela signifie qu’il va bientôt être supprimé. - Si vous souhaitez conserver une trace, vous pouvez le télécharger au format PDF. - - else - Votre dossier a été déposé, mais va bientôt expirer. Cela signifie qu’il va bientôt être supprimé sans avoir été traité par l’administration. - 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. - - - if dossier.expiration_can_be_extended? - %br - = button_to 'Repousser sa suppression', users_dossier_repousser_expiration_path(dossier), class: 'button secondary' %ul.tabs = dynamic_tab_item(t('views.users.dossiers.show.header.summary'), dossier_path(dossier)) diff --git a/config/locales/en.yml b/config/locales/en.yml index 23e2369be..b0abb4c73 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -160,6 +160,7 @@ en: request: "Request" mailbox: "Mailbox" dossier_number: "File n. %{dossier_id}" + created_date: "- Draft on %{date_du_dossier}" submit_date: "- Submit on %{date_du_dossier}" print: "print" print_dossier: "All the file" diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 4e5ec3b6a..ea9696470 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -156,6 +156,7 @@ fr: request: "Demande" mailbox: "Messagerie" dossier_number: "Dossier nº %{dossier_id}" + created_date: "- En brouillon depuis le %{date_du_dossier}" submit_date: "- Déposé le %{date_du_dossier}" print: "imprimer" print_dossier: "Tout le dossier" diff --git a/config/locales/shared.en.yml b/config/locales/shared.en.yml index 7ef3d6294..fa45fd22a 100644 --- a/config/locales/shared.en.yml +++ b/config/locales/shared.en.yml @@ -7,6 +7,14 @@ en: numero_allocataire_notice: It is usually composed of 7 digits. code_postal_label: postal code code_postal_notice: It is usually composed of 5 digits. + header: + expires_at: + brouillon: "Expires at %{date}" + en_construction: "Expires at %{date}" + en_instruction: "This file is being instructed, the administration will answer as soon as possible" + accepte: "Expires at %{date}" + refuse: "Expires at %{date}" + sans_suite: "Expires at %{date}" champs: cnaf: show: diff --git a/config/locales/shared.fr.yml b/config/locales/shared.fr.yml index a0bf159e1..ffcca7a87 100644 --- a/config/locales/shared.fr.yml +++ b/config/locales/shared.fr.yml @@ -7,6 +7,14 @@ fr: numero_allocataire_notice: Il est généralement composé de 7 chiffres. code_postal_label: Le code postal code_postal_notice: Il est généralement composé de 5 chiffres. + header: + expires_at: + brouillon: "Expirera le %{date}" + en_construction: "Expirera le %{date}" + en_instruction: "Ce dossier est en instruction, il n'expirera pas" + accepte: "Expirera le %{date}" + refuse: "Expirera le %{date}" + sans_suite: "Expirera le %{date}" champs: cnaf: show: diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb index 10ed09c19..912664fe0 100644 --- a/spec/support/capybara.rb +++ b/spec/support/capybara.rb @@ -9,7 +9,7 @@ end Capybara.register_driver :headless_chrome do |app| options = Selenium::WebDriver::Chrome::Options.new - options.add_argument('--headless') + options.add_argument('--headless') unless ENV['NO_HEADLESS'] options.add_argument('--window-size=1440,900') capabilities = Selenium::WebDriver::Remote::Capabilities.chrome( diff --git a/spec/system/users/brouillon_spec.rb b/spec/system/users/brouillon_spec.rb index 29beb58b0..bc4c3f1b3 100644 --- a/spec/system/users/brouillon_spec.rb +++ b/spec/system/users/brouillon_spec.rb @@ -165,6 +165,27 @@ describe 'The user' do expect(page).to have_current_path(merci_dossier_path(user_dossier)) end + scenario 'extends dossier experation date more than one time, ', js: true do + user_old_dossier = create(:dossier, + procedure: simple_procedure, + created_at: simple_procedure.duree_conservation_dossiers_dans_ds.month.ago, + user: user) + login_as(user, scope: :user) + visit brouillon_dossier_path(user_old_dossier) + + expect(page).to have_css('.card-title', text: 'Votre dossier va expirer', visible: true) + click_on "Repousser sa suppression" + expect(page).not_to have_button("Repousser sa suppression") + + Timecop.freeze(1.month.from_now) do + visit brouillon_dossier_path(user_old_dossier) + + expect(page).to have_css('.card-title', text: 'Votre dossier va expirer', visible: true) + click_on "Repousser sa suppression" + expect(page).not_to have_button("Repousser sa suppression") + end + end + let(:procedure_with_pj) do tdcs = [build(:type_de_champ_piece_justificative, mandatory: true, libelle: 'Pièce justificative')] create(:procedure, :published, :for_individual, types_de_champ: tdcs) diff --git a/spec/views/shared/dossiers/_header.html.haml_spec.rb b/spec/views/shared/dossiers/_header.html.haml_spec.rb new file mode 100644 index 000000000..25567efea --- /dev/null +++ b/spec/views/shared/dossiers/_header.html.haml_spec.rb @@ -0,0 +1,56 @@ +describe 'shared/dossiers/short_expires_message.html.haml', type: :view do + include DossierHelper + let(:dossier) do + build(:dossier, state, attributes.merge(id: 1, state: state)) + end + let(:i18n_key_state) { state } + subject do + render('shared/dossiers/short_expires_message.html.haml', + dossier: dossier, + current_user: build(:user)) + end + + context 'with dossier.brouillon?' do + let(:attributes) { { created_at: 6.months.ago } } + let(:state) { :brouillon } + + 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))) + end + end + + 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))) + 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))) + end + end +end