From 22a518c921a26f4d2ab9ef642d49a127579d4b4a Mon Sep 17 00:00:00 2001 From: clemkeirua Date: Mon, 18 May 2020 11:07:51 +0200 Subject: [PATCH 1/9] ajout d'un bouton d'export des mails des demandeurs --- app/controllers/manager/procedures_controller.rb | 7 +++++++ app/views/manager/procedures/show.html.erb | 3 +++ config/routes.rb | 1 + 3 files changed, 11 insertions(+) diff --git a/app/controllers/manager/procedures_controller.rb b/app/controllers/manager/procedures_controller.rb index a6c45dbd9..33843a6c1 100644 --- a/app/controllers/manager/procedures_controller.rb +++ b/app/controllers/manager/procedures_controller.rb @@ -39,6 +39,13 @@ module Manager redirect_to manager_procedure_path(procedure) end + def export_mail_brouillons + dossiers = procedure.dossiers.state_brouillon + emails = dossiers.map { |d| d.user.email }.sort + date = Time.zone.now.strftime('%d-%m-%Y') + send_data(emails.join("\n"), :filename => "brouillons-#{procedure.id}-au-#{date}.csv") + end + def add_administrateur administrateur = Administrateur.by_email(params[:email]) if administrateur diff --git a/app/views/manager/procedures/show.html.erb b/app/views/manager/procedures/show.html.erb index faa058874..cae25c6be 100644 --- a/app/views/manager/procedures/show.html.erb +++ b/app/views/manager/procedures/show.html.erb @@ -71,5 +71,8 @@ as well as a link to its edit page. <% end %> <% end %> +
+ Télécharger un export CSV contenant les emails des demandeurs ayant effectué une demandes en brouillon +
diff --git a/config/routes.rb b/config/routes.rb index bdbcc31a3..3375caf17 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -13,6 +13,7 @@ Rails.application.routes.draw do post 'restore', on: :member post 'add_administrateur', on: :member post 'change_piece_justificative_template', on: :member + get 'export_mail_brouillons', on: :member end resources :dossiers, only: [:index, :show] do From 8542fd9f4763ab2b6ac37fafa50262cf01ae7dec Mon Sep 17 00:00:00 2001 From: clemkeirua Date: Mon, 18 May 2020 12:17:21 +0200 Subject: [PATCH 2/9] unique emails + remove n+1 --- app/controllers/manager/procedures_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/manager/procedures_controller.rb b/app/controllers/manager/procedures_controller.rb index 33843a6c1..ec45022a9 100644 --- a/app/controllers/manager/procedures_controller.rb +++ b/app/controllers/manager/procedures_controller.rb @@ -40,8 +40,8 @@ module Manager end def export_mail_brouillons - dossiers = procedure.dossiers.state_brouillon - emails = dossiers.map { |d| d.user.email }.sort + dossiers = procedure.dossiers.state_brouillon.includes(:user) + emails = dossiers.map { |d| d.user.email }.sort.uniq date = Time.zone.now.strftime('%d-%m-%Y') send_data(emails.join("\n"), :filename => "brouillons-#{procedure.id}-au-#{date}.csv") end From 77101208a46dd10692d4bc4fd58e1dbaf326390c Mon Sep 17 00:00:00 2001 From: clemkeirua Date: Tue, 12 May 2020 17:58:00 +0200 Subject: [PATCH 3/9] Anonymisation de l'instructeur dans la messagerie --- app/models/commentaire.rb | 6 ++++- spec/models/commentaire_spec.rb | 15 +++++++++-- .../messages/message.html.haml_spec.rb | 27 +++++++++++++++++++ 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/app/models/commentaire.rb b/app/models/commentaire.rb index 7d4a520ae..90ad80582 100644 --- a/app/models/commentaire.rb +++ b/app/models/commentaire.rb @@ -34,7 +34,11 @@ class Commentaire < ApplicationRecord def redacted_email if instructeur.present? - instructeur.email.split('@').first + if Flipper.enabled?(:hide_instructeur_email, dossier.procedure) + "Instructeur n° #{instructeur.id}" + else + instructeur.email.split('@').first + end else email end diff --git a/spec/models/commentaire_spec.rb b/spec/models/commentaire_spec.rb index 703a0def4..c38a0f3d8 100644 --- a/spec/models/commentaire_spec.rb +++ b/spec/models/commentaire_spec.rb @@ -44,11 +44,22 @@ describe Commentaire do describe "#redacted_email" do subject { commentaire.redacted_email } + let(:procedure) { create(:procedure) } + let(:dossier) { create(:dossier, procedure: procedure) } + context 'with a commentaire created by a instructeur' do - let(:commentaire) { build :commentaire, instructeur: instructeur } + let(:commentaire) { build :commentaire, instructeur: instructeur, dossier: dossier } let(:instructeur) { build :instructeur, email: 'some_user@exemple.fr' } - it { is_expected.to eq 'some_user' } + context 'when the procedure shows instructeurs email' do + before { Flipper.disable(:hide_instructeur_email, procedure) } + it { is_expected.to eq 'some_user' } + end + + context 'when the procedure hides instructeurs email' do + before { Flipper.enable(:hide_instructeur_email, procedure) } + it { is_expected.to eq "Instructeur n° #{instructeur.id}" } + end end context 'with a commentaire created by a user' do diff --git a/spec/views/shared/dossiers/messages/message.html.haml_spec.rb b/spec/views/shared/dossiers/messages/message.html.haml_spec.rb index e5674ae6b..010de240a 100644 --- a/spec/views/shared/dossiers/messages/message.html.haml_spec.rb +++ b/spec/views/shared/dossiers/messages/message.html.haml_spec.rb @@ -20,4 +20,31 @@ describe 'shared/dossiers/messages/message.html.haml', type: :view do it { is_expected.to have_css(".highlighted") } end + + context 'with an instructeur message' do + let(:instructeur) { create(:instructeur) } + let(:procedure) { create(:procedure) } + let(:commentaire) { create(:commentaire, instructeur: instructeur, body: 'Second message') } + let(:dossier) { create(:dossier, :en_construction, commentaires: [commentaire], procedure: procedure) } + + context 'on a procedure with anonymous instructeurs' do + before { Flipper.enable_actor(:hide_instructeur_email, procedure) } + + context 'redacts the instructeur email' do + it { is_expected.to have_text(commentaire.body) } + it { is_expected.to have_text("Instructeur n° #{instructeur.id}") } + it { is_expected.not_to have_text(instructeur.email) } + end + end + + context 'on a procedure where instructeurs names are not redacted' do + before { Flipper.disable_actor(:hide_instructeur_email, procedure) } + + context 'redacts the instructeur email but keeps the name' do + it { is_expected.to have_text(commentaire.body) } + it { is_expected.to have_text(instructeur.email.split('@').first) } + it { is_expected.not_to have_text(instructeur.email) } + end + end + end end From 00adb006b41b6c8ca1a8d08459d979e1e4f6dfa2 Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Wed, 13 May 2020 13:57:40 +0000 Subject: [PATCH 4/9] specs: improve ProcedureExportService spec --- spec/services/procedure_export_service_spec.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/spec/services/procedure_export_service_spec.rb b/spec/services/procedure_export_service_spec.rb index 1b6f87a04..5a29e4a3a 100644 --- a/spec/services/procedure_export_service_spec.rb +++ b/spec/services/procedure_export_service_spec.rb @@ -25,13 +25,13 @@ describe ProcedureExportService do procedure.reload end - context 'dossiers' do - it 'should have sheets' do + describe 'sheets' do + it 'should have a sheet for each record type' do expect(subject.sheets.map(&:name)).to eq(['Dossiers', 'Etablissements', 'Avis']) end end - context 'with dossier' do + describe 'Dossiers sheet' do let!(:dossier) { create(:dossier, :en_instruction, :with_all_champs, :with_individual, procedure: procedure) } let(:nominal_headers) do @@ -97,14 +97,14 @@ describe ProcedureExportService do context 'with a procedure routee' do before { procedure.groupe_instructeurs.create(label: '2') } - let(:routee_header) { nominal_headers.insert(nominal_headers.index('textarea'), 'Groupe instructeur') } + let(:routee_headers) { nominal_headers.insert(nominal_headers.index('textarea'), 'Groupe instructeur') } - it { expect(dossiers_sheet.headers).to match(routee_header) } + it { expect(dossiers_sheet.headers).to match(routee_headers) } it { expect(dossiers_sheet.data[0][dossiers_sheet.headers.index('Groupe instructeur')]).to eq('défaut') } end end - context 'with etablissement' do + describe 'Etablissement sheet' do let(:procedure) { create(:procedure, :published, :with_all_champs) } let!(:dossier) { create(:dossier, :en_instruction, :with_all_champs, :with_entreprise, procedure: procedure) } @@ -284,7 +284,7 @@ describe ProcedureExportService do end end - context 'with avis' do + describe 'Avis sheet' do let!(:dossier) { create(:dossier, :en_instruction, :with_all_champs, :with_individual, procedure: procedure) } let!(:avis) { create(:avis, :with_answer, dossier: dossier) } @@ -305,7 +305,7 @@ describe ProcedureExportService do end end - context 'with repetitions' do + describe 'Repetitions sheet' do let!(:dossiers) do [ create(:dossier, :en_instruction, :with_all_champs, :with_individual, procedure: procedure), From b63fa0e6b884b0e492175bbada6e9bb8064d39c5 Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Wed, 13 May 2020 15:57:56 +0200 Subject: [PATCH 5/9] dossier: remove "Birthdate" column from export --- app/models/dossier.rb | 6 ++++-- spec/services/procedure_export_service_spec.rb | 14 +++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 292a5eae3..87f2d27cd 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -626,9 +626,11 @@ class Dossier < ApplicationRecord columns += [ ['Civilité', individual&.gender], ['Nom', individual&.nom], - ['Prénom', individual&.prenom], - ['Date de naissance', individual&.birthdate] + ['Prénom', individual&.prenom] ] + if procedure.ask_birthday + columns += [['Date de naissance', individual&.birthdate]] + end elsif with_etablissement columns += [ ['Établissement SIRET', etablissement&.siret], diff --git a/spec/services/procedure_export_service_spec.rb b/spec/services/procedure_export_service_spec.rb index 5a29e4a3a..e21237946 100644 --- a/spec/services/procedure_export_service_spec.rb +++ b/spec/services/procedure_export_service_spec.rb @@ -41,7 +41,6 @@ describe ProcedureExportService do "Civilité", "Nom", "Prénom", - "Date de naissance", "Archivé", "État du dossier", "Dernière mise à jour le", @@ -88,12 +87,21 @@ describe ProcedureExportService do # SimpleXlsxReader is transforming datetimes in utc... It is only used in test so we just hack around. offset = dossier.en_construction_at.utc_offset - en_construction_at = Time.zone.at(dossiers_sheet.data[0][9] - offset.seconds) - en_instruction_at = Time.zone.at(dossiers_sheet.data[0][10] - offset.seconds) + en_construction_at = Time.zone.at(dossiers_sheet.data[0][8] - offset.seconds) + en_instruction_at = Time.zone.at(dossiers_sheet.data[0][9] - offset.seconds) expect(en_construction_at).to eq(dossier.en_construction_at.round) expect(en_instruction_at).to eq(dossier.en_instruction_at.round) end + context 'with a birthdate' do + before { procedure.update(ask_birthday: true) } + + let(:birthdate_headers) { nominal_headers.insert(nominal_headers.index('Archivé'), 'Date de naissance') } + + it { expect(dossiers_sheet.headers).to match(birthdate_headers) } + it { expect(dossiers_sheet.data[0][dossiers_sheet.headers.index('Date de naissance')]).to be_a(Date) } + end + context 'with a procedure routee' do before { procedure.groupe_instructeurs.create(label: '2') } From d4bb5d1f56cc915e14e3851ce12c5ce6ecfe1d99 Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Tue, 12 May 2020 10:05:22 +0200 Subject: [PATCH 6/9] =?UTF-8?q?indique=20=C3=A0=20l'usager=20les=20pi?= =?UTF-8?q?=C3=A8ces=20ajout=C3=A9es=20au=20dossier?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - attestation sociale - attestation fiscale - bilans bdf --- .../dossiers/_identite_entreprise.html.haml | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/app/views/shared/dossiers/_identite_entreprise.html.haml b/app/views/shared/dossiers/_identite_entreprise.html.haml index b22f27335..030f3e5a7 100644 --- a/app/views/shared/dossiers/_identite_entreprise.html.haml +++ b/app/views/shared/dossiers/_identite_entreprise.html.haml @@ -67,26 +67,34 @@ - elsif etablissement.exercices.present? = t('activemodel.models.exercices_summary', count: etablissement.exercices.count) - - if profile == 'instructeur' - - if etablissement.entreprise_attestation_sociale.attached? - %tr - %th.libelle Attestation sociale + - if etablissement.entreprise_attestation_sociale.attached? + %tr + %th.libelle Attestation sociale + - if profile == 'instructeur' %td= link_to "Consulter l'attestation", url_for(etablissement.entreprise_attestation_sociale) + - else + %td Une attestation de vigilance délivrée par l'ACOSS a été jointe à votre dossier. - - if etablissement.entreprise_attestation_fiscale.attached? - %tr - %th.libelle Attestation fiscale + - if etablissement.entreprise_attestation_fiscale.attached? + %tr + %th.libelle Attestation fiscale + - if profile == 'instructeur' %td= link_to "Consulter l'attestation", url_for(etablissement.entreprise_attestation_fiscale) + - else + %td Une attestation fiscale délivrée par l'URSSAF a été jointe à votre dossier. - - if etablissement.entreprise_bilans_bdf.present? - %tr - %th.libelle - Bilans Banque de France - = "en #{etablissement.entreprise_bilans_bdf_monnaie}" + - if etablissement.entreprise_bilans_bdf.present? + %tr + %th.libelle + Bilans Banque de France + = "en #{etablissement.entreprise_bilans_bdf_monnaie}" + - if profile == 'instructeur' - if controller.is_a?(Instructeurs::AvisController) %td= link_to "Consulter les bilans", bilans_bdf_instructeur_avis_path(@avis.id) - else %td= link_to "Consulter les bilans", bilans_bdf_instructeur_dossier_path(procedure_id: @dossier.procedure.id, dossier_id: @dossier.id) + - else + %td Les 3 derniers bilans connus de votre entreprise par la Banque de France ont été joints à votre dossier. - if etablissement.association? %tr From 9f1407b6d7915455cd0baf8266e642b5c7069b50 Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Mon, 18 May 2020 16:24:08 +0200 Subject: [PATCH 7/9] expiration: fix the mailer arguments The mailers expect serializable arguments, but were given ActiveRecord::Relation objects instead. This made the mailers throw an exception. But how was that possible ? This code is tested, and the tests were green. Well, the specs spy on the mailer implementation, in order to check that the mailers methods were properly called. Fair enough. But if the specs mock the mailer code (instead of calling the original implementation), we may not notice that the original implementation rejects our method parameters. Here this is the case: once we actually call the original implementation the tests start to fail, because some arguments are not converted from an ActiveRecord::Relation to a serializable array. This is fixed by ensuring that the mailer code is executed (and doesn't throw an exception). --- .../expired_dossiers_deletion_service.rb | 4 ++-- .../expired_dossiers_deletion_service_spec.rb | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/services/expired_dossiers_deletion_service.rb b/app/services/expired_dossiers_deletion_service.rb index 9e1aec31b..6e0bb1f0d 100644 --- a/app/services/expired_dossiers_deletion_service.rb +++ b/app/services/expired_dossiers_deletion_service.rb @@ -117,14 +117,14 @@ class ExpiredDossiersDeletionService .group_by(&:user) .each do |(user, dossiers)| DossierMailer.notify_automatic_deletion_to_user( - DeletedDossier.where(dossier_id: dossiers.map(&:id)), + DeletedDossier.where(dossier_id: dossiers.map(&:id)).to_a, user.email ).deliver_later end self.group_by_fonctionnaire_email(dossiers_to_remove).each do |(email, dossiers)| DossierMailer.notify_automatic_deletion_to_administration( - DeletedDossier.where(dossier_id: dossiers.map(&:id)), + DeletedDossier.where(dossier_id: dossiers.map(&:id)).to_a, email ).deliver_later diff --git a/spec/services/expired_dossiers_deletion_service_spec.rb b/spec/services/expired_dossiers_deletion_service_spec.rb index 29d89f8ce..6417a0b74 100644 --- a/spec/services/expired_dossiers_deletion_service_spec.rb +++ b/spec/services/expired_dossiers_deletion_service_spec.rb @@ -19,8 +19,8 @@ describe ExpiredDossiersDeletionService do let!(:valid_brouillon) { create(:dossier, procedure: procedure, created_at: date_not_expired) } before do - allow(DossierMailer).to receive(:notify_brouillon_near_deletion).and_return(double(deliver_later: nil)) - allow(DossierMailer).to receive(:notify_brouillon_deletion).and_return(double(deliver_later: nil)) + allow(DossierMailer).to receive(:notify_brouillon_near_deletion).and_call_original + allow(DossierMailer).to receive(:notify_brouillon_deletion).and_call_original ExpiredDossiersDeletionService.process_expired_dossiers_brouillon end @@ -207,8 +207,8 @@ describe ExpiredDossiersDeletionService do after { Timecop.return } before do - allow(DossierMailer).to receive(:notify_automatic_deletion_to_user).and_return(double(deliver_later: nil)) - allow(DossierMailer).to receive(:notify_automatic_deletion_to_administration).and_return(double(deliver_later: nil)) + allow(DossierMailer).to receive(:notify_automatic_deletion_to_user).and_call_original + allow(DossierMailer).to receive(:notify_automatic_deletion_to_administration).and_call_original end context 'with a single dossier' do @@ -275,8 +275,8 @@ describe ExpiredDossiersDeletionService do after { Timecop.return } before do - allow(DossierMailer).to receive(:notify_near_deletion_to_user).and_return(double(deliver_later: nil)) - allow(DossierMailer).to receive(:notify_near_deletion_to_administration).and_return(double(deliver_later: nil)) + allow(DossierMailer).to receive(:notify_near_deletion_to_user).and_call_original + allow(DossierMailer).to receive(:notify_near_deletion_to_administration).and_call_original end context 'with a single dossier' do @@ -344,8 +344,8 @@ describe ExpiredDossiersDeletionService do after { Timecop.return } before do - allow(DossierMailer).to receive(:notify_automatic_deletion_to_user).and_return(double(deliver_later: nil)) - allow(DossierMailer).to receive(:notify_automatic_deletion_to_administration).and_return(double(deliver_later: nil)) + allow(DossierMailer).to receive(:notify_automatic_deletion_to_user).and_call_original + allow(DossierMailer).to receive(:notify_automatic_deletion_to_administration).and_call_original end context 'with a single dossier' do From 6eca93faab6c140d12f19ec268f1c566e66f0dde Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Mon, 18 May 2020 15:24:11 +0200 Subject: [PATCH 8/9] urls: fix link to admin FAQ category --- config/initializers/urls.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/initializers/urls.rb b/config/initializers/urls.rb index f3b71544b..b15b98a5d 100644 --- a/config/initializers/urls.rb +++ b/config/initializers/urls.rb @@ -23,7 +23,7 @@ API_DOC_URL = [DOC_URL, "pour-aller-plus-loin", "api"].join("/") WEBHOOK_DOC_URL = [DOC_URL, "pour-aller-plus-loin", "webhook"].join("/") ARCHIVAGE_DOC_URL = [DOC_URL, "pour-aller-plus-loin", "archivage-longue-duree-des-demarches"].join("/") FAQ_URL = "https://faq.demarches-simplifiees.fr" -FAQ_ADMIN_URL = "https://faq.demarches-simplifiees.fr/collection/1-administrateur" +FAQ_ADMIN_URL = [FAQ_URL, "collection", "1-administrateur-creation-dun-formulaire"].join("/") COMMENT_TROUVER_MA_DEMARCHE_URL = [FAQ_URL, "article", "59-comment-trouver-ma-demarche"].join("/") STATUS_PAGE_URL = "https://status.demarches-simplifiees.fr" MATOMO_IFRAME_URL = "https://stats.data.gouv.fr/index.php?module=CoreAdminHome&action=optOut&language=fr&&fontColor=333333&fontSize=16px&fontFamily=Muli" From c9820adbc471d038b60b8342f404a47370a77c80 Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Mon, 18 May 2020 13:24:21 +0000 Subject: [PATCH 9/9] urls: fix link to autosave FAQ article --- app/views/users/dossiers/_autosave.html.haml | 6 ++---- config/initializers/urls.rb | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/views/users/dossiers/_autosave.html.haml b/app/views/users/dossiers/_autosave.html.haml index c19bdc030..7c06f936b 100644 --- a/app/views/users/dossiers/_autosave.html.haml +++ b/app/views/users/dossiers/_autosave.html.haml @@ -1,16 +1,14 @@ -- more_infos_url = 'https://faq.demarches-simplifiees.fr/article/73-enregistrer-mon-dossier?preview=5dcbf0bb2c7d3a7e9ae3e33f' - .autosave.autosave-state-idle %p.autosave-explanation %span.autosave-explanation-text Votre brouillon est automatiquement enregistré. - = link_to 'En savoir plus', more_infos_url, target: '_blank', rel: 'noopener', class: 'autosave-more-infos' + = link_to 'En savoir plus', FAQ_AUTOSAVE_URL, target: '_blank', rel: 'noopener', class: 'autosave-more-infos' %p.autosave-status.succeeded %span.autosave-icon.icon.accept %span.autosave-label Brouillon enregistré - = link_to 'En savoir plus', more_infos_url, target: '_blank', rel: 'noopener', class: 'autosave-more-infos' + = link_to 'En savoir plus', FAQ_AUTOSAVE_URL, target: '_blank', rel: 'noopener', class: 'autosave-more-infos' %p.autosave-status.failed %span.autosave-icon ⚠️ diff --git a/config/initializers/urls.rb b/config/initializers/urls.rb index b15b98a5d..7c7fdad65 100644 --- a/config/initializers/urls.rb +++ b/config/initializers/urls.rb @@ -24,6 +24,7 @@ WEBHOOK_DOC_URL = [DOC_URL, "pour-aller-plus-loin", "webhook"].join("/") ARCHIVAGE_DOC_URL = [DOC_URL, "pour-aller-plus-loin", "archivage-longue-duree-des-demarches"].join("/") FAQ_URL = "https://faq.demarches-simplifiees.fr" FAQ_ADMIN_URL = [FAQ_URL, "collection", "1-administrateur-creation-dun-formulaire"].join("/") +FAQ_AUTOSAVE_URL = [FAQ_URL, "article", "77-enregistrer-mon-formulaire-pour-le-reprendre-plus-tard?preview=5ec28ca1042863474d1aee00"].join("/") COMMENT_TROUVER_MA_DEMARCHE_URL = [FAQ_URL, "article", "59-comment-trouver-ma-demarche"].join("/") STATUS_PAGE_URL = "https://status.demarches-simplifiees.fr" MATOMO_IFRAME_URL = "https://stats.data.gouv.fr/index.php?module=CoreAdminHome&action=optOut&language=fr&&fontColor=333333&fontSize=16px&fontFamily=Muli"