diff --git a/app/components/dossiers/batch_alert_component/batch_alert_component.en.yml b/app/components/dossiers/batch_alert_component/batch_alert_component.en.yml index 8a626130d..f3daadc7d 100644 --- a/app/components/dossiers/batch_alert_component/batch_alert_component.en.yml +++ b/app/components/dossiers/batch_alert_component/batch_alert_component.en.yml @@ -26,6 +26,15 @@ en: text_success: one: "1 is being changed to instructing" other: "%{success_count}/%{count} files have been changed to instructing" + repousser_expiration: + finish: + text_success: + one: "%{success_count}/1 file will be kept for an additional month" + other: "%{success_count}/%{count} files will be kept for an additional month" + in_progress: + text_success: + one: "1 is being kept for an additional month" + other: "%{success_count}/%{count} files will be kept for an additional month" accepter: finish: text_success: diff --git a/app/components/dossiers/batch_alert_component/batch_alert_component.fr.yml b/app/components/dossiers/batch_alert_component/batch_alert_component.fr.yml index 480805390..4c2556e58 100644 --- a/app/components/dossiers/batch_alert_component/batch_alert_component.fr.yml +++ b/app/components/dossiers/batch_alert_component/batch_alert_component.fr.yml @@ -26,6 +26,15 @@ fr: text_success: one: "1 dossier est en cours d'instruction" other: "%{success_count}/%{count} dossiers ont été passés en instruction" + repousser_expiration: + finish: + text_success: + one: "%{success_count}/1 dossier sera conservé 1 mois supplémentaire" + other: "%{success_count}/%{count} dossiers seront conservé 1 mois supplémentaire" + in_progress: + text_success: + one: "1 dossier est en cours d'être conservé 1 mois supplémentaire" + other: "%{success_count}/%{count} dossiers seront conservé 1 mois supplémentaire" accepter: finish: text_success: diff --git a/app/components/dossiers/batch_operation_component.rb b/app/components/dossiers/batch_operation_component.rb index e7685ed26..ee76d1472 100644 --- a/app/components/dossiers/batch_operation_component.rb +++ b/app/components/dossiers/batch_operation_component.rb @@ -7,17 +7,25 @@ class Dossiers::BatchOperationComponent < ApplicationComponent end def render? - ['a-suivre', 'traites', 'suivis', 'archives', 'supprimes_recemment'].include?(@statut) + ['a-suivre', 'traites', 'suivis', 'archives', 'supprimes_recemment', 'expirant'].include?(@statut) end def operations_for_dossier(dossier) case dossier.state + when Dossier.states.fetch(:brouillon) + [BatchOperation.operations.fetch(:repousser_expiration)] when Dossier.states.fetch(:en_construction) - [BatchOperation.operations.fetch(:passer_en_instruction)] + [BatchOperation.operations.fetch(:passer_en_instruction), BatchOperation.operations.fetch(:repousser_expiration)] when Dossier.states.fetch(:en_instruction) - [BatchOperation.operations.fetch(:accepter), BatchOperation.operations.fetch(:refuser), BatchOperation.operations.fetch(:classer_sans_suite), BatchOperation.operations.fetch(:repasser_en_construction)] + [ + BatchOperation.operations.fetch(:accepter), BatchOperation.operations.fetch(:refuser), + BatchOperation.operations.fetch(:classer_sans_suite), BatchOperation.operations.fetch(:repasser_en_construction) + ] when Dossier.states.fetch(:accepte), Dossier.states.fetch(:refuse), Dossier.states.fetch(:sans_suite) - [BatchOperation.operations.fetch(:archiver), BatchOperation.operations.fetch(:desarchiver), BatchOperation.operations.fetch(:supprimer), BatchOperation.operations.fetch(:restaurer)] + [ + BatchOperation.operations.fetch(:archiver), BatchOperation.operations.fetch(:desarchiver), BatchOperation.operations.fetch(:supprimer), + BatchOperation.operations.fetch(:restaurer), BatchOperation.operations.fetch(:repousser_expiration) + ] else [] end.append(BatchOperation.operations.fetch(:follow), BatchOperation.operations.fetch(:unfollow)) @@ -65,6 +73,16 @@ class Dossiers::BatchOperationComponent < ApplicationComponent } ] } + when 'expirant' then + { + options: + [ + { + label: t(".operations.repousser_expiration"), + operation: BatchOperation.operations.fetch(:repousser_expiration) + } + ] + } when 'supprimes_recemment' then { options: diff --git a/app/components/dossiers/batch_operation_component/batch_operation_component.en.yml b/app/components/dossiers/batch_operation_component/batch_operation_component.en.yml index e71e37c26..81dc47e35 100644 --- a/app/components/dossiers/batch_operation_component/batch_operation_component.en.yml +++ b/app/components/dossiers/batch_operation_component/batch_operation_component.en.yml @@ -4,6 +4,7 @@ fr: desarchiver: 'Unarchive selected files' passer_en_instruction: 'Change selected files to instructing' instruction: Instructing files + repousser_expiration: Keep for one more month accepter: 'Accept seleted files' supprimer: Delete seleted files restaurer: Restore seleted files diff --git a/app/components/dossiers/batch_operation_component/batch_operation_component.fr.yml b/app/components/dossiers/batch_operation_component/batch_operation_component.fr.yml index 486c9f455..ec7f61a03 100644 --- a/app/components/dossiers/batch_operation_component/batch_operation_component.fr.yml +++ b/app/components/dossiers/batch_operation_component/batch_operation_component.fr.yml @@ -4,6 +4,7 @@ fr: desarchiver: 'Désarchiver les dossiers' passer_en_instruction: 'Passer les dossiers en instruction' instruction: Instruire les dossiers + repousser_expiration: Conserver un mois de plus accepter: 'Accepter les dossiers' supprimer: 'Supprimer les dossiers' restaurer: 'Restaurer les dossiers' diff --git a/app/models/batch_operation.rb b/app/models/batch_operation.rb index 346350b2a..76a9b05a2 100644 --- a/app/models/batch_operation.rb +++ b/app/models/batch_operation.rb @@ -7,6 +7,7 @@ class BatchOperation < ApplicationRecord desarchiver: 'desarchiver', follow: 'follow', passer_en_instruction: 'passer_en_instruction', + repousser_expiration: 'repousser_expiration', repasser_en_construction: 'repasser_en_construction', restaurer: 'restaurer', unfollow: 'unfollow', @@ -56,6 +57,8 @@ class BatchOperation < ApplicationRecord query.visible_by_administration.state_en_instruction when BatchOperation.operations.fetch(:follow) then query.visible_by_administration.without_followers.en_cours + when BatchOperation.operations.fetch(:repousser_expiration) then + query.visible_by_administration.close_to_expiration when BatchOperation.operations.fetch(:repasser_en_construction) then query.visible_by_administration.state_en_instruction when BatchOperation.operations.fetch(:unfollow) then @@ -88,6 +91,8 @@ class BatchOperation < ApplicationRecord dossier.classer_sans_suite(instructeur: instructeur, motivation: motivation, justificatif: justificatif_motivation) when BatchOperation.operations.fetch(:follow) instructeur.follow(dossier) + when BatchOperation.operations.fetch(:repousser_expiration) + dossier.extend_conservation(1.month) when BatchOperation.operations.fetch(:repasser_en_construction) dossier.repasser_en_construction!(instructeur: instructeur) when BatchOperation.operations.fetch(:unfollow) diff --git a/spec/components/dossiers/batch_alert_component_spec.rb b/spec/components/dossiers/batch_alert_component_spec.rb index 72062f890..f607f497b 100644 --- a/spec/components/dossiers/batch_alert_component_spec.rb +++ b/spec/components/dossiers/batch_alert_component_spec.rb @@ -166,6 +166,60 @@ RSpec.describe Dossiers::BatchAlertComponent, type: :component do end end + describe 'repousser_expiration' do + let(:component) do + described_class.new( + batch: batch_operation, + procedure: procedure + ) + end + let!(:dossier) { create(:dossier, :accepte, procedure: procedure) } + let!(:dossier_2) { create(:dossier, :accepte, procedure: procedure) } + let!(:batch_operation) { create(:batch_operation, operation: :repousser_expiration, dossiers: [dossier, dossier_2], instructeur: instructeur) } + context 'in_progress' do + before { + batch_operation.track_processed_dossier(true, dossier) + batch_operation.reload + } + + it { is_expected.to have_selector('.fr-alert--info') } + it { is_expected.to have_text("Une action de masse est en cours") } + it { is_expected.to have_text("1/2 dossiers seront conservé 1 mois supplémentaire") } + it { is_expected.to have_text("Cette opération a été lancée par #{instructeur.email}, il y a moins d'une minute") } + end + + context 'finished and success' do + before { + batch_operation.track_processed_dossier(true, dossier) + batch_operation.track_processed_dossier(true, dossier_2) + batch_operation.reload + } + + it { is_expected.to have_selector('.fr-alert--success') } + it { is_expected.to have_text("L’action de masse est terminée") } + it { is_expected.to have_text("2 dossiers seront conservé 1 mois supplémentaire") } + it { expect(batch_operation.seen_at).to eq(nil) } + end + + context 'finished and fail' do + before { + batch_operation.track_processed_dossier(false, dossier) + batch_operation.track_processed_dossier(true, dossier_2) + batch_operation.reload + } + + it { is_expected.to have_selector('.fr-alert--warning') } + it { is_expected.to have_text("L’action de masse est terminée") } + it { is_expected.to have_text("1/2 dossiers seront conservé 1 mois supplémentaire") } + it { expect(batch_operation.seen_at).to eq(nil) } + + it 'on next render "seen_at" is set to avoid rendering alert' do + render_inline(component).to_html + expect(batch_operation.seen_at).not_to eq(nil) + end + end + end + describe 'accepter' do let(:component) do described_class.new( diff --git a/spec/factories/batch_operation.rb b/spec/factories/batch_operation.rb index fa3c49a73..27742eebb 100644 --- a/spec/factories/batch_operation.rb +++ b/spec/factories/batch_operation.rb @@ -41,6 +41,17 @@ FactoryBot.define do end end + trait :repousser_expiration do + operation { BatchOperation.operations.fetch(:repousser_expiration) } + after(:build) do |batch_operation, evaluator| + procedure = create(:simple_procedure, :published, instructeurs: [evaluator.invalid_instructeur.presence || batch_operation.instructeur], administrateurs: [create(:administrateur)]) + batch_operation.dossiers = [ + create(:dossier, :with_individual, :accepte, procedure: procedure, processed_at: 12.months.ago), + create(:dossier, :with_individual, :accepte, procedure: procedure, processed_at: 12.months.ago) + ] + end + end + trait :accepter do operation { BatchOperation.operations.fetch(:accepter) } after(:build) do |batch_operation, evaluator| diff --git a/spec/jobs/batch_operation_process_one_job_spec.rb b/spec/jobs/batch_operation_process_one_job_spec.rb index 0bbccf43d..49571d282 100644 --- a/spec/jobs/batch_operation_process_one_job_spec.rb +++ b/spec/jobs/batch_operation_process_one_job_spec.rb @@ -59,6 +59,19 @@ describe BatchOperationProcessOneJob, type: :job do end end + context 'when operation is "repousser_expiration"' do + let(:batch_operation) do + create(:batch_operation, :repousser_expiration, + options.merge(instructeur: create(:instructeur))) + end + it 'archives the dossier in the batch' do + expect { subject.perform_now } + .to change { dossier_job.reload.conservation_extension } + .from(dossier_job.conservation_extension) + .to(dossier_job.conservation_extension + 1.month) + end + end + context 'when operation is "follow"' do let(:batch_operation) do create(:batch_operation, :follow,