From 32e8f34511bf2db1016ddff4399c0bad6973cc44 Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 7 Feb 2024 17:33:34 +0100 Subject: [PATCH] refactor(PiecesJustificativesService): stop passing flags, pass user_profile and manage ACL from the service itself --- app/controllers/experts/avis_controller.rb | 4 +- .../instructeurs/dossiers_controller.rb | 5 +- app/controllers/users/dossiers_controller.rb | 3 +- app/lib/active_storage/downloadable_file.rb | 13 +- app/services/pieces_justificatives_service.rb | 141 ++++--- app/services/procedure_export_service.rb | 2 +- app/views/dossiers/show.pdf.prawn | 18 +- lib/tasks/benchmarks.rake | 8 +- .../experts/avis_controller_spec.rb | 10 +- .../instructeurs/dossiers_controller_spec.rb | 4 +- .../users/dossiers_controller_spec.rb | 4 +- spec/jobs/export_job_spec.rb | 6 +- .../active_storage/downloadable_file_spec.rb | 5 +- .../pieces_justificatives_service_spec.rb | 394 ++++++++++-------- .../procedure_archive_service_spec.rb | 9 +- .../services/procedure_export_service_spec.rb | 6 +- 16 files changed, 355 insertions(+), 277 deletions(-) diff --git a/app/controllers/experts/avis_controller.rb b/app/controllers/experts/avis_controller.rb index 2a38ed108..c9e4592d3 100644 --- a/app/controllers/experts/avis_controller.rb +++ b/app/controllers/experts/avis_controller.rb @@ -159,9 +159,7 @@ module Experts end def telecharger_pjs - @dossier = dossier_with_champs - - files = ActiveStorage::DownloadableFile.create_list_from_dossiers(Dossier.where(id: @dossier.id), include_avis_for_expert: current_expert) + files = ActiveStorage::DownloadableFile.create_list_from_dossiers(user_profile: current_expert, dossiers: Dossier.where(id: @dossier.id)) cleaned_files = ActiveStorage::DownloadableFile.cleanup_list_from_dossier(files) zipline(cleaned_files, "dossier-#{@dossier.id}.zip") diff --git a/app/controllers/instructeurs/dossiers_controller.rb b/app/controllers/instructeurs/dossiers_controller.rb index 8ba1829ee..675aa39d3 100644 --- a/app/controllers/instructeurs/dossiers_controller.rb +++ b/app/controllers/instructeurs/dossiers_controller.rb @@ -49,10 +49,9 @@ module Instructeurs def show @demande_seen_at = current_instructeur.follows.find_by(dossier: dossier_with_champs)&.demande_seen_at @is_dossier_in_batch_operation = dossier.batch_operation.present? - respond_to do |format| format.pdf do - @include_infos_administration = true + @acls = PiecesJustificativesService.new(user_profile: current_instructeur).acl_for_dossier_export render(template: 'dossiers/show', formats: [:pdf]) end format.all @@ -322,7 +321,7 @@ module Instructeurs end def telecharger_pjs - files = ActiveStorage::DownloadableFile.create_list_from_dossiers(Dossier.where(id: dossier.id), with_champs_private: true, include_infos_administration: true) + files = ActiveStorage::DownloadableFile.create_list_from_dossiers(dossiers: Dossier.where(id: dossier.id), user_profile: current_instructeur) cleaned_files = ActiveStorage::DownloadableFile.cleanup_list_from_dossier(files) zipline(cleaned_files, "dossier-#{dossier.id}.zip") diff --git a/app/controllers/users/dossiers_controller.rb b/app/controllers/users/dossiers_controller.rb index dc0fcd9ed..9094633ce 100644 --- a/app/controllers/users/dossiers_controller.rb +++ b/app/controllers/users/dossiers_controller.rb @@ -88,10 +88,11 @@ module Users end def show + pj_service = PiecesJustificativesService.new(user_profile: current_user) respond_to do |format| format.pdf do @dossier = dossier_with_champs(pj_template: false) - @include_infos_administration = false + @acls = pj_service.acl_for_dossier_export render(template: 'dossiers/show', formats: [:pdf]) end format.all do diff --git a/app/lib/active_storage/downloadable_file.rb b/app/lib/active_storage/downloadable_file.rb index de116e651..b89c20997 100644 --- a/app/lib/active_storage/downloadable_file.rb +++ b/app/lib/active_storage/downloadable_file.rb @@ -1,15 +1,10 @@ require 'fog/openstack' class ActiveStorage::DownloadableFile - def self.create_list_from_dossiers( - dossiers, - with_bills: false, - with_champs_private: false, - include_infos_administration: false, - include_avis_for_expert: false - ) - PiecesJustificativesService.generate_dossier_export(dossiers, include_infos_administration:, include_avis_for_expert:) + - PiecesJustificativesService.liste_documents(dossiers, with_bills:, with_champs_private:, with_avis_piece_justificative: include_infos_administration) + def self.create_list_from_dossiers(dossiers:, user_profile:) + pj_service = PiecesJustificativesService.new(user_profile:) + + pj_service.generate_dossiers_export(dossiers) + pj_service.liste_documents(dossiers) end def self.cleanup_list_from_dossier(files) diff --git a/app/services/pieces_justificatives_service.rb b/app/services/pieces_justificatives_service.rb index d02341a3f..9cb94b66f 100644 --- a/app/services/pieces_justificatives_service.rb +++ b/app/services/pieces_justificatives_service.rb @@ -1,17 +1,18 @@ class PiecesJustificativesService - def self.liste_documents(dossiers, - with_bills:, - with_champs_private:, - with_avis_piece_justificative:) + def initialize(user_profile:) + @user_profile = user_profile + end + + def liste_documents(dossiers) bill_ids = [] docs = dossiers.in_batches.flat_map do |batch| - pjs = pjs_for_champs(batch, with_champs_private:) + + pjs = pjs_for_champs(batch) + pjs_for_commentaires(batch) + pjs_for_dossier(batch) + - pjs_for_avis(batch, with_avis_piece_justificative:) + pjs_for_avis(batch) - if with_bills + if liste_documents_allows?(:with_bills) # some bills are shared among operations # so first, all the bill_ids are fetched operation_logs, some_bill_ids = operation_logs_and_signature_ids(batch) @@ -23,7 +24,7 @@ class PiecesJustificativesService pjs end - if with_bills + if liste_documents_allows?(:with_bills) # then the bills are retrieved without duplication docs += signatures(bill_ids.uniq) end @@ -31,6 +32,38 @@ class PiecesJustificativesService docs end + def generate_dossiers_export(dossiers) + return [] if dossiers.empty? + + pdfs = [] + + procedure = dossiers.first.procedure + dossiers = dossiers.includes(:individual, :traitement, :etablissement, user: :france_connect_information, avis: :expert, commentaires: [:instructeur, :expert]) + dossiers = DossierPreloader.new(dossiers).in_batches + dossiers.each do |dossier| + dossier.association(:procedure).target = procedure + + pdf = ApplicationController + .render(template: 'dossiers/show', formats: [:pdf], + assigns: { + acls: acl_for_dossier_export, + dossier: dossier + }) + + a = ActiveStorage::FakeAttachment.new( + file: StringIO.new(pdf), + filename: "export-#{dossier.id}.pdf", + name: 'pdf_export_for_instructeur', + id: dossier.id, + created_at: dossier.updated_at + ) + + pdfs << ActiveStorage::DownloadableFile.pj_and_path(dossier.id, a) + end + + pdfs + end + def self.serialize_types_de_champ_as_type_pj(revision) tdcs = revision.types_de_champ_public.filter { |type_champ| type_champ.old_pj.present? } tdcs.map.with_index do |type_champ, order_place| @@ -48,47 +81,52 @@ class PiecesJustificativesService end end - def self.generate_dossier_export(dossiers, include_infos_administration: false, include_avis_for_expert: false) - return [] if dossiers.empty? - - pdfs = [] - - procedure = dossiers.first.procedure - dossiers = dossiers.includes(:individual, :traitement, :etablissement, user: :france_connect_information, avis: :expert, commentaires: [:instructeur, :expert]) - dossiers = DossierPreloader.new(dossiers).in_batches - dossiers.each do |dossier| - dossier.association(:procedure).target = procedure - - pdf = ApplicationController - .render(template: 'dossiers/show', formats: [:pdf], - assigns: { - include_infos_administration:, - include_avis_for_expert:, - dossier: dossier - }) - - a = ActiveStorage::FakeAttachment.new( - file: StringIO.new(pdf), - filename: "export-#{dossier.id}.pdf", - name: 'pdf_export_for_instructeur', - id: dossier.id, - created_at: dossier.updated_at - ) - - pdfs << ActiveStorage::DownloadableFile.pj_and_path(dossier.id, a) + def acl_for_dossier_export + case @user_profile + when Expert + { include_infos_administration: true, include_avis_for_expert: true, only_for_expert: @user_profile } + when Instructeur, Administrateur + { include_infos_administration: true, include_avis_for_expert: true, only_for_export: false } + when User + { include_infos_administration: false, include_avis_for_expert: false, only_for_expert: false } + else + raise 'not supported' end - - pdfs end private - def self.pjs_for_champs(dossiers, with_champs_private:) + def liste_documents_allows?(scope) + case @user_profile + when Expert + { + with_bills: false, + with_champs_private: false, + with_avis_piece_justificative: false + } + when Instructeur + { + with_bills: false, + with_champs_private: true, + with_avis_piece_justificative: true + } + when Administrateur + { + with_bills: true, + with_champs_private: true, + with_avis_piece_justificative: true + } + else + raise 'not supported' + end.fetch(scope) + end + + def pjs_for_champs(dossiers) champs = Champ .joins(:piece_justificative_file_attachments) .where(type: "Champs::PieceJustificativeChamp", dossier: dossiers) - if !with_champs_private + if !liste_documents_allows?(:with_champs_private) champs = champs.where(private: false) end @@ -106,7 +144,7 @@ class PiecesJustificativesService end end - def self.pjs_for_commentaires(dossiers) + def pjs_for_commentaires(dossiers) commentaire_id_dossier_id = Commentaire .joins(:piece_jointe_attachment) .where(dossier: dossiers) @@ -123,13 +161,13 @@ class PiecesJustificativesService end end - def self.pjs_for_dossier(dossiers) + def pjs_for_dossier(dossiers) motivations(dossiers) + attestations(dossiers) + etablissements(dossiers) end - def self.etablissements(dossiers) + def etablissements(dossiers) etablissement_id_dossier_id = Etablissement .where(dossier: dossiers) .pluck(:id, :dossier_id) @@ -144,7 +182,7 @@ class PiecesJustificativesService end end - def self.motivations(dossiers) + def motivations(dossiers) ActiveStorage::Attachment .includes(:blob) .where(record_type: "Dossier", name: "justificatif_motivation", record_id: dossiers) @@ -155,7 +193,7 @@ class PiecesJustificativesService end end - def self.attestations(dossiers) + def attestations(dossiers) attestation_id_dossier_id = Attestation .joins(:pdf_attachment) .where(dossier: dossiers) @@ -171,10 +209,9 @@ class PiecesJustificativesService end end - def self.pjs_for_avis(dossiers, with_avis_piece_justificative:) - avis_ids_dossier_id_query = Avis.joins(:dossier) - .where(dossier: dossiers) - avis_ids_dossier_id_query = avis_ids_dossier_id_query.where(confidentiel: false) if !with_avis_piece_justificative + def pjs_for_avis(dossiers) + avis_ids_dossier_id_query = Avis.joins(:dossier).where(dossier: dossiers) + avis_ids_dossier_id_query = avis_ids_dossier_id_query.where(confidentiel: false) if !liste_documents_allows?(:with_avis_piece_justificative) avis_ids_dossier_id = avis_ids_dossier_id_query.pluck(:id, :dossier_id).to_h ActiveStorage::Attachment @@ -187,7 +224,7 @@ class PiecesJustificativesService end end - def self.operation_logs_and_signature_ids(dossiers) + def operation_logs_and_signature_ids(dossiers) dol_id_dossier_id_bill_id = DossierOperationLog .where(dossier: dossiers, data: nil) .pluck(:bill_signature_id, :id, :dossier_id) @@ -227,14 +264,14 @@ class PiecesJustificativesService [serialized_dols, bill_ids] end - def self.signatures(bill_ids) + def signatures(bill_ids) ActiveStorage::Attachment .includes(:blob) .where(record_type: "BillSignature", record_id: bill_ids) .map { |bill| ActiveStorage::DownloadableFile.bill_and_path(bill) } end - def self.safe_attachment(attachment) + def safe_attachment(attachment) attachment .blob .virus_scan_result == ActiveStorage::VirusScanner::SAFE diff --git a/app/services/procedure_export_service.rb b/app/services/procedure_export_service.rb index eb2b1dd93..41390b740 100644 --- a/app/services/procedure_export_service.rb +++ b/app/services/procedure_export_service.rb @@ -36,7 +36,7 @@ class ProcedureExportService end def to_zip - attachments = ActiveStorage::DownloadableFile.create_list_from_dossiers(dossiers, with_champs_private: true, include_infos_administration: true) + attachments = ActiveStorage::DownloadableFile.create_list_from_dossiers(dossiers:, user_profile: @user_profile) DownloadableFileService.download_and_zip(procedure, attachments, base_filename) do |zip_filepath| ArchiveUploader.new(procedure: procedure, filename: filename(:zip), filepath: zip_filepath).blob diff --git a/app/views/dossiers/show.pdf.prawn b/app/views/dossiers/show.pdf.prawn index fad7d2d40..cde96074b 100644 --- a/app/views/dossiers/show.pdf.prawn +++ b/app/views/dossiers/show.pdf.prawn @@ -110,7 +110,7 @@ def add_identite_etablissement(pdf, etablissement) format_in_2_columns(pdf, "État administratif", humanized_entreprise_etat_administratif(etablissement)) end - if @include_infos_administration + if @acls[:include_infos_administration] if etablissement.entreprise_effectif_mensuel.present? format_in_2_columns(pdf, "Effectif mensuel #{try_format_mois_effectif(etablissement)} de l'établissement (URSSAF ou MSA) ", number_with_delimiter(etablissement.entreprise_effectif_mensuel.to_s)) end @@ -349,22 +349,28 @@ prawn_document(page_size: "A4") do |pdf| add_title(pdf, 'Formulaire') add_champs(pdf, @dossier.champs_public) - if @include_infos_administration && @dossier.champs_private.present? + if @acls[:include_infos_administration] && @dossier.champs_private.present? add_title(pdf, "Annotations privées") add_champs(pdf, @dossier.champs_private) end - if @include_infos_administration && @dossier.avis.present? + if @acls[:include_infos_administration] && @dossier.avis.present? add_title(pdf, "Avis") @dossier.avis.each do |avis| add_avis(pdf, avis) end end - if @include_avis_for_expert && @dossier.avis.present? + if @acls[:include_avis_for_expert] && @dossier.avis.present? add_title(pdf, "Avis") - @dossier.avis_for_expert(@include_avis_for_expert).each do |avis| - add_avis(pdf, avis) + if @acls[:only_for_expert] + @dossier.avis_for_expert(@acls[:only_for_expert]).each do |avis| + add_avis(pdf, avis) + end + else + @dossier.avis.each do |avis| + add_avis(pdf, avis) + end end end diff --git a/lib/tasks/benchmarks.rake b/lib/tasks/benchmarks.rake index 237cd7fe5..5ed648ded 100644 --- a/lib/tasks/benchmarks.rake +++ b/lib/tasks/benchmarks.rake @@ -4,8 +4,8 @@ namespace :benchmarks do p_45964 = Procedure.find(45964) p_55824 = Procedure.find(55824) Benchmark.bm do |x| - x.report("Démarche 45964") { ProcedureExportService.new(p_45964, p_45964.dossiers).to_xlsx } - x.report("Démarche 55824") { ProcedureExportService.new(p_55824, p_55824.dossiers).to_xlsx } + x.report("Démarche 45964") { ProcedureExportService.new(p_45964, p_45964.dossiers, p_45964.administrateurs.first).to_xlsx } + x.report("Démarche 55824") { ProcedureExportService.new(p_55824, p_55824.dossiers, p_55824.administrateurs.first).to_xlsx } end end @@ -25,8 +25,8 @@ namespace :benchmarks do p_45964 = Procedure.find(45964) p_55824 = Procedure.find(55824) Benchmark.bm do |x| - x.report("Démarche 45964") { PiecesJustificativesService.generate_dossier_export(p_45964.dossiers) } - x.report("Démarche 55824") { PiecesJustificativesService.generate_dossier_export(p_55824.dossiers.limit(10_000)) } + x.report("Démarche 45964") { PiecesJustificativesService.new(user_profile: p_45964.administrateurs.first).generate_dossiers_export(p_45964.dossiers) } + x.report("Démarche 55824") { PiecesJustificativesService.new(user_profile: p_55824.administrateurs.first).generate_dossiers_export(p_55824.dossiers.limit(10_000)) } end end diff --git a/spec/controllers/experts/avis_controller_spec.rb b/spec/controllers/experts/avis_controller_spec.rb index e350a1cae..946474bb3 100644 --- a/spec/controllers/experts/avis_controller_spec.rb +++ b/spec/controllers/experts/avis_controller_spec.rb @@ -116,17 +116,15 @@ describe Experts::AvisController, type: :controller do describe '#telecharger_pjs' do let(:avis) { avis_with_answer } - subject { get :telecharger_pjs, params: { id: avis.id, procedure_id: } } - before do - allow(PiecesJustificativesService).to receive(:generate_dossier_export).and_return([]).with([dossier], include_infos_administration: false, include_avis_for_expert: expert) - end - context 'with a valid avis' do it do + service = instance_double(PiecesJustificativesService) + expect(PiecesJustificativesService).to receive(:new).with(user_profile: expert).and_return(service) + expect(service).to receive(:generate_dossiers_export).with(Dossier.where(id: dossier)).and_return([]) + expect(service).to receive(:liste_documents).with(Dossier.where(id: dossier)).and_return([]) is_expected.to have_http_status(:success) - expect(PiecesJustificativesService).to have_received(:generate_dossier_export) end end diff --git a/spec/controllers/instructeurs/dossiers_controller_spec.rb b/spec/controllers/instructeurs/dossiers_controller_spec.rb index d30fcbe51..017496238 100644 --- a/spec/controllers/instructeurs/dossiers_controller_spec.rb +++ b/spec/controllers/instructeurs/dossiers_controller_spec.rb @@ -910,7 +910,7 @@ describe Instructeurs::DossiersController, type: :controller do subject end - it { expect(assigns(:include_infos_administration)).to eq(true) } + it { expect(assigns(:acls)).to eq(PiecesJustificativesService.new(user_profile: instructeur).acl_for_dossier_export) } it { expect(assigns(:is_dossier_in_batch_operation)).to eq(false) } it { expect(response).to render_template 'dossiers/show' } @@ -1053,7 +1053,7 @@ describe Instructeurs::DossiersController, type: :controller do end before do - allow(PiecesJustificativesService).to receive(:generate_dossier_export).with([dossier], include_infos_administration: true, include_avis_for_expert: false).and_call_original + allow_any_instance_of(PiecesJustificativesService).to receive(:generate_dossiers_export).with([dossier]).and_call_original end it 'includes an attachment' do diff --git a/spec/controllers/users/dossiers_controller_spec.rb b/spec/controllers/users/dossiers_controller_spec.rb index b582dbba3..88d0865ba 100644 --- a/spec/controllers/users/dossiers_controller_spec.rb +++ b/spec/controllers/users/dossiers_controller_spec.rb @@ -1114,8 +1114,8 @@ describe Users::DossiersController, type: :controller do end context 'when the dossier has been submitted' do - it { expect(assigns(:include_infos_administration)).to eq(false) } - it { expect(response).to render_template 'dossiers/show' } + it { expect(assigns(:acls)).to eq(PiecesJustificativesService.new(user_profile: user).acl_for_dossier_export) } + it { expect(response).to render_template('dossiers/show') } end end end diff --git a/spec/jobs/export_job_spec.rb b/spec/jobs/export_job_spec.rb index 81893d4c4..fda04f5ce 100644 --- a/spec/jobs/export_job_spec.rb +++ b/spec/jobs/export_job_spec.rb @@ -1,6 +1,6 @@ describe ExportJob do - let(:procedure) { create(:procedure, instructeurs: [instructeur]) } - let(:instructeur) { create(:instructeur) } + let(:procedure) { create(:procedure, instructeurs: [user_profile]) } + let(:user_profile) { create(:instructeur) } let(:time_span_type) { :everything } let(:status) { :tous } let(:key) { '123' } @@ -8,7 +8,7 @@ describe ExportJob do create(:export, format:, time_span_type:, key:, - instructeur:, + user_profile:, groupe_instructeurs: procedure.groupe_instructeurs) end diff --git a/spec/lib/active_storage/downloadable_file_spec.rb b/spec/lib/active_storage/downloadable_file_spec.rb index 7119ff164..7e0031a36 100644 --- a/spec/lib/active_storage/downloadable_file_spec.rb +++ b/spec/lib/active_storage/downloadable_file_spec.rb @@ -1,7 +1,8 @@ describe ActiveStorage::DownloadableFile do let(:dossier) { create(:dossier, :en_construction) } - - subject(:list) { ActiveStorage::DownloadableFile.create_list_from_dossiers(Dossier.where(id: dossier.id), with_bills: true, with_champs_private: true) } + let(:user_profile) { create(:administrateur) } + let(:dossiers) { Dossier.where(id: dossier.id) } + subject(:list) { ActiveStorage::DownloadableFile.create_list_from_dossiers(user_profile:, dossiers:) } describe 'create_list_from_dossiers' do context 'when no piece_justificative is present' do diff --git a/spec/services/pieces_justificatives_service_spec.rb b/spec/services/pieces_justificatives_service_spec.rb index 9c065e717..fca7c3a0f 100644 --- a/spec/services/pieces_justificatives_service_spec.rb +++ b/spec/services/pieces_justificatives_service_spec.rb @@ -1,54 +1,129 @@ describe PiecesJustificativesService do describe '.liste_documents' do - let(:with_champs_private) { true } - let(:with_bills) { true } - let(:with_avis_piece_justificative) { true } - + let(:dossier) { create(:dossier, procedure: procedure) } + let(:dossiers) { Dossier.where(id: dossier.id) } subject do - PiecesJustificativesService - .liste_documents(Dossier.where(id: dossier.id), with_bills:, with_champs_private:, with_avis_piece_justificative:) - .map(&:first) + PiecesJustificativesService.new(user_profile:).liste_documents(dossiers).map(&:first) end - context 'with a pj champ' do + context 'no acl' do + let(:user_profile) { build(:administrateur) } let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :piece_justificative }]) } - let(:dossier) { create(:dossier, procedure: procedure) } let(:witness) { create(:dossier, procedure: procedure) } - let(:pj_champ) { -> (d) { d.champs_public.find { |c| c.type == 'Champs::PieceJustificativeChamp' } } } - before do - attach_file_to_champ(pj_champ.call(dossier)) - attach_file_to_champ(pj_champ.call(witness)) - end - context 'with a single attachment' do + before do + attach_file_to_champ(pj_champ.call(dossier)) + attach_file_to_champ(pj_champ.call(witness)) + end + it { expect(subject).to match_array(pj_champ.call(dossier).piece_justificative_file.attachments) } end context 'with a multiple attachments' do before do attach_file_to_champ(pj_champ.call(dossier)) + attach_file_to_champ(pj_champ.call(witness)) + attach_file_to_champ(pj_champ.call(dossier)) end it { expect(subject.count).to eq(2) } it { expect(subject).to match_array(pj_champ.call(dossier).piece_justificative_file.attachments) } end + + context 'with a pj not safe on a champ' do + let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :piece_justificative }]) } + let(:dossier) { create(:dossier, procedure: procedure) } + let(:pj_champ) { -> (d) { d.champs_public.find { |c| c.type == 'Champs::PieceJustificativeChamp' } } } + + before { attach_file_to_champ(pj_champ.call(dossier), safe = false) } + + it { expect(subject).to be_empty } + end + + context 'with a identite champ pj' do + let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :titre_identite }]) } + let(:dossier) { create(:dossier, procedure: procedure) } + let(:witness) { create(:dossier, procedure: procedure) } + + let(:champ_identite) { dossier.champs_public.find { |c| c.type == 'Champs::TitreIdentiteChamp' } } + + before { attach_file_to_champ(champ_identite) } + + it "doesn't return sensitive documents like titre_identite" do + expect(champ_identite.piece_justificative_file).to be_attached + expect(subject).to be_empty + end + end + + context 'with a pj on an commentaire' do + let(:dossier) { create(:dossier) } + let(:witness) { create(:dossier) } + + let!(:commentaire) { create(:commentaire, dossier: dossier) } + let!(:witness_commentaire) { create(:commentaire, dossier: witness) } + + before do + attach_file(commentaire.piece_jointe) + attach_file(witness_commentaire.piece_jointe) + end + + it { expect(subject).to match_array(dossier.commentaires.first.piece_jointe.attachment) } + end + + context 'with a pj not safe on a commentaire' do + let(:dossier) { create(:dossier) } + let!(:commentaire) { create(:commentaire, dossier: dossier) } + + before { attach_file(commentaire.piece_jointe, safe = false) } + + it { expect(subject).to be_empty } + end + + context 'with a motivation' do + let(:dossier) { create(:dossier, :with_justificatif) } + let!(:witness) { create(:dossier, :with_justificatif) } + + it { expect(subject).to match_array(dossier.justificatif_motivation.attachment) } + end + + context 'with a motivation not safe' do + let(:dossier) { create(:dossier) } + + before { attach_file(dossier.justificatif_motivation, safe = false) } + + it { expect(subject).to be_empty } + end + + context 'with an attestation' do + let(:dossier) { create(:dossier, :with_attestation) } + let!(:witness) { create(:dossier, :with_attestation) } + + it { expect(subject).to match_array(dossier.attestation.pdf.attachment) } + end + + context 'with an etablissement' do + let(:dossier) { create(:dossier, :with_entreprise) } + let(:attestation_sociale) { dossier.etablissement.entreprise_attestation_sociale } + let(:attestation_fiscale) { dossier.etablissement.entreprise_attestation_fiscale } + + let!(:witness) { create(:dossier, :with_entreprise) } + let!(:witness_attestation_sociale) { witness.etablissement.entreprise_attestation_sociale } + let!(:witness_attestation_fiscale) { witness.etablissement.entreprise_attestation_fiscale } + + before do + attach_file(attestation_sociale) + attach_file(attestation_fiscale) + end + + it { expect(subject).to match_array([attestation_sociale.attachment, attestation_fiscale.attachment]) } + end end - context 'with a pj not safe on a champ' do + context 'acl on private pj champ' do let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :piece_justificative }]) } let(:dossier) { create(:dossier, procedure: procedure) } - let(:pj_champ) { -> (d) { d.champs_public.find { |c| c.type == 'Champs::PieceJustificativeChamp' } } } - - before { attach_file_to_champ(pj_champ.call(dossier), safe = false) } - - it { expect(subject).to be_empty } - end - - context 'with a private pj champ' do - let(:procedure) { create(:procedure) } - let(:dossier) { create(:dossier, procedure: procedure) } let(:witness) { create(:dossier, procedure: procedure) } let!(:private_pj) { create(:type_de_champ_piece_justificative, procedure: procedure, private: true) } @@ -59,138 +134,23 @@ describe PiecesJustificativesService do attach_file_to_champ(private_pj_champ.call(witness)) end - it { expect(subject).to match_array(private_pj_champ.call(dossier).piece_justificative_file.attachments) } + context 'given an administrateur' do + let(:user_profile) { build(:administrateur) } + it { expect(subject).to match_array(private_pj_champ.call(dossier).piece_justificative_file.attachments) } + end - context 'without private champ' do - let(:with_champs_private) { false } + context 'given an instructeur' do + let(:user_profile) { create(:instructeur) } + it { expect(subject).to match_array(private_pj_champ.call(dossier).piece_justificative_file.attachments) } + end - it { expect(subject).to be_empty } + context 'given an expert' do + let(:user_profile) { create(:expert) } + it { expect(subject).not_to match_array(private_pj_champ.call(dossier).piece_justificative_file.attachments) } end end - context 'with avis.piece_justificative being confidentiel' do - let(:procedure) { create(:procedure) } - let(:dossier) { create(:dossier, procedure: procedure) } - let(:avis) { create(:avis, dossier: dossier, confidentiel: true) } - let(:with_avis_piece_justificative) { false } - - before do - to_be_attached = { - io: StringIO.new("toto"), - filename: "toto.png", - content_type: "image/png", - metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE } - } - - avis.piece_justificative_file.attach(to_be_attached) - end - - it "doesn't return confidentiel avis.piece_justificative_file" do - expect(subject).to be_empty - end - end - - context 'with avis.piece_justificative being public' do - let(:procedure) { create(:procedure) } - let(:dossier) { create(:dossier, procedure: procedure) } - let(:avis) { create(:avis, dossier: dossier) } - let(:with_avis_piece_justificative) { false } - - before do - to_be_attached = { - io: StringIO.new("toto"), - filename: "toto.png", - content_type: "image/png", - metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE } - } - - avis.piece_justificative_file.attach(to_be_attached) - end - - it "return avis.piece_justificative_file not confidentiel" do - expect(subject).not_to be_empty - end - end - - context 'with a identite champ pj' do - let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :titre_identite }]) } - let(:dossier) { create(:dossier, procedure: procedure) } - let(:witness) { create(:dossier, procedure: procedure) } - - let(:champ_identite) { dossier.champs_public.find { |c| c.type == 'Champs::TitreIdentiteChamp' } } - - before { attach_file_to_champ(champ_identite) } - - it "doesn't return sensitive documents like titre_identite" do - expect(champ_identite.piece_justificative_file).to be_attached - expect(subject).to be_empty - end - end - - context 'with a pj on an commentaire' do - let(:dossier) { create(:dossier) } - let(:witness) { create(:dossier) } - - let!(:commentaire) { create(:commentaire, dossier: dossier) } - let!(:witness_commentaire) { create(:commentaire, dossier: witness) } - - before do - attach_file(commentaire.piece_jointe) - attach_file(witness_commentaire.piece_jointe) - end - - it { expect(subject).to match_array(dossier.commentaires.first.piece_jointe.attachment) } - end - - context 'with a pj not safe on a commentaire' do - let(:dossier) { create(:dossier) } - let!(:commentaire) { create(:commentaire, dossier: dossier) } - - before { attach_file(commentaire.piece_jointe, safe = false) } - - it { expect(subject).to be_empty } - end - - context 'with a motivation' do - let(:dossier) { create(:dossier, :with_justificatif) } - let!(:witness) { create(:dossier, :with_justificatif) } - - it { expect(subject).to match_array(dossier.justificatif_motivation.attachment) } - end - - context 'with a motivation not safe' do - let(:dossier) { create(:dossier) } - - before { attach_file(dossier.justificatif_motivation, safe = false) } - - it { expect(subject).to be_empty } - end - - context 'with an attestation' do - let(:dossier) { create(:dossier, :with_attestation) } - let!(:witness) { create(:dossier, :with_attestation) } - - it { expect(subject).to match_array(dossier.attestation.pdf.attachment) } - end - - context 'with an etablissement' do - let(:dossier) { create(:dossier, :with_entreprise) } - let(:attestation_sociale) { dossier.etablissement.entreprise_attestation_sociale } - let(:attestation_fiscale) { dossier.etablissement.entreprise_attestation_fiscale } - - let!(:witness) { create(:dossier, :with_entreprise) } - let!(:witness_attestation_sociale) { witness.etablissement.entreprise_attestation_sociale } - let!(:witness_attestation_fiscale) { witness.etablissement.entreprise_attestation_fiscale } - - before do - attach_file(attestation_sociale) - attach_file(attestation_fiscale) - end - - it { expect(subject).to match_array([attestation_sociale.attachment, attestation_fiscale.attachment]) } - end - - context 'with a bill' do + context 'acl on bill' do let(:dossier) { create(:dossier) } let(:witness) { create(:dossier) } @@ -213,58 +173,140 @@ describe PiecesJustificativesService do let(:dossier_bs) { dossier.dossier_operation_logs.first.bill_signature } - it "returns serialized bill and signature" do - expect(subject).to match_array([dossier_bs.serialized.attachment, dossier_bs.signature.attachment]) + context 'given an Administrateur, includes bills' do + let(:user_profile) { build(:administrateur) } + + it "returns serialized bill and signature" do + expect(subject).to match_array([dossier_bs.serialized.attachment, dossier_bs.signature.attachment]) + end + + context 'with a dol' do + let(:dol) { create(:dossier_operation_log, dossier: dossier) } + let(:witness_dol) { create(:dossier_operation_log, dossier: witness) } + + before do + attach_file(dol.serialized) + attach_file(witness_dol.serialized) + end + + it { expect(subject).to include(dol.serialized.attachment) } + end end - context 'without bills' do - let(:with_bills) { false } + context 'given an expert, does not includes bills' do + let(:user_profile) { create(:expert) } + + it { expect(subject).to be_empty } + end + + context 'given an instructeur, does not includes bills' do + let(:user_profile) { create(:instructeur) } it { expect(subject).to be_empty } end end - context 'with a dol' do - let(:dossier) { create(:dossier) } - let(:witness) { create(:dossier) } + context 'acl on with_avis_piece_justificative' do + let(:user_profile) { create(:expert) } + let(:procedure) { create(:procedure) } + let(:dossier) { create(:dossier, procedure: procedure) } - let(:dol) { create(:dossier_operation_log, dossier: dossier) } - let(:witness_dol) { create(:dossier_operation_log, dossier: witness) } + context 'with avis.piece_justificative being confidentiel' do + let(:procedure) { create(:procedure) } + let(:avis) { create(:avis, dossier: dossier, confidentiel: true) } - before do - attach_file(dol.serialized) - attach_file(witness_dol.serialized) + before do + to_be_attached = { + io: StringIO.new("toto"), + filename: "toto.png", + content_type: "image/png", + metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE } + } + + avis.piece_justificative_file.attach(to_be_attached) + end + + context 'given an administrateur' do + let(:user_profile) { build(:administrateur) } + it "doesn't return confidentiel avis.piece_justificative_file" do + expect(subject).not_to be_empty + end + end + + context 'given an instructeur' do + let(:user_profile) { create(:instructeur) } + it "doesn't return confidentiel avis.piece_justificative_file" do + expect(subject).not_to be_empty + end + end + + context 'given an expert' do + let(:user_profile) { create(:expert) } + it "doesn't return confidentiel avis.piece_justificative_file" do + expect(subject).to be_empty + end + end end - it { expect(subject).to match_array(dol.serialized.attachment) } + context 'with avis.piece_justificative being public' do + let(:procedure) { create(:procedure) } + let(:dossier) { create(:dossier, procedure: procedure) } + let(:avis) { create(:avis, dossier: dossier) } + before do + to_be_attached = { + io: StringIO.new("toto"), + filename: "toto.png", + content_type: "image/png", + metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE } + } - context 'without bills' do - let(:with_bills) { false } + avis.piece_justificative_file.attach(to_be_attached) + end - it { expect(subject).to be_empty } + context 'given an administrateur' do + let(:user_profile) { build(:administrateur) } + it "doesn't return confidentiel avis.piece_justificative_file" do + expect(subject).not_to be_empty + end + end + + context 'given an instructeur' do + let(:user_profile) { create(:instructeur) } + it "doesn't return confidentiel avis.piece_justificative_file" do + expect(subject).not_to be_empty + end + end + + context 'given an expert' do + let(:user_profile) { create(:expert) } + it "doesn't return confidentiel avis.piece_justificative_file" do + expect(subject).not_to be_empty + end + end end end end - describe '.generate_dossier_export' do + describe '.generate_dossiers_export' do + let(:user_profile) { build(:administrateur) } let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :repetition, children: [{ type: :piece_justificative }] }]) } let(:dossier) { create(:dossier, :with_populated_champs, procedure: procedure) } - - subject { PiecesJustificativesService.generate_dossier_export(Dossier.where(id: dossier.id)) } + let(:dossiers) { Dossier.where(id: dossier.id) } + subject { PiecesJustificativesService.new(user_profile:).generate_dossiers_export(dossiers) } it "doesn't update dossier" do expect { subject }.not_to change { dossier.updated_at } end context 'when given an expert' do - let!(:expert) { create(:expert) } + let!(:user_profile) { create(:expert) } let!(:confidentiel_avis) { create(:avis, :confidentiel, dossier: dossier) } let!(:not_confidentiel_avis) { create(:avis, :not_confidentiel, dossier: dossier) } - let!(:expert_avis) { create(:avis, :confidentiel, dossier: dossier, expert: expert) } + let!(:expert_avis) { create(:avis, :confidentiel, dossier: dossier, expert: user_profile) } - subject { PiecesJustificativesService.generate_dossier_export(Dossier.where(id: dossier.id), include_avis_for_expert: expert) } + subject { PiecesJustificativesService.new(user_profile:).generate_dossiers_export(dossiers) } it "includes avis not confidentiel as well as expert's avis" do - expect_any_instance_of(Dossier).to receive(:avis_for_expert).with(expert).and_return([]) + expect_any_instance_of(Dossier).to receive(:avis_for_expert).with(user_profile).and_return([]) subject end end diff --git a/spec/services/procedure_archive_service_spec.rb b/spec/services/procedure_archive_service_spec.rb index 94d2dce81..8edd88065 100644 --- a/spec/services/procedure_archive_service_spec.rb +++ b/spec/services/procedure_archive_service_spec.rb @@ -1,6 +1,7 @@ describe ProcedureArchiveService do - let(:procedure) { create(:procedure, :published) } + let(:procedure) { create(:procedure, :published, administrateurs: [administrateur]) } let(:instructeur) { create(:instructeur) } + let(:administrateur) { create(:administrateur) } let(:service) { ProcedureArchiveService.new(procedure) } let(:year) { 2020 } let(:month) { 3 } @@ -18,7 +19,7 @@ describe ProcedureArchiveService do after { Timecop.return } context 'for a specific month' do - let(:archive) { create(:archive, time_span_type: 'monthly', job_status: 'pending', month: date_month, groupe_instructeurs: groupe_instructeurs) } + let(:archive) { create(:archive, user_profile: administrateur, time_span_type: 'monthly', job_status: 'pending', month: date_month, groupe_instructeurs: groupe_instructeurs) } let(:year) { 2021 } it 'collects files with success' do @@ -86,7 +87,7 @@ describe ProcedureArchiveService do let(:documents) { [pj, bad_pj].map { |p| ActiveStorage::DownloadableFile.pj_and_path(dossier.id, p) } } before do - allow(PiecesJustificativesService).to receive(:liste_documents).and_return(documents) + allow_any_instance_of(PiecesJustificativesService).to receive(:liste_documents).and_return(documents) end it 'collect files without raising exception' do @@ -120,7 +121,7 @@ describe ProcedureArchiveService do end context 'for all months' do - let(:archive) { create(:archive, time_span_type: 'everything', job_status: 'pending', groupe_instructeurs: groupe_instructeurs) } + let(:archive) { create(:archive, user_profile: administrateur, time_span_type: 'everything', job_status: 'pending', groupe_instructeurs: groupe_instructeurs) } it 'collect files' do allow_any_instance_of(ActiveStorage::Attachment).to receive(:url).and_return("https://opengraph.githubassets.com/5e61989aecb78e369c93674f877d7bf4ecde378850114a9563cdf8b6a2472536/typhoeus/typhoeus/issues/110") diff --git a/spec/services/procedure_export_service_spec.rb b/spec/services/procedure_export_service_spec.rb index e7196047b..18d8f25af 100644 --- a/spec/services/procedure_export_service_spec.rb +++ b/spec/services/procedure_export_service_spec.rb @@ -510,16 +510,16 @@ describe ProcedureExportService do end end - context 'generate_dossier_export' do + context 'generate_dossiers_export' do it 'include_infos_administration (so it includes avis, champs privés)' do - expect(ActiveStorage::DownloadableFile).to receive(:create_list_from_dossiers).with(anything, with_champs_private: true, include_infos_administration: true).and_return([]) + expect(ActiveStorage::DownloadableFile).to receive(:create_list_from_dossiers).with(dossiers: anything, user_profile: instructeur).and_return([]) subject end end context 'with files (and http calls)' do let!(:dossier) { create(:dossier, :accepte, :with_populated_champs, :with_individual, procedure: procedure) } - let(:dossier_exports) { PiecesJustificativesService.generate_dossier_export(Dossier.where(id: dossier)) } + let(:dossier_exports) { PiecesJustificativesService.new(user_profile: instructeur).generate_dossiers_export(Dossier.where(id: dossier)) } before do allow_any_instance_of(ActiveStorage::Attachment).to receive(:url).and_return("https://opengraph.githubassets.com/d0e7862b24d8026a3c03516d865b28151eb3859029c6c6c2e86605891fbdcd7a/socketry/async-io") end