feat(procedure): can filter from repetion content
This commit is contained in:
parent
4bd518f85e
commit
e7bc7c4783
13 changed files with 96 additions and 94 deletions
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Column
|
class Column
|
||||||
|
TYPE_DE_CHAMP_TABLE = 'type_de_champ'
|
||||||
|
|
||||||
attr_reader :table, :column, :label, :classname, :type, :scope, :value_column, :filterable, :displayable
|
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: '')
|
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
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
included do
|
included do
|
||||||
def columns(table:)
|
def columns(displayable: true, prefix: nil)
|
||||||
super.concat([
|
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(
|
Columns::JSONPathColumn.new(
|
||||||
table:,
|
table: Column::TYPE_DE_CHAMP_TABLE,
|
||||||
displayable: false,
|
|
||||||
column: stable_id,
|
column: stable_id,
|
||||||
label: "#{libelle} – code postal (5 chiffres)",
|
label: "#{libelle_with_prefix(prefix)} – #{label}",
|
||||||
type: :text,
|
|
||||||
value_column: ['postal_code']
|
|
||||||
),
|
|
||||||
Columns::JSONPathColumn.new(
|
|
||||||
table:,
|
|
||||||
displayable: false,
|
displayable: false,
|
||||||
column: stable_id,
|
type:,
|
||||||
label: "#{libelle} – commune",
|
value_column:
|
||||||
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']
|
|
||||||
)
|
)
|
||||||
])
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,8 +4,6 @@ module ColumnsConcern
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
included do
|
included do
|
||||||
TYPE_DE_CHAMP = 'type_de_champ'
|
|
||||||
|
|
||||||
def find_column(id:) = columns.find { |f| f.id == id }
|
def find_column(id:) = columns.find { |f| f.id == id }
|
||||||
|
|
||||||
def columns
|
def columns
|
||||||
|
@ -75,13 +73,7 @@ module ColumnsConcern
|
||||||
end
|
end
|
||||||
|
|
||||||
def types_de_champ_columns
|
def types_de_champ_columns
|
||||||
types_de_champ_for_procedure_presentation
|
all_revisions_types_de_champ.flat_map(&:columns)
|
||||||
.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
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -74,7 +74,7 @@ class Procedure < ApplicationRecord
|
||||||
brouillon? ? draft_revision : published_revision
|
brouillon? ? draft_revision : published_revision
|
||||||
end
|
end
|
||||||
|
|
||||||
def types_de_champ_for_procedure_presentation(parent = nil)
|
def all_revisions_types_de_champ(parent: nil)
|
||||||
if brouillon?
|
if brouillon?
|
||||||
if parent.nil?
|
if parent.nil?
|
||||||
TypeDeChamp.fillable
|
TypeDeChamp.fillable
|
||||||
|
@ -85,45 +85,15 @@ class Procedure < ApplicationRecord
|
||||||
draft_revision.children_of(parent)
|
draft_revision.children_of(parent)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
# all published revisions
|
cache_key = ['all_revisions_types_de_champ', published_revision, parent].compact
|
||||||
revision_ids = revisions.ids - [draft_revision_id]
|
Rails.cache.fetch(cache_key, expires_in: 1.month) { published_revisions_types_de_champ(parent) }
|
||||||
# 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
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def types_de_champ_for_procedure_export
|
||||||
|
all_revisions_types_de_champ.not_repetition
|
||||||
|
end
|
||||||
|
|
||||||
def types_de_champ_for_tags
|
def types_de_champ_for_tags
|
||||||
TypeDeChamp
|
TypeDeChamp
|
||||||
.fillable
|
.fillable
|
||||||
|
@ -1034,6 +1004,45 @@ class Procedure < ApplicationRecord
|
||||||
|
|
||||||
private
|
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
|
def validates_associated_draft_revision_with_context
|
||||||
return if draft_revision.blank?
|
return if draft_revision.blank?
|
||||||
return if draft_revision.validate(validation_context)
|
return if draft_revision.validate(validation_context)
|
||||||
|
|
|
@ -148,7 +148,7 @@ class TypeDeChamp < ApplicationRecord
|
||||||
has_one :revision, through: :revision_type_de_champ
|
has_one :revision, through: :revision_type_de_champ
|
||||||
has_one :procedure, through: :revision
|
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
|
delegate :used_by_routing_rules?, to: :revision_type_de_champ
|
||||||
|
|
||||||
class WithIndifferentAccess
|
class WithIndifferentAccess
|
||||||
|
|
|
@ -26,4 +26,10 @@ class TypesDeChamp::RepetitionTypeDeChamp < TypesDeChamp::TypeDeChampBase
|
||||||
# /\*?[] are invalid Excel worksheet characters
|
# /\*?[] are invalid Excel worksheet characters
|
||||||
ActiveStorage::Filename.new(str.delete('[]*?')).sanitized
|
ActiveStorage::Filename.new(str.delete('[]*?')).sanitized
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -98,20 +98,25 @@ class TypesDeChamp::TypeDeChampBase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def columns(table:)
|
def columns(displayable: true, prefix: nil)
|
||||||
[
|
[
|
||||||
Column.new(
|
Column.new(
|
||||||
table:,
|
table: Column::TYPE_DE_CHAMP_TABLE,
|
||||||
column: stable_id.to_s,
|
column: stable_id.to_s,
|
||||||
label: libelle,
|
label: libelle_with_prefix(prefix),
|
||||||
type: TypeDeChamp.filter_hash_type(type_champ),
|
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
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def libelle_with_prefix(prefix)
|
||||||
|
[prefix, libelle].compact.join(' – ')
|
||||||
|
end
|
||||||
|
|
||||||
def paths
|
def paths
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
|
|
@ -107,10 +107,10 @@ class ProcedureExportService
|
||||||
.group_by(&:stable_id)
|
.group_by(&:stable_id)
|
||||||
|
|
||||||
procedure
|
procedure
|
||||||
.types_de_champ_for_procedure_presentation
|
.all_revisions_types_de_champ
|
||||||
.repetition
|
.repetition
|
||||||
.filter_map do |type_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)
|
rows = champs_by_stable_id.fetch(type_de_champ_repetition.stable_id, []).flat_map(&:rows_for_export)
|
||||||
|
|
||||||
if types_de_champ.present? && rows.present?
|
if types_de_champ.present? && rows.present?
|
||||||
|
@ -151,7 +151,7 @@ class ProcedureExportService
|
||||||
end
|
end
|
||||||
|
|
||||||
def spreadsheet_columns(format)
|
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|
|
Proc.new do |instance|
|
||||||
instance.send(:"spreadsheet_columns_#{format}", types_de_champ: types_de_champ)
|
instance.send(:"spreadsheet_columns_#{format}", types_de_champ: types_de_champ)
|
||||||
|
|
|
@ -2005,7 +2005,7 @@ describe Dossier, type: :model do
|
||||||
expect {
|
expect {
|
||||||
integer_number_type_de_champ.update(type_champ: :decimal_number)
|
integer_number_type_de_champ.update(type_champ: :decimal_number)
|
||||||
procedure.update(published_revision: procedure.draft_revision, draft_revision: procedure.create_new_revision)
|
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]])
|
.from([["c1", 42]]).to([["c1", 42.0]])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -2032,8 +2032,8 @@ describe Dossier, type: :model do
|
||||||
let(:repetition_second_revision_champ) { dossier_second_revision.champs_public.find(&:repetition?) }
|
let(:repetition_second_revision_champ) { dossier_second_revision.champs_public.find(&:repetition?) }
|
||||||
let(:dossier) { create(:dossier, procedure: procedure) }
|
let(:dossier) { create(:dossier, procedure: procedure) }
|
||||||
let(:dossier_second_revision) { 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_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_presentation.not_repetition) }
|
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
|
context "when procedure published" do
|
||||||
before 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)
|
draft.add_type_de_champ(type_champ: :communes, libelle: "communes", parent_stable_id: tdc_repetition.stable_id)
|
||||||
|
|
||||||
dossier_test = create(:dossier, procedure: proc_test)
|
dossier_test = create(:dossier, procedure: proc_test)
|
||||||
repetition = proc_test.types_de_champ_for_procedure_presentation.repetition.first
|
type_champs = proc_test.all_revisions_types_de_champ(parent: tdc_repetition).to_a
|
||||||
type_champs = proc_test.types_de_champ_for_procedure_presentation(repetition).to_a
|
|
||||||
expect(type_champs.size).to eq(1)
|
expect(type_champs.size).to eq(1)
|
||||||
expect(dossier.champs_for_export(type_champs).size).to eq(3)
|
expect(dossier.champs_for_export(type_champs).size).to eq(3)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
describe ProcedurePresentation do
|
describe ProcedurePresentation do
|
||||||
describe "#types_de_champ_for_procedure_presentation" do
|
describe "#types_de_champ_for_procedure_export" do
|
||||||
subject { procedure.types_de_champ_for_procedure_presentation.not_repetition.pluck(:libelle) }
|
subject { procedure.types_de_champ_for_procedure_export.pluck(:libelle) }
|
||||||
|
|
||||||
context 'for a draft procedure' do
|
context 'for a draft procedure' do
|
||||||
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :number, libelle: 'libelle 1' }]) }
|
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :number, libelle: 'libelle 1' }]) }
|
||||||
|
|
|
@ -568,7 +568,7 @@ describe ProcedurePresentation do
|
||||||
context 'for type_de_champ using AddressableColumnConcern' do
|
context 'for type_de_champ using AddressableColumnConcern' do
|
||||||
let(:types_de_champ_public) { [{ type: :rna, stable_id: 1 }] }
|
let(:types_de_champ_public) { [{ type: :rna, stable_id: 1 }] }
|
||||||
let(:type_de_champ) { procedure.active_revision.types_de_champ.first }
|
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(:column) { available_columns.find { _1.value_column == value_column_searched } }
|
||||||
let(:filter) { [column.to_json.merge({ "value" => value })] }
|
let(:filter) { [column.to_json.merge({ "value" => value })] }
|
||||||
let(:kept_dossier) { create(:dossier, procedure: procedure) }
|
let(:kept_dossier) { create(:dossier, procedure: procedure) }
|
||||||
|
|
|
@ -123,7 +123,7 @@ describe ProcedureRevision do
|
||||||
draft.reload
|
draft.reload
|
||||||
expect(draft.revision_types_de_champ_public.map(&:position)).to eq([0, 1, 2, 3])
|
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.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
|
end
|
||||||
|
|
||||||
it 'move up' do
|
it 'move up' do
|
||||||
|
@ -132,7 +132,7 @@ describe ProcedureRevision do
|
||||||
draft.reload
|
draft.reload
|
||||||
expect(draft.revision_types_de_champ_public.map(&:position)).to eq([0, 1, 2, 3])
|
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.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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -109,6 +109,14 @@ describe "procedure filters" do
|
||||||
end
|
end
|
||||||
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 'with a vcr cached cassette' do
|
||||||
describe 'departements' do
|
describe 'departements' do
|
||||||
let(:types_de_champ_public) { [{ type: :departements }] }
|
let(:types_de_champ_public) { [{ type: :departements }] }
|
||||||
|
|
Loading…
Reference in a new issue