2019-11-21-01 (#4553)

2019-11-21-01
This commit is contained in:
Pierre de La Morinerie 2019-11-21 14:28:38 +01:00 committed by GitHub
commit 85accfd389
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 501 additions and 1008 deletions

View file

@ -130,6 +130,10 @@
}
}
.state-button {
display: inline-block;
}
.dropdown {
display: inline-block;
position: relative;
@ -213,10 +217,13 @@
}
}
&.selected,
&:hover:not(.inactive) {
background: $light-grey;
&:not(.inactive) {
cursor: pointer;
&:hover,
&.selected {
background: $light-grey;
}
}
&.danger {

View file

@ -150,6 +150,8 @@ $header-mobile-breakpoint: 550px;
}
button {
@extend %outline;
padding: 9px;
border: none;
background: none;

View file

@ -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

View file

@ -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
@ -210,12 +208,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

View file

@ -143,18 +143,19 @@ module Users
errors = update_dossier_and_compute_errors
if errors.present?
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? && errors.blank?
@dossier.en_construction!
NotificationMailer.send_initiated_notification(@dossier).deliver_later
redirect_to merci_dossier_path(@dossier)
return redirect_to(merci_dossier_path(@dossier))
elsif errors.present?
flash.now.alert = errors
else
flash.now.notice = 'Votre brouillon a bien été sauvegardé.'
end
respond_to do |format|
format.html { render :brouillon }
format.json { head :ok }
end
end
@ -368,7 +369,7 @@ module Users
end
def save_draft?
dossier.brouillon? && params[:save_draft]
dossier.brouillon? && !params[:submit_draft]
end
end
end

View file

@ -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 = {

View file

@ -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) {

View file

@ -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)

View file

@ -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
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
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
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

View file

@ -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

View file

@ -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,

View file

@ -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
@ -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 }

View file

@ -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

View file

@ -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, jusquau #{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"

View file

@ -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

View file

@ -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
@ -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

View file

@ -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
@ -60,7 +60,6 @@
= 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 }

View file

@ -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 }

View file

@ -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
@ -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…" }

View file

@ -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

View file

@ -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: '')

View file

@ -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 daide pour remplir votre dossier ?" : "Une question sur votre dossier ?"

View file

@ -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'

View file

@ -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

View file

@ -3,8 +3,9 @@
- content_for :footer do
= render partial: "users/procedure_footer", locals: { procedure: @dossier.procedure, dossier: @dossier }
.dossier-header.sub-header
#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 }

View file

@ -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

View file

@ -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 (par exemple)
# * pour de la prod: www.demarches-simplifiees.fr
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"
# 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: enabled
DS_PROXY_URL=""
# Service externe: authentification France Connect
FC_PARTICULIER_ID=""
FC_PARTICULIER_SECRET=""
FC_PARTICULIER_BASE_URL=""
# Service externe: Authentification pour manager (auth Github obligatoire), permet d'accéder à /manager
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=""
# 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
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=""

View file

@ -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

View file

@ -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

View file

@ -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')

View file

@ -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
[
: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
]
it 'should have sheets' do
expect(subject.sheets.map(&:name)).to eq(['Dossiers', 'Etablissements', 'Avis'])
end
it 'should have headers' do
expect(headers).to eq(nominal_header)
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) }
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) {
let(:nominal_headers) do
[
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"
"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
let(:champs_data) {
dossier.reload.champs.reject(&:exclude_from_export?).map(&:for_export)
}
it 'should have headers' do
expect(dossiers_sheet.headers).to match(nominal_headers)
end
let(:etablissement_data) {
Array.new(24)
}
it 'should have data' do
expect(dossiers_sheet.data.size).to eq(1)
expect(etablissements_sheet.data.size).to eq(1)
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
])
# 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') }
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) }
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 'and etablissement' do
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(:etablissement_data) {
let(:dossier_etablissement) { etablissements_sheet.data[1] }
let(:champ_etablissement) { etablissements_sheet.data[0] }
let(:nominal_headers) do
[
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
"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"
]
}
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
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
context 'etablissements' do
let(:table) { :etablissements }
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(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)
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 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 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 }
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) }
let(:etablissement_data) {
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
[
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
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

View file

@ -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