From b824ea5a1dd3b4a0e90ab32410a397baf04fa817 Mon Sep 17 00:00:00 2001 From: seb-by-ouidou Date: Thu, 16 Nov 2023 09:30:16 +0000 Subject: [PATCH 1/3] feat: batch action for supprimer --- .../batch_alert_component.en.yml | 9 ++++ .../batch_alert_component.fr.yml | 9 ++++ .../dossiers/batch_operation_component.rb | 7 ++- .../batch_operation_component.en.yml | 1 + .../batch_operation_component.fr.yml | 1 + app/models/batch_operation.rb | 7 ++- .../dossiers/batch_alert_component_spec.rb | 54 +++++++++++++++++++ spec/factories/batch_operation.rb | 11 ++++ .../batch_operation_process_one_job_spec.rb | 14 +++++ 9 files changed, 111 insertions(+), 2 deletions(-) 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 764c2f611e9..e8d3ac03548 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 @@ -35,6 +35,15 @@ en: text_success: one: "1 is being refused" other: "%{success_count}/%{count} files have been refused" + supprimer: + finish: + text_success: + one: "%{success_count}/1 file has been deleted" + other: "%{success_count}/%{count} files have been deleted" + in_progress: + text_success: + one: "1 is being deleted" + other: "%{success_count}/%{count} files have been deleted" classer_sans_suite: 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 ed386a22721..58b9ef308a6 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 @@ -35,6 +35,15 @@ fr: text_success: one: "1 dossier est en cours de refus" other: "%{success_count}/%{count} dossiers ont été refusés" + supprimer: + finish: + text_success: + one: "%{success_count}/1 dossier a été supprimé" + other: "%{success_count}/%{count} dossiers ont été supprimés" + in_progress: + text_success: + one: "1 dossier est en cours de suppression" + other: "%{success_count}/%{count} dossiers ont été supprimés" classer_sans_suite: finish: text_success: diff --git a/app/components/dossiers/batch_operation_component.rb b/app/components/dossiers/batch_operation_component.rb index 3164f91f7fa..f9756e1d1b9 100644 --- a/app/components/dossiers/batch_operation_component.rb +++ b/app/components/dossiers/batch_operation_component.rb @@ -17,7 +17,7 @@ def operations_for_dossier(dossier) 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)] when Dossier.states.fetch(:accepte), Dossier.states.fetch(:refuse), Dossier.states.fetch(:sans_suite) - [BatchOperation.operations.fetch(:archiver)] + [BatchOperation.operations.fetch(:archiver), BatchOperation.operations.fetch(:supprimer)] else [] end.append(BatchOperation.operations.fetch(:follow), BatchOperation.operations.fetch(:unfollow)) @@ -44,6 +44,10 @@ def available_operations { label: t(".operations.archiver"), operation: BatchOperation.operations.fetch(:archiver) + }, + { + label: t(".operations.supprimer"), + operation: BatchOperation.operations.fetch(:supprimer) } ] } @@ -110,6 +114,7 @@ def icons follow: 'fr-icon-star-line', passer_en_instruction: 'fr-icon-edit-line', repasser_en_construction: 'fr-icon-draft-line', + supprimer: 'fr-icon-delete-line', unfollow: 'fr-icon-star-fill' } end 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 fb10bc43058..f8646ebdbe9 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: passer_en_instruction: 'Change selected files to instructing' instruction: Instructing files accepter: 'Accept seleted files' + supprimer: Delete seleted files accepter_description: Users will be notified that their file has been accepted refuser: 'Refuse seleted files' refuser_description: Users will be notified that their file has been refused 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 ef7980d3930..6c15180a084 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: passer_en_instruction: 'Passer les dossiers en instruction' instruction: Instruire les dossiers accepter: 'Accepter les dossiers' + supprimer: 'Supprimer les dossiers' accepter_description: Les usagers seront informés que leur dossier a été accepté refuser: 'Refuser les dossiers' refuser_description: Les usagers seront informés que leur dossier a été refusé diff --git a/app/models/batch_operation.rb b/app/models/batch_operation.rb index c1da424c37b..437b24e850c 100644 --- a/app/models/batch_operation.rb +++ b/app/models/batch_operation.rb @@ -7,7 +7,8 @@ class BatchOperation < ApplicationRecord follow: 'follow', passer_en_instruction: 'passer_en_instruction', repasser_en_construction: 'repasser_en_construction', - unfollow: 'unfollow' + unfollow: 'unfollow', + supprimer: 'supprimer' } has_many :dossiers, dependent: :nullify @@ -56,6 +57,8 @@ def dossiers_safe_scope(dossier_ids = self.dossier_ids) query.state_en_instruction when BatchOperation.operations.fetch(:unfollow) then query.with_followers.en_cours + when BatchOperation.operations.fetch(:supprimer) then + query.state_termine end end @@ -82,6 +85,8 @@ def process_one(dossier) dossier.repasser_en_construction!(instructeur: instructeur) when BatchOperation.operations.fetch(:unfollow) instructeur.unfollow(dossier) + when BatchOperation.operations.fetch(:supprimer) + dossier.hide_and_keep_track!(instructeur, :instructeur_request) end end diff --git a/spec/components/dossiers/batch_alert_component_spec.rb b/spec/components/dossiers/batch_alert_component_spec.rb index d43c3bf405f..41fe867f00f 100644 --- a/spec/components/dossiers/batch_alert_component_spec.rb +++ b/spec/components/dossiers/batch_alert_component_spec.rb @@ -399,4 +399,58 @@ end end end + + describe 'supprimer' 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: :supprimer, 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 ont été supprimés") } + 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 ont été supprimés") } + 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 ont été supprimés") } + 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 end diff --git a/spec/factories/batch_operation.rb b/spec/factories/batch_operation.rb index 9d91cfb0561..a5f8bb34bd0 100644 --- a/spec/factories/batch_operation.rb +++ b/spec/factories/batch_operation.rb @@ -94,5 +94,16 @@ ] end end + + trait :supprimer do + operation { BatchOperation.operations.fetch(:supprimer) } + 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), + create(:dossier, :with_individual, :refuse, procedure: procedure) + ] + end + end end end diff --git a/spec/jobs/batch_operation_process_one_job_spec.rb b/spec/jobs/batch_operation_process_one_job_spec.rb index e808ac59fc4..39f41afd34d 100644 --- a/spec/jobs/batch_operation_process_one_job_spec.rb +++ b/spec/jobs/batch_operation_process_one_job_spec.rb @@ -196,6 +196,20 @@ end end + context 'when operation is "supprimer"' do + let(:batch_operation) do + create(:batch_operation, :supprimer, + options.merge(instructeur: create(:instructeur))) + end + + it 'changed the dossier to en construction' do + expect { subject.perform_now } + .to change { dossier_job.reload.hidden_by_administration? } + .from(false) + .to(true) + end + end + context 'when the dossier is out of sync (ie: someone applied a transition somewhere we do not know)' do let(:instructeur) { create(:instructeur) } let(:procedure) { create(:simple_procedure, instructeurs: [instructeur]) } From eedebdc69bf108f042383120d0dca8534e8b3db6 Mon Sep 17 00:00:00 2001 From: seb-by-ouidou Date: Wed, 29 Nov 2023 18:40:51 +0000 Subject: [PATCH 2/3] feat: batch action for restaurer --- .../batch_alert_component.en.yml | 9 ++++ .../batch_alert_component.fr.yml | 9 ++++ .../dossiers/batch_operation_component.rb | 15 +++++- .../batch_operation_component.en.yml | 1 + .../batch_operation_component.fr.yml | 1 + app/models/batch_operation.rb | 24 +++++---- .../dossiers/batch_alert_component_spec.rb | 54 +++++++++++++++++++ spec/factories/batch_operation.rb | 11 ++++ .../batch_operation_process_one_job_spec.rb | 14 +++++ 9 files changed, 126 insertions(+), 12 deletions(-) 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 e8d3ac03548..fb08c4bf291 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 @@ -44,6 +44,15 @@ en: text_success: one: "1 is being deleted" other: "%{success_count}/%{count} files have been deleted" + restaurer: + finish: + text_success: + one: "%{success_count}/1 file has been restored" + other: "%{success_count}/%{count} files have been restored" + in_progress: + text_success: + one: "1 is being restored" + other: "%{success_count}/%{count} files have been restored" classer_sans_suite: 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 58b9ef308a6..f1d669246ee 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 @@ -44,6 +44,15 @@ fr: text_success: one: "1 dossier est en cours de suppression" other: "%{success_count}/%{count} dossiers ont été supprimés" + restaurer: + finish: + text_success: + one: "%{success_count}/1 dossier a été restauré" + other: "%{success_count}/%{count} dossiers ont été restaurés" + in_progress: + text_success: + one: "1 dossier est en cours de restauration" + other: "%{success_count}/%{count} dossiers ont été restaurés" classer_sans_suite: finish: text_success: diff --git a/app/components/dossiers/batch_operation_component.rb b/app/components/dossiers/batch_operation_component.rb index f9756e1d1b9..68b1a2e0185 100644 --- a/app/components/dossiers/batch_operation_component.rb +++ b/app/components/dossiers/batch_operation_component.rb @@ -7,7 +7,7 @@ def initialize(statut:, procedure:) end def render? - ['a-suivre', 'traites', 'suivis'].include?(@statut) + ['a-suivre', 'traites', 'suivis', 'supprimes_recemment'].include?(@statut) end def operations_for_dossier(dossier) @@ -17,7 +17,7 @@ def operations_for_dossier(dossier) 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)] when Dossier.states.fetch(:accepte), Dossier.states.fetch(:refuse), Dossier.states.fetch(:sans_suite) - [BatchOperation.operations.fetch(:archiver), BatchOperation.operations.fetch(:supprimer)] + [BatchOperation.operations.fetch(:archiver), BatchOperation.operations.fetch(:supprimer), BatchOperation.operations.fetch(:restaurer)] else [] end.append(BatchOperation.operations.fetch(:follow), BatchOperation.operations.fetch(:unfollow)) @@ -51,6 +51,16 @@ def available_operations } ] } + when 'supprimes_recemment' then + { + options: + [ + { + label: t(".operations.restaurer"), + operation: BatchOperation.operations.fetch(:restaurer) + } + ] + } when 'suivis' then { options: @@ -115,6 +125,7 @@ def icons passer_en_instruction: 'fr-icon-edit-line', repasser_en_construction: 'fr-icon-draft-line', supprimer: 'fr-icon-delete-line', + restaurer: 'fr-icon-refresh-line', unfollow: 'fr-icon-star-fill' } end 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 f8646ebdbe9..1f3f01294fd 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 @@ -5,6 +5,7 @@ fr: instruction: Instructing files accepter: 'Accept seleted files' supprimer: Delete seleted files + restaurer: Restore seleted files accepter_description: Users will be notified that their file has been accepted refuser: 'Refuse seleted files' refuser_description: Users will be notified that their file has been refused 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 6c15180a084..df91f4b49f8 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 @@ -5,6 +5,7 @@ fr: instruction: Instruire les dossiers accepter: 'Accepter les dossiers' supprimer: 'Supprimer les dossiers' + restaurer: 'Restaurer les dossiers' accepter_description: Les usagers seront informés que leur dossier a été accepté refuser: 'Refuser les dossiers' refuser_description: Les usagers seront informés que leur dossier a été refusé diff --git a/app/models/batch_operation.rb b/app/models/batch_operation.rb index 437b24e850c..29ee9a2eb9e 100644 --- a/app/models/batch_operation.rb +++ b/app/models/batch_operation.rb @@ -7,6 +7,7 @@ class BatchOperation < ApplicationRecord follow: 'follow', passer_en_instruction: 'passer_en_instruction', repasser_en_construction: 'repasser_en_construction', + restaurer: 'restaurer', unfollow: 'unfollow', supprimer: 'supprimer' } @@ -38,27 +39,28 @@ class BatchOperation < ApplicationRecord def dossiers_safe_scope(dossier_ids = self.dossier_ids) query = instructeur .dossiers - .visible_by_administration .where(id: dossier_ids) case operation when BatchOperation.operations.fetch(:archiver) then - query.not_archived.state_termine + query.visible_by_administration.not_archived.state_termine when BatchOperation.operations.fetch(:passer_en_instruction) then - query.state_en_construction + query.visible_by_administration.state_en_construction when BatchOperation.operations.fetch(:accepter) then - query.state_en_instruction + query.visible_by_administration.state_en_instruction when BatchOperation.operations.fetch(:refuser) then - query.state_en_instruction + query.visible_by_administration.state_en_instruction when BatchOperation.operations.fetch(:classer_sans_suite) then - query.state_en_instruction + query.visible_by_administration.state_en_instruction when BatchOperation.operations.fetch(:follow) then - query.without_followers.en_cours + query.visible_by_administration.without_followers.en_cours when BatchOperation.operations.fetch(:repasser_en_construction) then - query.state_en_instruction + query.visible_by_administration.state_en_instruction when BatchOperation.operations.fetch(:unfollow) then - query.with_followers.en_cours + query.visible_by_administration.with_followers.en_cours when BatchOperation.operations.fetch(:supprimer) then - query.state_termine + query.visible_by_administration.state_termine + when BatchOperation.operations.fetch(:restaurer) then + query.hidden_by_administration end end @@ -87,6 +89,8 @@ def process_one(dossier) instructeur.unfollow(dossier) when BatchOperation.operations.fetch(:supprimer) dossier.hide_and_keep_track!(instructeur, :instructeur_request) + when BatchOperation.operations.fetch(:restaurer) + dossier.restore(instructeur) end end diff --git a/spec/components/dossiers/batch_alert_component_spec.rb b/spec/components/dossiers/batch_alert_component_spec.rb index 41fe867f00f..a8f15cdb81e 100644 --- a/spec/components/dossiers/batch_alert_component_spec.rb +++ b/spec/components/dossiers/batch_alert_component_spec.rb @@ -346,6 +346,60 @@ end end + describe 'restaurer' do + let(:component) do + described_class.new( + batch: batch_operation, + procedure: procedure + ) + end + let!(:dossier) { create(:dossier, :accepte, procedure: procedure, hidden_by_administration_at: Time.zone.now) } + let!(:dossier_2) { create(:dossier, :accepte, procedure: procedure, hidden_by_administration_at: Time.zone.now) } + let!(:batch_operation) { create(:batch_operation, operation: :restaurer, 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 ont été restaurés") } + 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 ont été restaurés") } + 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 ont été restaurés") } + 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 'repasser en construction' do let(:component) do described_class.new( diff --git a/spec/factories/batch_operation.rb b/spec/factories/batch_operation.rb index a5f8bb34bd0..b495c8172db 100644 --- a/spec/factories/batch_operation.rb +++ b/spec/factories/batch_operation.rb @@ -84,6 +84,17 @@ end end + trait :restaurer do + operation { BatchOperation.operations.fetch(:restaurer) } + 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, hidden_by_administration_at: Time.zone.now), + create(:dossier, :with_individual, :refuse, procedure: procedure, hidden_by_administration_at: Time.zone.now) + ] + end + end + trait :repasser_en_construction do operation { BatchOperation.operations.fetch(:repasser_en_construction) } 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 39f41afd34d..3a62456fa47 100644 --- a/spec/jobs/batch_operation_process_one_job_spec.rb +++ b/spec/jobs/batch_operation_process_one_job_spec.rb @@ -175,6 +175,20 @@ end end + context 'when operation is "restaurer"' do + let(:batch_operation) do + create(:batch_operation, :restaurer, + options.merge(instructeur: create(:instructeur))) + end + + it 'changed the dossier to en construction' do + expect { subject.perform_now } + .to change { dossier_job.reload.hidden_by_administration? } + .from(true) + .to(false) + end + end + context 'when operation is "classer_sans_suite"' do let(:batch_operation) do create(:batch_operation, :classer_sans_suite, From 8829f9699deedbd490109eea1bd133274720c34f Mon Sep 17 00:00:00 2001 From: seb-by-ouidou Date: Wed, 29 Nov 2023 21:36:55 +0000 Subject: [PATCH 3/3] feat: batch action for desarchiver --- .../batch_alert_component.en.yml | 9 ++++ .../batch_alert_component.fr.yml | 9 ++++ .../dossiers/batch_operation_component.rb | 15 +++++- .../batch_operation_component.en.yml | 1 + .../batch_operation_component.fr.yml | 1 + app/models/batch_operation.rb | 5 ++ .../dossiers/batch_alert_component_spec.rb | 54 +++++++++++++++++++ spec/factories/batch_operation.rb | 12 +++++ .../batch_operation_process_one_job_spec.rb | 13 +++++ 9 files changed, 117 insertions(+), 2 deletions(-) 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 fb08c4bf291..8a626130d1f 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 @@ -8,6 +8,15 @@ en: text_success: one: "1 is being archived" other: "%{success_count}/%{count} files have been archived" + desarchiver: + finish: + text_success: + one: "%{success_count}/1 file has been unarchived" + other: "%{success_count}/%{count} files have been unarchived" + in_progress: + text_success: + one: "1 is being unarchived" + other: "%{success_count}/%{count} files have been unarchived" passer_en_instruction: 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 f1d669246ee..48080539076 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 @@ -8,6 +8,15 @@ fr: text_success: one: "1 dossier est en cours d'archivage" other: "%{success_count}/%{count} dossiers ont été archivés" + desarchiver: + finish: + text_success: + one: "%{success_count}/1 dossier a été désarchivé" + other: "%{success_count}/%{count} dossiers ont été désarchivés" + in_progress: + text_success: + one: "1 dossier est en cours d'désarchivage" + other: "%{success_count}/%{count} dossiers ont été désarchivés" passer_en_instruction: finish: text_success: diff --git a/app/components/dossiers/batch_operation_component.rb b/app/components/dossiers/batch_operation_component.rb index 68b1a2e0185..5e517623405 100644 --- a/app/components/dossiers/batch_operation_component.rb +++ b/app/components/dossiers/batch_operation_component.rb @@ -7,7 +7,7 @@ def initialize(statut:, procedure:) end def render? - ['a-suivre', 'traites', 'suivis', 'supprimes_recemment'].include?(@statut) + ['a-suivre', 'traites', 'suivis', 'archives', 'supprimes_recemment'].include?(@statut) end def operations_for_dossier(dossier) @@ -17,7 +17,7 @@ def operations_for_dossier(dossier) 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)] when Dossier.states.fetch(:accepte), Dossier.states.fetch(:refuse), Dossier.states.fetch(:sans_suite) - [BatchOperation.operations.fetch(:archiver), BatchOperation.operations.fetch(:supprimer), BatchOperation.operations.fetch(:restaurer)] + [BatchOperation.operations.fetch(:archiver), BatchOperation.operations.fetch(:desarchiver), BatchOperation.operations.fetch(:supprimer), BatchOperation.operations.fetch(:restaurer)] else [] end.append(BatchOperation.operations.fetch(:follow), BatchOperation.operations.fetch(:unfollow)) @@ -37,6 +37,16 @@ def available_operations } ] } + when 'archives' then + { + options: + [ + { + label: t(".operations.desarchiver"), + operation: BatchOperation.operations.fetch(:desarchiver) + } + ] + } when 'traites' then { options: @@ -121,6 +131,7 @@ def icons { accepter: 'fr-icon-success-line', archiver: 'fr-icon-folder-2-line', + desarchiver: 'fr-icon-upload-2-line', follow: 'fr-icon-star-line', passer_en_instruction: 'fr-icon-edit-line', repasser_en_construction: 'fr-icon-draft-line', 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 1f3f01294fd..e71e37c2634 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 @@ -1,6 +1,7 @@ fr: operations: archiver: 'Archive selected files' + desarchiver: 'Unarchive selected files' passer_en_instruction: 'Change selected files to instructing' instruction: Instructing files accepter: 'Accept 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 df91f4b49f8..2b4ef31fcdc 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 @@ -1,6 +1,7 @@ fr: operations: archiver: 'Archiver les dossiers' + desarchiver: 'Désarchiver les dossiers' passer_en_instruction: 'Passer les dossiers en instruction' instruction: Instruire les dossiers accepter: 'Accepter les dossiers' diff --git a/app/models/batch_operation.rb b/app/models/batch_operation.rb index 29ee9a2eb9e..346350b2a57 100644 --- a/app/models/batch_operation.rb +++ b/app/models/batch_operation.rb @@ -4,6 +4,7 @@ class BatchOperation < ApplicationRecord refuser: 'refuser', classer_sans_suite: 'classer_sans_suite', archiver: 'archiver', + desarchiver: 'desarchiver', follow: 'follow', passer_en_instruction: 'passer_en_instruction', repasser_en_construction: 'repasser_en_construction', @@ -43,6 +44,8 @@ def dossiers_safe_scope(dossier_ids = self.dossier_ids) case operation when BatchOperation.operations.fetch(:archiver) then query.visible_by_administration.not_archived.state_termine + when BatchOperation.operations.fetch(:desarchiver) then + query.visible_by_administration.archived.state_termine when BatchOperation.operations.fetch(:passer_en_instruction) then query.visible_by_administration.state_en_construction when BatchOperation.operations.fetch(:accepter) then @@ -73,6 +76,8 @@ def process_one(dossier) case operation when BatchOperation.operations.fetch(:archiver) dossier.archiver!(instructeur) + when BatchOperation.operations.fetch(:desarchiver) + dossier.desarchiver! when BatchOperation.operations.fetch(:passer_en_instruction) dossier.passer_en_instruction(instructeur: instructeur) when BatchOperation.operations.fetch(:accepter) diff --git a/spec/components/dossiers/batch_alert_component_spec.rb b/spec/components/dossiers/batch_alert_component_spec.rb index a8f15cdb81e..72062f89093 100644 --- a/spec/components/dossiers/batch_alert_component_spec.rb +++ b/spec/components/dossiers/batch_alert_component_spec.rb @@ -58,6 +58,60 @@ end end + describe 'desarchiver' 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: :desarchiver, 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 ont été désarchivés") } + 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 ont été désarchivés") } + 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 ont été désarchivés") } + 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 'passer_en_instruction' do let(:component) do described_class.new( diff --git a/spec/factories/batch_operation.rb b/spec/factories/batch_operation.rb index b495c8172db..fa3c49a735a 100644 --- a/spec/factories/batch_operation.rb +++ b/spec/factories/batch_operation.rb @@ -18,6 +18,18 @@ end end + trait :desarchiver do + operation { BatchOperation.operations.fetch(:desarchiver) } + 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, archived: true), + create(:dossier, :with_individual, :refuse, procedure: procedure, archived: true), + create(:dossier, :with_individual, :sans_suite, procedure: procedure, archived: true) + ] + end + end + trait :passer_en_instruction do operation { BatchOperation.operations.fetch(:passer_en_instruction) } 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 3a62456fa47..0bbccf43da6 100644 --- a/spec/jobs/batch_operation_process_one_job_spec.rb +++ b/spec/jobs/batch_operation_process_one_job_spec.rb @@ -32,6 +32,19 @@ end end + context 'when operation is "desarchiver"' do + let(:batch_operation) do + create(:batch_operation, :desarchiver, + options.merge(instructeur: create(:instructeur))) + end + it 'archives the dossier in the batch' do + expect { subject.perform_now } + .to change { dossier_job.reload.archived? } + .from(true) + .to(false) + end + end + context 'when operation is "passer_en_instruction"' do let(:batch_operation) do create(:batch_operation, :passer_en_instruction,