Merge pull request #10625 from demarches-simplifiees/US/cleanup-procedure-presentation
ETQ tech, je souhaite isoler le concept des colonnes extractable/cherchable/affichable d'une demarche
This commit is contained in:
commit
bc7264ee08
33 changed files with 622 additions and 543 deletions
|
@ -8,6 +8,10 @@ class ApplicationComponent < ViewComponent::Base
|
||||||
controller.current_user
|
controller.current_user
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def current_instructeur
|
||||||
|
controller.current_instructeur
|
||||||
|
end
|
||||||
|
|
||||||
def current_administrateur
|
def current_administrateur
|
||||||
controller.current_administrateur
|
controller.current_administrateur
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
class Dossiers::InstructeurFilterComponent < ApplicationComponent
|
|
||||||
def initialize(procedure:, procedure_presentation:, statut:, field_id: nil)
|
|
||||||
@procedure = procedure
|
|
||||||
@procedure_presentation = procedure_presentation
|
|
||||||
@statut = statut
|
|
||||||
@field_id = field_id
|
|
||||||
end
|
|
||||||
|
|
||||||
attr_reader :procedure, :procedure_presentation, :statut, :field_id
|
|
||||||
|
|
||||||
def field_type
|
|
||||||
return :text if field_id.nil?
|
|
||||||
procedure_presentation.field_type(field_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
def options_for_select_of_field
|
|
||||||
procedure_presentation.field_enum(field_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
def filter_react_props
|
|
||||||
{
|
|
||||||
selected_key: @field_id || '',
|
|
||||||
items: procedure_presentation.filterable_fields_options,
|
|
||||||
name: :field,
|
|
||||||
id: 'search-filter',
|
|
||||||
'aria-describedby': 'instructeur-filter-combo-label',
|
|
||||||
form: 'filter-component',
|
|
||||||
data: { no_autosubmit: 'input blur', no_autosubmit_on_empty: 'true', autosubmit_target: 'input' }
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,4 +1,4 @@
|
||||||
= form_tag update_sort_instructeur_procedure_path(procedure_id: @procedure.id, table: 'notifications', column: 'notifications', order: opposite_order), method: :get, data: { controller: 'autosubmit' } do
|
= form_tag update_sort_instructeur_procedure_path(procedure_id: @procedure.id, column_id: 'notifications/notifications', order: opposite_order), method: :get, data: { controller: 'autosubmit' } do
|
||||||
.fr-fieldset__element.fr-m-0
|
.fr-fieldset__element.fr-m-0
|
||||||
.fr-checkbox-group.fr-checkbox-group--sm
|
.fr-checkbox-group.fr-checkbox-group--sm
|
||||||
= check_box_tag :order, opposite_order, active?
|
= check_box_tag :order, opposite_order, active?
|
||||||
|
|
56
app/components/instructeurs/column_filter_component.rb
Normal file
56
app/components/instructeurs/column_filter_component.rb
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
class Instructeurs::ColumnFilterComponent < ApplicationComponent
|
||||||
|
attr_reader :procedure, :procedure_presentation, :statut, :column
|
||||||
|
|
||||||
|
def initialize(procedure:, procedure_presentation:, statut:, column: nil)
|
||||||
|
@procedure = procedure
|
||||||
|
@procedure_presentation = procedure_presentation
|
||||||
|
@statut = statut
|
||||||
|
@column = column
|
||||||
|
end
|
||||||
|
|
||||||
|
def column_type = column.present? ? column.type : :text
|
||||||
|
|
||||||
|
def options_for_select_of_column
|
||||||
|
if column.scope.present?
|
||||||
|
I18n.t(column.scope).map(&:to_a).map(&:reverse)
|
||||||
|
elsif column.table == 'groupe_instructeur'
|
||||||
|
current_instructeur.groupe_instructeurs.filter_map do
|
||||||
|
if _1.procedure_id == procedure.id
|
||||||
|
[_1.label, _1.id]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
find_type_de_champ(column.column).options_for_select
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def filter_react_props
|
||||||
|
{
|
||||||
|
selected_key: column.present? ? column.id : '',
|
||||||
|
items: filterable_columns_options,
|
||||||
|
name: :column,
|
||||||
|
id: 'search-filter',
|
||||||
|
'aria-describedby': 'instructeur-filter-combo-label',
|
||||||
|
form: 'filter-component',
|
||||||
|
data: { no_autosubmit: 'input blur', no_autosubmit_on_empty: 'true', autosubmit_target: 'input' }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def filterable_columns_options
|
||||||
|
procedure.columns.filter_map do |column|
|
||||||
|
next if column.filterable == false
|
||||||
|
|
||||||
|
[column.label, column.id]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def find_type_de_champ(column)
|
||||||
|
TypeDeChamp
|
||||||
|
.joins(:revision_types_de_champ)
|
||||||
|
.where(revision_types_de_champ: { revision_id: procedure.revisions })
|
||||||
|
.order(created_at: :desc)
|
||||||
|
.find_by(stable_id: column)
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,16 +1,16 @@
|
||||||
= form_tag add_filter_instructeur_procedure_path(procedure), method: :post, class: 'dropdown-form large', id: 'filter-component', data: { turbo: true, controller: 'autosubmit' } do
|
= form_tag add_filter_instructeur_procedure_path(procedure), method: :post, class: 'dropdown-form large', id: 'filter-component', data: { turbo: true, controller: 'autosubmit' } do
|
||||||
.fr-select-group
|
.fr-select-group
|
||||||
= label_tag :field, t('.column'), class: 'fr-label fr-m-0', id: 'instructeur-filter-combo-label', for: 'search-filter'
|
= label_tag :column, t('.column'), class: 'fr-label fr-m-0', id: 'instructeur-filter-combo-label', for: 'search-filter'
|
||||||
%react-fragment
|
%react-fragment
|
||||||
= render ReactComponent.new "ComboBox/SingleComboBox", **filter_react_props
|
= render ReactComponent.new "ComboBox/SingleComboBox", **filter_react_props
|
||||||
|
|
||||||
%input.hidden{ type: 'submit', formaction: update_filter_instructeur_procedure_path(procedure), data: { autosubmit_target: 'submitter' } }
|
%input.hidden{ type: 'submit', formaction: update_filter_instructeur_procedure_path(procedure), data: { autosubmit_target: 'submitter' } }
|
||||||
|
|
||||||
= label_tag :value, t('.value'), for: 'value', class: 'fr-label'
|
= label_tag :value, t('.value'), for: 'value', class: 'fr-label'
|
||||||
- if field_type == :enum
|
- if column_type == :enum
|
||||||
= select_tag :value, options_for_select(options_for_select_of_field), id: 'value', name: 'value', class: 'fr-select', data: { no_autosubmit: true }
|
= select_tag :value, options_for_select(options_for_select_of_column), id: 'value', name: 'value', class: 'fr-select', data: { no_autosubmit: true }
|
||||||
- else
|
- else
|
||||||
%input#value.fr-input{ type: field_type, name: :value, maxlength: ProcedurePresentation::FILTERS_VALUE_MAX_LENGTH, disabled: field_id.nil? ? true : false, data: { no_autosubmit: true } }
|
%input#value.fr-input{ type: column_type, name: :value, maxlength: ProcedurePresentation::FILTERS_VALUE_MAX_LENGTH, disabled: column.nil? ? true : false, data: { no_autosubmit: true } }
|
||||||
|
|
||||||
= hidden_field_tag :statut, statut
|
= hidden_field_tag :statut, statut
|
||||||
= submit_tag t('.add_filter'), class: 'fr-btn fr-btn--secondary fr-mt-2w'
|
= submit_tag t('.add_filter'), class: 'fr-btn fr-btn--secondary fr-mt-2w'
|
16
app/components/instructeurs/column_picker_component.rb
Normal file
16
app/components/instructeurs/column_picker_component.rb
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
class Instructeurs::ColumnPickerComponent < ApplicationComponent
|
||||||
|
attr_reader :procedure, :procedure_presentation
|
||||||
|
|
||||||
|
def initialize(procedure:, procedure_presentation:)
|
||||||
|
@procedure = procedure
|
||||||
|
@procedure_presentation = procedure_presentation
|
||||||
|
@displayable_columns_for_select, @displayable_columns_selected = displayable_columns_for_select
|
||||||
|
end
|
||||||
|
|
||||||
|
def displayable_columns_for_select
|
||||||
|
[
|
||||||
|
procedure.columns.reject(&:virtual).map { |column| [column.label, column.id] },
|
||||||
|
procedure_presentation.displayed_fields.map { Column.new(**_1.deep_symbolize_keys).id }
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,3 @@
|
||||||
|
---
|
||||||
|
en:
|
||||||
|
save: 'Save'
|
|
@ -0,0 +1,3 @@
|
||||||
|
---
|
||||||
|
fr:
|
||||||
|
save: 'Enregistrer'
|
|
@ -0,0 +1,5 @@
|
||||||
|
= form_tag update_displayed_fields_instructeur_procedure_path(@procedure), method: :patch, class: 'dropdown-form large columns-form' do
|
||||||
|
%react-fragment
|
||||||
|
= render ReactComponent.new "ComboBox/MultiComboBox", items: @displayable_columns_for_select, selected_keys: @displayable_columns_selected, name: 'values[]', 'aria-label': 'Colonne à afficher'
|
||||||
|
|
||||||
|
= submit_tag t('.save'), class: 'fr-btn fr-btn--secondary'
|
45
app/components/instructeurs/column_table_header_component.rb
Normal file
45
app/components/instructeurs/column_table_header_component.rb
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
class Instructeurs::ColumnTableHeaderComponent < ApplicationComponent
|
||||||
|
attr_reader :procedure_presentation, :column
|
||||||
|
# maybe extract a ColumnSorter class?
|
||||||
|
#
|
||||||
|
|
||||||
|
def initialize(procedure_presentation:, column:)
|
||||||
|
@procedure_presentation = procedure_presentation
|
||||||
|
@column = column
|
||||||
|
end
|
||||||
|
|
||||||
|
def column_id
|
||||||
|
column.id
|
||||||
|
end
|
||||||
|
|
||||||
|
def sorted_by_current_column?
|
||||||
|
procedure_presentation.sort['table'] == column.table &&
|
||||||
|
procedure_presentation.sort['column'] == column.column
|
||||||
|
end
|
||||||
|
|
||||||
|
def sorted_ascending?
|
||||||
|
current_sort_order == 'asc'
|
||||||
|
end
|
||||||
|
|
||||||
|
def sorted_descending?
|
||||||
|
current_sort_order == 'desc'
|
||||||
|
end
|
||||||
|
|
||||||
|
def aria_sort
|
||||||
|
if sorted_by_current_column?
|
||||||
|
if sorted_ascending?
|
||||||
|
{ "aria-sort": "ascending" }
|
||||||
|
elsif sorted_descending?
|
||||||
|
{ "aria-sort": "descending" }
|
||||||
|
end
|
||||||
|
else
|
||||||
|
{}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def current_sort_order
|
||||||
|
procedure_presentation.sort['order']
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,9 @@
|
||||||
|
%th{ aria_sort, scope: "col", class: column.classname }
|
||||||
|
= link_to update_sort_instructeur_procedure_path(@procedure_presentation.procedure, column_id:, order: @procedure_presentation.opposite_order_for(column.table, column.column)) do
|
||||||
|
- if sorted_by_current_column?
|
||||||
|
- if sorted_ascending?
|
||||||
|
#{column.label} ↑
|
||||||
|
- else
|
||||||
|
#{column.label} ↓
|
||||||
|
- else
|
||||||
|
#{column.label}
|
|
@ -72,7 +72,6 @@ module Instructeurs
|
||||||
@procedure_presentation = procedure_presentation
|
@procedure_presentation = procedure_presentation
|
||||||
|
|
||||||
@current_filters = current_filters
|
@current_filters = current_filters
|
||||||
@displayable_fields_for_select, @displayable_fields_selected = procedure_presentation.displayable_fields_for_select
|
|
||||||
@counts = current_instructeur
|
@counts = current_instructeur
|
||||||
.dossiers_count_summary(groupe_instructeur_ids)
|
.dossiers_count_summary(groupe_instructeur_ids)
|
||||||
.symbolize_keys
|
.symbolize_keys
|
||||||
|
@ -142,13 +141,13 @@ module Instructeurs
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_sort
|
def update_sort
|
||||||
procedure_presentation.update_sort(params[:table], params[:column], params[:order])
|
procedure_presentation.update_sort(params[:column_id], params[:order])
|
||||||
|
|
||||||
redirect_back(fallback_location: instructeur_procedure_url(procedure))
|
redirect_back(fallback_location: instructeur_procedure_url(procedure))
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_filter
|
def add_filter
|
||||||
if !procedure_presentation.add_filter(statut, params[:field], params[:value])
|
if !procedure_presentation.add_filter(statut, params[:column], params[:value])
|
||||||
flash.alert = procedure_presentation.errors.full_messages
|
flash.alert = procedure_presentation.errors.full_messages
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -159,11 +158,11 @@ module Instructeurs
|
||||||
@statut = statut
|
@statut = statut
|
||||||
@procedure = procedure
|
@procedure = procedure
|
||||||
@procedure_presentation = procedure_presentation
|
@procedure_presentation = procedure_presentation
|
||||||
@field = params[:field]
|
@column = procedure.find_column(id: params[:column])
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_filter
|
def remove_filter
|
||||||
procedure_presentation.remove_filter(statut, params[:field], params[:value])
|
procedure_presentation.remove_filter(statut, params[:column], params[:value])
|
||||||
|
|
||||||
redirect_back(fallback_location: instructeur_procedure_url(procedure))
|
redirect_back(fallback_location: instructeur_procedure_url(procedure))
|
||||||
end
|
end
|
||||||
|
|
29
app/models/column.rb
Normal file
29
app/models/column.rb
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
class Column
|
||||||
|
attr_reader :table, :column, :label, :classname, :virtual, :type, :scope, :value_column, :filterable
|
||||||
|
|
||||||
|
def initialize(table:, column:, label: nil, virtual: false, type: :text, value_column: :value, filterable: true, classname: '', scope: '')
|
||||||
|
@table = table
|
||||||
|
@column = column
|
||||||
|
@label = label || I18n.t(column, scope: [:activerecord, :attributes, :procedure_presentation, :fields, table])
|
||||||
|
@classname = classname
|
||||||
|
@virtual = virtual
|
||||||
|
@type = type
|
||||||
|
@scope = scope
|
||||||
|
@value_column = value_column
|
||||||
|
@filterable = filterable
|
||||||
|
end
|
||||||
|
|
||||||
|
def id
|
||||||
|
"#{table}/#{column}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def ==(other)
|
||||||
|
other.to_json == to_json
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_json
|
||||||
|
{
|
||||||
|
table:, column:, label:, classname:, virtual:, type:, scope:, value_column:, filterable:
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
85
app/models/concerns/columns_concern.rb
Normal file
85
app/models/concerns/columns_concern.rb
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
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
|
||||||
|
columns = dossier_columns
|
||||||
|
columns.concat(standard_columns)
|
||||||
|
columns.concat(individual_columns) if for_individual
|
||||||
|
columns.concat(moral_columns) if !for_individual
|
||||||
|
columns.concat(types_de_champ_columns)
|
||||||
|
end
|
||||||
|
|
||||||
|
def dossier_columns
|
||||||
|
common = [Column.new(table: 'self', column: 'id', classname: 'number-col'), Column.new(table: 'notifications', column: 'notifications', label: "notifications", filterable: false)]
|
||||||
|
|
||||||
|
dates = ['created_at', 'updated_at', 'depose_at', 'en_construction_at', 'en_instruction_at', 'processed_at']
|
||||||
|
.map { |column| Column.new(table: 'self', column:, type: :date) }
|
||||||
|
|
||||||
|
virtual_dates = ['updated_since', 'depose_since', 'en_construction_since', 'en_instruction_since', 'processed_since']
|
||||||
|
.map { |column| Column.new(table: 'self', column:, type: :date, virtual: true) }
|
||||||
|
|
||||||
|
states = [Column.new(table: 'self', column: 'state', type: :enum, scope: 'instructeurs.dossiers.filterable_state', virtual: true)]
|
||||||
|
|
||||||
|
[common, dates, sva_svr_columns(for_filters: true), virtual_dates, states].flatten.compact
|
||||||
|
end
|
||||||
|
|
||||||
|
def sva_svr_columns(for_filters: false)
|
||||||
|
return if !sva_svr_enabled?
|
||||||
|
|
||||||
|
scope = [:activerecord, :attributes, :procedure_presentation, :fields, :self]
|
||||||
|
|
||||||
|
columns = [
|
||||||
|
Column.new(table: 'self', column: 'sva_svr_decision_on', type: :date,
|
||||||
|
label: I18n.t("#{sva_svr_decision}_decision_on", scope:), classname: for_filters ? '' : 'sva-col')
|
||||||
|
]
|
||||||
|
|
||||||
|
if for_filters
|
||||||
|
columns << Column.new(table: 'self', column: 'sva_svr_decision_before', type: :date, virtual: true,
|
||||||
|
label: I18n.t("#{sva_svr_decision}_decision_before", scope:))
|
||||||
|
end
|
||||||
|
|
||||||
|
columns
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def standard_columns
|
||||||
|
[
|
||||||
|
Column.new(table: 'user', column: 'email'),
|
||||||
|
Column.new(table: 'followers_instructeurs', column: 'email'),
|
||||||
|
Column.new(table: 'groupe_instructeur', column: 'id', type: :enum),
|
||||||
|
Column.new(table: 'avis', column: 'question_answer', filterable: false)
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
def individual_columns
|
||||||
|
['nom', 'prenom', 'gender'].map { |column| Column.new(table: 'individual', column:) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def moral_columns
|
||||||
|
etablissements = ['entreprise_siren', 'entreprise_forme_juridique', 'entreprise_nom_commercial', 'entreprise_raison_sociale', 'entreprise_siret_siege_social']
|
||||||
|
.map { |column| Column.new(table: 'etablissement', column:) }
|
||||||
|
|
||||||
|
etablissement_dates = ['entreprise_date_creation'].map { |column| Column.new(table: 'etablissement', column:, type: :date) }
|
||||||
|
|
||||||
|
other = ['siret', 'libelle_naf', 'code_postal'].map { |column| Column.new(table: 'etablissement', column:) }
|
||||||
|
|
||||||
|
[etablissements, etablissement_dates, other].flatten
|
||||||
|
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
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -6,6 +6,7 @@ class Procedure < ApplicationRecord
|
||||||
include ProcedureSVASVRConcern
|
include ProcedureSVASVRConcern
|
||||||
include ProcedureChorusConcern
|
include ProcedureChorusConcern
|
||||||
include PiecesJointesListConcern
|
include PiecesJointesListConcern
|
||||||
|
include ColumnsConcern
|
||||||
|
|
||||||
include Discard::Model
|
include Discard::Model
|
||||||
self.discard_column = :hidden_at
|
self.discard_column = :hidden_at
|
||||||
|
|
|
@ -10,7 +10,6 @@ class ProcedurePresentation < ApplicationRecord
|
||||||
|
|
||||||
SLASH = '/'
|
SLASH = '/'
|
||||||
TYPE_DE_CHAMP = 'type_de_champ'
|
TYPE_DE_CHAMP = 'type_de_champ'
|
||||||
TYPE_DE_CHAMP_PRIVATE = 'type_de_champ_private'
|
|
||||||
|
|
||||||
FILTERS_VALUE_MAX_LENGTH = 100
|
FILTERS_VALUE_MAX_LENGTH = 100
|
||||||
|
|
||||||
|
@ -25,118 +24,127 @@ class ProcedurePresentation < ApplicationRecord
|
||||||
validate :check_allowed_filter_columns
|
validate :check_allowed_filter_columns
|
||||||
validate :check_filters_max_length
|
validate :check_filters_max_length
|
||||||
|
|
||||||
def self_fields
|
|
||||||
[
|
|
||||||
field_hash('self', 'created_at', type: :date),
|
|
||||||
field_hash('self', 'updated_at', type: :date),
|
|
||||||
field_hash('self', 'depose_at', type: :date),
|
|
||||||
field_hash('self', 'en_construction_at', type: :date),
|
|
||||||
field_hash('self', 'en_instruction_at', type: :date),
|
|
||||||
field_hash('self', 'processed_at', type: :date),
|
|
||||||
*sva_svr_fields(for_filters: true),
|
|
||||||
field_hash('self', 'updated_since', type: :date, virtual: true),
|
|
||||||
field_hash('self', 'depose_since', type: :date, virtual: true),
|
|
||||||
field_hash('self', 'en_construction_since', type: :date, virtual: true),
|
|
||||||
field_hash('self', 'en_instruction_since', type: :date, virtual: true),
|
|
||||||
field_hash('self', 'processed_since', type: :date, virtual: true),
|
|
||||||
field_hash('self', 'state', type: :enum, scope: 'instructeurs.dossiers.filterable_state', virtual: true)
|
|
||||||
].compact_blank
|
|
||||||
end
|
|
||||||
|
|
||||||
def fields
|
|
||||||
fields = self_fields
|
|
||||||
|
|
||||||
fields.push(
|
|
||||||
field_hash('user', 'email', type: :text),
|
|
||||||
field_hash('followers_instructeurs', 'email', type: :text),
|
|
||||||
field_hash('groupe_instructeur', 'id', type: :enum),
|
|
||||||
field_hash('avis', 'question_answer', filterable: false)
|
|
||||||
)
|
|
||||||
|
|
||||||
if procedure.for_individual
|
|
||||||
fields.push(
|
|
||||||
field_hash("individual", "prenom", type: :text),
|
|
||||||
field_hash("individual", "nom", type: :text),
|
|
||||||
field_hash("individual", "gender", type: :text)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
if !procedure.for_individual
|
|
||||||
fields.push(
|
|
||||||
field_hash('etablissement', 'entreprise_siren', type: :text),
|
|
||||||
field_hash('etablissement', 'entreprise_forme_juridique', type: :text),
|
|
||||||
field_hash('etablissement', 'entreprise_nom_commercial', type: :text),
|
|
||||||
field_hash('etablissement', 'entreprise_raison_sociale', type: :text),
|
|
||||||
field_hash('etablissement', 'entreprise_siret_siege_social', type: :text),
|
|
||||||
field_hash('etablissement', 'entreprise_date_creation', type: :date)
|
|
||||||
)
|
|
||||||
|
|
||||||
fields.push(
|
|
||||||
field_hash('etablissement', 'siret', type: :text),
|
|
||||||
field_hash('etablissement', 'libelle_naf', type: :text),
|
|
||||||
field_hash('etablissement', 'code_postal', type: :text)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
fields.concat(procedure.types_de_champ_for_procedure_presentation
|
|
||||||
.pluck(:type_champ, :libelle, :private, :stable_id)
|
|
||||||
.reject { |(type_champ)| type_champ == TypeDeChamp.type_champs.fetch(:repetition) }
|
|
||||||
.map do |(type_champ, libelle, is_private, stable_id)|
|
|
||||||
if is_private
|
|
||||||
field_hash_for_type_de_champ_private(type_champ, libelle, stable_id)
|
|
||||||
else
|
|
||||||
field_hash_for_type_de_champ_public(type_champ, libelle, stable_id)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
fields
|
|
||||||
end
|
|
||||||
|
|
||||||
def displayable_fields_for_select
|
|
||||||
[
|
|
||||||
fields.reject { |field| field['virtual'] }
|
|
||||||
.map { |field| [field['label'], field_id(field)] },
|
|
||||||
displayed_fields.map { |field| field_id(field) }
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
def filterable_fields_options
|
|
||||||
fields.filter_map do |field|
|
|
||||||
next if field['filterable'] == false
|
|
||||||
|
|
||||||
[field['label'], field_id(field)]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def displayed_fields_for_headers
|
def displayed_fields_for_headers
|
||||||
[
|
[
|
||||||
field_hash('self', 'id', classname: 'number-col'),
|
Column.new(table: 'self', column: 'id', classname: 'number-col'),
|
||||||
*displayed_fields,
|
*displayed_fields.map { Column.new(**_1.deep_symbolize_keys) },
|
||||||
field_hash('self', 'state', classname: 'state-col'),
|
Column.new(table: 'self', column: 'state', classname: 'state-col'),
|
||||||
*sva_svr_fields
|
*procedure.sva_svr_columns
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
def sva_svr_fields(for_filters: false)
|
def filtered_sorted_ids(dossiers, statut, count: nil)
|
||||||
return if !procedure.sva_svr_enabled?
|
dossiers_by_statut = dossiers.by_statut(statut, instructeur)
|
||||||
|
dossiers_sorted_ids = self.sorted_ids(dossiers_by_statut, count || dossiers_by_statut.size)
|
||||||
|
|
||||||
i18n_scope = [:activerecord, :attributes, :procedure_presentation, :fields, :self]
|
if filters[statut].present?
|
||||||
|
dossiers_sorted_ids.intersection(filtered_ids(dossiers_by_statut, statut))
|
||||||
|
else
|
||||||
|
dossiers_sorted_ids
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
fields = []
|
def human_value_for_filter(filter)
|
||||||
fields << field_hash('self', 'sva_svr_decision_on',
|
if filter[TABLE] == TYPE_DE_CHAMP
|
||||||
type: :date,
|
find_type_de_champ(filter[COLUMN]).dynamic_type.filter_to_human(filter['value'])
|
||||||
label: I18n.t("#{procedure.sva_svr_decision}_decision_on", scope: i18n_scope),
|
elsif filter['column'] == 'state'
|
||||||
classname: for_filters ? '' : 'sva-col')
|
if filter['value'] == 'pending_correction'
|
||||||
|
Dossier.human_attribute_name("pending_correction.for_instructeur")
|
||||||
|
else
|
||||||
|
Dossier.human_attribute_name("state.#{filter['value']}")
|
||||||
|
end
|
||||||
|
elsif filter['table'] == 'groupe_instructeur' && filter['column'] == 'id'
|
||||||
|
instructeur.groupe_instructeurs
|
||||||
|
.find { _1.id == filter['value'].to_i }&.label || filter['value']
|
||||||
|
else
|
||||||
|
column = procedure.columns.find { _1.table == filter[TABLE] && _1.column == filter[COLUMN] }
|
||||||
|
|
||||||
if for_filters
|
if column.type == :date
|
||||||
fields << field_hash('self', 'sva_svr_decision_before',
|
parsed_date = safe_parse_date(filter['value'])
|
||||||
label: I18n.t("#{procedure.sva_svr_decision}_decision_before", scope: i18n_scope),
|
|
||||||
type: :date, virtual: true)
|
return parsed_date.present? ? I18n.l(parsed_date) : nil
|
||||||
|
end
|
||||||
|
|
||||||
|
filter['value']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def safe_parse_date(string)
|
||||||
|
Date.parse(string)
|
||||||
|
rescue Date::Error
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_filter(statut, column_id, value)
|
||||||
|
if value.present?
|
||||||
|
column = procedure.find_column(id: column_id)
|
||||||
|
|
||||||
|
case column.table
|
||||||
|
when TYPE_DE_CHAMP
|
||||||
|
value = find_type_de_champ(column.column).dynamic_type.human_to_filter(value)
|
||||||
|
end
|
||||||
|
|
||||||
|
updated_filters = filters.dup
|
||||||
|
updated_filters[statut] << {
|
||||||
|
'label' => column.label,
|
||||||
|
TABLE => column.table,
|
||||||
|
COLUMN => column.column,
|
||||||
|
'value_column' => column.value_column,
|
||||||
|
'value' => value
|
||||||
|
}
|
||||||
|
|
||||||
|
update(filters: updated_filters)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_filter(statut, column_id, value)
|
||||||
|
column = procedure.find_column(id: column_id)
|
||||||
|
updated_filters = filters.dup
|
||||||
|
|
||||||
|
updated_filters[statut] = filters[statut].reject do |filter|
|
||||||
|
filter.values_at(TABLE, COLUMN, 'value') == [column.table, column.column, value]
|
||||||
end
|
end
|
||||||
|
|
||||||
fields
|
update!(filters: updated_filters)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update_displayed_fields(column_ids)
|
||||||
|
column_ids = Array.wrap(column_ids)
|
||||||
|
columns = column_ids.map { |id| procedure.find_column(id:) }
|
||||||
|
|
||||||
|
update!(displayed_fields: columns)
|
||||||
|
|
||||||
|
if !sort_to_column_id(sort).in?(column_ids)
|
||||||
|
update!(sort: Procedure.default_sort)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_sort(column_id, order)
|
||||||
|
column = procedure.find_column(id: column_id)
|
||||||
|
|
||||||
|
update!(sort: {
|
||||||
|
TABLE => column.table,
|
||||||
|
COLUMN => column.column,
|
||||||
|
ORDER => order.presence || opposite_order_for(column.table, column.column)
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
def opposite_order_for(table, column)
|
||||||
|
if sort.values_at(TABLE, COLUMN) == [table, column]
|
||||||
|
sort['order'] == 'asc' ? 'desc' : 'asc'
|
||||||
|
elsif [table, column] == ["notifications", "notifications"]
|
||||||
|
'desc' # default order for notifications
|
||||||
|
else
|
||||||
|
'asc'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def snapshot
|
||||||
|
slice(:filters, :sort, :displayed_fields)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
def sorted_ids(dossiers, count)
|
def sorted_ids(dossiers, count)
|
||||||
table, column, order = sort.values_at(TABLE, COLUMN, 'order')
|
table, column, order = sort.values_at(TABLE, COLUMN, 'order')
|
||||||
|
|
||||||
|
@ -161,17 +169,6 @@ class ProcedurePresentation < ApplicationRecord
|
||||||
else
|
else
|
||||||
ids
|
ids
|
||||||
end
|
end
|
||||||
when TYPE_DE_CHAMP_PRIVATE
|
|
||||||
ids = dossiers
|
|
||||||
.with_type_de_champ(column)
|
|
||||||
.order("champs.value #{order}")
|
|
||||||
.pluck(:id)
|
|
||||||
if ids.size != count
|
|
||||||
rest = dossiers.where.not(id: ids).order(id: order).pluck(:id)
|
|
||||||
order == 'asc' ? ids + rest : rest + ids
|
|
||||||
else
|
|
||||||
ids
|
|
||||||
end
|
|
||||||
when 'followers_instructeurs'
|
when 'followers_instructeurs'
|
||||||
assert_supported_column(table, column)
|
assert_supported_column(table, column)
|
||||||
# LEFT OUTER JOIN allows to keep dossiers without assigned instructeurs yet
|
# LEFT OUTER JOIN allows to keep dossiers without assigned instructeurs yet
|
||||||
|
@ -201,15 +198,15 @@ class ProcedurePresentation < ApplicationRecord
|
||||||
value_column = filters.pluck('value_column').compact.first || :value
|
value_column = filters.pluck('value_column').compact.first || :value
|
||||||
case table
|
case table
|
||||||
when 'self'
|
when 'self'
|
||||||
field = self_fields.find { |h| h['column'] == column }
|
field = procedure.dossier_columns.find { |h| h.column == column }
|
||||||
if field['type'] == :date
|
if field.type == :date
|
||||||
dates = values
|
dates = values
|
||||||
.filter_map { |v| Time.zone.parse(v).beginning_of_day rescue nil }
|
.filter_map { |v| Time.zone.parse(v).beginning_of_day rescue nil }
|
||||||
|
|
||||||
dossiers.filter_by_datetimes(column, dates)
|
dossiers.filter_by_datetimes(column, dates)
|
||||||
elsif field['column'] == "state" && values.include?("pending_correction")
|
elsif field.column == "state" && values.include?("pending_correction")
|
||||||
dossiers.joins(:corrections).where(corrections: DossierCorrection.pending)
|
dossiers.joins(:corrections).where(corrections: DossierCorrection.pending)
|
||||||
elsif field['column'] == "state" && values.include?("en_construction")
|
elsif field.column == "state" && values.include?("en_construction")
|
||||||
dossiers.where("dossiers.#{column} IN (?)", values).includes(:corrections).where.not(corrections: DossierCorrection.pending)
|
dossiers.where("dossiers.#{column} IN (?)", values).includes(:corrections).where.not(corrections: DossierCorrection.pending)
|
||||||
else
|
else
|
||||||
dossiers.where("dossiers.#{column} IN (?)", values)
|
dossiers.where("dossiers.#{column} IN (?)", values)
|
||||||
|
@ -217,9 +214,6 @@ class ProcedurePresentation < ApplicationRecord
|
||||||
when TYPE_DE_CHAMP
|
when TYPE_DE_CHAMP
|
||||||
dossiers.with_type_de_champ(column)
|
dossiers.with_type_de_champ(column)
|
||||||
.filter_ilike(:champs, value_column, values)
|
.filter_ilike(:champs, value_column, values)
|
||||||
when TYPE_DE_CHAMP_PRIVATE
|
|
||||||
dossiers.with_type_de_champ(column)
|
|
||||||
.filter_ilike(:champs, value_column, values)
|
|
||||||
when 'etablissement'
|
when 'etablissement'
|
||||||
if column == 'entreprise_date_creation'
|
if column == 'entreprise_date_creation'
|
||||||
dates = values
|
dates = values
|
||||||
|
@ -258,161 +252,9 @@ class ProcedurePresentation < ApplicationRecord
|
||||||
end.reduce(:&)
|
end.reduce(:&)
|
||||||
end
|
end
|
||||||
|
|
||||||
def filtered_sorted_ids(dossiers, statut, count: nil)
|
# type_de_champ/4373429
|
||||||
dossiers_by_statut = dossiers.by_statut(statut, instructeur)
|
def sort_to_column_id(sort)
|
||||||
dossiers_sorted_ids = self.sorted_ids(dossiers_by_statut, count || dossiers_by_statut.size)
|
[sort[TABLE], sort[COLUMN]].join(SLASH)
|
||||||
|
|
||||||
if filters[statut].present?
|
|
||||||
dossiers_sorted_ids.intersection(filtered_ids(dossiers_by_statut, statut))
|
|
||||||
else
|
|
||||||
dossiers_sorted_ids
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def human_value_for_filter(filter)
|
|
||||||
if [TYPE_DE_CHAMP, TYPE_DE_CHAMP_PRIVATE].include?(filter[TABLE])
|
|
||||||
find_type_de_champ(filter[COLUMN]).dynamic_type.filter_to_human(filter['value'])
|
|
||||||
elsif filter['column'] == 'state'
|
|
||||||
if filter['value'] == 'pending_correction'
|
|
||||||
Dossier.human_attribute_name("pending_correction.for_instructeur")
|
|
||||||
else
|
|
||||||
Dossier.human_attribute_name("state.#{filter['value']}")
|
|
||||||
end
|
|
||||||
elsif filter['table'] == 'groupe_instructeur' && filter['column'] == 'id'
|
|
||||||
instructeur.groupe_instructeurs
|
|
||||||
.find { _1.id == filter['value'].to_i }&.label || filter['value']
|
|
||||||
else
|
|
||||||
field = find_field(filter[TABLE], filter[COLUMN])
|
|
||||||
|
|
||||||
if field["type"] == :date
|
|
||||||
parsed_date = safe_parse_date(filter['value'])
|
|
||||||
|
|
||||||
return parsed_date.present? ? I18n.l(parsed_date) : nil
|
|
||||||
end
|
|
||||||
|
|
||||||
filter['value']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def safe_parse_date(string)
|
|
||||||
Date.parse(string)
|
|
||||||
rescue Date::Error
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_filter(statut, field, value)
|
|
||||||
if value.present?
|
|
||||||
table, column = field.split(SLASH)
|
|
||||||
label, value_column = find_field(table, column).values_at('label', 'value_column')
|
|
||||||
|
|
||||||
case table
|
|
||||||
when TYPE_DE_CHAMP, TYPE_DE_CHAMP_PRIVATE
|
|
||||||
value = find_type_de_champ(column).dynamic_type.human_to_filter(value)
|
|
||||||
end
|
|
||||||
|
|
||||||
updated_filters = filters.dup
|
|
||||||
updated_filters[statut] << {
|
|
||||||
'label' => label,
|
|
||||||
TABLE => table,
|
|
||||||
COLUMN => column,
|
|
||||||
'value_column' => value_column,
|
|
||||||
'value' => value
|
|
||||||
}
|
|
||||||
|
|
||||||
update(filters: updated_filters)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def remove_filter(statut, field, value)
|
|
||||||
table, column = field.split(SLASH)
|
|
||||||
|
|
||||||
updated_filters = filters.dup
|
|
||||||
updated_filters[statut] = filters[statut].reject do |filter|
|
|
||||||
filter.values_at(TABLE, COLUMN, 'value') == [table, column, value]
|
|
||||||
end
|
|
||||||
|
|
||||||
update!(filters: updated_filters)
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_displayed_fields(values)
|
|
||||||
if values.nil?
|
|
||||||
values = []
|
|
||||||
end
|
|
||||||
|
|
||||||
fields = values.map { |value| find_field(*value.split(SLASH)) }
|
|
||||||
|
|
||||||
update!(displayed_fields: fields)
|
|
||||||
|
|
||||||
if !values.include?(field_id(sort))
|
|
||||||
update!(sort: Procedure.default_sort)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_sort(table, column, order)
|
|
||||||
update!(sort: {
|
|
||||||
TABLE => table,
|
|
||||||
COLUMN => column,
|
|
||||||
ORDER => order.presence || opposite_order_for(table, column)
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
def opposite_order_for(table, column)
|
|
||||||
if sort.values_at(TABLE, COLUMN) == [table, column]
|
|
||||||
sort['order'] == 'asc' ? 'desc' : 'asc'
|
|
||||||
elsif [table, column] == ["notifications", "notifications"]
|
|
||||||
'desc' # default order for notifications
|
|
||||||
else
|
|
||||||
'asc'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def snapshot
|
|
||||||
slice(:filters, :sort, :displayed_fields)
|
|
||||||
end
|
|
||||||
|
|
||||||
def field_type(field_id)
|
|
||||||
find_field(*field_id.split(SLASH))['type']
|
|
||||||
end
|
|
||||||
|
|
||||||
def field_enum(field_id)
|
|
||||||
field = find_field(*field_id.split(SLASH))
|
|
||||||
if field['scope'].present?
|
|
||||||
I18n.t(field['scope']).map(&:to_a).map(&:reverse)
|
|
||||||
elsif field['table'] == 'groupe_instructeur'
|
|
||||||
instructeur.groupe_instructeurs.filter_map do
|
|
||||||
if _1.procedure_id == procedure.id
|
|
||||||
[_1.label, _1.id]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
find_type_de_champ(field['column']).options_for_select
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def sortable?(field)
|
|
||||||
sort['table'] == field['table'] && sort['column'] == field['column']
|
|
||||||
end
|
|
||||||
|
|
||||||
def aria_sort(order, field)
|
|
||||||
if sortable?(field)
|
|
||||||
if order == 'asc'
|
|
||||||
{ "aria-sort": "ascending" }
|
|
||||||
elsif order == 'desc'
|
|
||||||
{ "aria-sort": "descending" }
|
|
||||||
end
|
|
||||||
else
|
|
||||||
{}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def field_id(field)
|
|
||||||
field.values_at(TABLE, COLUMN).join(SLASH)
|
|
||||||
end
|
|
||||||
|
|
||||||
def find_field(table, column)
|
|
||||||
fields.find { |field| field.values_at(TABLE, COLUMN) == [table, column] }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_type_de_champ(column)
|
def find_type_de_champ(column)
|
||||||
|
@ -465,43 +307,15 @@ class ProcedurePresentation < ApplicationRecord
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def field_hash(table, column, label: nil, classname: '', virtual: false, type: :text, scope: '', value_column: :value, filterable: true)
|
|
||||||
{
|
|
||||||
'label' => label || I18n.t(column, scope: [:activerecord, :attributes, :procedure_presentation, :fields, table]),
|
|
||||||
TABLE => table,
|
|
||||||
COLUMN => column,
|
|
||||||
'classname' => classname,
|
|
||||||
'virtual' => virtual,
|
|
||||||
'type' => type,
|
|
||||||
'scope' => scope,
|
|
||||||
'value_column' => value_column,
|
|
||||||
'filterable' => filterable
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def field_hash_for_type_de_champ_public(type_champ, libelle, stable_id)
|
|
||||||
field_hash(TYPE_DE_CHAMP, stable_id.to_s,
|
|
||||||
label: libelle,
|
|
||||||
type: TypeDeChamp.filter_hash_type(type_champ),
|
|
||||||
value_column: TypeDeChamp.filter_hash_value_column(type_champ))
|
|
||||||
end
|
|
||||||
|
|
||||||
def field_hash_for_type_de_champ_private(type_champ, libelle, stable_id)
|
|
||||||
field_hash(TYPE_DE_CHAMP_PRIVATE, stable_id.to_s,
|
|
||||||
label: libelle,
|
|
||||||
type: TypeDeChamp.filter_hash_type(type_champ),
|
|
||||||
value_column: TypeDeChamp.filter_hash_value_column(type_champ))
|
|
||||||
end
|
|
||||||
|
|
||||||
def valid_column?(table, column, extra_columns = {})
|
def valid_column?(table, column, extra_columns = {})
|
||||||
valid_columns_for_table(table).include?(column) ||
|
valid_columns_for_table(table).include?(column) ||
|
||||||
extra_columns[table]&.include?(column)
|
extra_columns[table]&.include?(column)
|
||||||
end
|
end
|
||||||
|
|
||||||
def valid_columns_for_table(table)
|
def valid_columns_for_table(table)
|
||||||
@column_whitelist ||= fields
|
@column_whitelist ||= procedure.columns
|
||||||
.group_by { |field| field[TABLE] }
|
.group_by(&:table)
|
||||||
.transform_values { |fields| Set.new(fields.pluck(COLUMN)) }
|
.transform_values { |columns| Set.new(columns.map(&:column)) }
|
||||||
|
|
||||||
@column_whitelist[table] || []
|
@column_whitelist[table] || []
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
class TypesDeChamp::TypeDeChampBase
|
class TypesDeChamp::TypeDeChampBase
|
||||||
include ActiveModel::Validations
|
include ActiveModel::Validations
|
||||||
|
|
||||||
delegate :description, :libelle, :mandatory, :mandatory?, :stable_id, :fillable?, :public?, to: :@type_de_champ
|
delegate :description, :libelle, :mandatory, :mandatory?, :stable_id, :fillable?, :public?, :type_champ, to: :@type_de_champ
|
||||||
|
|
||||||
FILL_DURATION_SHORT = 10.seconds
|
FILL_DURATION_SHORT = 10.seconds
|
||||||
FILL_DURATION_MEDIUM = 1.minute
|
FILL_DURATION_MEDIUM = 1.minute
|
||||||
|
@ -96,6 +96,18 @@ class TypesDeChamp::TypeDeChampBase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def columns(table:)
|
||||||
|
[
|
||||||
|
Column.new(
|
||||||
|
table:,
|
||||||
|
column: stable_id.to_s,
|
||||||
|
label: libelle,
|
||||||
|
type: TypeDeChamp.filter_hash_type(type_champ),
|
||||||
|
value_column: TypeDeChamp.filter_hash_value_column(type_champ)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def paths
|
def paths
|
||||||
|
|
|
@ -39,6 +39,7 @@ class DossierProjectionService
|
||||||
# - the order of the intermediary query results are unknown
|
# - the order of the intermediary query results are unknown
|
||||||
# - some values can be missing (if a revision added or removed them)
|
# - some values can be missing (if a revision added or removed them)
|
||||||
def self.project(dossiers_ids, fields)
|
def self.project(dossiers_ids, fields)
|
||||||
|
fields = fields.deep_dup
|
||||||
state_field = { TABLE => 'self', COLUMN => 'state' }
|
state_field = { TABLE => 'self', COLUMN => 'state' }
|
||||||
archived_field = { TABLE => 'self', COLUMN => 'archived' }
|
archived_field = { TABLE => 'self', COLUMN => 'archived' }
|
||||||
batch_operation_field = { TABLE => 'self', COLUMN => 'batch_operation_id' }
|
batch_operation_field = { TABLE => 'self', COLUMN => 'batch_operation_id' }
|
||||||
|
|
|
@ -3,4 +3,4 @@
|
||||||
= t('views.instructeurs.dossiers.filters.title')
|
= t('views.instructeurs.dossiers.filters.title')
|
||||||
|
|
||||||
- menu.with_form do
|
- menu.with_form do
|
||||||
= render Dossiers::InstructeurFilterComponent.new(procedure: procedure, procedure_presentation: @procedure_presentation, statut: statut)
|
= render Instructeurs::ColumnFilterComponent.new(procedure:, procedure_presentation: @procedure_presentation, statut:)
|
||||||
|
|
|
@ -6,6 +6,6 @@
|
||||||
- filters.each_with_index do |filter, i|
|
- filters.each_with_index do |filter, i|
|
||||||
- if i > 0
|
- if i > 0
|
||||||
= " ou "
|
= " ou "
|
||||||
= link_to remove_filter_instructeur_procedure_path(procedure, { statut: statut, field: "#{filter['table']}/#{filter['column']}", value: filter['value'] }),
|
= link_to remove_filter_instructeur_procedure_path(procedure, { statut: statut, column: "#{filter['table']}/#{filter['column']}", value: filter['value'] }),
|
||||||
class: "fr-tag fr-tag--dismiss fr-my-1w", aria: { label: "Retirer le filtre #{filter['column']}" } do
|
class: "fr-tag fr-tag--dismiss fr-my-1w", aria: { label: "Retirer le filtre #{filter['column']}" } do
|
||||||
= "#{filter['label'].truncate(50)} : #{procedure_presentation.human_value_for_filter(filter)}"
|
= "#{filter['label'].truncate(50)} : #{procedure_presentation.human_value_for_filter(filter)}"
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
%th{ @procedure_presentation.aria_sort(@procedure_presentation.sort['order'], field), scope: "col", class: classname }
|
|
||||||
|
|
||||||
= link_to update_sort_instructeur_procedure_path(@procedure, table: field['table'], column: field['column'], order: @procedure_presentation.opposite_order_for(field['table'], field['column'])) do
|
|
||||||
- if @procedure_presentation.sortable?(field)
|
|
||||||
- if @procedure_presentation.sort['order'] == 'asc'
|
|
||||||
#{field['label']} ↑
|
|
||||||
- else
|
|
||||||
#{field['label']} ↓
|
|
||||||
- else
|
|
||||||
#{field['label']}
|
|
|
@ -98,8 +98,8 @@
|
||||||
%th.text-center
|
%th.text-center
|
||||||
%input{ type: "checkbox", disabled: @disable_checkbox_all, checked: @disable_checkbox_all, data: { action: "batch-operation#onCheckAll" }, id: dom_id(BatchOperation.new, :checkbox_all), aria: { label: t('views.instructeurs.dossiers.select_all') } }
|
%input{ type: "checkbox", disabled: @disable_checkbox_all, checked: @disable_checkbox_all, data: { action: "batch-operation#onCheckAll" }, id: dom_id(BatchOperation.new, :checkbox_all), aria: { label: t('views.instructeurs.dossiers.select_all') } }
|
||||||
|
|
||||||
- @procedure_presentation.displayed_fields_for_headers.each do |field|
|
- @procedure_presentation.displayed_fields_for_headers.each do |column|
|
||||||
= render partial: "header_field", locals: { field: field, classname: field['classname'] }
|
= render Instructeurs::ColumnTableHeaderComponent.new(procedure_presentation: @procedure_presentation, column:)
|
||||||
|
|
||||||
%th.follow-col
|
%th.follow-col
|
||||||
Actions
|
Actions
|
||||||
|
@ -109,12 +109,7 @@
|
||||||
- menu.with_button_inner_html do
|
- menu.with_button_inner_html do
|
||||||
= t('views.instructeurs.dossiers.personalize')
|
= t('views.instructeurs.dossiers.personalize')
|
||||||
- menu.with_form do
|
- menu.with_form do
|
||||||
= form_tag update_displayed_fields_instructeur_procedure_path(@procedure), method: :patch, class: 'dropdown-form large columns-form' do
|
= render Instructeurs::ColumnPickerComponent.new(procedure: @procedure, procedure_presentation: @procedure_presentation)
|
||||||
%react-fragment
|
|
||||||
= render ReactComponent.new "ComboBox/MultiComboBox", items: @displayable_fields_for_select, selected_keys: @displayable_fields_selected, name: 'values[]', 'aria-label': 'Colonne à afficher'
|
|
||||||
|
|
||||||
= submit_tag t('views.instructeurs.dossiers.save'), class: 'fr-btn fr-btn--secondary'
|
|
||||||
|
|
||||||
|
|
||||||
%tbody
|
%tbody
|
||||||
= render Dossiers::BatchSelectMoreComponent.new(dossiers_count: @dossiers_count, filtered_sorted_ids: @filtered_sorted_ids)
|
= render Dossiers::BatchSelectMoreComponent.new(dossiers_count: @dossiers_count, filtered_sorted_ids: @filtered_sorted_ids)
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
= turbo_stream.replace 'filter-component' do
|
= turbo_stream.replace 'filter-component' do
|
||||||
= render Dossiers::InstructeurFilterComponent.new(procedure: @procedure, procedure_presentation: @procedure_presentation, statut: @statut, field_id: @field)
|
= render Instructeurs::ColumnFilterComponent.new(procedure: @procedure, procedure_presentation: @procedure_presentation, statut: @statut, column: @column)
|
||||||
|
|
|
@ -414,7 +414,6 @@ en:
|
||||||
repasser_en_construction: Revert to submitted
|
repasser_en_construction: Revert to submitted
|
||||||
show_deleted_dossiers: Show deleted files
|
show_deleted_dossiers: Show deleted files
|
||||||
follow_file: Follow-up the file
|
follow_file: Follow-up the file
|
||||||
save: Save
|
|
||||||
stop_follow: No longer follow
|
stop_follow: No longer follow
|
||||||
no_file: No file
|
no_file: No file
|
||||||
dossier_synthesis: Summary of files
|
dossier_synthesis: Summary of files
|
||||||
|
|
|
@ -419,7 +419,6 @@ fr:
|
||||||
repasser_en_construction: Repasser en construction
|
repasser_en_construction: Repasser en construction
|
||||||
follow_file: Suivre le dossier
|
follow_file: Suivre le dossier
|
||||||
stop_follow: Ne plus suivre
|
stop_follow: Ne plus suivre
|
||||||
save: Enregistrer
|
|
||||||
no_file: Aucun dossier
|
no_file: Aucun dossier
|
||||||
dossier_synthesis: Synthèse des dossiers
|
dossier_synthesis: Synthèse des dossiers
|
||||||
passer_en_instruction_blocked_by_pending_correction: |
|
passer_en_instruction_blocked_by_pending_correction: |
|
||||||
|
|
|
@ -473,7 +473,7 @@ Rails.application.routes.draw do
|
||||||
end
|
end
|
||||||
|
|
||||||
patch 'update_displayed_fields'
|
patch 'update_displayed_fields'
|
||||||
get 'update_sort/:table/:column' => 'procedures#update_sort', as: 'update_sort'
|
get 'update_sort/:column_id' => 'procedures#update_sort', as: 'update_sort'
|
||||||
post 'add_filter'
|
post 'add_filter'
|
||||||
post 'update_filter'
|
post 'update_filter'
|
||||||
get 'remove_filter'
|
get 'remove_filter'
|
||||||
|
|
53
spec/components/instructeurs/column_filter_component_spec.rb
Normal file
53
spec/components/instructeurs/column_filter_component_spec.rb
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
describe Instructeurs::ColumnFilterComponent, type: :component do
|
||||||
|
let(:component) { described_class.new(procedure:, procedure_presentation:, statut:, column:) }
|
||||||
|
|
||||||
|
let(:instructeur) { create(:instructeur) }
|
||||||
|
let(:procedure) { create(:procedure, instructeurs: [instructeur]) }
|
||||||
|
let(:procedure_presentation) { nil }
|
||||||
|
let(:statut) { nil }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(component).to receive(:current_instructeur).and_return(instructeur)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe ".filterable_columns_options" do
|
||||||
|
context 'filders' do
|
||||||
|
let(:column) { nil }
|
||||||
|
let(:included_displayable_field) do
|
||||||
|
[
|
||||||
|
Column.new(label: 'email', table: 'user', column: 'email'),
|
||||||
|
Column.new(label: "depose_since", table: "self", column: "depose_since", virtual: true)
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
before { allow(procedure).to receive(:columns).and_return(included_displayable_field) }
|
||||||
|
|
||||||
|
subject { component.filterable_columns_options }
|
||||||
|
|
||||||
|
it { is_expected.to eq([["email", "user/email"], ["depose_since", "self/depose_since"]]) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '.options_for_select_of_column' do
|
||||||
|
subject { component.options_for_select_of_column }
|
||||||
|
|
||||||
|
context "column is groupe_instructeur" do
|
||||||
|
let(:column) { double("Column", scope: nil, table: 'groupe_instructeur') }
|
||||||
|
let!(:gi_2) { instructeur.groupe_instructeurs.create(label: 'gi2', procedure:) }
|
||||||
|
let!(:gi_3) { instructeur.groupe_instructeurs.create(label: 'gi3', procedure: create(:procedure)) }
|
||||||
|
|
||||||
|
it { is_expected.to eq([['défaut', procedure.defaut_groupe_instructeur.id], ['gi2', gi_2.id]]) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when column is dropdown' do
|
||||||
|
let(:types_de_champ_public) { [{ type: :drop_down_list, libelle: 'Votre ville', options: ['Paris', 'Lyon', 'Marseille'] }] }
|
||||||
|
let(:procedure) { create(:procedure, :published, types_de_champ_public:) }
|
||||||
|
let(:drop_down_stable_id) { procedure.active_revision.types_de_champ.first.stable_id }
|
||||||
|
let(:column) { Column.new(table: 'type_de_champ', scope: nil, column: drop_down_stable_id) }
|
||||||
|
|
||||||
|
it 'find most recent tdc' do
|
||||||
|
is_expected.to eq(['Paris', 'Lyon', 'Marseille'])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
24
spec/components/instructeurs/column_picker_component_spec.rb
Normal file
24
spec/components/instructeurs/column_picker_component_spec.rb
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
describe Instructeurs::ColumnPickerComponent, type: :component do
|
||||||
|
let(:component) { described_class.new(procedure:, procedure_presentation:) }
|
||||||
|
|
||||||
|
let(:procedure) { create(:procedure) }
|
||||||
|
let(:instructeur) { create(:instructeur) }
|
||||||
|
let(:assign_to) { create(:assign_to, procedure: procedure, instructeur: instructeur) }
|
||||||
|
let(:procedure_presentation) { create(:procedure_presentation, assign_to: assign_to) }
|
||||||
|
|
||||||
|
describe "#displayable_columns_for_select" do
|
||||||
|
let(:default_user_email) { Column.new(label: 'email', table: 'user', column: 'email') }
|
||||||
|
let(:excluded_displayable_field) { Column.new(label: "label1", table: "table1", column: "column1", virtual: true) }
|
||||||
|
|
||||||
|
subject { component.displayable_columns_for_select }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(procedure).to receive(:columns).and_return([
|
||||||
|
default_user_email,
|
||||||
|
excluded_displayable_field
|
||||||
|
])
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to eq([[["email", "user/email"]], ["user/email"]]) }
|
||||||
|
end
|
||||||
|
end
|
|
@ -872,6 +872,24 @@ describe Instructeurs::ProceduresController, type: :controller do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#update_filter' do
|
||||||
|
let(:instructeur) { create(:instructeur) }
|
||||||
|
let(:procedure) { create(:procedure, :for_individual) }
|
||||||
|
def procedure_presentation = instructeur.assign_to.first.procedure_presentation_or_default_and_errors.first
|
||||||
|
|
||||||
|
before do
|
||||||
|
create(:assign_to, instructeur:, groupe_instructeur: build(:groupe_instructeur, procedure:))
|
||||||
|
|
||||||
|
sign_in(instructeur.user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'can change order' do
|
||||||
|
expect { get :update_sort, params: { procedure_id: procedure.id, column_id: "individual/nom", order: 'asc' } }
|
||||||
|
.to change { procedure_presentation.sort }
|
||||||
|
.from({ "column" => "notifications", "order" => "desc", "table" => "notifications" })
|
||||||
|
.to({ "column" => "nom", "order" => "asc", "table" => "individual" })
|
||||||
|
end
|
||||||
|
end
|
||||||
describe '#add_filter' do
|
describe '#add_filter' do
|
||||||
let(:instructeur) { create(:instructeur) }
|
let(:instructeur) { create(:instructeur) }
|
||||||
let(:procedure) { create(:procedure, :for_individual) }
|
let(:procedure) { create(:procedure, :for_individual) }
|
||||||
|
@ -883,7 +901,7 @@ describe Instructeurs::ProceduresController, type: :controller do
|
||||||
end
|
end
|
||||||
|
|
||||||
subject do
|
subject do
|
||||||
post :add_filter, params: { procedure_id: procedure.id, field: "individual/nom", value: "n" * 110, statut: "a-suivre" }
|
post :add_filter, params: { procedure_id: procedure.id, column: "individual/nom", value: "n" * 110, statut: "a-suivre" }
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should render the error' do
|
it 'should render the error' do
|
||||||
|
|
101
spec/models/concerns/columns_concern_spec.rb
Normal file
101
spec/models/concerns/columns_concern_spec.rb
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
describe ColumnsConcern do
|
||||||
|
describe "#columns" do
|
||||||
|
subject { procedure.columns }
|
||||||
|
|
||||||
|
context 'when the procedure can have a SIRET number' do
|
||||||
|
let(:procedure) do
|
||||||
|
create(:procedure,
|
||||||
|
types_de_champ_public: Array.new(4) { { type: :text } },
|
||||||
|
types_de_champ_private: Array.new(4) { { type: :text } })
|
||||||
|
end
|
||||||
|
let(:tdc_1) { procedure.active_revision.types_de_champ_public[0] }
|
||||||
|
let(:tdc_2) { procedure.active_revision.types_de_champ_public[1] }
|
||||||
|
let(:tdc_private_1) { procedure.active_revision.types_de_champ_private[0] }
|
||||||
|
let(:tdc_private_2) { procedure.active_revision.types_de_champ_private[1] }
|
||||||
|
let(:expected) {
|
||||||
|
[
|
||||||
|
{ label: 'Nº dossier', table: 'self', column: 'id', classname: 'number-col', virtual: false, type: :text, scope: '', value_column: :value, filterable: true },
|
||||||
|
{ label: 'notifications', table: 'notifications', column: 'notifications', virtual: false, type: :text, scope: '', value_column: :value, filterable: false },
|
||||||
|
{ label: 'Créé le', table: 'self', column: 'created_at', classname: '', virtual: false, type: :date, scope: '', value_column: :value, filterable: true },
|
||||||
|
{ label: 'Mis à jour le', table: 'self', column: 'updated_at', classname: '', virtual: false, type: :date, scope: '', value_column: :value, filterable: true },
|
||||||
|
{ label: 'Déposé le', table: 'self', column: 'depose_at', classname: '', virtual: false, type: :date, scope: '', value_column: :value, filterable: true },
|
||||||
|
{ label: 'En construction le', table: 'self', column: 'en_construction_at', classname: '', virtual: false, type: :date, scope: '', value_column: :value, filterable: true },
|
||||||
|
{ label: 'En instruction le', table: 'self', column: 'en_instruction_at', classname: '', virtual: false, type: :date, scope: '', value_column: :value, filterable: true },
|
||||||
|
{ label: 'Terminé le', table: 'self', column: 'processed_at', classname: '', virtual: false, type: :date, scope: '', value_column: :value, filterable: true },
|
||||||
|
{ label: "Mis à jour depuis", table: "self", column: "updated_since", classname: "", virtual: true, type: :date, scope: '', value_column: :value, filterable: true },
|
||||||
|
{ label: "Déposé depuis", table: "self", column: "depose_since", classname: "", virtual: true, type: :date, scope: '', value_column: :value, filterable: true },
|
||||||
|
{ label: "En construction depuis", table: "self", column: "en_construction_since", classname: "", virtual: true, type: :date, scope: '', value_column: :value, filterable: true },
|
||||||
|
{ label: "En instruction depuis", table: "self", column: "en_instruction_since", classname: "", virtual: true, type: :date, scope: '', value_column: :value, filterable: true },
|
||||||
|
{ label: "Terminé depuis", table: "self", column: "processed_since", classname: "", virtual: true, type: :date, scope: '', value_column: :value, filterable: true },
|
||||||
|
{ label: "Statut", table: "self", column: "state", classname: "", virtual: true, scope: 'instructeurs.dossiers.filterable_state', type: :enum, value_column: :value, filterable: true },
|
||||||
|
{ label: 'Demandeur', table: 'user', column: 'email', classname: '', virtual: false, type: :text, scope: '', value_column: :value, filterable: true },
|
||||||
|
{ label: 'Email instructeur', table: 'followers_instructeurs', column: 'email', classname: '', virtual: false, type: :text, scope: '', value_column: :value, filterable: true },
|
||||||
|
{ label: 'Groupe instructeur', table: 'groupe_instructeur', column: 'id', classname: '', virtual: false, type: :enum, scope: '', value_column: :value, filterable: true },
|
||||||
|
{ label: 'Avis oui/non', table: 'avis', column: 'question_answer', classname: '', virtual: false, type: :text, scope: '', value_column: :value, filterable: false },
|
||||||
|
{ label: 'SIREN', table: 'etablissement', column: 'entreprise_siren', classname: '', virtual: false, type: :text, scope: '', value_column: :value, filterable: true },
|
||||||
|
{ label: 'Forme juridique', table: 'etablissement', column: 'entreprise_forme_juridique', classname: '', virtual: false, type: :text, scope: '', value_column: :value, filterable: true },
|
||||||
|
{ label: 'Nom commercial', table: 'etablissement', column: 'entreprise_nom_commercial', classname: '', virtual: false, type: :text, scope: '', value_column: :value, filterable: true },
|
||||||
|
{ label: 'Raison sociale', table: 'etablissement', column: 'entreprise_raison_sociale', classname: '', virtual: false, type: :text, scope: '', value_column: :value, filterable: true },
|
||||||
|
{ label: 'SIRET siège social', table: 'etablissement', column: 'entreprise_siret_siege_social', classname: '', virtual: false, type: :text, scope: '', value_column: :value, filterable: true },
|
||||||
|
{ label: 'Date de création', table: 'etablissement', column: 'entreprise_date_creation', classname: '', virtual: false, type: :date, scope: '', value_column: :value, filterable: true },
|
||||||
|
{ label: 'SIRET', table: 'etablissement', column: 'siret', classname: '', virtual: false, type: :text, scope: '', value_column: :value, filterable: true },
|
||||||
|
{ label: 'Libellé NAF', table: 'etablissement', column: 'libelle_naf', classname: '', virtual: false, type: :text, scope: '', value_column: :value, filterable: true },
|
||||||
|
{ label: 'Code postal', table: 'etablissement', column: 'code_postal', classname: '', virtual: false, type: :text, scope: '', value_column: :value, filterable: true },
|
||||||
|
{ label: tdc_1.libelle, table: 'type_de_champ', column: tdc_1.stable_id.to_s, classname: '', virtual: false, type: :text, scope: '', value_column: :value, filterable: true },
|
||||||
|
{ label: tdc_2.libelle, table: 'type_de_champ', column: tdc_2.stable_id.to_s, classname: '', virtual: false, type: :text, scope: '', value_column: :value, filterable: true },
|
||||||
|
{ label: tdc_private_1.libelle, table: 'type_de_champ', column: tdc_private_1.stable_id.to_s, classname: '', virtual: false, type: :text, scope: '', value_column: :value, filterable: true },
|
||||||
|
{ label: tdc_private_2.libelle, table: 'type_de_champ', column: tdc_private_2.stable_id.to_s, classname: '', virtual: false, type: :text, scope: '', value_column: :value, filterable: true }
|
||||||
|
].map { Column.new(**_1) }
|
||||||
|
}
|
||||||
|
|
||||||
|
context 'with explication/header_sections' do
|
||||||
|
let(:types_de_champ_public) { Array.new(4) { { type: :text } } }
|
||||||
|
let(:types_de_champ_private) { Array.new(4) { { type: :text } } }
|
||||||
|
before do
|
||||||
|
procedure.active_revision.types_de_champ_public[2].update_attribute(:type_champ, TypeDeChamp.type_champs.fetch(:header_section))
|
||||||
|
procedure.active_revision.types_de_champ_public[3].update_attribute(:type_champ, TypeDeChamp.type_champs.fetch(:explication))
|
||||||
|
procedure.active_revision.types_de_champ_private[2].update_attribute(:type_champ, TypeDeChamp.type_champs.fetch(:header_section))
|
||||||
|
procedure.active_revision.types_de_champ_private[3].update_attribute(:type_champ, TypeDeChamp.type_champs.fetch(:explication))
|
||||||
|
end
|
||||||
|
|
||||||
|
it { expect(subject).to eq(expected) }
|
||||||
|
end
|
||||||
|
|
||||||
|
xcontext 'with rna' do
|
||||||
|
let(:types_de_champ_public) { [{ type: :rna, libelle: 'rna' }] }
|
||||||
|
let(:types_de_champ_private) { [] }
|
||||||
|
xit { expect(subject.map(&:label)).to include('rna – commune') }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the procedure is for individuals' do
|
||||||
|
let(:name_field) { Column.new(label: "Prénom", table: "individual", column: "prenom", classname: '', virtual: false, type: :text, scope: '', value_column: :value, filterable: true) }
|
||||||
|
let(:surname_field) { Column.new(label: "Nom", table: "individual", column: "nom", classname: '', virtual: false, type: :text, scope: '', value_column: :value, filterable: true) }
|
||||||
|
let(:gender_field) { Column.new(label: "Civilité", table: "individual", column: "gender", classname: '', virtual: false, type: :text, scope: '', value_column: :value, filterable: true) }
|
||||||
|
let(:procedure) { create(:procedure, :for_individual) }
|
||||||
|
let(:procedure_presentation) { create(:procedure_presentation, assign_to: assign_to) }
|
||||||
|
|
||||||
|
it { is_expected.to include(name_field, surname_field, gender_field) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the procedure is sva' do
|
||||||
|
let(:procedure) { create(:procedure, :for_individual, :sva) }
|
||||||
|
let(:procedure_presentation) { create(:procedure_presentation, assign_to: assign_to) }
|
||||||
|
|
||||||
|
let(:decision_on) { Column.new(label: "Date décision SVA", table: "self", column: "sva_svr_decision_on", classname: '', virtual: false, type: :date, scope: '', value_column: :value, filterable: true) }
|
||||||
|
let(:decision_before_field) { Column.new(label: "Date décision SVA avant", table: "self", column: "sva_svr_decision_before", classname: '', virtual: true, type: :date, scope: '', value_column: :value, filterable: true) }
|
||||||
|
|
||||||
|
it { is_expected.to include(decision_on, decision_before_field) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the procedure is svr' do
|
||||||
|
let(:procedure) { create(:procedure, :for_individual, :svr) }
|
||||||
|
let(:procedure_presentation) { create(:procedure_presentation, assign_to: assign_to) }
|
||||||
|
|
||||||
|
let(:decision_on) { Column.new(label: "Date décision SVR", table: "self", column: "sva_svr_decision_on", classname: '', virtual: false, type: :date, scope: '', value_column: :value, filterable: true) }
|
||||||
|
let(:decision_before_field) { Column.new(label: "Date décision SVR avant", table: "self", column: "sva_svr_decision_before", classname: '', virtual: true, type: :date, scope: '', value_column: :value, filterable: true) }
|
||||||
|
|
||||||
|
it { is_expected.to include(decision_on, decision_before_field) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -10,10 +10,10 @@ describe ProcedurePresentation do
|
||||||
create(:procedure_presentation,
|
create(:procedure_presentation,
|
||||||
assign_to: assign_to,
|
assign_to: assign_to,
|
||||||
displayed_fields: [
|
displayed_fields: [
|
||||||
{ "label" => "test1", "table" => "user", "column" => "email" },
|
{ label: "test1", table: "user", column: "email" },
|
||||||
{ "label" => "test2", "table" => "type_de_champ", "column" => first_type_de_champ_id }
|
{ label: "test2", table: "type_de_champ", column: first_type_de_champ_id }
|
||||||
],
|
],
|
||||||
sort: { "table" => "user", "column" => "email", "order" => "asc" },
|
sort: { table: "user", column: "email", "order" => "asc" },
|
||||||
filters: filters)
|
filters: filters)
|
||||||
}
|
}
|
||||||
let(:procedure_presentation_id) { procedure_presentation.id }
|
let(:procedure_presentation_id) { procedure_presentation.id }
|
||||||
|
@ -35,153 +35,29 @@ describe ProcedurePresentation do
|
||||||
it { expect(build(:procedure_presentation)).to be_valid }
|
it { expect(build(:procedure_presentation)).to be_valid }
|
||||||
|
|
||||||
context 'of displayed fields' do
|
context 'of displayed fields' do
|
||||||
it { expect(build(:procedure_presentation, displayed_fields: [{ "table" => "user", "column" => "reset_password_token", "order" => "asc" }])).to be_invalid }
|
it { expect(build(:procedure_presentation, displayed_fields: [{ table: "user", column: "reset_password_token", "order" => "asc" }])).to be_invalid }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'of sort' do
|
context 'of sort' do
|
||||||
it { expect(build(:procedure_presentation, sort: { "table" => "notifications", "column" => "notifications", "order" => "asc" })).to be_valid }
|
it { expect(build(:procedure_presentation, sort: { table: "notifications", column: "notifications", "order" => "asc" })).to be_valid }
|
||||||
it { expect(build(:procedure_presentation, sort: { "table" => "self", "column" => "id", "order" => "asc" })).to be_valid }
|
it { expect(build(:procedure_presentation, sort: { table: "self", column: "id", "order" => "asc" })).to be_valid }
|
||||||
it { expect(build(:procedure_presentation, sort: { "table" => "self", "column" => "state", "order" => "asc" })).to be_valid }
|
it { expect(build(:procedure_presentation, sort: { table: "self", column: "state", "order" => "asc" })).to be_valid }
|
||||||
it { expect(build(:procedure_presentation, sort: { "table" => "user", "column" => "reset_password_token", "order" => "asc" })).to be_invalid }
|
it { expect(build(:procedure_presentation, sort: { table: "user", column: "reset_password_token", "order" => "asc" })).to be_invalid }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'of filters' do
|
context 'of filters' do
|
||||||
it { expect(build(:procedure_presentation, filters: { "suivis" => [{ "table" => "user", "column" => "reset_password_token", "order" => "asc" }] })).to be_invalid }
|
it { expect(build(:procedure_presentation, filters: { "suivis" => [{ table: "user", column: "reset_password_token", "order" => "asc" }] })).to be_invalid }
|
||||||
it { expect(build(:procedure_presentation, filters: { "suivis" => [{ "table" => "user", "column" => "email", "value" => "exceedingly long filter value" * 10 }] })).to be_invalid }
|
it { expect(build(:procedure_presentation, filters: { "suivis" => [{ table: "user", column: "email", "value" => "exceedingly long filter value" * 10 }] })).to be_invalid }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#fields" do
|
|
||||||
context 'when the procedure can have a SIRET number' do
|
|
||||||
let(:procedure) do
|
|
||||||
create(:procedure,
|
|
||||||
types_de_champ_public: Array.new(4) { { type: :text } },
|
|
||||||
types_de_champ_private: Array.new(4) { { type: :text } })
|
|
||||||
end
|
|
||||||
let(:tdc_1) { procedure.active_revision.types_de_champ_public[0] }
|
|
||||||
let(:tdc_2) { procedure.active_revision.types_de_champ_public[1] }
|
|
||||||
let(:tdc_private_1) { procedure.active_revision.types_de_champ_private[0] }
|
|
||||||
let(:tdc_private_2) { procedure.active_revision.types_de_champ_private[1] }
|
|
||||||
let(:expected) {
|
|
||||||
[
|
|
||||||
{ "label" => 'Créé le', "table" => 'self', "column" => 'created_at', 'classname' => '', 'virtual' => false, 'type' => :date, "scope" => '', "value_column" => :value, 'filterable' => true },
|
|
||||||
{ "label" => 'Mis à jour le', "table" => 'self', "column" => 'updated_at', 'classname' => '', 'virtual' => false, 'type' => :date, "scope" => '', "value_column" => :value, 'filterable' => true },
|
|
||||||
{ "label" => 'Déposé le', "table" => 'self', "column" => 'depose_at', 'classname' => '', 'virtual' => false, 'type' => :date, "scope" => '', "value_column" => :value, 'filterable' => true },
|
|
||||||
{ "label" => 'En construction le', "table" => 'self', "column" => 'en_construction_at', 'classname' => '', 'virtual' => false, 'type' => :date, "scope" => '', "value_column" => :value, 'filterable' => true },
|
|
||||||
{ "label" => 'En instruction le', "table" => 'self', "column" => 'en_instruction_at', 'classname' => '', 'virtual' => false, 'type' => :date, "scope" => '', "value_column" => :value, 'filterable' => true },
|
|
||||||
{ "label" => 'Terminé le', "table" => 'self', "column" => 'processed_at', 'classname' => '', 'virtual' => false, 'type' => :date, "scope" => '', "value_column" => :value, 'filterable' => true },
|
|
||||||
{ "label" => "Mis à jour depuis", "table" => "self", "column" => "updated_since", "classname" => "", 'virtual' => true, 'type' => :date, 'scope' => '', "value_column" => :value, 'filterable' => true },
|
|
||||||
{ "label" => "Déposé depuis", "table" => "self", "column" => "depose_since", "classname" => "", 'virtual' => true, 'type' => :date, 'scope' => '', "value_column" => :value, 'filterable' => true },
|
|
||||||
{ "label" => "En construction depuis", "table" => "self", "column" => "en_construction_since", "classname" => "", 'virtual' => true, 'type' => :date, 'scope' => '', "value_column" => :value, 'filterable' => true },
|
|
||||||
{ "label" => "En instruction depuis", "table" => "self", "column" => "en_instruction_since", "classname" => "", 'virtual' => true, 'type' => :date, 'scope' => '', "value_column" => :value, 'filterable' => true },
|
|
||||||
{ "label" => "Terminé depuis", "table" => "self", "column" => "processed_since", "classname" => "", 'virtual' => true, 'type' => :date, 'scope' => '', "value_column" => :value, 'filterable' => true },
|
|
||||||
{ "label" => "Statut", "table" => "self", "column" => "state", "classname" => "", 'virtual' => true, 'scope' => 'instructeurs.dossiers.filterable_state', 'type' => :enum, "value_column" => :value, 'filterable' => true },
|
|
||||||
{ "label" => 'Demandeur', "table" => 'user', "column" => 'email', 'classname' => '', 'virtual' => false, 'type' => :text, "scope" => '', "value_column" => :value, 'filterable' => true },
|
|
||||||
{ "label" => 'Email instructeur', "table" => 'followers_instructeurs', "column" => 'email', 'classname' => '', 'virtual' => false, 'type' => :text, "scope" => '', "value_column" => :value, 'filterable' => true },
|
|
||||||
{ "label" => 'Groupe instructeur', "table" => 'groupe_instructeur', "column" => 'id', 'classname' => '', 'virtual' => false, 'type' => :enum, "scope" => '', "value_column" => :value, 'filterable' => true },
|
|
||||||
{ "label" => 'Avis oui/non', "table" => 'avis', "column" => 'question_answer', 'classname' => '', 'virtual' => false, 'type' => :text, "scope" => '', "value_column" => :value, 'filterable' => false },
|
|
||||||
{ "label" => 'SIREN', "table" => 'etablissement', "column" => 'entreprise_siren', 'classname' => '', 'virtual' => false, 'type' => :text, "scope" => '', "value_column" => :value, 'filterable' => true },
|
|
||||||
{ "label" => 'Forme juridique', "table" => 'etablissement', "column" => 'entreprise_forme_juridique', 'classname' => '', 'virtual' => false, 'type' => :text, "scope" => '', "value_column" => :value, 'filterable' => true },
|
|
||||||
{ "label" => 'Nom commercial', "table" => 'etablissement', "column" => 'entreprise_nom_commercial', 'classname' => '', 'virtual' => false, 'type' => :text, "scope" => '', "value_column" => :value, 'filterable' => true },
|
|
||||||
{ "label" => 'Raison sociale', "table" => 'etablissement', "column" => 'entreprise_raison_sociale', 'classname' => '', 'virtual' => false, 'type' => :text, "scope" => '', "value_column" => :value, 'filterable' => true },
|
|
||||||
{ "label" => 'SIRET siège social', "table" => 'etablissement', "column" => 'entreprise_siret_siege_social', 'classname' => '', 'virtual' => false, 'type' => :text, "scope" => '', "value_column" => :value, 'filterable' => true },
|
|
||||||
{ "label" => 'Date de création', "table" => 'etablissement', "column" => 'entreprise_date_creation', 'classname' => '', 'virtual' => false, 'type' => :date, "scope" => '', "value_column" => :value, 'filterable' => true },
|
|
||||||
{ "label" => 'SIRET', "table" => 'etablissement', "column" => 'siret', 'classname' => '', 'virtual' => false, 'type' => :text, "scope" => '', "value_column" => :value, 'filterable' => true },
|
|
||||||
{ "label" => 'Libellé NAF', "table" => 'etablissement', "column" => 'libelle_naf', 'classname' => '', 'virtual' => false, 'type' => :text, "scope" => '', "value_column" => :value, 'filterable' => true },
|
|
||||||
{ "label" => 'Code postal', "table" => 'etablissement', "column" => 'code_postal', 'classname' => '', 'virtual' => false, 'type' => :text, "scope" => '', "value_column" => :value, 'filterable' => true },
|
|
||||||
{ "label" => tdc_1.libelle, "table" => 'type_de_champ', "column" => tdc_1.stable_id.to_s, 'classname' => '', 'virtual' => false, 'type' => :text, "scope" => '', "value_column" => :value, 'filterable' => true },
|
|
||||||
{ "label" => tdc_2.libelle, "table" => 'type_de_champ', "column" => tdc_2.stable_id.to_s, 'classname' => '', 'virtual' => false, 'type' => :text, "scope" => '', "value_column" => :value, 'filterable' => true },
|
|
||||||
{ "label" => tdc_private_1.libelle, "table" => 'type_de_champ_private', "column" => tdc_private_1.stable_id.to_s, 'classname' => '', 'virtual' => false, 'type' => :text, "scope" => '', "value_column" => :value, 'filterable' => true },
|
|
||||||
{ "label" => tdc_private_2.libelle, "table" => 'type_de_champ_private', "column" => tdc_private_2.stable_id.to_s, 'classname' => '', 'virtual' => false, 'type' => :text, "scope" => '', "value_column" => :value, 'filterable' => true }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
before do
|
|
||||||
procedure.active_revision.types_de_champ_public[2].update_attribute(:type_champ, TypeDeChamp.type_champs.fetch(:header_section))
|
|
||||||
procedure.active_revision.types_de_champ_public[3].update_attribute(:type_champ, TypeDeChamp.type_champs.fetch(:explication))
|
|
||||||
procedure.active_revision.types_de_champ_private[2].update_attribute(:type_champ, TypeDeChamp.type_champs.fetch(:header_section))
|
|
||||||
procedure.active_revision.types_de_champ_private[3].update_attribute(:type_champ, TypeDeChamp.type_champs.fetch(:explication))
|
|
||||||
end
|
|
||||||
|
|
||||||
subject { create(:procedure_presentation, assign_to: assign_to) }
|
|
||||||
|
|
||||||
it { expect(subject.fields).to eq(expected) }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when the procedure is for individuals' do
|
|
||||||
let(:name_field) { { "label" => "Prénom", "table" => "individual", "column" => "prenom", 'classname' => '', 'virtual' => false, "type" => :text, "scope" => '', "value_column" => :value, 'filterable' => true } }
|
|
||||||
let(:surname_field) { { "label" => "Nom", "table" => "individual", "column" => "nom", 'classname' => '', 'virtual' => false, "type" => :text, "scope" => '', "value_column" => :value, 'filterable' => true } }
|
|
||||||
let(:gender_field) { { "label" => "Civilité", "table" => "individual", "column" => "gender", 'classname' => '', 'virtual' => false, "type" => :text, "scope" => '', "value_column" => :value, 'filterable' => true } }
|
|
||||||
let(:procedure) { create(:procedure, :for_individual) }
|
|
||||||
let(:procedure_presentation) { create(:procedure_presentation, assign_to: assign_to) }
|
|
||||||
|
|
||||||
subject { procedure_presentation.fields }
|
|
||||||
|
|
||||||
it { is_expected.to include(name_field, surname_field, gender_field) }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when the procedure is sva' do
|
|
||||||
let(:procedure) { create(:procedure, :for_individual, :sva) }
|
|
||||||
let(:procedure_presentation) { create(:procedure_presentation, assign_to: assign_to) }
|
|
||||||
|
|
||||||
let(:decision_on) { { "label" => "Date décision SVA", "table" => "self", "column" => "sva_svr_decision_on", 'classname' => '', 'virtual' => false, "type" => :date, "scope" => '', "value_column" => :value, 'filterable' => true } }
|
|
||||||
let(:decision_before_field) { { "label" => "Date décision SVA avant", "table" => "self", "column" => "sva_svr_decision_before", 'classname' => '', 'virtual' => true, "type" => :date, "scope" => '', "value_column" => :value, 'filterable' => true } }
|
|
||||||
|
|
||||||
subject { procedure_presentation.fields }
|
|
||||||
|
|
||||||
it { is_expected.to include(decision_on, decision_before_field) }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when the procedure is svr' do
|
|
||||||
let(:procedure) { create(:procedure, :for_individual, :svr) }
|
|
||||||
let(:procedure_presentation) { create(:procedure_presentation, assign_to: assign_to) }
|
|
||||||
|
|
||||||
let(:decision_on) { { "label" => "Date décision SVR", "table" => "self", "column" => "sva_svr_decision_on", 'classname' => '', 'virtual' => false, "type" => :date, "scope" => '', "value_column" => :value, 'filterable' => true } }
|
|
||||||
let(:decision_before_field) { { "label" => "Date décision SVR avant", "table" => "self", "column" => "sva_svr_decision_before", 'classname' => '', 'virtual' => true, "type" => :date, "scope" => '', "value_column" => :value, 'filterable' => true } }
|
|
||||||
|
|
||||||
subject { procedure_presentation.fields }
|
|
||||||
|
|
||||||
it { is_expected.to include(decision_on, decision_before_field) }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#displayable_fields_for_select" do
|
|
||||||
subject { create(:procedure_presentation, assign_to: assign_to) }
|
|
||||||
let(:excluded_displayable_field) { { "label" => "depose_since", "table" => "self", "column" => "depose_since", 'virtual' => true } }
|
|
||||||
let(:included_displayable_field) { { "label" => "label1", "table" => "table1", "column" => "column1", 'virtual' => false } }
|
|
||||||
|
|
||||||
before do
|
|
||||||
allow(subject).to receive(:fields).and_return([
|
|
||||||
excluded_displayable_field,
|
|
||||||
included_displayable_field
|
|
||||||
])
|
|
||||||
end
|
|
||||||
|
|
||||||
it { expect(subject.displayable_fields_for_select).to eq([[["label1", "table1/column1"]], ["user/email"]]) }
|
|
||||||
end
|
|
||||||
describe "#filterable_fields_options" do
|
|
||||||
subject { create(:procedure_presentation, assign_to: assign_to) }
|
|
||||||
let(:included_displayable_field) do
|
|
||||||
[
|
|
||||||
{ "label" => "label1", "table" => "table1", "column" => "column1", 'virtual' => false },
|
|
||||||
{ "label" => "depose_since", "table" => "self", "column" => "depose_since", 'virtual' => true }
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
before do
|
|
||||||
allow(subject).to receive(:fields).and_return(included_displayable_field)
|
|
||||||
end
|
|
||||||
|
|
||||||
it { expect(subject.filterable_fields_options).to eq([["label1", "table1/column1"], ["depose_since", "self/depose_since"]]) }
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#sorted_ids' do
|
describe '#sorted_ids' do
|
||||||
let(:instructeur) { create(:instructeur) }
|
let(:instructeur) { create(:instructeur) }
|
||||||
let(:assign_to) { create(:assign_to, procedure: procedure, instructeur: instructeur) }
|
let(:assign_to) { create(:assign_to, procedure: procedure, instructeur: instructeur) }
|
||||||
let(:sort) { { 'table' => table, 'column' => column, 'order' => order } }
|
let(:sort) { { 'table' => table, 'column' => column, 'order' => order } }
|
||||||
let(:procedure_presentation) { create(:procedure_presentation, assign_to: assign_to, sort: sort) }
|
let(:procedure_presentation) { create(:procedure_presentation, assign_to: assign_to, sort: sort) }
|
||||||
|
|
||||||
subject { procedure_presentation.sorted_ids(procedure.dossiers, procedure.dossiers.count) }
|
subject { procedure_presentation.send(:sorted_ids, procedure.dossiers, procedure.dossiers.count) }
|
||||||
|
|
||||||
context 'for notifications table' do
|
context 'for notifications table' do
|
||||||
let(:table) { 'notifications' }
|
let(:table) { 'notifications' }
|
||||||
|
@ -310,7 +186,7 @@ describe ProcedurePresentation do
|
||||||
|
|
||||||
context 'for type_de_champ_private table' do
|
context 'for type_de_champ_private table' do
|
||||||
context 'with no revisions' do
|
context 'with no revisions' do
|
||||||
let(:table) { 'type_de_champ_private' }
|
let(:table) { 'type_de_champ' }
|
||||||
let(:column) { procedure.active_revision.types_de_champ_private.first.stable_id.to_s }
|
let(:column) { procedure.active_revision.types_de_champ_private.first.stable_id.to_s }
|
||||||
|
|
||||||
let(:biere_dossier) { create(:dossier, procedure: procedure) }
|
let(:biere_dossier) { create(:dossier, procedure: procedure) }
|
||||||
|
@ -336,7 +212,7 @@ describe ProcedurePresentation do
|
||||||
|
|
||||||
context 'with a revision adding a new type_de_champ' do
|
context 'with a revision adding a new type_de_champ' do
|
||||||
let!(:tdc) { { type_champ: :text, private: true, libelle: 'nouveau champ' } }
|
let!(:tdc) { { type_champ: :text, private: true, libelle: 'nouveau champ' } }
|
||||||
let(:table) { 'type_de_champ_private' }
|
let(:table) { 'type_de_champ' }
|
||||||
let(:column) { procedure.active_revision.types_de_champ_private.last.stable_id.to_s }
|
let(:column) { procedure.active_revision.types_de_champ_private.last.stable_id.to_s }
|
||||||
|
|
||||||
let(:nothing_dossier) { create(:dossier, procedure: procedure) }
|
let(:nothing_dossier) { create(:dossier, procedure: procedure) }
|
||||||
|
@ -445,7 +321,7 @@ describe ProcedurePresentation do
|
||||||
describe '#filtered_ids' do
|
describe '#filtered_ids' do
|
||||||
let(:procedure_presentation) { create(:procedure_presentation, assign_to: assign_to, filters: { "suivis" => filter }) }
|
let(:procedure_presentation) { create(:procedure_presentation, assign_to: assign_to, filters: { "suivis" => filter }) }
|
||||||
|
|
||||||
subject { procedure_presentation.filtered_ids(procedure.dossiers.joins(:user), 'suivis') }
|
subject { procedure_presentation.send(:filtered_ids, procedure.dossiers.joins(:user), 'suivis') }
|
||||||
|
|
||||||
context 'for self table' do
|
context 'for self table' do
|
||||||
context 'for created_at column' do
|
context 'for created_at column' do
|
||||||
|
@ -649,7 +525,7 @@ describe ProcedurePresentation do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'for type_de_champ_private table' do
|
context 'for type_de_champ_private table' do
|
||||||
let(:filter) { [{ 'table' => 'type_de_champ_private', 'column' => type_de_champ_private.stable_id.to_s, 'value' => 'keep' }] }
|
let(:filter) { [{ 'table' => 'type_de_champ', 'column' => type_de_champ_private.stable_id.to_s, 'value' => 'keep' }] }
|
||||||
|
|
||||||
let(:kept_dossier) { create(:dossier, procedure: procedure) }
|
let(:kept_dossier) { create(:dossier, procedure: procedure) }
|
||||||
let(:discarded_dossier) { create(:dossier, procedure: procedure) }
|
let(:discarded_dossier) { create(:dossier, procedure: procedure) }
|
||||||
|
@ -665,8 +541,8 @@ describe ProcedurePresentation do
|
||||||
context 'with multiple search values' do
|
context 'with multiple search values' do
|
||||||
let(:filter) do
|
let(:filter) do
|
||||||
[
|
[
|
||||||
{ 'table' => 'type_de_champ_private', 'column' => type_de_champ_private.stable_id.to_s, 'value' => 'keep' },
|
{ 'table' => 'type_de_champ', 'column' => type_de_champ_private.stable_id.to_s, 'value' => 'keep' },
|
||||||
{ 'table' => 'type_de_champ_private', 'column' => type_de_champ_private.stable_id.to_s, 'value' => 'and' }
|
{ 'table' => 'type_de_champ', 'column' => type_de_champ_private.stable_id.to_s, 'value' => 'and' }
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -859,7 +735,7 @@ describe ProcedurePresentation do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#human_value_for_filter" do
|
describe "#human_value_for_filter" do
|
||||||
let(:filters) { { "suivis" => [{ "label" => "label1", "table" => "type_de_champ", "column" => first_type_de_champ_id, "value" => "true" }] } }
|
let(:filters) { { "suivis" => [{ label: "label1", table: "type_de_champ", column: first_type_de_champ_id, "value" => "true" }] } }
|
||||||
|
|
||||||
subject { procedure_presentation.human_value_for_filter(procedure_presentation.filters["suivis"].first) }
|
subject { procedure_presentation.human_value_for_filter(procedure_presentation.filters["suivis"].first) }
|
||||||
|
|
||||||
|
@ -878,7 +754,7 @@ describe ProcedurePresentation do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when filter is state' do
|
context 'when filter is state' do
|
||||||
let(:filters) { { "suivis" => [{ "table" => "self", "column" => "state", "value" => "en_construction" }] } }
|
let(:filters) { { "suivis" => [{ table: "self", column: "state", "value" => "en_construction" }] } }
|
||||||
|
|
||||||
it 'should get i18n value' do
|
it 'should get i18n value' do
|
||||||
expect(subject).to eq("En construction")
|
expect(subject).to eq("En construction")
|
||||||
|
@ -886,7 +762,7 @@ describe ProcedurePresentation do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when filter is a date' do
|
context 'when filter is a date' do
|
||||||
let(:filters) { { "suivis" => [{ "table" => "self", "column" => "en_instruction_at", "value" => "15/06/2023" }] } }
|
let(:filters) { { "suivis" => [{ table: "self", column: "en_instruction_at", "value" => "15/06/2023" }] } }
|
||||||
|
|
||||||
it 'should get formatted value' do
|
it 'should get formatted value' do
|
||||||
expect(subject).to eq("15/06/2023")
|
expect(subject).to eq("15/06/2023")
|
||||||
|
@ -988,31 +864,4 @@ describe ProcedurePresentation do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#field_enum' do
|
|
||||||
context "field is groupe_instructeur" do
|
|
||||||
let!(:gi_2) { instructeur.groupe_instructeurs.create(label: 'gi2', procedure:) }
|
|
||||||
let!(:gi_3) { instructeur.groupe_instructeurs.create(label: 'gi3', procedure: create(:procedure)) }
|
|
||||||
|
|
||||||
subject { procedure_presentation.field_enum('groupe_instructeur/id') }
|
|
||||||
|
|
||||||
it { is_expected.to eq([['défaut', procedure.defaut_groupe_instructeur.id], ['gi2', gi_2.id]]) }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when field is dropdown' do
|
|
||||||
let(:procedure) { create(:procedure, :published, types_de_champ_public: [{ type: :text }], types_de_champ_private: [{}]) }
|
|
||||||
let(:tdc) { procedure.published_revision.types_de_champ_public.first }
|
|
||||||
before do
|
|
||||||
procedure.draft_revision
|
|
||||||
.find_and_ensure_exclusive_use(tdc.stable_id)
|
|
||||||
.update(type_champ: :drop_down_list,
|
|
||||||
drop_down_list_value: "Paris\nLyon\nMarseille")
|
|
||||||
procedure.publish_revision!
|
|
||||||
end
|
|
||||||
subject { procedure_presentation.field_enum("type_de_champ/#{tdc.id}") }
|
|
||||||
it 'find most recent tdc' do
|
|
||||||
expect(subject).to eq(["Paris", "Lyon", "Marseille"])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue