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