From fa808c8010201bb2562fa71b16d01b57ca8729a2 Mon Sep 17 00:00:00 2001 From: simon lehericey Date: Mon, 18 Nov 2019 23:12:03 +0100 Subject: [PATCH 01/24] Make login w3c compatible again --- app/views/users/sessions/new.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/users/sessions/new.html.haml b/app/views/users/sessions/new.html.haml index 5cdd40be4..de0d29092 100644 --- a/app/views/users/sessions/new.html.haml +++ b/app/views/users/sessions/new.html.haml @@ -13,7 +13,7 @@ .auth-options %div - = f.check_box :remember_me, as: :boolean + = f.check_box :remember_me = f.label :remember_me, "Se souvenir de moi", class: 'remember-me' .text-right From 41445564b4866ae166eaa2e57fd4b7c6731d02e9 Mon Sep 17 00:00:00 2001 From: simon lehericey Date: Tue, 19 Nov 2019 09:06:55 +0100 Subject: [PATCH 02/24] Remove unused .header-help div --- app/views/layouts/_new_header.haml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/app/views/layouts/_new_header.haml b/app/views/layouts/_new_header.haml index 7eae51026..a880f875c 100644 --- a/app/views/layouts/_new_header.haml +++ b/app/views/layouts/_new_header.haml @@ -60,15 +60,14 @@ = link_to "Connexion", new_user_session_path, class: "button secondary" %li - .header-help - - if dossier.present? && nav_bar_profile == :user - = render partial: 'shared/help/help_dropdown_dossier', locals: { dossier: dossier } + - if dossier.present? && nav_bar_profile == :user + = render partial: 'shared/help/help_dropdown_dossier', locals: { dossier: dossier } - - elsif procedure.present? && (nav_bar_profile == :user || nav_bar_profile == :guest) - = render partial: 'shared/help/help_dropdown_procedure', locals: { procedure: procedure } + - elsif procedure.present? && (nav_bar_profile == :user || nav_bar_profile == :guest) + = render partial: 'shared/help/help_dropdown_procedure', locals: { procedure: procedure } - - elsif nav_bar_profile == :instructeur - = render partial: 'shared/help/help_dropdown_instructeur' + - elsif nav_bar_profile == :instructeur + = render partial: 'shared/help/help_dropdown_instructeur' - - else - = render partial: 'shared/help/help_button' + - else + = render partial: 'shared/help/help_button' From 0a3a47339ced03c63756d8e10632ca80816b04a5 Mon Sep 17 00:00:00 2001 From: simon lehericey Date: Tue, 19 Nov 2019 09:32:11 +0100 Subject: [PATCH 03/24] Add title to print button --- app/views/instructeurs/dossiers/_header_actions.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/instructeurs/dossiers/_header_actions.html.haml b/app/views/instructeurs/dossiers/_header_actions.html.haml index dc63ef8d0..63778f4af 100644 --- a/app/views/instructeurs/dossiers/_header_actions.html.haml +++ b/app/views/instructeurs/dossiers/_header_actions.html.haml @@ -1,5 +1,5 @@ %span.dropdown.print-menu-opener - %button.button.dropdown-button.icon-only + %button.button.dropdown-button.icon-only{ title: 'imprimer' } %span.icon.printer %ul.print-menu.dropdown-content %li From 9f6b9c50281760fbfeef8a8967dab199748d116d Mon Sep 17 00:00:00 2001 From: simon lehericey Date: Tue, 19 Nov 2019 09:38:13 +0100 Subject: [PATCH 04/24] Make the help button accessible for keyboard --- app/views/shared/help/_help_dropdown_dossier.html.haml | 2 +- app/views/shared/help/_help_dropdown_instructeur.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/shared/help/_help_dropdown_dossier.html.haml b/app/views/shared/help/_help_dropdown_dossier.html.haml index 38b9af8bc..7155fedca 100644 --- a/app/views/shared/help/_help_dropdown_dossier.html.haml +++ b/app/views/shared/help/_help_dropdown_dossier.html.haml @@ -1,5 +1,5 @@ .dropdown.help-dropdown - .button.primary.dropdown-button Aide + %button.button.primary.dropdown-button Aide .dropdown-content.fade-in-down %ul.dropdown-items - title = dossier.brouillon? ? "Besoin d’aide pour remplir votre dossier ?" : "Une question sur votre dossier ?" diff --git a/app/views/shared/help/_help_dropdown_instructeur.html.haml b/app/views/shared/help/_help_dropdown_instructeur.html.haml index d5b2091b8..37581fb91 100644 --- a/app/views/shared/help/_help_dropdown_instructeur.html.haml +++ b/app/views/shared/help/_help_dropdown_instructeur.html.haml @@ -1,5 +1,5 @@ .dropdown.help-dropdown - .button.primary.dropdown-button Aide + %button.button.primary.dropdown-button Aide .dropdown-content.fade-in-down %ul.dropdown-items = render partial: 'shared/help/dropdown_items/faq_item' From 59432594a0234b844d0b3f33683e3cb6ef6f27bb Mon Sep 17 00:00:00 2001 From: simon lehericey Date: Tue, 19 Nov 2019 09:42:16 +0100 Subject: [PATCH 05/24] Header: same outline for the search button --- app/assets/stylesheets/new_design/new_header.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/assets/stylesheets/new_design/new_header.scss b/app/assets/stylesheets/new_design/new_header.scss index a7d33baa6..e22cffc73 100644 --- a/app/assets/stylesheets/new_design/new_header.scss +++ b/app/assets/stylesheets/new_design/new_header.scss @@ -150,6 +150,8 @@ $header-mobile-breakpoint: 550px; } button { + @extend %outline; + padding: 9px; border: none; background: none; From 279696451e3a7be26f89b90488362b005c1cbf77 Mon Sep 17 00:00:00 2001 From: simon lehericey Date: Tue, 19 Nov 2019 14:22:53 +0100 Subject: [PATCH 06/24] Fix html: Element div not allowed as child of element span --- app/assets/stylesheets/new_design/buttons.scss | 4 ++++ app/views/instructeurs/dossiers/_header_actions.html.haml | 2 +- app/views/instructeurs/dossiers/_state_button.html.haml | 2 +- app/views/invites/_dropdown.html.haml | 2 +- app/views/layouts/_account_dropdown.haml | 2 +- app/views/users/dossiers/_dossier_actions.html.haml | 2 +- 6 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/assets/stylesheets/new_design/buttons.scss b/app/assets/stylesheets/new_design/buttons.scss index e66e2c9fa..522c52d57 100644 --- a/app/assets/stylesheets/new_design/buttons.scss +++ b/app/assets/stylesheets/new_design/buttons.scss @@ -130,6 +130,10 @@ } } +.state-button { + display: inline-block; +} + .dropdown { display: inline-block; position: relative; diff --git a/app/views/instructeurs/dossiers/_header_actions.html.haml b/app/views/instructeurs/dossiers/_header_actions.html.haml index 63778f4af..57932439b 100644 --- a/app/views/instructeurs/dossiers/_header_actions.html.haml +++ b/app/views/instructeurs/dossiers/_header_actions.html.haml @@ -20,5 +20,5 @@ = render partial: "instructeurs/procedures/dossier_actions", locals: { procedure: dossier.procedure, dossier: dossier, dossier_is_followed: current_instructeur&.follow?(dossier) } -%span.state-button +.state-button = render partial: "state_button", locals: { dossier: dossier } diff --git a/app/views/instructeurs/dossiers/_state_button.html.haml b/app/views/instructeurs/dossiers/_state_button.html.haml index cbf3c2251..f30fd97d5 100644 --- a/app/views/instructeurs/dossiers/_state_button.html.haml +++ b/app/views/instructeurs/dossiers/_state_button.html.haml @@ -1,4 +1,4 @@ -%span.dropdown +.dropdown -# Dropdown button title %button.button.primary.dropdown-button{ class: button_or_label_class(dossier) } = dossier_display_state dossier diff --git a/app/views/invites/_dropdown.html.haml b/app/views/invites/_dropdown.html.haml index 7fda74dd5..91a451c9e 100644 --- a/app/views/invites/_dropdown.html.haml +++ b/app/views/invites/_dropdown.html.haml @@ -1,4 +1,4 @@ -%span.dropdown.invite-user-action +.dropdown.invite-user-action %button.button.dropdown-button %span.icon.person - if dossier.invites.count > 0 diff --git a/app/views/layouts/_account_dropdown.haml b/app/views/layouts/_account_dropdown.haml index 9f896c390..5cf464c75 100644 --- a/app/views/layouts/_account_dropdown.haml +++ b/app/views/layouts/_account_dropdown.haml @@ -1,4 +1,4 @@ -%span.dropdown.header-menu-opener +.dropdown.header-menu-opener %button.button.dropdown-button.header-menu-button = image_tag "icons/account-circle.svg", title: "Mon compte" %ul.header-menu.dropdown-content diff --git a/app/views/users/dossiers/_dossier_actions.html.haml b/app/views/users/dossiers/_dossier_actions.html.haml index bad0197ba..36d9c290d 100644 --- a/app/views/users/dossiers/_dossier_actions.html.haml +++ b/app/views/users/dossiers/_dossier_actions.html.haml @@ -4,7 +4,7 @@ - has_actions = has_delete_action || has_new_dossier_action - if has_actions - %span.dropdown.user-dossier-actions + .dropdown.user-dossier-actions %button.button.dropdown-button Actions .dropdown-content.fade-in-down From 0e6ffd0baa7e0a9bb48a072209b9b64d60487f41 Mon Sep 17 00:00:00 2001 From: simon lehericey Date: Tue, 19 Nov 2019 14:27:43 +0100 Subject: [PATCH 07/24] Fix table layout: tbody was wrapped in thead --- app/views/users/dossiers/index.html.haml | 40 ++++++++++++------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/app/views/users/dossiers/index.html.haml b/app/views/users/dossiers/index.html.haml index 9475432c3..fc4f1d4ab 100644 --- a/app/views/users/dossiers/index.html.haml +++ b/app/views/users/dossiers/index.html.haml @@ -30,26 +30,26 @@ %th.status-col Statut %th.updated-at-col Mis à jour %th - %tbody - - @dossiers.each do |dossier| - %tr{ data: { 'dossier-id': dossier.id } } - %td.folder-col - = link_to(url_for_dossier(dossier), class: 'cell-link') do - %span.icon.folder - %td.number-col - = link_to(url_for_dossier(dossier), class: 'cell-link') do - = dossier.id - %td - = link_to(url_for_dossier(dossier), class: 'cell-link') do - = procedure_libelle(dossier.procedure) - %td.status-col - = link_to(url_for_dossier(dossier), class: 'cell-link') do - = render partial: 'shared/dossiers/status_badge', locals: { dossier: dossier } - %td.updated-at-col - = link_to(url_for_dossier(dossier), class: 'cell-link') do - = try_format_date(dossier.updated_at) - %td.action-col.action-col - = render partial: 'dossier_actions', locals: { dossier: dossier } + %tbody + - @dossiers.each do |dossier| + %tr{ data: { 'dossier-id': dossier.id } } + %td.folder-col + = link_to(url_for_dossier(dossier), class: 'cell-link') do + %span.icon.folder + %td.number-col + = link_to(url_for_dossier(dossier), class: 'cell-link') do + = dossier.id + %td + = link_to(url_for_dossier(dossier), class: 'cell-link') do + = procedure_libelle(dossier.procedure) + %td.status-col + = link_to(url_for_dossier(dossier), class: 'cell-link') do + = render partial: 'shared/dossiers/status_badge', locals: { dossier: dossier } + %td.updated-at-col + = link_to(url_for_dossier(dossier), class: 'cell-link') do + = try_format_date(dossier.updated_at) + %td.action-col.action-col + = render partial: 'dossier_actions', locals: { dossier: dossier } = paginate(@dossiers) - if current_user.feedbacks.empty? || current_user.feedbacks.last.created_at < 1.month.ago From 4a5059ed343f7c7c2a638ff1cff3fdac0defc724 Mon Sep 17 00:00:00 2001 From: simon lehericey Date: Tue, 19 Nov 2019 14:41:41 +0100 Subject: [PATCH 08/24] Add alt='' to decorative image https://www.w3.org/WAI/tutorials/images/decorative/ --- app/views/layouts/_account_dropdown.haml | 12 ++++++------ app/views/layouts/_new_header.haml | 4 ++-- .../shared/dossiers/messages/_message_icon.html.haml | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/views/layouts/_account_dropdown.haml b/app/views/layouts/_account_dropdown.haml index 5cf464c75..21ddf9518 100644 --- a/app/views/layouts/_account_dropdown.haml +++ b/app/views/layouts/_account_dropdown.haml @@ -8,30 +8,30 @@ - if administration_signed_in? %li = link_to manager_root_path, class: "menu-item menu-link" do - = image_tag "icons/super-admin.svg" + = image_tag "icons/super-admin.svg", alt: '' Passer en super-admin - if multiple_devise_profile_connect? - if user_signed_in? && nav_bar_profile != :user %li = link_to dossiers_path, class: "menu-item menu-link" do - = image_tag "icons/switch-profile.svg" + = image_tag "icons/switch-profile.svg", alt: '' Passer en usager - if instructeur_signed_in? && nav_bar_profile != :instructeur %li = link_to instructeur_procedures_path, class: "menu-item menu-link" do - = image_tag "icons/switch-profile.svg" + = image_tag "icons/switch-profile.svg", alt: '' Passer en instructeur - if administrateur_signed_in? && nav_bar_profile != :administrateur %li = link_to admin_procedures_path, class: "menu-item menu-link" do - = image_tag "icons/switch-profile.svg" + = image_tag "icons/switch-profile.svg", alt: '' Passer en administrateur %li = link_to profil_path, class: "menu-item menu-link" do - = image_tag "icons/switch-profile.svg" + = image_tag "icons/switch-profile.svg", alt: '' Voir mon profil %li = link_to destroy_user_session_path, method: :delete, class: "menu-item menu-link" do - = image_tag "icons/sign-out.svg" + = image_tag "icons/sign-out.svg", alt: '' Se déconnecter diff --git a/app/views/layouts/_new_header.haml b/app/views/layouts/_new_header.haml index a880f875c..23abde450 100644 --- a/app/views/layouts/_new_header.haml +++ b/app/views/layouts/_new_header.haml @@ -38,7 +38,7 @@ = form_tag instructeur_recherche_path, method: :get, class: "form" do = text_field_tag "q", "#{@search_terms if @search_terms.present?}", placeholder: "Rechercher un dossier" %button{ title: "Rechercher" } - = image_tag "icons/search-blue.svg" + = image_tag "icons/search-blue.svg", alt: '' - if nav_bar_profile == :user && user_signed_in? && current_user.dossiers.count > 2 %li @@ -46,7 +46,7 @@ = form_tag recherche_dossiers_path, method: :post, class: "form" do = text_field_tag :dossier_id, "", placeholder: "Numéro de dossier" %button{ title: "Rechercher" } - = image_tag "icons/search-blue.svg" + = image_tag "icons/search-blue.svg", alt: '' - if instructeur_signed_in? || user_signed_in? %li diff --git a/app/views/shared/dossiers/messages/_message_icon.html.haml b/app/views/shared/dossiers/messages/_message_icon.html.haml index 0d8eb2db1..849337641 100644 --- a/app/views/shared/dossiers/messages/_message_icon.html.haml +++ b/app/views/shared/dossiers/messages/_message_icon.html.haml @@ -1,7 +1,7 @@ - if commentaire.sent_by_system? - = image_tag('icons/mail.svg', class: 'person-icon') + = image_tag('icons/mail.svg', class: 'person-icon', alt: '') - elsif commentaire.sent_by?(connected_user) - = image_tag('icons/account-circle.svg', class: 'person-icon') + = image_tag('icons/account-circle.svg', class: 'person-icon', alt: '') - else - = image_tag('icons/blue-person.svg', class: 'person-icon') + = image_tag('icons/blue-person.svg', class: 'person-icon', alt: '') From 45ff1fd697936028b26e2439d5997ca111117b96 Mon Sep 17 00:00:00 2001 From: simon lehericey Date: Tue, 19 Nov 2019 14:52:07 +0100 Subject: [PATCH 09/24] Change span by div to fix Element p not allowed as child of element span --- app/views/shared/dossiers/_champ_row.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/dossiers/_champ_row.html.haml b/app/views/shared/dossiers/_champ_row.html.haml index 8670a15c0..cfb9f6be9 100644 --- a/app/views/shared/dossiers/_champ_row.html.haml +++ b/app/views/shared/dossiers/_champ_row.html.haml @@ -16,7 +16,7 @@ %th.libelle{ class: repetition ? 'padded' : '' } = "#{c.libelle} :" %td.rich-text - %span{ class: highlight_if_unseen_class(demande_seen_at, c.updated_at) } + %div{ class: highlight_if_unseen_class(demande_seen_at, c.updated_at) } - case c.type_champ - when TypeDeChamp.type_champs.fetch(:carte) = render partial: "shared/champs/carte/show", locals: { champ: c } From 59d5cd7abc763b2e56d62d4250a9bc3695d563a7 Mon Sep 17 00:00:00 2001 From: simon lehericey Date: Tue, 19 Nov 2019 15:01:06 +0100 Subject: [PATCH 10/24] fix label > div by label > span --- app/views/shared/dossiers/messages/_form.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/dossiers/messages/_form.html.haml b/app/views/shared/dossiers/messages/_form.html.haml index 87e88d33c..d7848e585 100644 --- a/app/views/shared/dossiers/messages/_form.html.haml +++ b/app/views/shared/dossiers/messages/_form.html.haml @@ -7,7 +7,7 @@ %div = f.file_field :piece_jointe, id: 'piece_jointe', direct_upload: true %label{ for: :piece_jointe } - .notice + %span.notice (taille max : 20 Mo) %div From 1a63d7e4e2f57b87b1948090242195721d859a8b Mon Sep 17 00:00:00 2001 From: Matthieu FAURE Date: Tue, 19 Nov 2019 15:24:57 +0100 Subject: [PATCH 11/24] DOC ajout commentaires + explications pour env.example --- config/env.example | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/config/env.example b/config/env.example index aa54c288a..f7e4ae444 100644 --- a/config/env.example +++ b/config/env.example @@ -1,20 +1,35 @@ +# 3 valeurs: +# * tps: environnement de production +# * tps_dev: environnement de pre-production +# * tps_local: machine de développeur APP_NAME="tps_local" + +# Nom d'hôte de l'appli +# * Pour du dev local: localhost:3000 +# * pour de la preprod: preprod.ds.organisme.fr +# * pour de la prod: www.demarches-simplifiees.fr APP_HOST="localhost:3000" + +# Utilisé pour les logs LogRage SOURCE="tps_local" +# ?? SECRET_KEY_BASE="05a2d479d8e412198dabd08ef0eee9d6e180f5cbb48661a35fd1cae287f0a93d40b5f1da08f06780d698bbd458a0ea97f730f83ee780de5d4e31f649a0130cf0" SIGNING_KEY="aef3153a9829fa4ba10acb02927ac855df6b92795b1ad265d654443c4b14a017" +# Database DB_DATABASE="tps_development" DB_HOST="localhost" DB_POOL="" DB_USERNAME="tps_development" DB_PASSWORD="tps_development" +# Protection simple de l'instance par mot de passe (utile pour la pre-prod) BASIC_AUTH_ENABLED="disabled" BASIC_AUTH_USERNAME="" BASIC_AUTH_PASSWORD="" +# Object Storage pour les pièces jointes FOG_OPENSTACK_TENANT="" FOG_OPENSTACK_API_KEY="" FOG_OPENSTACK_USERNAME="" @@ -22,29 +37,36 @@ FOG_OPENSTACK_URL="" FOG_OPENSTACK_IDENTITY_API_VERSION="" FOG_OPENSTACK_REGION="" FOG_DIRECTORY="" -FOG_ENABLED="" +FOG_ENABLED="" # valeur attendue: enable DS_PROXY_URL="" +# Service externe: authentification France Connect FC_PARTICULIER_ID="" FC_PARTICULIER_SECRET="" FC_PARTICULIER_BASE_URL="" +# Service externe: Authention pour Super-Admin (auth Github obligatoire) GITHUB_CLIENT_ID="" GITHUB_CLIENT_SECRET="" +# Service externe: Support Utilisateur HelpScout | Spécifique démarches-simplifiées.fr HELPSCOUT_MAILBOX_ID="" HELPSCOUT_CLIENT_ID="" HELPSCOUT_CLIENT_SECRET="" HELPSCOUT_WEBHOOK_SECRET="" +# Service externe: Supervision exterieure | Spécifique démarches-simplifiées.fr SENTRY_ENABLED="disabled" SENTRY_CURRENT_ENV="development" SENTRY_DSN_RAILS="" SENTRY_DSN_JS="" +# Statistiques web MATOMO_ENABLED="disabled" MATOMO_ID="73" +# Missing MATOMO_HOST (thus hardcoded) +# SMTP Provider: Send In Blue SENDINBLUE_BALANCING="" SENDINBLUE_BALANCING_VALUE="" SENDINBLUE_ENABLED="" @@ -52,26 +74,34 @@ SENDINBLUE_CLIENT_KEY="" SENDINBLUE_SMTP_KEY="" SENDINBLUE_USER_NAME="" - +# Service externe: Fournisseur de tchat pour administrateur | Spécifique démarches-simplifiées.fr CRISP_ENABLED="disabled" CRISP_CLIENT_KEY="" +# Service externe: rattrapage de mails envoyés, utile en préprod | Spécifique démarches-simplifiées.fr MAILTRAP_ENABLED="disabled" MAILTRAP_USERNAME="" MAILTRAP_PASSWORD="" +# SMTP Provider: Mailjet MAILJET_API_KEY="" MAILJET_SECRET_KEY="" +# API Entreprise https://api.gouv.fr/api/api-entreprise.html API_ENTREPRISE_KEY="" +# Service externe: CRM de suivi de création d'administrateur | Spécifique démarches-simplifiées.fr PIPEDRIVE_KEY="" +# ?? TRUSTED_NETWORKS="" +# Service externe: mesure de performance d'appli Rails | Spécifique démarches-simplifiées.fr SKYLIGHT_AUTHENTICATION_KEY="" +# Activer ou non les logs LogRage LOGRAGE_ENABLED="disabled" +# Service externe d'horodatage des changements de statut des dossiers (effectué quotidiennement) UNIVERSIGN_API_URL="" UNIVERSIGN_USERPWD="" From b90cd9f28faafe1fc7d146a209121c36b41bd50a Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Wed, 20 Nov 2019 10:48:53 +0100 Subject: [PATCH 12/24] styles: fix hover on dropdown items MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit More specifically, fix items in the Help dropdown displaying a pointer cursor on the whole area – where only the links inside the item should have the hover cursor. Also, having a `cursor: pointer` rule applied only on hover state is difficult to debug: better to apply it always, and let the browser handle it. --- app/assets/stylesheets/new_design/buttons.scss | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/new_design/buttons.scss b/app/assets/stylesheets/new_design/buttons.scss index 522c52d57..ba5804564 100644 --- a/app/assets/stylesheets/new_design/buttons.scss +++ b/app/assets/stylesheets/new_design/buttons.scss @@ -217,10 +217,13 @@ } } - &.selected, - &:hover:not(.inactive) { - background: $light-grey; + &:not(.inactive) { cursor: pointer; + + &:hover, + &.selected { + background: $light-grey; + } } &.danger { From 98d545b1d996381e1f2ab01e51962f4b818550cf Mon Sep 17 00:00:00 2001 From: Matthieu FAURE Date: Wed, 20 Nov 2019 11:01:27 +0100 Subject: [PATCH 13/24] Update config/env.example based on @keirua review Co-Authored-By: Keirua --- config/env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/env.example b/config/env.example index f7e4ae444..dc112a98a 100644 --- a/config/env.example +++ b/config/env.example @@ -6,7 +6,7 @@ APP_NAME="tps_local" # Nom d'hôte de l'appli # * Pour du dev local: localhost:3000 -# * pour de la preprod: preprod.ds.organisme.fr +# * pour de la preprod: preprod.ds.organisme.fr (par exemple) # * pour de la prod: www.demarches-simplifiees.fr APP_HOST="localhost:3000" From 0089a9d520f07a7f953f63f7f04c7100da04bc6d Mon Sep 17 00:00:00 2001 From: Matthieu FAURE Date: Wed, 20 Nov 2019 11:01:54 +0100 Subject: [PATCH 14/24] Update config/env.example based on @keirua review Co-Authored-By: Keirua --- config/env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/env.example b/config/env.example index dc112a98a..393b98eb2 100644 --- a/config/env.example +++ b/config/env.example @@ -37,7 +37,7 @@ FOG_OPENSTACK_URL="" FOG_OPENSTACK_IDENTITY_API_VERSION="" FOG_OPENSTACK_REGION="" FOG_DIRECTORY="" -FOG_ENABLED="" # valeur attendue: enable +FOG_ENABLED="" # valeur attendue: enabled DS_PROXY_URL="" # Service externe: authentification France Connect From 85bbafc2565b079a44c5713c1b3cde767374d7ec Mon Sep 17 00:00:00 2001 From: Matthieu FAURE Date: Wed, 20 Nov 2019 11:03:19 +0100 Subject: [PATCH 15/24] Update config/env.example based on @keirua review Co-Authored-By: Keirua --- config/env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/env.example b/config/env.example index 393b98eb2..4747ef32f 100644 --- a/config/env.example +++ b/config/env.example @@ -45,7 +45,7 @@ FC_PARTICULIER_ID="" FC_PARTICULIER_SECRET="" FC_PARTICULIER_BASE_URL="" -# Service externe: Authention pour Super-Admin (auth Github obligatoire) +# Service externe: Authentification pour manager (auth Github obligatoire), permet d'accéder à /manager GITHUB_CLIENT_ID="" GITHUB_CLIENT_SECRET="" From 03a7bc3d5adc5322b73bafc92b61000d8cd185ef Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Wed, 6 Nov 2019 14:03:08 +0000 Subject: [PATCH 16/24] dossiers: give the edit form an unique id --- app/views/shared/dossiers/_edit.html.haml | 8 ++++---- app/views/users/dossiers/brouillon.html.haml | 9 +++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/app/views/shared/dossiers/_edit.html.haml b/app/views/shared/dossiers/_edit.html.haml index c617a2450..318489100 100644 --- a/app/views/shared/dossiers/_edit.html.haml +++ b/app/views/shared/dossiers/_edit.html.haml @@ -2,13 +2,13 @@ = render partial: "shared/dossiers/submit_is_over", locals: { dossier: dossier } - if apercu - - form_options = { url: '', method: :get, html: { class: 'form', multipart: true } } + - form_options = { url: '', method: :get } - elsif dossier.brouillon? - - form_options = { url: brouillon_dossier_url(dossier), method: :patch, html: { class: 'form', multipart: true } } + - form_options = { url: brouillon_dossier_url(dossier), method: :patch } - else - - form_options = { url: modifier_dossier_url(dossier), method: :patch, html: { class: 'form', multipart: true } } + - form_options = { url: modifier_dossier_url(dossier), method: :patch } - = form_for dossier, form_options do |f| + = form_for dossier, form_options.merge({ html: { id: 'dossier-edit-form', class: 'form', multipart: true } }) do |f| .prologue %p.mandatory-explanation diff --git a/app/views/users/dossiers/brouillon.html.haml b/app/views/users/dossiers/brouillon.html.haml index 766708cef..580ecd2d7 100644 --- a/app/views/users/dossiers/brouillon.html.haml +++ b/app/views/users/dossiers/brouillon.html.haml @@ -3,8 +3,9 @@ - content_for :footer do = render partial: "users/procedure_footer", locals: { procedure: @dossier.procedure, dossier: @dossier } -.dossier-header.sub-header - .container - = render partial: "shared/dossiers/header", locals: { dossier: @dossier, apercu: false } +#dossier-draft + .dossier-header.sub-header + .container + = render partial: "shared/dossiers/header", locals: { dossier: @dossier, apercu: false } -= render partial: "shared/dossiers/edit", locals: { dossier: @dossier, apercu: false } + = render partial: "shared/dossiers/edit", locals: { dossier: @dossier, apercu: false } From 8b8a96abda61059af7b767190275b6252e58756b Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Wed, 6 Nov 2019 15:54:14 +0000 Subject: [PATCH 17/24] dossiers: save draft by default (instead of submitting) Make the default behavior of `update_brouillon` be to update the draft, instead of submitting the dossier. This makes all requests made to `update_brouillon` without specifying an extra `submit_draft` parameter to just save the draft. It will make autosaving the draft easier and safer. --- app/controllers/users/dossiers_controller.rb | 10 +++++----- app/views/shared/dossiers/_edit.html.haml | 3 ++- .../users/dossiers_controller_spec.rb | 18 +++++++++--------- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/app/controllers/users/dossiers_controller.rb b/app/controllers/users/dossiers_controller.rb index 8c0ccad0f..78039da87 100644 --- a/app/controllers/users/dossiers_controller.rb +++ b/app/controllers/users/dossiers_controller.rb @@ -147,13 +147,13 @@ module Users flash.now.alert = errors render :brouillon else - if save_draft? - flash.now.notice = 'Votre brouillon a bien été sauvegardé.' - render :brouillon - else + if passage_en_construction? @dossier.en_construction! NotificationMailer.send_initiated_notification(@dossier).deliver_later redirect_to merci_dossier_path(@dossier) + else + flash.now.notice = 'Votre brouillon a bien été sauvegardé.' + render :brouillon end end end @@ -368,7 +368,7 @@ module Users end def save_draft? - dossier.brouillon? && params[:save_draft] + dossier.brouillon? && !params[:submit_draft] end end end diff --git a/app/views/shared/dossiers/_edit.html.haml b/app/views/shared/dossiers/_edit.html.haml index 318489100..7691145af 100644 --- a/app/views/shared/dossiers/_edit.html.haml +++ b/app/views/shared/dossiers/_edit.html.haml @@ -43,13 +43,14 @@ - if dossier.brouillon? = f.button 'Enregistrer le brouillon', formnovalidate: true, - name: :save_draft, value: true, class: 'button send secondary', data: { 'disable-with': "Envoi en cours…" } - if dossier.can_transition_to_en_construction? = f.button 'Déposer le dossier', + name: :submit_draft, + value: true, class: 'button send primary', disabled: !current_user.owns?(dossier), data: { 'disable-with': "Envoi en cours…" } diff --git a/spec/controllers/users/dossiers_controller_spec.rb b/spec/controllers/users/dossiers_controller_spec.rb index 4c60081bc..e4bf93fed 100644 --- a/spec/controllers/users/dossiers_controller_spec.rb +++ b/spec/controllers/users/dossiers_controller_spec.rb @@ -100,38 +100,38 @@ describe Users::DossiersController, type: :controller do let(:user) { create(:user) } let(:asked_dossier) { create(:dossier) } let(:ensure_authorized) { :forbid_invite_submission! } - let(:draft) { false } + let(:submit) { true } before do - @controller.params = @controller.params.merge(dossier_id: asked_dossier.id, save_draft: draft) + @controller.params = @controller.params.merge(dossier_id: asked_dossier.id, submit_draft: submit) allow(@controller).to receive(:current_user).and_return(user) allow(@controller).to receive(:redirect_to) end context 'when a user save their own draft' do let(:asked_dossier) { create(:dossier, user: user) } - let(:draft) { true } + let(:submit) { false } it_behaves_like 'does not redirect nor flash' end context 'when a user submit their own dossier' do let(:asked_dossier) { create(:dossier, user: user) } - let(:draft) { false } + let(:submit) { true } it_behaves_like 'does not redirect nor flash' end context 'when an invite save the draft for a dossier where they where invited' do before { create(:invite, dossier: asked_dossier, user: user) } - let(:draft) { true } + let(:submit) { false } it_behaves_like 'does not redirect nor flash' end context 'when an invite submit a dossier where they where invited' do before { create(:invite, dossier: asked_dossier, user: user) } - let(:draft) { false } + let(:submit) { true } it_behaves_like 'redirects and flashes' end @@ -394,7 +394,7 @@ describe Users::DossiersController, type: :controller do } } end - let(:payload) { submit_payload } + let(:payload) { submit_payload.merge(submit_draft: true) } subject do Timecop.freeze(now) do @@ -480,7 +480,7 @@ describe Users::DossiersController, type: :controller do it { expect(flash.alert).to eq(['Le champ l doit être rempli.']) } context 'and the user saves a draft' do - let(:payload) { submit_payload.merge(save_draft: true) } + let(:payload) { submit_payload.except(:submit_draft) } it { expect(response).to render_template(:brouillon) } it { expect(flash.notice).to eq('Votre brouillon a bien été sauvegardé.') } @@ -510,7 +510,7 @@ describe Users::DossiersController, type: :controller do let!(:invite) { create(:invite, dossier: dossier, user: user) } context 'and the invite saves a draft' do - let(:payload) { submit_payload.merge(save_draft: true) } + let(:payload) { submit_payload.except(:submit_draft) } before do first_champ.type_de_champ.update(mandatory: true, libelle: 'l') From 5f9a9d059efd53b17050f314504616f73124f981 Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Thu, 7 Nov 2019 15:11:24 +0000 Subject: [PATCH 18/24] dossiers: render JSON if needed When receiving a request that expects JSON, return a simple '200'. This avoids the unecessary work of rendering all the HTML page (which ultimately will not be used). --- app/controllers/users/dossiers_controller.rb | 21 ++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/app/controllers/users/dossiers_controller.rb b/app/controllers/users/dossiers_controller.rb index 78039da87..575473a69 100644 --- a/app/controllers/users/dossiers_controller.rb +++ b/app/controllers/users/dossiers_controller.rb @@ -143,18 +143,19 @@ module Users errors = update_dossier_and_compute_errors - if errors.present? + if passage_en_construction? && errors.blank? + @dossier.en_construction! + NotificationMailer.send_initiated_notification(@dossier).deliver_later + return redirect_to(merci_dossier_path(@dossier)) + elsif errors.present? flash.now.alert = errors - render :brouillon else - if passage_en_construction? - @dossier.en_construction! - NotificationMailer.send_initiated_notification(@dossier).deliver_later - redirect_to merci_dossier_path(@dossier) - else - flash.now.notice = 'Votre brouillon a bien été sauvegardé.' - render :brouillon - end + flash.now.notice = 'Votre brouillon a bien été sauvegardé.' + end + + respond_to do |format| + format.html { render :brouillon } + format.json { head :ok } end end From 1f2f904f8f5f427c41aea7aae4ab50e6f1741e6c Mon Sep 17 00:00:00 2001 From: Matthieu FAURE Date: Wed, 20 Nov 2019 15:39:50 +0100 Subject: [PATCH 19/24] Update config/env.example Co-Authored-By: Keirua --- config/env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/env.example b/config/env.example index 4747ef32f..37b0e48c6 100644 --- a/config/env.example +++ b/config/env.example @@ -93,7 +93,7 @@ API_ENTREPRISE_KEY="" # Service externe: CRM de suivi de création d'administrateur | Spécifique démarches-simplifiées.fr PIPEDRIVE_KEY="" -# ?? +# Liste des réseaux qui passent outre la génération de token pour identifier un device, ainsi que le throttling par rack-attack TRUSTED_NETWORKS="" # Service externe: mesure de performance d'appli Rails | Spécifique démarches-simplifiées.fr From 87813c42d97e822a364f49cbcd1e458133051ea3 Mon Sep 17 00:00:00 2001 From: Matthieu FAURE Date: Wed, 20 Nov 2019 15:40:02 +0100 Subject: [PATCH 20/24] Update config/env.example Co-Authored-By: Keirua --- config/env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/env.example b/config/env.example index 37b0e48c6..5d9134cec 100644 --- a/config/env.example +++ b/config/env.example @@ -13,7 +13,7 @@ APP_HOST="localhost:3000" # Utilisé pour les logs LogRage SOURCE="tps_local" -# ?? +# Clé de chiffrement de rails, cf https://api.rubyonrails.org/classes/Rails/Application.html SECRET_KEY_BASE="05a2d479d8e412198dabd08ef0eee9d6e180f5cbb48661a35fd1cae287f0a93d40b5f1da08f06780d698bbd458a0ea97f730f83ee780de5d4e31f649a0130cf0" SIGNING_KEY="aef3153a9829fa4ba10acb02927ac855df6b92795b1ad265d654443c4b14a017" From b42f21264ebe8e85620df9715624de256e6e3f55 Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Tue, 19 Nov 2019 16:01:43 +0100 Subject: [PATCH 21/24] =?UTF-8?q?permet=20le=20suivi=20auto=20apr=C3=A8s?= =?UTF-8?q?=20envoi=20instructeur?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Close #4163 --- app/controllers/instructeurs/dossiers_controller.rb | 1 + .../instructeurs/dossiers/_envoyer_dossier_block.html.haml | 2 ++ spec/controllers/instructeurs/dossiers_controller_spec.rb | 1 + 3 files changed, 4 insertions(+) diff --git a/app/controllers/instructeurs/dossiers_controller.rb b/app/controllers/instructeurs/dossiers_controller.rb index 69bc65b34..e02d91a49 100644 --- a/app/controllers/instructeurs/dossiers_controller.rb +++ b/app/controllers/instructeurs/dossiers_controller.rb @@ -56,6 +56,7 @@ module Instructeurs recipients = Instructeur.find(params[:recipients]) recipients.each do |recipient| + recipient.follow(dossier) InstructeurMailer.send_dossier(current_instructeur, dossier, recipient).deliver_later end diff --git a/app/views/instructeurs/dossiers/_envoyer_dossier_block.html.haml b/app/views/instructeurs/dossiers/_envoyer_dossier_block.html.haml index 1fe8d5714..e5e0afd78 100644 --- a/app/views/instructeurs/dossiers/_envoyer_dossier_block.html.haml +++ b/app/views/instructeurs/dossiers/_envoyer_dossier_block.html.haml @@ -4,6 +4,8 @@ %p.tab-paragraph Vous êtes le seul instructeur assigné sur cette démarche - else + %p.tab-paragrah.mb-1 + Le destinataire suivra automatiquement le dossier = form_for dossier, url: send_to_instructeurs_instructeur_dossier_path(dossier.procedure, dossier), method: :post, html: { class: 'form recipients-form' } do |f| .flex.justify-start.align-start = select_tag(:recipients, diff --git a/spec/controllers/instructeurs/dossiers_controller_spec.rb b/spec/controllers/instructeurs/dossiers_controller_spec.rb index d5587ec87..71e808cb1 100644 --- a/spec/controllers/instructeurs/dossiers_controller_spec.rb +++ b/spec/controllers/instructeurs/dossiers_controller_spec.rb @@ -48,6 +48,7 @@ describe Instructeurs::DossiersController, type: :controller do end it { expect(response).to redirect_to(personnes_impliquees_instructeur_dossier_url) } + it { expect(recipient.followed_dossiers).to include(dossier) } end describe '#follow' do From 74a9db65804e32404544eb5df262a28ceb222b76 Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Wed, 20 Nov 2019 10:33:56 +0000 Subject: [PATCH 22/24] javascript: make utils resilient to missing elements --- app/javascript/shared/utils.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/javascript/shared/utils.js b/app/javascript/shared/utils.js index 68a3ee08c..f50bc15ac 100644 --- a/app/javascript/shared/utils.js +++ b/app/javascript/shared/utils.js @@ -5,16 +5,16 @@ import debounce from 'debounce'; export { debounce }; export const { fire, ajax } = Rails; -export function show({ classList }) { - classList.remove('hidden'); +export function show(el) { + el && el.classList.remove('hidden'); } -export function hide({ classList }) { - classList.add('hidden'); +export function hide(el) { + el && el.classList.add('hidden'); } -export function toggle({ classList }) { - classList.toggle('hidden'); +export function toggle(el) { + el && el.classList.toggle('hidden'); } export function delegate(eventNames, selector, callback) { From be7fde11036247281ebc6fe4d576a2781fb4c769 Mon Sep 17 00:00:00 2001 From: clemkeirua Date: Wed, 20 Nov 2019 17:38:46 +0100 Subject: [PATCH 23/24] fix UnknownFormat raised in Instructeurs::ProceduresController#download_export --- app/controllers/instructeurs/procedures_controller.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/controllers/instructeurs/procedures_controller.rb b/app/controllers/instructeurs/procedures_controller.rb index 3bd2f35e1..c8239b652 100644 --- a/app/controllers/instructeurs/procedures_controller.rb +++ b/app/controllers/instructeurs/procedures_controller.rb @@ -210,12 +210,13 @@ module Instructeurs notice_message = "Nous générons cet export. Lorsque celui-ci sera disponible, vous recevrez une notification par email accompagnée d'un lien de téléchargement." if procedure.should_generate_export?(export_format) procedure.queue_export(current_instructeur, export_format) + flash.notice = notice_message respond_to do |format| format.js do - flash.notice = notice_message @procedure = procedure end + format.all { redirect_to procedure } end elsif procedure.export_queued?(export_format) flash.notice = notice_message From 0c6705f7fd88a84039d527a14fea85420738e3ce Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Tue, 25 Jun 2019 15:46:10 +0200 Subject: [PATCH 24/24] Drop old export service --- .../instructeurs/procedures_controller.rb | 8 +- app/helpers/procedure_helper.rb | 7 - app/models/procedure.rb | 24 +- app/services/procedure_export_service.rb | 244 ++------ app/services/procedure_export_v2_service.rb | 76 --- .../procedures/_download_dossiers.html.haml | 13 +- .../procedures_controller_spec.rb | 2 +- .../services/procedure_export_service_spec.rb | 553 ++++++++++-------- .../procedure_export_v2_service_spec.rb | 352 ----------- 9 files changed, 363 insertions(+), 916 deletions(-) delete mode 100644 app/services/procedure_export_v2_service.rb delete mode 100644 spec/services/procedure_export_v2_service_spec.rb diff --git a/app/controllers/instructeurs/procedures_controller.rb b/app/controllers/instructeurs/procedures_controller.rb index c8239b652..185cb540d 100644 --- a/app/controllers/instructeurs/procedures_controller.rb +++ b/app/controllers/instructeurs/procedures_controller.rb @@ -185,21 +185,19 @@ module Instructeurs end def download_dossiers - options = params.permit(:version, :limit, :since, tables: []) - dossiers = current_instructeur.dossiers.for_procedure(procedure) respond_to do |format| format.csv do - send_data(procedure.to_csv(dossiers, options), + send_data(procedure.to_csv(dossiers), filename: procedure.export_filename(:csv)) end format.xlsx do - send_data(procedure.to_xlsx(dossiers, options), + send_data(procedure.to_xlsx(dossiers), filename: procedure.export_filename(:xlsx)) end format.ods do - send_data(procedure.to_ods(dossiers, options), + send_data(procedure.to_ods(dossiers), filename: procedure.export_filename(:ods)) end end diff --git a/app/helpers/procedure_helper.rb b/app/helpers/procedure_helper.rb index 38cab945b..ba5ff74d5 100644 --- a/app/helpers/procedure_helper.rb +++ b/app/helpers/procedure_helper.rb @@ -38,13 +38,6 @@ module ProcedureHelper } end - def procedure_dossiers_download_path(procedure, format:, version:) - download_dossiers_instructeur_procedure_path(format: format, - procedure_id: procedure.id, - tables: [:etablissements], - version: version) - end - private TOGGLES = { diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 6abe348c7..ddb533995 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -193,7 +193,7 @@ class Procedure < ApplicationRecord end def prepare_export_download(format) - service = ProcedureExportV2Service.new(self, self.dossiers) + service = ProcedureExportService.new(self, self.dossiers) filename = export_filename(format) case format.to_sym @@ -440,26 +440,20 @@ class Procedure < ApplicationRecord "dossiers_#{procedure_identifier}_#{Time.zone.now.strftime('%Y-%m-%d_%H-%M')}.#{format}" end - def export(dossiers, options = {}) - version = options.delete(:version) - if version == 'v2' - options.delete(:tables) - ProcedureExportV2Service.new(self, dossiers) - else - ProcedureExportService.new(self, dossiers, **options.to_h.symbolize_keys) - end + def export(dossiers) + ProcedureExportService.new(self, dossiers) end - def to_csv(dossiers, options = {}) - export(dossiers, options).to_csv + def to_csv(dossiers) + export(dossiers).to_csv end - def to_xlsx(dossiers, options = {}) - export(dossiers, options).to_xlsx + def to_xlsx(dossiers) + export(dossiers).to_xlsx end - def to_ods(dossiers, options = {}) - export(dossiers, options).to_ods + def to_ods(dossiers) + export(dossiers).to_ods end def procedure_overview(start_date) diff --git a/app/services/procedure_export_service.rb b/app/services/procedure_export_service.rb index 6d0b1386c..4e7a79778 100644 --- a/app/services/procedure_export_service.rb +++ b/app/services/procedure_export_service.rb @@ -1,238 +1,76 @@ class ProcedureExportService - include DossierHelper + attr_reader :dossiers - ATTRIBUTES = [ - :id, - :created_at, - :updated_at, - :archived, - :email, - :state, - :initiated_at, - :received_at, - :processed_at, - :motivation, - :emails_instructeurs, - :individual_gender, - :individual_prenom, - :individual_nom, - :individual_birthdate - ] - - ETABLISSEMENT_ATTRIBUTES = [ - :siret, - :siege_social, - :naf, - :libelle_naf, - :adresse, - :numero_voie, - :type_voie, - :nom_voie, - :complement_adresse, - :code_postal, - :localite, - :code_insee_localite - ] - - ENTREPRISE_ATTRIBUTES = [ - :siren, - :capital_social, - :numero_tva_intracommunautaire, - :forme_juridique, - :forme_juridique_code, - :nom_commercial, - :raison_sociale, - :siret_siege_social, - :code_effectif_entreprise, - :date_creation, - :nom, - :prenom - ] - - def initialize(procedure, dossiers, tables: []) + def initialize(procedure, dossiers) @procedure = procedure - - @attributes = ATTRIBUTES.dup - - if procedure.routee? - @attributes << :groupe_instructeur_label - end - @dossiers = dossiers.downloadable_sorted - @dossiers = @dossiers.to_a - @tables = tables.map(&:to_sym) + @tables = [:dossiers, :etablissements, :avis] + champs_repetables_options end def to_csv - SpreadsheetArchitect.to_csv(to_data(:dossiers)) + SpreadsheetArchitect.to_csv(options_for(:dossiers, :csv)) end def to_xlsx - package = SpreadsheetArchitect.to_axlsx_package(to_data(:dossiers)) - - # Next we recursively build multi page spreadsheet - @tables.reduce(package) do |package, table| - SpreadsheetArchitect.to_axlsx_package(to_data(table), package) + # We recursively build multi page spreadsheet + @tables.reduce(nil) do |package, table| + SpreadsheetArchitect.to_axlsx_package(options_for(table, :xlsx), package) end.to_stream.read end def to_ods - spreadsheet = SpreadsheetArchitect.to_rodf_spreadsheet(to_data(:dossiers)) - - # Next we recursively build multi page spreadsheet - @tables.reduce(spreadsheet) do |spreadsheet, table| - SpreadsheetArchitect.to_rodf_spreadsheet(to_data(table), spreadsheet) + # We recursively build multi page spreadsheet + @tables.reduce(nil) do |spreadsheet, table| + SpreadsheetArchitect.to_rodf_spreadsheet(options_for(table, :ods), spreadsheet) end.bytes end - def to_data(table) - case table - when :dossiers - dossiers_table_data - when :etablissements - etablissements_table_data - end - end - private - def empty_table_data(sheet_name, headers = []) - { - sheet_name: sheet_name, - headers: headers, - data: [[]] - } - end - - def dossiers_table_data - if @dossiers.any? - { - sheet_name: 'Dossiers', - headers: dossiers_headers, - data: dossiers_data - } - else - empty_table_data('Dossiers', dossiers_headers) - end - end - - def etablissements_table_data - @etablissements = @dossiers.flat_map do |dossier| + def etablissements + @etablissements ||= dossiers.flat_map do |dossier| [dossier.champs, dossier.champs_private] .flatten .filter { |champ| champ.is_a?(Champs::SiretChamp) } - end.map(&:etablissement).compact - - if @etablissements.any? - { - sheet_name: 'Etablissements', - headers: etablissements_headers, - data: etablissements_data - } - else - empty_table_data('Etablissements', etablissements_headers) - end + end.map(&:etablissement).compact + dossiers.map(&:etablissement).compact end - def dossiers_headers - headers = @attributes.map(&:to_s) + - @procedure.types_de_champ.reject(&:exclude_from_export?).map(&:libelle) + - @procedure.types_de_champ_private.reject(&:exclude_from_export?).map(&:libelle) + - ETABLISSEMENT_ATTRIBUTES.map { |key| "etablissement.#{key}" } + - ENTREPRISE_ATTRIBUTES.map { |key| "entreprise.#{key}" } - - headers.map { |header| label_for_export(header) } + def avis + @avis ||= dossiers.flat_map(&:avis) end - def dossiers_data - @dossiers.map do |dossier| - values = @attributes.map do |key| - case key - when :email - dossier.user.email - when :state - dossier_legacy_state(dossier) - when :initiated_at - dossier.en_construction_at - when :received_at - dossier.en_instruction_at - when :individual_prenom - dossier.individual&.prenom - when :individual_nom - dossier.individual&.nom - when :individual_birthdate - dossier.individual&.birthdate - when :individual_gender - dossier.individual&.gender - when :emails_instructeurs - dossier.followers_instructeurs.map(&:email).join(' ') - when :groupe_instructeur_label - dossier.groupe_instructeur.label - else - dossier.read_attribute(key) - end - end - - normalize_values(values) + - dossier.champs.reject(&:exclude_from_export?).map(&:for_export) + - dossier.champs_private.reject(&:exclude_from_export?).map(&:for_export) + - etablissement_data(dossier.etablissement) - end + def champs_repetables + @champs_repetables ||= dossiers.flat_map do |dossier| + [dossier.champs, dossier.champs_private] + .flatten + .filter { |champ| champ.is_a?(Champs::RepetitionChamp) } + end.group_by(&:libelle_for_export) end - def etablissements_headers - headers = ["dossier_id", "libelle"] + - ETABLISSEMENT_ATTRIBUTES.map { |key| "etablissement.#{key}" } + - ENTREPRISE_ATTRIBUTES.map { |key| "entreprise.#{key}" } - - headers.map { |header| label_for_export(header) } - end - - def etablissements_data - @etablissements.map do |etablissement| + def champs_repetables_options + champs_repetables.map do |libelle, champs| [ - etablissement.champ.dossier_id, - label_for_export(etablissement.champ.libelle).to_s - ] + etablissement_data(etablissement) + libelle, + champs.flat_map(&:rows_for_export) + ] end end - def etablissement_data(etablissement) - data = ETABLISSEMENT_ATTRIBUTES.map do |key| - if etablissement.present? - case key - when :adresse - etablissement.adresse&.chomp&.gsub("\r\n", ' ')&.delete("\r") - else - etablissement.read_attribute(key) - end - end - end - data += ENTREPRISE_ATTRIBUTES.map do |key| - if etablissement.present? - case key - when :date_creation - etablissement.entreprise_date_creation&.to_datetime - else - etablissement.read_attribute(:"entreprise_#{key}") - end - end - end - normalize_values(data) - end + DEFAULT_STYLES = { + header_style: { background_color: "000000", color: "FFFFFF", font_size: 12, bold: true }, + row_style: { background_color: nil, color: "000000", font_size: 12 } + } - def label_for_export(label) - label.parameterize.underscore.to_sym - end - - def normalize_values(values) - values.map do |value| - case value - when TrueClass, FalseClass - value.to_s - else - value.blank? ? nil : value.to_s - end - end + def options_for(table, format) + case table + when :dossiers + { instances: dossiers.to_a, sheet_name: 'Dossiers', spreadsheet_columns: :"spreadsheet_columns_#{format}" } + when :etablissements + { instances: etablissements.to_a, sheet_name: 'Etablissements' } + when :avis + { instances: avis.to_a, sheet_name: 'Avis' } + when Array + { instances: table.last, sheet_name: table.first } + end.merge(DEFAULT_STYLES) end end diff --git a/app/services/procedure_export_v2_service.rb b/app/services/procedure_export_v2_service.rb deleted file mode 100644 index eae682c1e..000000000 --- a/app/services/procedure_export_v2_service.rb +++ /dev/null @@ -1,76 +0,0 @@ -class ProcedureExportV2Service - attr_reader :dossiers - - def initialize(procedure, dossiers) - @procedure = procedure - @dossiers = dossiers.downloadable_sorted - @tables = [:dossiers, :etablissements, :avis] + champs_repetables_options - end - - def to_csv - SpreadsheetArchitect.to_csv(options_for(:dossiers, :csv)) - end - - def to_xlsx - # We recursively build multi page spreadsheet - @tables.reduce(nil) do |package, table| - SpreadsheetArchitect.to_axlsx_package(options_for(table, :xlsx), package) - end.to_stream.read - end - - def to_ods - # We recursively build multi page spreadsheet - @tables.reduce(nil) do |spreadsheet, table| - SpreadsheetArchitect.to_rodf_spreadsheet(options_for(table, :ods), spreadsheet) - end.bytes - end - - private - - def etablissements - @etablissements ||= dossiers.flat_map do |dossier| - [dossier.champs, dossier.champs_private] - .flatten - .filter { |champ| champ.is_a?(Champs::SiretChamp) } - end.map(&:etablissement).compact + dossiers.map(&:etablissement).compact - end - - def avis - @avis ||= dossiers.flat_map(&:avis) - end - - def champs_repetables - @champs_repetables ||= dossiers.flat_map do |dossier| - [dossier.champs, dossier.champs_private] - .flatten - .filter { |champ| champ.is_a?(Champs::RepetitionChamp) } - end.group_by(&:libelle_for_export) - end - - def champs_repetables_options - champs_repetables.map do |libelle, champs| - [ - libelle, - champs.flat_map(&:rows_for_export) - ] - end - end - - DEFAULT_STYLES = { - header_style: { background_color: "000000", color: "FFFFFF", font_size: 12, bold: true }, - row_style: { background_color: nil, color: "000000", font_size: 12 } - } - - def options_for(table, format) - case table - when :dossiers - { instances: dossiers.to_a, sheet_name: 'Dossiers', spreadsheet_columns: :"spreadsheet_columns_#{format}" }.merge(DEFAULT_STYLES) - when :etablissements - { instances: etablissements.to_a, sheet_name: 'Etablissements' }.merge(DEFAULT_STYLES) - when :avis - { instances: avis.to_a, sheet_name: 'Avis' }.merge(DEFAULT_STYLES) - when Array - { instances: table.last, sheet_name: table.first }.merge(DEFAULT_STYLES) - end - end -end diff --git a/app/views/instructeurs/procedures/_download_dossiers.html.haml b/app/views/instructeurs/procedures/_download_dossiers.html.haml index ef10c8a3b..40fc7a2c1 100644 --- a/app/views/instructeurs/procedures/_download_dossiers.html.haml +++ b/app/views/instructeurs/procedures/_download_dossiers.html.haml @@ -2,9 +2,7 @@ %span.dropdown %button.button.dropdown-button Télécharger tous les dossiers - - old_format_limit_date = Date.parse("Nov 15 2019") - - export_v1_enabled = old_format_limit_date > Time.zone.today - .dropdown-content.fade-in-down{ style: !export_v1_enabled ? 'width: 330px' : '' } + .dropdown-content.fade-in-down{ style: 'width: 330px' } %ul.dropdown-items %li - if procedure.xlsx_export_stale? @@ -30,12 +28,3 @@ = link_to "Exporter au format .csv", download_export_instructeur_procedure_path(procedure, export_format: :csv), remote: true - else = link_to "Au format .csv", url_for(procedure.csv_export_file), target: "_blank", rel: "noopener" - - - if export_v1_enabled - - old_format_message = "(ancien format, jusqu’au #{old_format_limit_date.strftime('%d/%m/%Y')})" - %li - = link_to "Au format .xlsx #{old_format_message}", procedure_dossiers_download_path(procedure, format: :xlsx, version: 'v1'), target: "_blank", rel: "noopener" - %li - = link_to "Au format .ods #{old_format_message}", procedure_dossiers_download_path(procedure, format: :ods, version: 'v1'), target: "_blank", rel: "noopener" - %li - = link_to "Au format .csv #{old_format_message}", procedure_dossiers_download_path(procedure, format: :csv, version: 'v1'), target: "_blank", rel: "noopener" diff --git a/spec/controllers/instructeurs/procedures_controller_spec.rb b/spec/controllers/instructeurs/procedures_controller_spec.rb index c95044d79..de0280657 100644 --- a/spec/controllers/instructeurs/procedures_controller_spec.rb +++ b/spec/controllers/instructeurs/procedures_controller_spec.rb @@ -426,7 +426,7 @@ describe Instructeurs::ProceduresController, type: :controller do context "csv" do before do expect_any_instance_of(Procedure).to receive(:to_csv) - .with(instructeur.dossiers.for_procedure(procedure), {}) + .with(instructeur.dossiers.for_procedure(procedure)) get :download_dossiers, params: { procedure_id: procedure.id }, format: 'csv' end diff --git a/spec/services/procedure_export_service_spec.rb b/spec/services/procedure_export_service_spec.rb index 235a2baa7..8be461c80 100644 --- a/spec/services/procedure_export_service_spec.rb +++ b/spec/services/procedure_export_service_spec.rb @@ -1,13 +1,21 @@ require 'spec_helper' +require 'csv' describe ProcedureExportService do describe 'to_data' do - let(:procedure) { create(:procedure, :published, :with_all_champs) } - let(:table) { :dossiers } - subject { ProcedureExportService.new(procedure, procedure.dossiers).to_data(table) } + let(:procedure) { create(:procedure, :published, :for_individual, :with_all_champs) } + subject do + Tempfile.create do |f| + f << ProcedureExportService.new(procedure, procedure.dossiers).to_xlsx + f.rewind + SimpleXlsxReader.open(f.path) + end + end - let(:headers) { subject[:headers] } - let(:data) { subject[:data] } + let(:dossiers_sheet) { subject.sheets.first } + let(:etablissements_sheet) { subject.sheets.second } + let(:avis_sheet) { subject.sheets.third } + let(:repetition_sheet) { subject.sheets.fourth } before do # change one tdc place to check if the header is ordered @@ -19,269 +27,324 @@ describe ProcedureExportService do end context 'dossiers' do - let(:nominal_header) do + it 'should have sheets' do + expect(subject.sheets.map(&:name)).to eq(['Dossiers', 'Etablissements', 'Avis']) + end + end + + context 'with dossier' do + let!(:dossier) { create(:dossier, :en_instruction, :with_all_champs, :for_individual, procedure: procedure) } + + let(:nominal_headers) do [ - :id, - :created_at, - :updated_at, - :archived, - :email, - :state, - :initiated_at, - :received_at, - :processed_at, - :motivation, - :emails_instructeurs, - :individual_gender, - :individual_prenom, - :individual_nom, - :individual_birthdate, - - :textarea, - :date, - :datetime, - :number, - :decimal_number, - :integer_number, - :checkbox, - :civilite, - :email, - :phone, - :address, - :yes_no, - :simple_drop_down_list, - :multiple_drop_down_list, - :linked_drop_down_list, - :pays, - :regions, - :departements, - :engagement, - :dossier_link, - :piece_justificative, - :siret, - :carte, - :text, - - :etablissement_siret, - :etablissement_siege_social, - :etablissement_naf, - :etablissement_libelle_naf, - :etablissement_adresse, - :etablissement_numero_voie, - :etablissement_type_voie, - :etablissement_nom_voie, - :etablissement_complement_adresse, - :etablissement_code_postal, - :etablissement_localite, - :etablissement_code_insee_localite, - :entreprise_siren, - :entreprise_capital_social, - :entreprise_numero_tva_intracommunautaire, - :entreprise_forme_juridique, - :entreprise_forme_juridique_code, - :entreprise_nom_commercial, - :entreprise_raison_sociale, - :entreprise_siret_siege_social, - :entreprise_code_effectif_entreprise, - :entreprise_date_creation, - :entreprise_nom, - :entreprise_prenom + "ID", + "Email", + "Civilité", + "Nom", + "Prénom", + "Date de naissance", + "Archivé", + "État du dossier", + "Dernière mise à jour le", + "Déposé le", + "Passé en instruction le", + "Traité le", + "Motivation de la décision", + "Instructeurs", + "textarea", + "date", + "datetime", + "number", + "decimal_number", + "integer_number", + "checkbox", + "civilite", + "email", + "phone", + "address", + "yes_no", + "simple_drop_down_list", + "multiple_drop_down_list", + "linked_drop_down_list", + "pays", + "regions", + "departements", + "engagement", + "dossier_link", + "piece_justificative", + "siret", + "carte", + "text" ] end it 'should have headers' do - expect(headers).to eq(nominal_header) + expect(dossiers_sheet.headers).to match(nominal_headers) + end + + it 'should have data' do + expect(dossiers_sheet.data.size).to eq(1) + expect(etablissements_sheet.data.size).to eq(1) + + # 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) + 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 procedure routee' do before { procedure.groupe_instructeurs.create(label: '2') } - let(:routee_header) { nominal_header.insert(nominal_header.index(:textarea), :groupe_instructeur_label) } + let(:routee_header) { nominal_headers.insert(nominal_headers.index('textarea'), 'Groupe instructeur') } - it { expect(headers).to eq(routee_header) } - end - - it 'should have empty values' do - expect(data).to eq([[]]) - end - - context 'with dossier' do - let!(:dossier) { create(:dossier, :en_instruction, :with_all_champs, :for_individual, procedure: procedure) } - - let(:dossier_data) { - [ - dossier.id.to_s, - dossier.created_at.to_s, - dossier.updated_at.to_s, - "false", - dossier.user.email, - "received", - dossier.en_construction_at.to_s, - dossier.en_instruction_at.to_s, - nil, - nil, - nil - ] + individual_data - } - - let(:individual_data) { - [ - "M.", - "Xavier", - "Julien", - "1991-11-01" - ] - } - - let(:champs_data) { - dossier.reload.champs.reject(&:exclude_from_export?).map(&:for_export) - } - - let(:etablissement_data) { - Array.new(24) - } - - it 'should have values' do - expect(data.first[0..14]).to eq(dossier_data) - expect(data.first[15..38]).to eq(champs_data) - expect(data.first[39..62]).to eq(etablissement_data) - - expect(data).to eq([ - dossier_data + champs_data + etablissement_data - ]) - end - - context 'with a procedure routee' do - before { procedure.groupe_instructeurs.create(label: '2') } - - it { expect(data.first[15]).to eq('défaut') } - it { expect(data.first.count).to eq(dossier_data.count + champs_data.count + etablissement_data.count + 1) } - end - - context 'and etablissement' do - let!(:dossier) { create(:dossier, :en_instruction, :with_all_champs, :with_entreprise, procedure: procedure) } - - let(:etablissement_data) { - [ - dossier.etablissement.siret, - dossier.etablissement.siege_social.to_s, - dossier.etablissement.naf, - dossier.etablissement.libelle_naf, - dossier.etablissement.adresse&.chomp&.gsub("\r\n", ' ')&.delete("\r"), - dossier.etablissement.numero_voie, - dossier.etablissement.type_voie, - dossier.etablissement.nom_voie, - dossier.etablissement.complement_adresse, - dossier.etablissement.code_postal, - dossier.etablissement.localite, - dossier.etablissement.code_insee_localite, - dossier.etablissement.entreprise_siren, - dossier.etablissement.entreprise_capital_social.to_s, - dossier.etablissement.entreprise_numero_tva_intracommunautaire, - dossier.etablissement.entreprise_forme_juridique, - dossier.etablissement.entreprise_forme_juridique_code, - dossier.etablissement.entreprise_nom_commercial, - dossier.etablissement.entreprise_raison_sociale, - dossier.etablissement.entreprise_siret_siege_social, - dossier.etablissement.entreprise_code_effectif_entreprise, - dossier.etablissement.entreprise_date_creation.to_datetime.to_s, - dossier.etablissement.entreprise_nom, - dossier.etablissement.entreprise_prenom - ] - } - - let(:individual_data) { - Array.new(4) - } - - it 'should have values' do - expect(data.first[0..14]).to eq(dossier_data) - expect(data.first[15..38]).to eq(champs_data) - expect(data.first[39..62]).to eq(etablissement_data) - - expect(data).to eq([ - dossier_data + champs_data + etablissement_data - ]) - end - end + it { expect(dossiers_sheet.headers).to match(routee_header) } + it { expect(dossiers_sheet.data[0][dossiers_sheet.headers.index('Groupe instructeur')]).to eq('défaut') } end end - context 'etablissements' do - let(:table) { :etablissements } + context 'with etablissement' do + let(:procedure) { create(:procedure, :published, :with_all_champs) } + let!(:dossier) { create(:dossier, :en_instruction, :with_all_champs, :with_entreprise, procedure: procedure) } + + let(:dossier_etablissement) { etablissements_sheet.data[1] } + let(:champ_etablissement) { etablissements_sheet.data[0] } + + let(:nominal_headers) do + [ + "ID", + "Email", + "Entreprise raison sociale", + "Archivé", + "État du dossier", + "Dernière mise à jour le", + "Déposé le", + "Passé en instruction le", + "Traité le", + "Motivation de la décision", + "Instructeurs", + "textarea", + "date", + "datetime", + "number", + "decimal_number", + "integer_number", + "checkbox", + "civilite", + "email", + "phone", + "address", + "yes_no", + "simple_drop_down_list", + "multiple_drop_down_list", + "linked_drop_down_list", + "pays", + "regions", + "departements", + "engagement", + "dossier_link", + "piece_justificative", + "siret", + "carte", + "text" + ] + end + + context 'as csv' do + subject do + Tempfile.create do |f| + f << ProcedureExportService.new(procedure, procedure.dossiers).to_csv + f.rewind + CSV.read(f.path) + end + end + + let(:nominal_headers) do + [ + "ID", + "Email", + "Établissement SIRET", + "Établissement siège social", + "Établissement NAF", + "Établissement libellé NAF", + "Établissement Adresse", + "Établissement numero voie", + "Établissement type voie", + "Établissement nom voie", + "Établissement complément adresse", + "Établissement code postal", + "Établissement localité", + "Établissement code INSEE localité", + "Entreprise SIREN", + "Entreprise capital social", + "Entreprise numero TVA intracommunautaire", + "Entreprise forme juridique", + "Entreprise forme juridique code", + "Entreprise nom commercial", + "Entreprise raison sociale", + "Entreprise SIRET siège social", + "Entreprise code effectif entreprise", + "Entreprise date de création", + "Entreprise nom", + "Entreprise prénom", + "Association RNA", + "Association titre", + "Association objet", + "Association date de création", + "Association date de déclaration", + "Association date de publication", + "Archivé", + "État du dossier", + "Dernière mise à jour le", + "Déposé le", + "Passé en instruction le", + "Traité le", + "Motivation de la décision", + "Instructeurs", + "textarea", + "date", + "datetime", + "number", + "decimal_number", + "integer_number", + "checkbox", + "civilite", + "email", + "phone", + "address", + "yes_no", + "simple_drop_down_list", + "multiple_drop_down_list", + "linked_drop_down_list", + "pays", + "regions", + "departements", + "engagement", + "dossier_link", + "piece_justificative", + "siret", + "carte", + "text" + ] + end + + let(:dossiers_sheet_headers) { subject.first } + + it 'should have headers' do + expect(dossiers_sheet_headers).to match(nominal_headers) + end + end it 'should have headers' do - expect(headers).to eq([ - :dossier_id, - :libelle, - :etablissement_siret, - :etablissement_siege_social, - :etablissement_naf, - :etablissement_libelle_naf, - :etablissement_adresse, - :etablissement_numero_voie, - :etablissement_type_voie, - :etablissement_nom_voie, - :etablissement_complement_adresse, - :etablissement_code_postal, - :etablissement_localite, - :etablissement_code_insee_localite, - :entreprise_siren, - :entreprise_capital_social, - :entreprise_numero_tva_intracommunautaire, - :entreprise_forme_juridique, - :entreprise_forme_juridique_code, - :entreprise_nom_commercial, - :entreprise_raison_sociale, - :entreprise_siret_siege_social, - :entreprise_code_effectif_entreprise, - :entreprise_date_creation, - :entreprise_nom, - :entreprise_prenom + expect(dossiers_sheet.headers).to match(nominal_headers) + + expect(etablissements_sheet.headers).to eq([ + "Dossier ID", + "Champ", + "Établissement SIRET", + "Établissement siège social", + "Établissement NAF", + "Établissement libellé NAF", + "Établissement Adresse", + "Établissement numero voie", + "Établissement type voie", + "Établissement nom voie", + "Établissement complément adresse", + "Établissement code postal", + "Établissement localité", + "Établissement code INSEE localité", + "Entreprise SIREN", + "Entreprise capital social", + "Entreprise numero TVA intracommunautaire", + "Entreprise forme juridique", + "Entreprise forme juridique code", + "Entreprise nom commercial", + "Entreprise raison sociale", + "Entreprise SIRET siège social", + "Entreprise code effectif entreprise", + "Entreprise date de création", + "Entreprise nom", + "Entreprise prénom", + "Association RNA", + "Association titre", + "Association objet", + "Association date de création", + "Association date de déclaration", + "Association date de publication" ]) end - it 'should have empty values' do - expect(data).to eq([[]]) + it 'should have data' do + expect(etablissements_sheet.data.size).to eq(2) + expect(dossier_etablissement[1]).to eq("Dossier") + expect(champ_etablissement[1]).to eq("siret") + end + end + + context 'with avis' do + let!(:dossier) { create(:dossier, :en_instruction, :with_all_champs, :for_individual, procedure: procedure) } + let!(:avis) { create(:avis, :with_answer, dossier: dossier) } + + it 'should have headers' do + expect(avis_sheet.headers).to eq([ + "Dossier ID", + "Question / Introduction", + "Réponse", + "Créé le", + "Répondu le" + ]) end - context 'with dossier containing champ siret' do - let!(:dossier) { create(:dossier, :en_instruction, :with_all_champs, procedure: procedure) } - let(:etablissement) { dossier.champs.find { |champ| champ.type_champ == 'siret' }.etablissement } + it 'should have data' do + expect(avis_sheet.data.size).to eq(1) + end + end - let(:etablissement_data) { - [ - dossier.id, - 'siret', - etablissement.siret, - etablissement.siege_social.to_s, - etablissement.naf, - etablissement.libelle_naf, - etablissement.adresse&.chomp&.gsub("\r\n", ' ')&.delete("\r"), - etablissement.numero_voie, - etablissement.type_voie, - etablissement.nom_voie, - etablissement.complement_adresse, - etablissement.code_postal, - etablissement.localite, - etablissement.code_insee_localite, - etablissement.entreprise_siren, - etablissement.entreprise_capital_social.to_s, - etablissement.entreprise_numero_tva_intracommunautaire, - etablissement.entreprise_forme_juridique, - etablissement.entreprise_forme_juridique_code, - etablissement.entreprise_nom_commercial, - etablissement.entreprise_raison_sociale, - etablissement.entreprise_siret_siege_social, - etablissement.entreprise_code_effectif_entreprise, - etablissement.entreprise_date_creation.to_datetime.to_s, - etablissement.entreprise_nom, - etablissement.entreprise_prenom - ] - } + context 'with repetitions' do + let!(:dossiers) do + [ + create(:dossier, :en_instruction, :with_all_champs, :for_individual, procedure: procedure), + create(:dossier, :en_instruction, :with_all_champs, :for_individual, procedure: procedure) + ] + end + let(:champ_repetition) { dossiers.first.champs.find { |champ| champ.type_champ == 'repetition' } } - it 'should have values' do - expect(data.first).to eq(etablissement_data) + it 'should have sheets' do + expect(subject.sheets.map(&:name)).to eq(['Dossiers', 'Etablissements', 'Avis', champ_repetition.libelle_for_export]) + end + + it 'should have headers' do + expect(repetition_sheet.headers).to eq([ + "Dossier ID", + "Ligne", + "Nom", + "Age" + ]) + end + + it 'should have data' do + expect(repetition_sheet.data.size).to eq(4) + end + + context 'with invalid characters' do + before do + champ_repetition.type_de_champ.update(libelle: 'A / B \ C') + end + + it 'should have valid sheet name' do + expect(subject.sheets.map(&:name)).to eq(['Dossiers', 'Etablissements', 'Avis', "(#{champ_repetition.type_de_champ.stable_id}) A - B - C"]) + end + end + + context 'with non unique labels' do + let(:dossier) { create(:dossier, :en_instruction, :with_all_champs, :for_individual, procedure: procedure) } + let(:champ_repetition) { dossier.champs.find { |champ| champ.type_champ == 'repetition' } } + let(:type_de_champ_repetition) { create(:type_de_champ_repetition, procedure: procedure, libelle: champ_repetition.libelle) } + let!(:another_champ_repetition) { create(:champ_repetition, type_de_champ: type_de_champ_repetition, dossier: dossier) } + + it 'should have sheets' do + expect(subject.sheets.map(&:name)).to eq(['Dossiers', 'Etablissements', 'Avis', champ_repetition.libelle_for_export, another_champ_repetition.libelle_for_export]) end end end diff --git a/spec/services/procedure_export_v2_service_spec.rb b/spec/services/procedure_export_v2_service_spec.rb deleted file mode 100644 index d8c30ecd7..000000000 --- a/spec/services/procedure_export_v2_service_spec.rb +++ /dev/null @@ -1,352 +0,0 @@ -require 'spec_helper' -require 'csv' - -describe ProcedureExportV2Service do - describe 'to_data' do - let(:procedure) { create(:procedure, :published, :for_individual, :with_all_champs) } - subject do - Tempfile.create do |f| - f << ProcedureExportV2Service.new(procedure, procedure.dossiers).to_xlsx - f.rewind - SimpleXlsxReader.open(f.path) - end - end - - let(:dossiers_sheet) { subject.sheets.first } - let(:etablissements_sheet) { subject.sheets.second } - let(:avis_sheet) { subject.sheets.third } - let(:repetition_sheet) { subject.sheets.fourth } - - before do - # change one tdc place to check if the header is ordered - tdc_first = procedure.types_de_champ.first - tdc_last = procedure.types_de_champ.last - - tdc_first.update(order_place: tdc_last.order_place + 1) - procedure.reload - end - - context 'dossiers' do - it 'should have sheets' do - expect(subject.sheets.map(&:name)).to eq(['Dossiers', 'Etablissements', 'Avis']) - end - end - - context 'with dossier' do - let!(:dossier) { create(:dossier, :en_instruction, :with_all_champs, :for_individual, procedure: procedure) } - - let(:nominal_headers) do - [ - "ID", - "Email", - "Civilité", - "Nom", - "Prénom", - "Date de naissance", - "Archivé", - "État du dossier", - "Dernière mise à jour le", - "Déposé le", - "Passé en instruction le", - "Traité le", - "Motivation de la décision", - "Instructeurs", - "textarea", - "date", - "datetime", - "number", - "decimal_number", - "integer_number", - "checkbox", - "civilite", - "email", - "phone", - "address", - "yes_no", - "simple_drop_down_list", - "multiple_drop_down_list", - "linked_drop_down_list", - "pays", - "regions", - "departements", - "engagement", - "dossier_link", - "piece_justificative", - "siret", - "carte", - "text" - ] - end - - it 'should have headers' do - expect(dossiers_sheet.headers).to match(nominal_headers) - end - - it 'should have data' do - expect(dossiers_sheet.data.size).to eq(1) - expect(etablissements_sheet.data.size).to eq(1) - - # 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) - 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 procedure routee' do - before { procedure.groupe_instructeurs.create(label: '2') } - - let(:routee_header) { nominal_headers.insert(nominal_headers.index('textarea'), 'Groupe instructeur') } - - it { expect(dossiers_sheet.headers).to match(routee_header) } - it { expect(dossiers_sheet.data[0][dossiers_sheet.headers.index('Groupe instructeur')]).to eq('défaut') } - end - end - - context 'with etablissement' do - let(:procedure) { create(:procedure, :published, :with_all_champs) } - let!(:dossier) { create(:dossier, :en_instruction, :with_all_champs, :with_entreprise, procedure: procedure) } - - let(:dossier_etablissement) { etablissements_sheet.data[1] } - let(:champ_etablissement) { etablissements_sheet.data[0] } - - let(:nominal_headers) do - [ - "ID", - "Email", - "Entreprise raison sociale", - "Archivé", - "État du dossier", - "Dernière mise à jour le", - "Déposé le", - "Passé en instruction le", - "Traité le", - "Motivation de la décision", - "Instructeurs", - "textarea", - "date", - "datetime", - "number", - "decimal_number", - "integer_number", - "checkbox", - "civilite", - "email", - "phone", - "address", - "yes_no", - "simple_drop_down_list", - "multiple_drop_down_list", - "linked_drop_down_list", - "pays", - "regions", - "departements", - "engagement", - "dossier_link", - "piece_justificative", - "siret", - "carte", - "text" - ] - end - - context 'as csv' do - subject do - Tempfile.create do |f| - f << ProcedureExportV2Service.new(procedure, procedure.dossiers).to_csv - f.rewind - CSV.read(f.path) - end - end - - let(:nominal_headers) do - [ - "ID", - "Email", - "Établissement SIRET", - "Établissement siège social", - "Établissement NAF", - "Établissement libellé NAF", - "Établissement Adresse", - "Établissement numero voie", - "Établissement type voie", - "Établissement nom voie", - "Établissement complément adresse", - "Établissement code postal", - "Établissement localité", - "Établissement code INSEE localité", - "Entreprise SIREN", - "Entreprise capital social", - "Entreprise numero TVA intracommunautaire", - "Entreprise forme juridique", - "Entreprise forme juridique code", - "Entreprise nom commercial", - "Entreprise raison sociale", - "Entreprise SIRET siège social", - "Entreprise code effectif entreprise", - "Entreprise date de création", - "Entreprise nom", - "Entreprise prénom", - "Association RNA", - "Association titre", - "Association objet", - "Association date de création", - "Association date de déclaration", - "Association date de publication", - "Archivé", - "État du dossier", - "Dernière mise à jour le", - "Déposé le", - "Passé en instruction le", - "Traité le", - "Motivation de la décision", - "Instructeurs", - "textarea", - "date", - "datetime", - "number", - "decimal_number", - "integer_number", - "checkbox", - "civilite", - "email", - "phone", - "address", - "yes_no", - "simple_drop_down_list", - "multiple_drop_down_list", - "linked_drop_down_list", - "pays", - "regions", - "departements", - "engagement", - "dossier_link", - "piece_justificative", - "siret", - "carte", - "text" - ] - end - - let(:dossiers_sheet_headers) { subject.first } - - it 'should have headers' do - expect(dossiers_sheet_headers).to match(nominal_headers) - end - end - - it 'should have headers' do - expect(dossiers_sheet.headers).to match(nominal_headers) - - expect(etablissements_sheet.headers).to eq([ - "Dossier ID", - "Champ", - "Établissement SIRET", - "Établissement siège social", - "Établissement NAF", - "Établissement libellé NAF", - "Établissement Adresse", - "Établissement numero voie", - "Établissement type voie", - "Établissement nom voie", - "Établissement complément adresse", - "Établissement code postal", - "Établissement localité", - "Établissement code INSEE localité", - "Entreprise SIREN", - "Entreprise capital social", - "Entreprise numero TVA intracommunautaire", - "Entreprise forme juridique", - "Entreprise forme juridique code", - "Entreprise nom commercial", - "Entreprise raison sociale", - "Entreprise SIRET siège social", - "Entreprise code effectif entreprise", - "Entreprise date de création", - "Entreprise nom", - "Entreprise prénom", - "Association RNA", - "Association titre", - "Association objet", - "Association date de création", - "Association date de déclaration", - "Association date de publication" - ]) - end - - it 'should have data' do - expect(etablissements_sheet.data.size).to eq(2) - expect(dossier_etablissement[1]).to eq("Dossier") - expect(champ_etablissement[1]).to eq("siret") - end - end - - context 'with avis' do - let!(:dossier) { create(:dossier, :en_instruction, :with_all_champs, :for_individual, procedure: procedure) } - let!(:avis) { create(:avis, :with_answer, dossier: dossier) } - - it 'should have headers' do - expect(avis_sheet.headers).to eq([ - "Dossier ID", - "Question / Introduction", - "Réponse", - "Créé le", - "Répondu le" - ]) - end - - it 'should have data' do - expect(avis_sheet.data.size).to eq(1) - end - end - - context 'with repetitions' do - let!(:dossiers) do - [ - create(:dossier, :en_instruction, :with_all_champs, :for_individual, procedure: procedure), - create(:dossier, :en_instruction, :with_all_champs, :for_individual, procedure: procedure) - ] - end - let(:champ_repetition) { dossiers.first.champs.find { |champ| champ.type_champ == 'repetition' } } - - it 'should have sheets' do - expect(subject.sheets.map(&:name)).to eq(['Dossiers', 'Etablissements', 'Avis', champ_repetition.libelle_for_export]) - end - - it 'should have headers' do - expect(repetition_sheet.headers).to eq([ - "Dossier ID", - "Ligne", - "Nom", - "Age" - ]) - end - - it 'should have data' do - expect(repetition_sheet.data.size).to eq(4) - end - - context 'with invalid characters' do - before do - champ_repetition.type_de_champ.update(libelle: 'A / B \ C') - end - - it 'should have valid sheet name' do - expect(subject.sheets.map(&:name)).to eq(['Dossiers', 'Etablissements', 'Avis', "(#{champ_repetition.type_de_champ.stable_id}) A - B - C"]) - end - end - - context 'with non unique labels' do - let(:dossier) { create(:dossier, :en_instruction, :with_all_champs, :for_individual, procedure: procedure) } - let(:champ_repetition) { dossier.champs.find { |champ| champ.type_champ == 'repetition' } } - let(:type_de_champ_repetition) { create(:type_de_champ_repetition, procedure: procedure, libelle: champ_repetition.libelle) } - let!(:another_champ_repetition) { create(:champ_repetition, type_de_champ: type_de_champ_repetition, dossier: dossier) } - - it 'should have sheets' do - expect(subject.sheets.map(&:name)).to eq(['Dossiers', 'Etablissements', 'Avis', champ_repetition.libelle_for_export, another_champ_repetition.libelle_for_export]) - end - end - end - end -end