From 6d89d914e2c2615b6c8dfee002245013d6f869bb Mon Sep 17 00:00:00 2001 From: kara Diaby <kdiaby.pro@gmail.com> Date: Mon, 27 Sep 2021 15:07:32 +0200 Subject: [PATCH 01/11] modify expert avis controller --- app/controllers/experts/avis_controller.rb | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/controllers/experts/avis_controller.rb b/app/controllers/experts/avis_controller.rb index 1bc066d91..3848397e4 100644 --- a/app/controllers/experts/avis_controller.rb +++ b/app/controllers/experts/avis_controller.rb @@ -1,11 +1,12 @@ module Experts class AvisController < ExpertController include CreateAvisConcern + include Zipline before_action :authenticate_expert!, except: [:sign_up, :update_expert] before_action :check_if_avis_revoked, only: [:show] before_action :redirect_if_no_sign_up_needed, only: [:sign_up, :update_expert] - before_action :set_avis_and_dossier, only: [:show, :instruction, :messagerie, :create_commentaire, :update] + before_action :set_avis_and_dossier, only: [:show, :instruction, :messagerie, :create_commentaire, :update, :telecharger_pjs] A_DONNER_STATUS = 'a-donner' DONNES_STATUS = 'donnes' @@ -120,6 +121,14 @@ module Experts end end + def telecharger_pjs + return head(:forbidden) if !avis.dossier.export_and_attachments_downloadable? + + files = ActiveStorage::DownloadableFile.create_list_from_dossier(@dossier, true) + + zipline(files, "dossier-#{@dossier.id}.zip") + end + private def redirect_if_no_sign_up_needed From bfee9c275c0addb46f35812569a263dabe4b4895 Mon Sep 17 00:00:00 2001 From: kara Diaby <kdiaby.pro@gmail.com> Date: Mon, 27 Sep 2021 15:07:42 +0200 Subject: [PATCH 02/11] layout --- app/views/experts/avis/_header.html.haml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/views/experts/avis/_header.html.haml b/app/views/experts/avis/_header.html.haml index 803e317f4..ce3fa5f8c 100644 --- a/app/views/experts/avis/_header.html.haml +++ b/app/views/experts/avis/_header.html.haml @@ -5,6 +5,16 @@ %li= link_to(dossier.procedure.libelle, procedure_expert_avis_index_path(avis.procedure)) %li= link_to("Dossier nº #{dossier.id}", expert_avis_path(avis.procedure, avis)) + %span.dropdown.print-menu-opener + %button.button.dropdown-button.icon-only{ 'aria-expanded' => 'false', 'aria-controls' => 'print-pj-menu' } + %span.icon.attached + %ul#print-pj-menu.print-menu.dropdown-content + %li + - if dossier.export_and_attachments_downloadable? + = link_to "Télécharger le dossier et toutes ses pièces jointes", telecharger_pjs_expert_avis_path(avis), target: "_blank", rel: "noopener", class: "menu-item menu-link" + - else + %p.menu-item Le téléchargement des pièces jointes est désactivé pour les dossiers de plus de #{number_to_human_size Dossier::TAILLE_MAX_ZIP}. + %ul.tabs = dynamic_tab_item('Demande', expert_avis_path(avis.procedure, avis)) = dynamic_tab_item('Avis', instruction_expert_avis_path(avis.procedure, avis), notification: avis.answer.blank?) From 6c82e40ddb2955418b1183cbcb6a648478c89fd8 Mon Sep 17 00:00:00 2001 From: kara Diaby <kdiaby.pro@gmail.com> Date: Mon, 27 Sep 2021 15:07:50 +0200 Subject: [PATCH 03/11] routes --- config/routes.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/config/routes.rb b/config/routes.rb index dcb922acb..2c8bb1c31 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -295,6 +295,7 @@ Rails.application.routes.draw do post 'commentaire' => 'avis#create_commentaire' post 'avis' => 'avis#create_avis' get 'bilans_bdf' + get 'telecharger_pjs' => 'avis#telecharger_pjs' get 'sign_up' => 'avis#sign_up' post 'sign_up' => 'avis#update_expert' From cbedef996be174295e30fb1df4a539e989db5eea Mon Sep 17 00:00:00 2001 From: kara Diaby <kdiaby.pro@gmail.com> Date: Mon, 27 Sep 2021 15:07:56 +0200 Subject: [PATCH 04/11] tests --- spec/features/experts/expert_spec.rb | 39 ++++++++++++++++++- .../active_storage/downloadable_file_spec.rb | 20 ++++++++++ .../pieces_justificatives_service_spec.rb | 2 +- 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/spec/features/experts/expert_spec.rb b/spec/features/experts/expert_spec.rb index 5a1398da7..06eaa6126 100644 --- a/spec/features/experts/expert_spec.rb +++ b/spec/features/experts/expert_spec.rb @@ -5,9 +5,10 @@ feature 'Inviting an expert:' do context 'as an invited Expert' do let(:expert) { create(:expert) } let(:instructeur) { create(:instructeur) } - let(:procedure) { create(:procedure, :published, instructeurs: [instructeur]) } + let(:procedure) { create(:procedure, :published, :with_piece_justificative, instructeurs: [instructeur]) } let(:experts_procedure) { create(:experts_procedure, expert: expert, procedure: procedure) } let(:dossier) { create(:dossier, :en_construction, :with_dossier_link, procedure: procedure) } + let(:champ) { dossier.champs.first } let(:avis) { create(:avis, dossier: dossier, claimant: instructeur, experts_procedure: experts_procedure, confidentiel: true) } context 'when I don’t already have an account' do @@ -83,6 +84,42 @@ feature 'Inviting an expert:' do # scenario 'I can invite other experts' do # end + + context 'with dossiers having attached files', js: true do + let(:path) { 'spec/fixtures/files/piece_justificative_0.pdf' } + let(:commentaire) { create(:commentaire, instructeur: instructeur, dossier: dossier) } + + before do + champ.piece_justificative_file.attach(io: File.open(path), filename: "piece_justificative_0.pdf", content_type: "application/pdf") + dossier.champs_private << create(:champ_piece_justificative, :with_piece_justificative_file, private: true, dossier: dossier) + end + + scenario 'An Expert can download an archive containing attachments without any private champ, bill signature and operations logs' do + avis # create avis + login_as expert.user, scope: :user + visit expert_all_avis_path + + click_on '1 avis à donner' + click_on avis.dossier.user.email + + find(:css, '.attached').click + click_on 'Télécharger le dossier et toutes ses pièces jointes' + # For some reason, clicking the download link does not trigger the download in the headless browser ; + # So we need to go to the download link directly + visit telecharger_pjs_expert_avis_path(avis.dossier.procedure, avis) + + DownloadHelpers.wait_for_download + files = ZipTricks::FileReader.read_zip_structure(io: File.open(DownloadHelpers.download)) + expect(DownloadHelpers.download).to include "dossier-#{dossier.id}.zip" + expect(files.size).to be 2 + expect(files[0].filename.include?('export')).to be_truthy + expect(files[1].filename.include?('piece_justificative_0')).to be_truthy + expect(files[1].uncompressed_size).to be File.size(path) + end + + before { DownloadHelpers.clear_downloads } + after { DownloadHelpers.clear_downloads } + end end context 'when there are two experts' do diff --git a/spec/lib/active_storage/downloadable_file_spec.rb b/spec/lib/active_storage/downloadable_file_spec.rb index 835e8b731..755a0643c 100644 --- a/spec/lib/active_storage/downloadable_file_spec.rb +++ b/spec/lib/active_storage/downloadable_file_spec.rb @@ -50,5 +50,25 @@ describe ActiveStorage::DownloadableFile do it { expect(list.length).to eq 2 } end + + context 'when the files are asked by an expert with piece justificative and private piece justificative' do + let(:expert) { create(:expert) } + let(:instructeur) { create(:instructeur) } + let(:procedure) { create(:procedure, :published, :with_piece_justificative, instructeurs: [instructeur]) } + let(:experts_procedure) { create(:experts_procedure, expert: expert, procedure: procedure) } + let(:dossier) { create(:dossier, :en_construction, :with_dossier_link, procedure: procedure) } + let(:champ) { dossier.champs.first } + let(:avis) { create(:avis, dossier: dossier, claimant: instructeur, experts_procedure: experts_procedure, confidentiel: true) } + + subject(:list) { ActiveStorage::DownloadableFile.create_list_from_dossier(dossier, true) } + + before do + dossier.champs_private << create(:champ_piece_justificative, :with_piece_justificative_file, private: true, dossier: dossier) + + dossier.champs << create(:champ_piece_justificative, :with_piece_justificative_file, dossier: dossier) + end + + it { expect(list.length).to eq 2 } + end end end diff --git a/spec/services/pieces_justificatives_service_spec.rb b/spec/services/pieces_justificatives_service_spec.rb index a9a78742c..32e46303d 100644 --- a/spec/services/pieces_justificatives_service_spec.rb +++ b/spec/services/pieces_justificatives_service_spec.rb @@ -33,7 +33,7 @@ describe PiecesJustificativesService do end describe '.liste_documents' do - subject { PiecesJustificativesService.liste_documents(dossier) } + subject { PiecesJustificativesService.liste_documents(dossier, false) } it "doesn't return sensitive documents like titre_identite" do expect(champ_identite.piece_justificative_file).to be_attached From 6ef5b5d47486eaf66faf7f0fb4e9c9fc5088d2f1 Mon Sep 17 00:00:00 2001 From: kara Diaby <kdiaby.pro@gmail.com> Date: Tue, 28 Sep 2021 11:23:54 +0200 Subject: [PATCH 05/11] modify downloable file --- app/lib/active_storage/downloadable_file.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/lib/active_storage/downloadable_file.rb b/app/lib/active_storage/downloadable_file.rb index bdfe5760b..49bd45f32 100644 --- a/app/lib/active_storage/downloadable_file.rb +++ b/app/lib/active_storage/downloadable_file.rb @@ -1,7 +1,7 @@ class ActiveStorage::DownloadableFile - def self.create_list_from_dossier(dossier) + def self.create_list_from_dossier(dossier, for_expert = false) dossier_export = PiecesJustificativesService.generate_dossier_export(dossier) - pjs = [dossier_export] + PiecesJustificativesService.liste_documents(dossier) + pjs = [dossier_export] + PiecesJustificativesService.liste_documents(dossier, for_expert) pjs.map do |piece_justificative| [ piece_justificative, From 0bd879ec9baa222f0cce8878cdcb1968c5d07168 Mon Sep 17 00:00:00 2001 From: kara Diaby <kdiaby.pro@gmail.com> Date: Tue, 28 Sep 2021 11:24:06 +0200 Subject: [PATCH 06/11] modify pj service --- app/services/pieces_justificatives_service.rb | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/app/services/pieces_justificatives_service.rb b/app/services/pieces_justificatives_service.rb index 8b1d59a7c..da59962b4 100644 --- a/app/services/pieces_justificatives_service.rb +++ b/app/services/pieces_justificatives_service.rb @@ -1,8 +1,8 @@ class PiecesJustificativesService - def self.liste_documents(dossier) - pjs_champs = pjs_for_champs(dossier) + def self.liste_documents(dossier, for_expert) + pjs_champs = pjs_for_champs(dossier, for_expert) pjs_commentaires = pjs_for_commentaires(dossier) - pjs_dossier = pjs_for_dossier(dossier) + pjs_dossier = pjs_for_dossier(dossier, for_expert) (pjs_champs + pjs_commentaires + pjs_dossier) .filter(&:attached?) @@ -120,8 +120,8 @@ class PiecesJustificativesService private - def self.pjs_for_champs(dossier) - allowed_champs = dossier.champs + dossier.champs_private + def self.pjs_for_champs(dossier, for_expert = false) + allowed_champs = for_expert ? dossier.champs : dossier.champs + dossier.champs_private allowed_child_champs = allowed_champs .filter { |c| c.type_champ == TypeDeChamp.type_champs.fetch(:repetition) } @@ -138,17 +138,22 @@ class PiecesJustificativesService .map(&:piece_jointe) end - def self.pjs_for_dossier(dossier) - bill_signatures = dossier.dossier_operation_logs.filter_map(&:bill_signature).uniq - - [ + def self.pjs_for_dossier(dossier, for_expert = false) + pjs = [ dossier.justificatif_motivation, dossier.attestation&.pdf, dossier.etablissement&.entreprise_attestation_sociale, - dossier.etablissement&.entreprise_attestation_fiscale, - dossier.dossier_operation_logs.map(&:serialized), - bill_signatures.map(&:serialized), - bill_signatures.map(&:signature) + dossier.etablissement&.entreprise_attestation_fiscale ].flatten.compact + + if !for_expert + bill_signatures = dossier.dossier_operation_logs.filter_map(&:bill_signature).uniq + pjs += [ + dossier.dossier_operation_logs.map(&:serialized), + bill_signatures.map(&:serialized), + bill_signatures.map(&:signature) + ].flatten.compact + end + pjs end end From 9c9eeb8e76409f0c1b1e133b0a8f1a32a81371c0 Mon Sep 17 00:00:00 2001 From: kara Diaby <kdiaby.pro@gmail.com> Date: Mon, 4 Oct 2021 16:02:19 +0200 Subject: [PATCH 07/11] modify groupe instructeurs controller$ --- .../groupe_instructeurs_controller.rb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/app/controllers/new_administrateur/groupe_instructeurs_controller.rb b/app/controllers/new_administrateur/groupe_instructeurs_controller.rb index d3e6d3f9f..5ab1b9eb6 100644 --- a/app/controllers/new_administrateur/groupe_instructeurs_controller.rb +++ b/app/controllers/new_administrateur/groupe_instructeurs_controller.rb @@ -236,6 +236,24 @@ module NewAdministrateur end end + def export_groupe_instructeurs + groupe_instructeurs = procedure.groupe_instructeurs + + data = CSV.generate(headers: true) do |csv| + column_names = ["Groupe", "Email"] + csv << column_names + groupe_instructeurs.each do |gi| + gi.instructeurs.each do |instructeur| + csv << [gi.label, instructeur.email] + end + end + end + + respond_to do |format| + format.csv { send_data data, filename: "#{procedure.id}-groupe-instructeurs-#{Date.today}.csv" } + end + end + private def create_instructeur(email) From 6251c3369b72553681b39db22ebab8f1b4e83ed9 Mon Sep 17 00:00:00 2001 From: kara Diaby <kdiaby.pro@gmail.com> Date: Mon, 4 Oct 2021 16:02:37 +0200 Subject: [PATCH 08/11] routes --- config/routes.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/config/routes.rb b/config/routes.rb index 2c8bb1c31..3e8b1168b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -427,6 +427,7 @@ Rails.application.routes.draw do patch 'update_routing_enabled' patch 'update_instructeurs_self_management_enabled' post 'import' + get 'export_groupe_instructeurs' end end From 177074bf327225aa12e3aa0e88eb8348732a3fed Mon Sep 17 00:00:00 2001 From: kara Diaby <kdiaby.pro@gmail.com> Date: Mon, 4 Oct 2021 16:02:50 +0200 Subject: [PATCH 09/11] css --- app/assets/stylesheets/labels.scss | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/assets/stylesheets/labels.scss b/app/assets/stylesheets/labels.scss index b41676f3b..50be851d2 100644 --- a/app/assets/stylesheets/labels.scss +++ b/app/assets/stylesheets/labels.scss @@ -69,3 +69,10 @@ position: absolute !important; word-wrap: normal !important; } + +.label-bold { + font-size: 18px; + margin-bottom: 16px; + display: block; + font-weight: bold; +} From e4b61eae21333a540de20c40d17db69dac0e8258 Mon Sep 17 00:00:00 2001 From: kara Diaby <kdiaby.pro@gmail.com> Date: Mon, 4 Oct 2021 16:02:58 +0200 Subject: [PATCH 10/11] layout --- .../new_administrateur/groupe_instructeurs/_edit.html.haml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/views/new_administrateur/groupe_instructeurs/_edit.html.haml b/app/views/new_administrateur/groupe_instructeurs/_edit.html.haml index c72ba1068..98892fd39 100644 --- a/app/views/new_administrateur/groupe_instructeurs/_edit.html.haml +++ b/app/views/new_administrateur/groupe_instructeurs/_edit.html.haml @@ -1,3 +1,4 @@ +- groupe_instructeurs_count = procedure.groupe_instructeurs.count .card = form_for procedure, url: { action: :update_routing_criteria_name }, @@ -17,7 +18,7 @@ Ajouter un groupe %p.notice Ce groupe sera un choix de la liste « #{procedure.routing_criteria_name} » . = f.text_field :label, placeholder: 'ex. Ville de Bordeaux', required: true - = f.submit 'Ajouter le groupe', class: 'button primary send' + = f.submit 'Ajouter le groupe', class: "button primary send" - csv_max_size = NewAdministrateur::GroupeInstructeursController::CSV_MAX_SIZE = form_tag import_admin_procedure_groupe_instructeurs_path(procedure), method: :post, multipart: true, class: "mt-4 form" do @@ -48,5 +49,7 @@ = link_to reaffecter_dossiers_admin_procedure_groupe_instructeur_path(procedure, group), class: 'button', title:'Réaffecter les dossiers à un autre groupe afin de pouvoir le supprimer' do %span.icon.follow déplacer les dossiers + - if groupe_instructeurs_count > 1 + = link_to "Exporter au format CSV", export_groupe_instructeurs_admin_procedure_groupe_instructeurs_path(procedure, format: :csv) = paginate groupes_instructeurs From 919d708ec459fe6337fae9e74d66a14dbd44187f Mon Sep 17 00:00:00 2001 From: kara Diaby <kdiaby.pro@gmail.com> Date: Mon, 4 Oct 2021 18:18:54 +0200 Subject: [PATCH 11/11] tests --- .../groupe_instructeurs_controller_spec.rb | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/spec/controllers/new_administrateur/groupe_instructeurs_controller_spec.rb b/spec/controllers/new_administrateur/groupe_instructeurs_controller_spec.rb index de9d81cd5..c789f8fc2 100644 --- a/spec/controllers/new_administrateur/groupe_instructeurs_controller_spec.rb +++ b/spec/controllers/new_administrateur/groupe_instructeurs_controller_spec.rb @@ -422,6 +422,31 @@ describe NewAdministrateur::GroupeInstructeursController, type: :controller do end end + describe '#export_groupe_instructeurs' do + let(:procedure) { create(:procedure, :published) } + let(:gi_1_2) { procedure.groupe_instructeurs.create(label: 'groupe instructeur 1 2') } + let(:instructeur_assigned_1) { create :instructeur, email: 'instructeur_1@ministere_a.gouv.fr', administrateurs: [admin] } + let(:instructeur_assigned_2) { create :instructeur, email: 'instructeur_2@ministere_b.gouv.fr', administrateurs: [admin] } + + subject do + get :export_groupe_instructeurs, params: { procedure_id: procedure.id, format: :csv } + end + + before do + procedure.administrateurs << admin + gi_1_2.instructeurs << [instructeur_assigned_1, instructeur_assigned_2] + end + + it 'generates a CSV file containing the instructeurs and groups' do + expect(subject.status).to eq(200) + expect(subject.stream.body.split("\n").size).to eq(3) + expect(subject.stream.body).to include("groupe instructeur 1 2") + expect(subject.stream.body).to include(instructeur_assigned_1.email) + expect(subject.stream.body).to include(instructeur_assigned_2.email) + expect(subject.header["Content-Disposition"]).to include("#{procedure.id}-groupe-instructeurs-#{Date.today}.csv") + end + end + describe '#update_routing_criteria_name' do before do patch :update_routing_criteria_name,