diff --git a/app/controllers/new_gestionnaire/procedures_controller.rb b/app/controllers/new_gestionnaire/procedures_controller.rb index 2b5bd6464..5810348f9 100644 --- a/app/controllers/new_gestionnaire/procedures_controller.rb +++ b/app/controllers/new_gestionnaire/procedures_controller.rb @@ -67,10 +67,11 @@ module NewGestionnaire @archived_dossiers end - sorted_ids = DossierFieldService.sorted_ids(@dossiers, procedure_presentation, current_gestionnaire) + dossier_field_service = DossierFieldService.new + sorted_ids = dossier_field_service.sorted_ids(@dossiers, procedure_presentation, current_gestionnaire) if @current_filters.count > 0 - filtered_ids = DossierFieldService.filtered_ids(@dossiers, current_filters) + filtered_ids = dossier_field_service.filtered_ids(@dossiers, current_filters) filtered_sorted_ids = sorted_ids.select { |id| filtered_ids.include?(id) } else filtered_sorted_ids = sorted_ids diff --git a/app/models/dossier.rb b/app/models/dossier.rb index db405b172..f4c28c44d 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -223,10 +223,6 @@ class Dossier < ApplicationRecord end end - def get_value(table, column) - DossierFieldService.get_value(self, table, column) - end - def owner_name if etablissement.present? etablissement.entreprise_raison_sociale diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 6ffe9a620..006a3bf3c 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -293,7 +293,7 @@ class Procedure < ApplicationRecord end def fields - DossierFieldService.fields(self) + DossierFieldService.new.fields(self) end def fields_for_select diff --git a/app/models/procedure_presentation.rb b/app/models/procedure_presentation.rb index b9fccc2d7..7b0c61479 100644 --- a/app/models/procedure_presentation.rb +++ b/app/models/procedure_presentation.rb @@ -16,7 +16,7 @@ class ProcedurePresentation < ApplicationRecord displayed_fields.each do |field| table = field['table'] column = field['column'] - if !DossierFieldService.valid_column?(procedure, table, column) + if !dossier_field_service.valid_column?(procedure, table, column) errors.add(:filters, "#{table}.#{column} n’est pas une colonne permise") end end @@ -35,7 +35,7 @@ class ProcedurePresentation < ApplicationRecord columns.each do |column| table = column['table'] column = column['column'] - if !DossierFieldService.valid_column?(procedure, table, column) + if !dossier_field_service.valid_column?(procedure, table, column) errors.add(:filters, "#{table}.#{column} n’est pas une colonne permise") end end @@ -44,7 +44,11 @@ class ProcedurePresentation < ApplicationRecord private + def dossier_field_service + @dossier_field_service ||= DossierFieldService.new + end + def valid_sort_column?(procedure, table, column) - DossierFieldService.valid_column?(procedure, table, column) || EXTRA_SORT_COLUMNS[table]&.include?(column) + dossier_field_service.valid_column?(procedure, table, column) || EXTRA_SORT_COLUMNS[table]&.include?(column) end end diff --git a/app/services/dossier_field_service.rb b/app/services/dossier_field_service.rb index dad49c2f9..4ca6028f0 100644 --- a/app/services/dossier_field_service.rb +++ b/app/services/dossier_field_service.rb @@ -1,172 +1,172 @@ class DossierFieldService - @@column_whitelist = {} + def initialize + @column_whitelist = {} + end - class << self - def fields(procedure) - fields = [ - field_hash('Créé le', 'self', 'created_at'), - field_hash('Mis à jour le', 'self', 'updated_at'), - field_hash('Demandeur', 'user', 'email') - ] + def fields(procedure) + fields = [ + field_hash('Créé le', 'self', 'created_at'), + field_hash('Mis à jour le', 'self', 'updated_at'), + field_hash('Demandeur', 'user', 'email') + ] - if !procedure.for_individual || (procedure.for_individual && procedure.individual_with_siret) - fields.push( - field_hash('SIREN', 'etablissement', 'entreprise_siren'), - field_hash('Forme juridique', 'etablissement', 'entreprise_forme_juridique'), - field_hash('Nom commercial', 'etablissement', 'entreprise_nom_commercial'), - field_hash('Raison sociale', 'etablissement', 'entreprise_raison_sociale'), - field_hash('SIRET siège social', 'etablissement', 'entreprise_siret_siege_social'), - field_hash('Date de création', 'etablissement', 'entreprise_date_creation') - ) + if !procedure.for_individual || (procedure.for_individual && procedure.individual_with_siret) + fields.push( + field_hash('SIREN', 'etablissement', 'entreprise_siren'), + field_hash('Forme juridique', 'etablissement', 'entreprise_forme_juridique'), + field_hash('Nom commercial', 'etablissement', 'entreprise_nom_commercial'), + field_hash('Raison sociale', 'etablissement', 'entreprise_raison_sociale'), + field_hash('SIRET siège social', 'etablissement', 'entreprise_siret_siege_social'), + field_hash('Date de création', 'etablissement', 'entreprise_date_creation') + ) - fields.push( - field_hash('SIRET', 'etablissement', 'siret'), - field_hash('Libellé NAF', 'etablissement', 'libelle_naf'), - field_hash('Code postal', 'etablissement', 'code_postal') - ) - end - - explanatory_types_de_champ = [:header_section, :explication].map{ |k| TypeDeChamp.type_champs.fetch(k) } - - fields.concat procedure.types_de_champ - .reject { |tdc| explanatory_types_de_champ.include?(tdc.type_champ) } - .map { |type_de_champ| field_hash(type_de_champ.libelle, 'type_de_champ', type_de_champ.id.to_s) } - - fields.concat procedure.types_de_champ_private - .reject { |tdc| explanatory_types_de_champ.include?(tdc.type_champ) } - .map { |type_de_champ| field_hash(type_de_champ.libelle, 'type_de_champ_private', type_de_champ.id.to_s) } - - fields + fields.push( + field_hash('SIRET', 'etablissement', 'siret'), + field_hash('Libellé NAF', 'etablissement', 'libelle_naf'), + field_hash('Code postal', 'etablissement', 'code_postal') + ) end - def get_value(dossier, table, column) - assert_valid_column(dossier.procedure, table, column) + explanatory_types_de_champ = [:header_section, :explication].map{ |k| TypeDeChamp.type_champs.fetch(k) } + fields.concat procedure.types_de_champ + .reject { |tdc| explanatory_types_de_champ.include?(tdc.type_champ) } + .map { |type_de_champ| field_hash(type_de_champ.libelle, 'type_de_champ', type_de_champ.id.to_s) } + + fields.concat procedure.types_de_champ_private + .reject { |tdc| explanatory_types_de_champ.include?(tdc.type_champ) } + .map { |type_de_champ| field_hash(type_de_champ.libelle, 'type_de_champ_private', type_de_champ.id.to_s) } + + fields + end + + def get_value(dossier, table, column) + assert_valid_column(dossier.procedure, table, column) + + case table + when 'self' + dossier.send(column) + when 'user' + dossier.user.send(column) + when 'etablissement' + dossier.etablissement&.send(column) + when 'type_de_champ' + dossier.champs.find { |c| c.type_de_champ_id == column.to_i }.value + when 'type_de_champ_private' + dossier.champs_private.find { |c| c.type_de_champ_id == column.to_i }.value + end + end + + def assert_valid_column(procedure, table, column) + if !valid_column?(procedure, table, column) + raise "Invalid column #{table}.#{column}" + end + end + + def valid_column?(procedure, table, column) + valid_columns_for_table(procedure, table).include?(column) + end + + def filtered_ids(dossiers, filters) + filters.map do |filter| + table = filter['table'] + column = sanitized_column(filter) case table when 'self' - dossier.send(column) - when 'user' - dossier.user.send(column) + dossiers.where("? ILIKE ?", filter['column'], "%#{filter['value']}%") + + when 'type_de_champ', 'type_de_champ_private' + relation = table == 'type_de_champ' ? :champs : :champs_private + dossiers + .includes(relation) + .where("champs.type_de_champ_id = ?", filter['column'].to_i) + .where("champs.value ILIKE ?", "%#{filter['value']}%") when 'etablissement' - dossier.etablissement&.send(column) - when 'type_de_champ' - dossier.champs.find { |c| c.type_de_champ_id == column.to_i }.value - when 'type_de_champ_private' - dossier.champs_private.find { |c| c.type_de_champ_id == column.to_i }.value - end - end - - def assert_valid_column(procedure, table, column) - if !valid_column?(procedure, table, column) - raise "Invalid column #{table}.#{column}" - end - end - - def valid_column?(procedure, table, column) - valid_columns_for_table(procedure, table).include?(column) - end - - def filtered_ids(dossiers, filters) - filters.map do |filter| - table = filter['table'] - column = sanitized_column(filter) - case table - when 'self' - dossiers.where("? ILIKE ?", filter['column'], "%#{filter['value']}%") - - when 'type_de_champ', 'type_de_champ_private' - relation = table == 'type_de_champ' ? :champs : :champs_private + if filter['column'] == 'entreprise_date_creation' + date = filter['value'].to_date rescue nil dossiers - .includes(relation) - .where("champs.type_de_champ_id = ?", filter['column'].to_i) - .where("champs.value ILIKE ?", "%#{filter['value']}%") - when 'etablissement' - if filter['column'] == 'entreprise_date_creation' - date = filter['value'].to_date rescue nil - dossiers - .includes(table) - .where("#{column} = ?", date) - else - dossiers - .includes(table) - .where("#{column} ILIKE ?", "%#{filter['value']}%") - end - when 'user' + .includes(table) + .where("#{column} = ?", date) + else dossiers .includes(table) .where("#{column} ILIKE ?", "%#{filter['value']}%") - end.pluck(:id) - end.reduce(:&) - end - - def sorted_ids(dossiers, procedure_presentation, gestionnaire) - table = procedure_presentation.sort['table'] - column = sanitized_column(procedure_presentation.sort) - order = procedure_presentation.sort['order'] - assert_valid_order(order) - - case table - when 'notifications' - procedure = procedure_presentation.assign_to.procedure - dossiers_id_with_notification = gestionnaire.notifications_for_procedure(procedure) - if order == 'desc' - return dossiers_id_with_notification + - (dossiers.order('dossiers.updated_at desc').ids - dossiers_id_with_notification) - else - return (dossiers.order('dossiers.updated_at asc').ids - dossiers_id_with_notification) + - dossiers_id_with_notification end - when 'self' - return dossiers - .order("#{column} #{order}") - .pluck(:id) - when 'type_de_champ', 'type_de_champ_private' - return dossiers - .includes(table == 'type_de_champ' ? :champs : :champs_private) - .where("champs.type_de_champ_id = #{procedure_presentation.sort['column'].to_i}") - .order("champs.value #{order}") - .pluck(:id) + when 'user' + dossiers + .includes(table) + .where("#{column} ILIKE ?", "%#{filter['value']}%") + end.pluck(:id) + end.reduce(:&) + end + + def sorted_ids(dossiers, procedure_presentation, gestionnaire) + table = procedure_presentation.sort['table'] + column = sanitized_column(procedure_presentation.sort) + order = procedure_presentation.sort['order'] + assert_valid_order(order) + + case table + when 'notifications' + procedure = procedure_presentation.assign_to.procedure + dossiers_id_with_notification = gestionnaire.notifications_for_procedure(procedure) + if order == 'desc' + return dossiers_id_with_notification + + (dossiers.order('dossiers.updated_at desc').ids - dossiers_id_with_notification) else - return dossiers - .includes(table) - .order("#{column} #{order}") - .pluck(:id) + return (dossiers.order('dossiers.updated_at asc').ids - dossiers_id_with_notification) + + dossiers_id_with_notification end - end - - private - - def valid_columns_for_table(procedure, table) - if !@@column_whitelist.key?(procedure.id) - @@column_whitelist[procedure.id] = fields(procedure) - .group_by { |field| field['table'] } - .map { |table, fields| [table, Set.new(fields.map { |field| field['column'] }) ] } - .to_h - end - - @@column_whitelist[procedure.id][table] || [] - end - - def sanitized_column(field) - table = field['table'] - table = ActiveRecord::Base.connection.quote_column_name((table == 'self' ? 'dossier' : table).pluralize) - column = ActiveRecord::Base.connection.quote_column_name(field['column']) - - table + '.' + column - end - - def assert_valid_order(order) - if !["asc", "desc"].include?(order) - raise "Invalid order #{order}" - end - end - - def field_hash(label, table, column) - { - 'label' => label, - 'table' => table, - 'column' => column - } + when 'self' + return dossiers + .order("#{column} #{order}") + .pluck(:id) + when 'type_de_champ', 'type_de_champ_private' + return dossiers + .includes(table == 'type_de_champ' ? :champs : :champs_private) + .where("champs.type_de_champ_id = #{procedure_presentation.sort['column'].to_i}") + .order("champs.value #{order}") + .pluck(:id) + else + return dossiers + .includes(table) + .order("#{column} #{order}") + .pluck(:id) end end + + private + + def valid_columns_for_table(procedure, table) + if !@column_whitelist.key?(procedure.id) + @column_whitelist[procedure.id] = fields(procedure) + .group_by { |field| field['table'] } + .map { |table, fields| [table, Set.new(fields.map { |field| field['column'] }) ] } + .to_h + end + + @column_whitelist[procedure.id][table] || [] + end + + def sanitized_column(field) + table = field['table'] + table = ActiveRecord::Base.connection.quote_column_name((table == 'self' ? 'dossier' : table).pluralize) + column = ActiveRecord::Base.connection.quote_column_name(field['column']) + + table + '.' + column + end + + def assert_valid_order(order) + if !["asc", "desc"].include?(order) + raise "Invalid order #{order}" + end + end + + def field_hash(label, table, column) + { + 'label' => label, + 'table' => table, + 'column' => column + } + end end diff --git a/app/views/new_gestionnaire/procedures/show.html.haml b/app/views/new_gestionnaire/procedures/show.html.haml index ebcf41b7f..8b5054168 100644 --- a/app/views/new_gestionnaire/procedures/show.html.haml +++ b/app/views/new_gestionnaire/procedures/show.html.haml @@ -92,6 +92,7 @@ = submit_tag "Enregistrer", class: 'button' %tbody + - dossier_field_service = DossierFieldService.new - @dossiers.each do |dossier| %tr %td.folder-col @@ -107,7 +108,7 @@ - @displayed_fields.each do |field| %td = link_to(gestionnaire_dossier_path(@procedure, dossier), class: 'cell-link') do - = dossier.get_value(field['table'], field['column']) + = dossier_field_service.get_value(dossier, field['table'], field['column']) %td.status-col = link_to(gestionnaire_dossier_path(@procedure, dossier), class: 'cell-link') do diff --git a/spec/models/dossier_spec.rb b/spec/models/dossier_spec.rb index 3d6960488..351a890d8 100644 --- a/spec/models/dossier_spec.rb +++ b/spec/models/dossier_spec.rb @@ -732,29 +732,6 @@ describe Dossier do end end - describe "#get_value" do - let(:dossier) { create(:dossier, :with_entreprise, user: user) } - - before do - FranceConnectInformation.create(france_connect_particulier_id: 123, user: user, gender: 'male') - - @champ_public = dossier.champs.first - @champ_public.value = "kiwi" - @champ_public.save - - @champ_private = dossier.champs_private.first - @champ_private.value = "banane" - @champ_private.save - end - - it { expect(dossier.get_value('self', 'created_at')).to eq(dossier.created_at) } - it { expect(dossier.get_value('user', 'email')).to eq(user.email) } - it { expect(dossier.get_value('etablissement', 'entreprise_siren')).to eq(dossier.etablissement.entreprise_siren) } - it { expect(dossier.get_value('etablissement', 'siret')).to eq(dossier.etablissement.siret) } - it { expect(dossier.get_value('type_de_champ', @champ_public.type_de_champ.id.to_s)).to eq(dossier.champs.first.value) } - it { expect(dossier.get_value('type_de_champ_private', @champ_private.type_de_champ.id.to_s)).to eq(dossier.champs_private.first.value) } - end - describe 'updated_at' do let!(:dossier) { create(:dossier) } let(:modif_date) { DateTime.parse('01/01/2100') } diff --git a/spec/services/dossier_field_service_spec.rb b/spec/services/dossier_field_service_spec.rb index 5f1a1a5fb..1d439c006 100644 --- a/spec/services/dossier_field_service_spec.rb +++ b/spec/services/dossier_field_service_spec.rb @@ -14,7 +14,7 @@ describe DossierFieldService do type_de_champ.champ.create(dossier: discarded_dossier, value: 'discard me') end - subject { described_class.filtered_ids(procedure.dossiers, [{ 'table' => 'type_de_champ', 'column' => type_de_champ.id, 'value' => 'keep' }]) } + subject { described_class.new.filtered_ids(procedure.dossiers, [{ 'table' => 'type_de_champ', 'column' => type_de_champ.id, 'value' => 'keep' }]) } it { is_expected.to contain_exactly(kept_dossier.id) } end @@ -29,7 +29,7 @@ describe DossierFieldService do type_de_champ_private.champ.create(dossier: discarded_dossier, value: 'discard me') end - subject { described_class.filtered_ids(procedure.dossiers, [{ 'table' => 'type_de_champ_private', 'column' => type_de_champ_private.id, 'value' => 'keep' }]) } + subject { described_class.new.filtered_ids(procedure.dossiers, [{ 'table' => 'type_de_champ_private', 'column' => type_de_champ_private.id, 'value' => 'keep' }]) } it { is_expected.to contain_exactly(kept_dossier.id) } end @@ -39,7 +39,7 @@ describe DossierFieldService do let!(:kept_dossier) { create(:dossier, procedure: procedure, etablissement: create(:etablissement, entreprise_date_creation: DateTime.new(2018, 6, 21))) } let!(:discarded_dossier) { create(:dossier, procedure: procedure, etablissement: create(:etablissement, entreprise_date_creation: DateTime.new(2008, 6, 21))) } - subject { described_class.filtered_ids(procedure.dossiers, [{ 'table' => 'etablissement', 'column' => 'entreprise_date_creation', 'value' => '21/6/2018' }]) } + subject { described_class.new.filtered_ids(procedure.dossiers, [{ 'table' => 'etablissement', 'column' => 'entreprise_date_creation', 'value' => '21/6/2018' }]) } it { is_expected.to contain_exactly(kept_dossier.id) } end @@ -50,7 +50,7 @@ describe DossierFieldService do let!(:kept_dossier) { create(:dossier, procedure: procedure, etablissement: create(:etablissement, code_postal: '75017')) } let!(:discarded_dossier) { create(:dossier, procedure: procedure, etablissement: create(:etablissement, code_postal: '25000')) } - subject { described_class.filtered_ids(procedure.dossiers, [{ 'table' => 'etablissement', 'column' => 'code_postal', 'value' => '75017' }]) } + subject { described_class.new.filtered_ids(procedure.dossiers, [{ 'table' => 'etablissement', 'column' => 'code_postal', 'value' => '75017' }]) } it { is_expected.to contain_exactly(kept_dossier.id) } end @@ -60,7 +60,7 @@ describe DossierFieldService do let!(:kept_dossier) { create(:dossier, procedure: procedure, user: create(:user, email: 'me@keepmail.com')) } let!(:discarded_dossier) { create(:dossier, procedure: procedure, user: create(:user, email: 'me@discard.com')) } - subject { described_class.filtered_ids(procedure.dossiers, [{ 'table' => 'user', 'column' => 'email', 'value' => 'keepmail' }]) } + subject { described_class.new.filtered_ids(procedure.dossiers, [{ 'table' => 'user', 'column' => 'email', 'value' => 'keepmail' }]) } it { is_expected.to contain_exactly(kept_dossier.id) } end @@ -72,7 +72,7 @@ describe DossierFieldService do let(:sort) { { 'table' => table, 'column' => column, 'order' => order } } let(:procedure_presentation) { ProcedurePresentation.create(assign_to: assign_to, sort: sort) } - subject { DossierFieldService.sorted_ids(procedure.dossiers, procedure_presentation, gestionnaire) } + subject { described_class.new.sorted_ids(procedure.dossiers, procedure_presentation, gestionnaire) } context 'for notifications table' do let(:table) { 'notifications' } @@ -164,7 +164,7 @@ describe DossierFieldService do end describe '#get_value' do - subject { DossierFieldService.get_value(dossier, table, column) } + subject { described_class.new.get_value(dossier, table, column) } context 'for self table' do let(:table) { 'self' }