commit
d97f2a79f5
12 changed files with 155 additions and 19 deletions
|
@ -69,3 +69,10 @@
|
||||||
position: absolute !important;
|
position: absolute !important;
|
||||||
word-wrap: normal !important;
|
word-wrap: normal !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.label-bold {
|
||||||
|
font-size: 18px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
display: block;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
module Experts
|
module Experts
|
||||||
class AvisController < ExpertController
|
class AvisController < ExpertController
|
||||||
include CreateAvisConcern
|
include CreateAvisConcern
|
||||||
|
include Zipline
|
||||||
|
|
||||||
before_action :authenticate_expert!, except: [:sign_up, :update_expert]
|
before_action :authenticate_expert!, except: [:sign_up, :update_expert]
|
||||||
before_action :check_if_avis_revoked, only: [:show]
|
before_action :check_if_avis_revoked, only: [:show]
|
||||||
before_action :redirect_if_no_sign_up_needed, only: [:sign_up, :update_expert]
|
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'
|
A_DONNER_STATUS = 'a-donner'
|
||||||
DONNES_STATUS = 'donnes'
|
DONNES_STATUS = 'donnes'
|
||||||
|
@ -120,6 +121,14 @@ module Experts
|
||||||
end
|
end
|
||||||
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
|
private
|
||||||
|
|
||||||
def redirect_if_no_sign_up_needed
|
def redirect_if_no_sign_up_needed
|
||||||
|
|
|
@ -236,6 +236,24 @@ module NewAdministrateur
|
||||||
end
|
end
|
||||||
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
|
private
|
||||||
|
|
||||||
def create_instructeur(email)
|
def create_instructeur(email)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
class ActiveStorage::DownloadableFile
|
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)
|
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|
|
pjs.map do |piece_justificative|
|
||||||
[
|
[
|
||||||
piece_justificative,
|
piece_justificative,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
class PiecesJustificativesService
|
class PiecesJustificativesService
|
||||||
def self.liste_documents(dossier)
|
def self.liste_documents(dossier, for_expert)
|
||||||
pjs_champs = pjs_for_champs(dossier)
|
pjs_champs = pjs_for_champs(dossier, for_expert)
|
||||||
pjs_commentaires = pjs_for_commentaires(dossier)
|
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)
|
(pjs_champs + pjs_commentaires + pjs_dossier)
|
||||||
.filter(&:attached?)
|
.filter(&:attached?)
|
||||||
|
@ -120,8 +120,8 @@ class PiecesJustificativesService
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def self.pjs_for_champs(dossier)
|
def self.pjs_for_champs(dossier, for_expert = false)
|
||||||
allowed_champs = dossier.champs + dossier.champs_private
|
allowed_champs = for_expert ? dossier.champs : dossier.champs + dossier.champs_private
|
||||||
|
|
||||||
allowed_child_champs = allowed_champs
|
allowed_child_champs = allowed_champs
|
||||||
.filter { |c| c.type_champ == TypeDeChamp.type_champs.fetch(:repetition) }
|
.filter { |c| c.type_champ == TypeDeChamp.type_champs.fetch(:repetition) }
|
||||||
|
@ -138,17 +138,22 @@ class PiecesJustificativesService
|
||||||
.map(&:piece_jointe)
|
.map(&:piece_jointe)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.pjs_for_dossier(dossier)
|
def self.pjs_for_dossier(dossier, for_expert = false)
|
||||||
bill_signatures = dossier.dossier_operation_logs.filter_map(&:bill_signature).uniq
|
pjs = [
|
||||||
|
|
||||||
[
|
|
||||||
dossier.justificatif_motivation,
|
dossier.justificatif_motivation,
|
||||||
dossier.attestation&.pdf,
|
dossier.attestation&.pdf,
|
||||||
dossier.etablissement&.entreprise_attestation_sociale,
|
dossier.etablissement&.entreprise_attestation_sociale,
|
||||||
dossier.etablissement&.entreprise_attestation_fiscale,
|
dossier.etablissement&.entreprise_attestation_fiscale
|
||||||
dossier.dossier_operation_logs.map(&:serialized),
|
|
||||||
bill_signatures.map(&:serialized),
|
|
||||||
bill_signatures.map(&:signature)
|
|
||||||
].flatten.compact
|
].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
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,6 +5,16 @@
|
||||||
%li= link_to(dossier.procedure.libelle, procedure_expert_avis_index_path(avis.procedure))
|
%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))
|
%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
|
%ul.tabs
|
||||||
= dynamic_tab_item('Demande', expert_avis_path(avis.procedure, avis))
|
= 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?)
|
= dynamic_tab_item('Avis', instruction_expert_avis_path(avis.procedure, avis), notification: avis.answer.blank?)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
- groupe_instructeurs_count = procedure.groupe_instructeurs.count
|
||||||
.card
|
.card
|
||||||
= form_for procedure,
|
= form_for procedure,
|
||||||
url: { action: :update_routing_criteria_name },
|
url: { action: :update_routing_criteria_name },
|
||||||
|
@ -17,7 +18,7 @@
|
||||||
Ajouter un groupe
|
Ajouter un groupe
|
||||||
%p.notice Ce groupe sera un choix de la liste « #{procedure.routing_criteria_name} » .
|
%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.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
|
- 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
|
= 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
|
= 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
|
%span.icon.follow
|
||||||
déplacer les dossiers
|
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
|
= paginate groupes_instructeurs
|
||||||
|
|
|
@ -295,6 +295,7 @@ Rails.application.routes.draw do
|
||||||
post 'commentaire' => 'avis#create_commentaire'
|
post 'commentaire' => 'avis#create_commentaire'
|
||||||
post 'avis' => 'avis#create_avis'
|
post 'avis' => 'avis#create_avis'
|
||||||
get 'bilans_bdf'
|
get 'bilans_bdf'
|
||||||
|
get 'telecharger_pjs' => 'avis#telecharger_pjs'
|
||||||
|
|
||||||
get 'sign_up' => 'avis#sign_up'
|
get 'sign_up' => 'avis#sign_up'
|
||||||
post 'sign_up' => 'avis#update_expert'
|
post 'sign_up' => 'avis#update_expert'
|
||||||
|
@ -426,6 +427,7 @@ Rails.application.routes.draw do
|
||||||
patch 'update_routing_enabled'
|
patch 'update_routing_enabled'
|
||||||
patch 'update_instructeurs_self_management_enabled'
|
patch 'update_instructeurs_self_management_enabled'
|
||||||
post 'import'
|
post 'import'
|
||||||
|
get 'export_groupe_instructeurs'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -422,6 +422,31 @@ describe NewAdministrateur::GroupeInstructeursController, type: :controller do
|
||||||
end
|
end
|
||||||
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
|
describe '#update_routing_criteria_name' do
|
||||||
before do
|
before do
|
||||||
patch :update_routing_criteria_name,
|
patch :update_routing_criteria_name,
|
||||||
|
|
|
@ -5,9 +5,10 @@ feature 'Inviting an expert:' do
|
||||||
context 'as an invited Expert' do
|
context 'as an invited Expert' do
|
||||||
let(:expert) { create(:expert) }
|
let(:expert) { create(:expert) }
|
||||||
let(:instructeur) { create(:instructeur) }
|
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(:experts_procedure) { create(:experts_procedure, expert: expert, procedure: procedure) }
|
||||||
let(:dossier) { create(:dossier, :en_construction, :with_dossier_link, 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) }
|
let(:avis) { create(:avis, dossier: dossier, claimant: instructeur, experts_procedure: experts_procedure, confidentiel: true) }
|
||||||
|
|
||||||
context 'when I don’t already have an account' do
|
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
|
# scenario 'I can invite other experts' do
|
||||||
# end
|
# 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
|
end
|
||||||
|
|
||||||
context 'when there are two experts' do
|
context 'when there are two experts' do
|
||||||
|
|
|
@ -50,5 +50,25 @@ describe ActiveStorage::DownloadableFile do
|
||||||
|
|
||||||
it { expect(list.length).to eq 2 }
|
it { expect(list.length).to eq 2 }
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -33,7 +33,7 @@ describe PiecesJustificativesService do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.liste_documents' do
|
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
|
it "doesn't return sensitive documents like titre_identite" do
|
||||||
expect(champ_identite.piece_justificative_file).to be_attached
|
expect(champ_identite.piece_justificative_file).to be_attached
|
||||||
|
|
Loading…
Reference in a new issue