Merge pull request #10462 from demarches-simplifiees/can-filter-from-repetition-content
ETQ instructeur je peux filtrer à partir du contenu d'un bloc répétable
This commit is contained in:
commit
34a05d0bf0
13 changed files with 96 additions and 94 deletions
|
@ -1,6 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Column
|
||||
TYPE_DE_CHAMP_TABLE = 'type_de_champ'
|
||||
|
||||
attr_reader :table, :column, :label, :classname, :type, :scope, :value_column, :filterable, :displayable
|
||||
|
||||
def initialize(table:, column:, label: nil, type: :text, value_column: :value, filterable: true, displayable: true, classname: '', scope: '')
|
||||
|
|
|
@ -4,41 +4,22 @@ module AddressableColumnConcern
|
|||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
def columns(table:)
|
||||
def columns(displayable: true, prefix: nil)
|
||||
super.concat([
|
||||
["code postal (5 chiffres)", ['postal_code'], :text],
|
||||
["commune", ['city_name'], :text],
|
||||
["département", ['departement_code'], :enum],
|
||||
["region", ['region_name'], :enum]
|
||||
].map do |(label, value_column, type)|
|
||||
Columns::JSONPathColumn.new(
|
||||
table:,
|
||||
displayable: false,
|
||||
table: Column::TYPE_DE_CHAMP_TABLE,
|
||||
column: stable_id,
|
||||
label: "#{libelle} – code postal (5 chiffres)",
|
||||
type: :text,
|
||||
value_column: ['postal_code']
|
||||
),
|
||||
Columns::JSONPathColumn.new(
|
||||
table:,
|
||||
label: "#{libelle_with_prefix(prefix)} – #{label}",
|
||||
displayable: false,
|
||||
column: stable_id,
|
||||
label: "#{libelle} – commune",
|
||||
type: :text,
|
||||
value_column: ['city_name']
|
||||
),
|
||||
Columns::JSONPathColumn.new(
|
||||
table:,
|
||||
displayable: false,
|
||||
column: stable_id,
|
||||
label: "#{libelle} – département",
|
||||
type: :enum,
|
||||
value_column: ['departement_code']
|
||||
),
|
||||
Columns::JSONPathColumn.new(
|
||||
table:,
|
||||
displayable: false,
|
||||
column: stable_id,
|
||||
label: "#{libelle} – région",
|
||||
type: :enum,
|
||||
value_column: ['region_name']
|
||||
type:,
|
||||
value_column:
|
||||
)
|
||||
])
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,8 +4,6 @@ module ColumnsConcern
|
|||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
TYPE_DE_CHAMP = 'type_de_champ'
|
||||
|
||||
def find_column(id:) = columns.find { |f| f.id == id }
|
||||
|
||||
def columns
|
||||
|
@ -75,13 +73,7 @@ module ColumnsConcern
|
|||
end
|
||||
|
||||
def types_de_champ_columns
|
||||
types_de_champ_for_procedure_presentation
|
||||
.pluck(:type_champ, :libelle, :stable_id)
|
||||
.reject { |(type_champ)| type_champ == TypeDeChamp.type_champs.fetch(:repetition) }
|
||||
.flat_map do |(type_champ, libelle, stable_id)|
|
||||
tdc = TypeDeChamp.new(type_champ:, libelle:, stable_id:)
|
||||
tdc.dynamic_type.columns(table: TYPE_DE_CHAMP)
|
||||
end
|
||||
all_revisions_types_de_champ.flat_map(&:columns)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -74,7 +74,7 @@ class Procedure < ApplicationRecord
|
|||
brouillon? ? draft_revision : published_revision
|
||||
end
|
||||
|
||||
def types_de_champ_for_procedure_presentation(parent = nil)
|
||||
def all_revisions_types_de_champ(parent: nil)
|
||||
if brouillon?
|
||||
if parent.nil?
|
||||
TypeDeChamp.fillable
|
||||
|
@ -85,45 +85,15 @@ class Procedure < ApplicationRecord
|
|||
draft_revision.children_of(parent)
|
||||
end
|
||||
else
|
||||
# all published revisions
|
||||
revision_ids = revisions.ids - [draft_revision_id]
|
||||
# fetch all parent types de champ
|
||||
parent_ids = if parent.present?
|
||||
ProcedureRevisionTypeDeChamp
|
||||
.where(revision_id: revision_ids)
|
||||
.joins(:type_de_champ)
|
||||
.where(type_de_champ: { stable_id: parent.stable_id })
|
||||
.ids
|
||||
end
|
||||
|
||||
# fetch all type_de_champ.stable_id for all the revisions expect draft
|
||||
# and for each stable_id take the bigger (more recent) type_de_champ.id
|
||||
recent_ids = TypeDeChamp
|
||||
.fillable
|
||||
.joins(:revision_types_de_champ)
|
||||
.where(revision_types_de_champ: { revision_id: revision_ids, parent_id: parent_ids })
|
||||
.group(:stable_id).select('MAX(types_de_champ.id)')
|
||||
|
||||
# fetch the more recent procedure_revision_types_de_champ
|
||||
# which includes recents_ids
|
||||
recents_prtdc = ProcedureRevisionTypeDeChamp
|
||||
.where(type_de_champ_id: recent_ids)
|
||||
.where.not(revision_id: draft_revision_id)
|
||||
.group(:type_de_champ_id)
|
||||
.select('MAX(id)')
|
||||
|
||||
TypeDeChamp
|
||||
.joins(:revision_types_de_champ)
|
||||
.where(revision_types_de_champ: { id: recents_prtdc }).then do |relation|
|
||||
if feature_enabled?(:export_order_by_revision) # Fonds Verts, en attente d'exports personnalisables
|
||||
relation.order(:private, 'revision_types_de_champ.revision_id': :desc, position: :asc)
|
||||
else
|
||||
relation.order(:private, :position, 'revision_types_de_champ.revision_id': :desc)
|
||||
end
|
||||
end
|
||||
cache_key = ['all_revisions_types_de_champ', published_revision, parent].compact
|
||||
Rails.cache.fetch(cache_key, expires_in: 1.month) { published_revisions_types_de_champ(parent) }
|
||||
end
|
||||
end
|
||||
|
||||
def types_de_champ_for_procedure_export
|
||||
all_revisions_types_de_champ.not_repetition
|
||||
end
|
||||
|
||||
def types_de_champ_for_tags
|
||||
TypeDeChamp
|
||||
.fillable
|
||||
|
@ -1034,6 +1004,45 @@ class Procedure < ApplicationRecord
|
|||
|
||||
private
|
||||
|
||||
def published_revisions_types_de_champ(parent = nil)
|
||||
# all published revisions
|
||||
revision_ids = revisions.ids - [draft_revision_id]
|
||||
# fetch all parent types de champ
|
||||
parent_ids = if parent.present?
|
||||
ProcedureRevisionTypeDeChamp
|
||||
.where(revision_id: revision_ids)
|
||||
.joins(:type_de_champ)
|
||||
.where(type_de_champ: { stable_id: parent.stable_id })
|
||||
.ids
|
||||
end
|
||||
|
||||
# fetch all type_de_champ.stable_id for all the revisions expect draft
|
||||
# and for each stable_id take the bigger (more recent) type_de_champ.id
|
||||
recent_ids = TypeDeChamp
|
||||
.fillable
|
||||
.joins(:revision_types_de_champ)
|
||||
.where(revision_types_de_champ: { revision_id: revision_ids, parent_id: parent_ids })
|
||||
.group(:stable_id).select('MAX(types_de_champ.id)')
|
||||
|
||||
# fetch the more recent procedure_revision_types_de_champ
|
||||
# which includes recents_ids
|
||||
recents_prtdc = ProcedureRevisionTypeDeChamp
|
||||
.where(type_de_champ_id: recent_ids)
|
||||
.where.not(revision_id: draft_revision_id)
|
||||
.group(:type_de_champ_id)
|
||||
.select('MAX(id)')
|
||||
|
||||
TypeDeChamp
|
||||
.joins(:revision_types_de_champ)
|
||||
.where(revision_types_de_champ: { id: recents_prtdc }).then do |relation|
|
||||
if feature_enabled?(:export_order_by_revision) # Fonds Verts, en attente d'exports personnalisables
|
||||
relation.order(:private, 'revision_types_de_champ.revision_id': :desc, position: :asc)
|
||||
else
|
||||
relation.order(:private, :position, 'revision_types_de_champ.revision_id': :desc)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def validates_associated_draft_revision_with_context
|
||||
return if draft_revision.blank?
|
||||
return if draft_revision.validate(validation_context)
|
||||
|
|
|
@ -148,7 +148,7 @@ class TypeDeChamp < ApplicationRecord
|
|||
has_one :revision, through: :revision_type_de_champ
|
||||
has_one :procedure, through: :revision
|
||||
|
||||
delegate :estimated_fill_duration, :estimated_read_duration, :tags_for_template, :libelles_for_export, :libelle_for_export, :primary_options, :secondary_options, to: :dynamic_type
|
||||
delegate :estimated_fill_duration, :estimated_read_duration, :tags_for_template, :libelles_for_export, :libelle_for_export, :primary_options, :secondary_options, :columns, to: :dynamic_type
|
||||
delegate :used_by_routing_rules?, to: :revision_type_de_champ
|
||||
|
||||
class WithIndifferentAccess
|
||||
|
|
|
@ -26,4 +26,10 @@ class TypesDeChamp::RepetitionTypeDeChamp < TypesDeChamp::TypeDeChampBase
|
|||
# /\*?[] are invalid Excel worksheet characters
|
||||
ActiveStorage::Filename.new(str.delete('[]*?')).sanitized
|
||||
end
|
||||
|
||||
def columns(displayable: true, prefix: nil)
|
||||
@type_de_champ.procedure
|
||||
.all_revisions_types_de_champ(parent: @type_de_champ)
|
||||
.flat_map { _1.columns(displayable: false, prefix: libelle) }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -98,20 +98,25 @@ class TypesDeChamp::TypeDeChampBase
|
|||
end
|
||||
end
|
||||
|
||||
def columns(table:)
|
||||
def columns(displayable: true, prefix: nil)
|
||||
[
|
||||
Column.new(
|
||||
table:,
|
||||
table: Column::TYPE_DE_CHAMP_TABLE,
|
||||
column: stable_id.to_s,
|
||||
label: libelle,
|
||||
label: libelle_with_prefix(prefix),
|
||||
type: TypeDeChamp.filter_hash_type(type_champ),
|
||||
value_column: TypeDeChamp.filter_hash_value_column(type_champ)
|
||||
value_column: TypeDeChamp.filter_hash_value_column(type_champ),
|
||||
displayable:
|
||||
)
|
||||
]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def libelle_with_prefix(prefix)
|
||||
[prefix, libelle].compact.join(' – ')
|
||||
end
|
||||
|
||||
def paths
|
||||
[
|
||||
{
|
||||
|
|
|
@ -107,10 +107,10 @@ class ProcedureExportService
|
|||
.group_by(&:stable_id)
|
||||
|
||||
procedure
|
||||
.types_de_champ_for_procedure_presentation
|
||||
.all_revisions_types_de_champ
|
||||
.repetition
|
||||
.filter_map do |type_de_champ_repetition|
|
||||
types_de_champ = procedure.types_de_champ_for_procedure_presentation(type_de_champ_repetition).to_a
|
||||
types_de_champ = procedure.all_revisions_types_de_champ(parent: type_de_champ_repetition).to_a
|
||||
rows = champs_by_stable_id.fetch(type_de_champ_repetition.stable_id, []).flat_map(&:rows_for_export)
|
||||
|
||||
if types_de_champ.present? && rows.present?
|
||||
|
@ -151,7 +151,7 @@ class ProcedureExportService
|
|||
end
|
||||
|
||||
def spreadsheet_columns(format)
|
||||
types_de_champ = procedure.types_de_champ_for_procedure_presentation.not_repetition.to_a
|
||||
types_de_champ = procedure.types_de_champ_for_procedure_export.to_a
|
||||
|
||||
Proc.new do |instance|
|
||||
instance.send(:"spreadsheet_columns_#{format}", types_de_champ: types_de_champ)
|
||||
|
|
|
@ -2005,7 +2005,7 @@ describe Dossier, type: :model do
|
|||
expect {
|
||||
integer_number_type_de_champ.update(type_champ: :decimal_number)
|
||||
procedure.update(published_revision: procedure.draft_revision, draft_revision: procedure.create_new_revision)
|
||||
}.to change { dossier.reload.champs_for_export(procedure.types_de_champ_for_procedure_presentation.not_repetition.to_a) }
|
||||
}.to change { dossier.reload.champs_for_export(procedure.all_revisions_types_de_champ.not_repetition.to_a) }
|
||||
.from([["c1", 42]]).to([["c1", 42.0]])
|
||||
end
|
||||
end
|
||||
|
@ -2032,8 +2032,8 @@ describe Dossier, type: :model do
|
|||
let(:repetition_second_revision_champ) { dossier_second_revision.champs_public.find(&:repetition?) }
|
||||
let(:dossier) { create(:dossier, procedure: procedure) }
|
||||
let(:dossier_second_revision) { create(:dossier, procedure: procedure) }
|
||||
let(:dossier_champs_for_export) { dossier.champs_for_export(procedure.types_de_champ_for_procedure_presentation.not_repetition) }
|
||||
let(:dossier_second_revision_champs_for_export) { dossier_second_revision.champs_for_export(procedure.types_de_champ_for_procedure_presentation.not_repetition) }
|
||||
let(:dossier_champs_for_export) { dossier.champs_for_export(procedure.types_de_champ_for_procedure_export) }
|
||||
let(:dossier_second_revision_champs_for_export) { dossier_second_revision.champs_for_export(procedure.types_de_champ_for_procedure_export) }
|
||||
|
||||
context "when procedure published" do
|
||||
before do
|
||||
|
@ -2066,8 +2066,7 @@ describe Dossier, type: :model do
|
|||
draft.add_type_de_champ(type_champ: :communes, libelle: "communes", parent_stable_id: tdc_repetition.stable_id)
|
||||
|
||||
dossier_test = create(:dossier, procedure: proc_test)
|
||||
repetition = proc_test.types_de_champ_for_procedure_presentation.repetition.first
|
||||
type_champs = proc_test.types_de_champ_for_procedure_presentation(repetition).to_a
|
||||
type_champs = proc_test.all_revisions_types_de_champ(parent: tdc_repetition).to_a
|
||||
expect(type_champs.size).to eq(1)
|
||||
expect(dossier.champs_for_export(type_champs).size).to eq(3)
|
||||
end
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
describe ProcedurePresentation do
|
||||
describe "#types_de_champ_for_procedure_presentation" do
|
||||
subject { procedure.types_de_champ_for_procedure_presentation.not_repetition.pluck(:libelle) }
|
||||
describe "#types_de_champ_for_procedure_export" do
|
||||
subject { procedure.types_de_champ_for_procedure_export.pluck(:libelle) }
|
||||
|
||||
context 'for a draft procedure' do
|
||||
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :number, libelle: 'libelle 1' }]) }
|
||||
|
|
|
@ -573,7 +573,7 @@ describe ProcedurePresentation do
|
|||
context 'for type_de_champ using AddressableColumnConcern' do
|
||||
let(:types_de_champ_public) { [{ type: :rna, stable_id: 1 }] }
|
||||
let(:type_de_champ) { procedure.active_revision.types_de_champ.first }
|
||||
let(:available_columns) { type_de_champ.dynamic_type.columns(table: 'type_de_champ') }
|
||||
let(:available_columns) { type_de_champ.columns }
|
||||
let(:column) { available_columns.find { _1.value_column == value_column_searched } }
|
||||
let(:filter) { [column.to_json.merge({ "value" => value })] }
|
||||
let(:kept_dossier) { create(:dossier, procedure: procedure) }
|
||||
|
|
|
@ -123,7 +123,7 @@ describe ProcedureRevision do
|
|||
draft.reload
|
||||
expect(draft.revision_types_de_champ_public.map(&:position)).to eq([0, 1, 2, 3])
|
||||
expect(draft.types_de_champ_public.index(type_de_champ_public)).to eq(2)
|
||||
expect(draft.procedure.types_de_champ_for_procedure_presentation.not_repetition.index(type_de_champ_public)).to eq(2)
|
||||
expect(draft.procedure.types_de_champ_for_procedure_export.index(type_de_champ_public)).to eq(2)
|
||||
end
|
||||
|
||||
it 'move up' do
|
||||
|
@ -132,7 +132,7 @@ describe ProcedureRevision do
|
|||
draft.reload
|
||||
expect(draft.revision_types_de_champ_public.map(&:position)).to eq([0, 1, 2, 3])
|
||||
expect(draft.types_de_champ_public.index(last_type_de_champ)).to eq(0)
|
||||
expect(draft.procedure.types_de_champ_for_procedure_presentation.not_repetition.index(last_type_de_champ)).to eq(0)
|
||||
expect(draft.procedure.types_de_champ_for_procedure_export.index(last_type_de_champ)).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -109,6 +109,14 @@ describe "procedure filters" do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'with repetition' do
|
||||
let(:types_de_champ_public) { [{ type: :repetition, libelle: 'Enfants', children: [{ libelle: 'Nom' }] }] }
|
||||
|
||||
scenario "should be able to user custom fiters", js: true do
|
||||
add_filter('Enfants – Nom', 'Greer')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with a vcr cached cassette' do
|
||||
describe 'departements' do
|
||||
let(:types_de_champ_public) { [{ type: :departements }] }
|
||||
|
|
Loading…
Reference in a new issue