From 283ee72d516c0f8f8ec4d78e41aff1c5d3805096 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Tue, 28 Nov 2023 16:22:17 +0000 Subject: [PATCH 01/13] refactor(dossier): implement champs_for_revision --- app/models/dossier.rb | 14 ++++++++++++++ app/models/procedure_revision.rb | 16 ++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 8b9f18cdf..e707b06ea 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -1391,6 +1391,20 @@ class Dossier < ApplicationRecord user.france_connect_information.present? end + def champs_by_stable_id_with_row + champs_for_revision.index_by(&:stable_id_with_row) + end + + def champs_for_revision(scope = nil, root = false) + champs_index = champs.group_by(&:stable_id) + + if scope.is_a?(TypeDeChamp) + revision.children_of(scope) + else + revision.types_de_champ_for(scope, root) + end.flat_map { champs_index[_1.stable_id] || [] } + end + private def create_missing_traitemets diff --git a/app/models/procedure_revision.rb b/app/models/procedure_revision.rb index 78714241a..8e1d07811 100644 --- a/app/models/procedure_revision.rb +++ b/app/models/procedure_revision.rb @@ -155,6 +155,22 @@ class ProcedureRevision < ApplicationRecord dossier end + def types_de_champ_for(scope = nil, root = false) + # We return an unordered collection + return types_de_champ if !root && scope.nil? + return types_de_champ.filter { scope == :public ? _1.public? : _1.private? } if !root + + # We return an ordered collection + case scope + when :public + types_de_champ_public + when :private + types_de_champ_private + else + types_de_champ_public + types_de_champ_private + end + end + def children_of(tdc) if revision_types_de_champ.loaded? parent_coordinate_id = revision_types_de_champ From 02591da16b4cbc30a512d0c19f214b9714c88507 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Tue, 28 Nov 2023 16:27:09 +0000 Subject: [PATCH 02/13] refactor(section): update section components --- .../champs_rows_show_component.html.haml | 9 ++++---- .../repetition_component.html.haml | 4 ++-- .../repetition_row_component.rb | 16 ++++++++++++-- .../repetition_row_component.html.haml | 11 +++++----- .../editable_champ/section_component.rb | 17 ++++++++++----- .../viewable_champ/section_component.rb | 21 ++++++++++++------- app/models/concerns/treeable_concern.rb | 18 ++++++++-------- 7 files changed, 59 insertions(+), 37 deletions(-) diff --git a/app/components/dossiers/champs_rows_show_component/champs_rows_show_component.html.haml b/app/components/dossiers/champs_rows_show_component/champs_rows_show_component.html.haml index fb07850b8..11382ad4a 100644 --- a/app/components/dossiers/champs_rows_show_component/champs_rows_show_component.html.haml +++ b/app/components/dossiers/champs_rows_show_component/champs_rows_show_component.html.haml @@ -1,10 +1,9 @@ - each_champ do |champ| - if champ.repetition? - - champ.rows.each.with_index do |row, i| - .fr-background-alt--grey.fr-p-2w.fr-my-3w.fr-ml-2w.champ-repetition - %p.font-weight-bold= "#{champ.libelle} #{i + 1} :" - - = render Dossiers::ChampsRowsShowComponent.new(champs: row, seen_at:, profile:) + - types_de_champ = champ.dossier.revision.children_of(champ.type_de_champ) + - champs_by_stable_id_with_row = champ.dossier.champs_by_stable_id_with_row + - champ.row_ids.each do |row_id| + = render ViewableChamp::SectionComponent.new(types_de_champ:, champs_by_stable_id_with_row:, row_id:, demande_seen_at: seen_at, profile:) - else = render Dossiers::RowShowComponent.new(label: champ.libelle, seen_at:, profile:, content_class: champ.type_champ, updated_at: updated_after_deposer?(champ) ? champ.updated_at : nil) do |c| diff --git a/app/components/editable_champ/repetition_component/repetition_component.html.haml b/app/components/editable_champ/repetition_component/repetition_component.html.haml index 6e0f83525..e2b565d89 100644 --- a/app/components/editable_champ/repetition_component/repetition_component.html.haml +++ b/app/components/editable_champ/repetition_component/repetition_component.html.haml @@ -5,8 +5,8 @@ .repetition{ id: dom_id(@champ, :rows) } - - @champ.rows.each do |champs| - = render EditableChamp::RepetitionRowComponent.new(form: @form, champ: @champ, row: champs, seen_at: @seen_at) + - @champ.row_ids.each do |row_id| + = render EditableChamp::RepetitionRowComponent.new(form: @form, champ: @champ, row_id:, seen_at: @seen_at) .actions = render NestedForms::OwnedButtonComponent.new(formaction: champs_repetition_path(@champ.id), http_method: :create, opt: { class: "fr-btn fr-btn--secondary fr-btn--icon-left fr-icon-add-circle-line fr-mb-3w", title: t(".add_title", libelle: @champ.libelle), id: dom_id(@champ, :create_repetition)}) do diff --git a/app/components/editable_champ/repetition_row_component.rb b/app/components/editable_champ/repetition_row_component.rb index e647b5640..207d1a241 100644 --- a/app/components/editable_champ/repetition_row_component.rb +++ b/app/components/editable_champ/repetition_row_component.rb @@ -1,5 +1,17 @@ class EditableChamp::RepetitionRowComponent < ApplicationComponent - def initialize(form:, champ:, row:, seen_at: nil) - @form, @champ, @row, @seen_at = form, champ, row, seen_at + def initialize(form:, champ:, row_id:, seen_at: nil) + @form, @champ, @row_id, @seen_at = form, champ, row_id, seen_at + + @types_de_champ = champ.dossier.revision.children_of(champ.type_de_champ) + @champs_by_stable_id_with_row = champ.dossier.champs_by_stable_id_with_row + @row_number = champ.row_ids.find_index(row_id) + end + + attr_reader :row_id, :row_number + + private + + def section_component + EditableChamp::SectionComponent.new(types_de_champ: @types_de_champ, champs_by_stable_id_with_row: @champs_by_stable_id_with_row, row_id:) end end diff --git a/app/components/editable_champ/repetition_row_component/repetition_row_component.html.haml b/app/components/editable_champ/repetition_row_component/repetition_row_component.html.haml index 8b49d0f99..df3dc005b 100644 --- a/app/components/editable_champ/repetition_row_component/repetition_row_component.html.haml +++ b/app/components/editable_champ/repetition_row_component/repetition_row_component.html.haml @@ -1,12 +1,11 @@ -- row_id = "safe-row-selector-#{@row.first.row_id}" -.row{ id: row_id } - - if @row.size > 1 +.row{ id: "safe-row-selector-#{row_id}" } + - if @types_de_champ.size > 1 %fieldset %legend.block-id= "#{@champ.libelle} " - = render EditableChamp::SectionComponent.new(champs: @row) + = render section_component - else - = render EditableChamp::SectionComponent.new(champs: @row) + = render section_component .flex.row-reverse - = render NestedForms::OwnedButtonComponent.new(formaction: champs_repetition_path(@champ.id, row_id: @row.first.row_id), http_method: :delete, opt: { class: "fr-btn fr-btn--sm fr-btn--tertiary fr-text-action-high--red-marianne", title: t(".delete_title", row_number: @champ.rows.find_index(@row))}) do + = render NestedForms::OwnedButtonComponent.new(formaction: champs_repetition_path(@champ.id, row_id:), http_method: :delete, opt: { class: "fr-btn fr-btn--sm fr-btn--tertiary fr-text-action-high--red-marianne", title: t(".delete_title", row_number:)}) do = t(".delete") diff --git a/app/components/editable_champ/section_component.rb b/app/components/editable_champ/section_component.rb index 55007bc66..6a00123cd 100644 --- a/app/components/editable_champ/section_component.rb +++ b/app/components/editable_champ/section_component.rb @@ -2,8 +2,10 @@ class EditableChamp::SectionComponent < ApplicationComponent include ApplicationHelper include TreeableConcern - def initialize(nodes: nil, champs: nil) - nodes ||= to_tree(champs:) + def initialize(nodes: nil, types_de_champ: nil, row_id: nil, champs_by_stable_id_with_row:) + nodes ||= to_tree(types_de_champ:) + @champs_by_stable_id_with_row = champs_by_stable_id_with_row + @row_id = row_id @nodes = to_fieldset(nodes:) end @@ -12,7 +14,8 @@ class EditableChamp::SectionComponent < ApplicationComponent end def header_section - @nodes.first if @nodes.first.is_a?(Champs::HeaderSectionChamp) + node = @nodes.first + champ_for_type_de_champ(node) if node.is_a?(TypeDeChamp) && node.header_section? end def splitted_tail @@ -35,17 +38,21 @@ class EditableChamp::SectionComponent < ApplicationComponent when EditableChamp::SectionComponent [node, nil] else - [nil, node] + [nil, champ_for_type_de_champ(node)] end end private def to_fieldset(nodes:) - nodes.map { _1.is_a?(Array) ? EditableChamp::SectionComponent.new(nodes: _1) : _1 } + nodes.map { _1.is_a?(Array) ? EditableChamp::SectionComponent.new(nodes: _1, row_id: @row_id, champs_by_stable_id_with_row: @champs_by_stable_id_with_row) : _1 } end def first_champ_is_an_header_section? header_section.present? end + + def champ_for_type_de_champ(type_de_champ) + @champs_by_stable_id_with_row[[@row_id, type_de_champ.stable_id].compact] + end end diff --git a/app/components/viewable_champ/section_component.rb b/app/components/viewable_champ/section_component.rb index 6debfb874..b3024f318 100644 --- a/app/components/viewable_champ/section_component.rb +++ b/app/components/viewable_champ/section_component.rb @@ -2,9 +2,9 @@ class ViewableChamp::SectionComponent < ApplicationComponent include ApplicationHelper include TreeableConcern - def initialize(champs: nil, nodes: nil, demande_seen_at:, profile:) - @demande_seen_at, @profile, @repetition = demande_seen_at, profile - nodes ||= to_tree(champs:) + def initialize(nodes: nil, types_de_champ: nil, row_id: nil, demande_seen_at:, profile:, champs_by_stable_id_with_row:) + @demande_seen_at, @profile, @row_id, @champs_by_stable_id_with_row = demande_seen_at, profile, row_id, champs_by_stable_id_with_row + nodes ||= to_tree(types_de_champ:) @nodes = to_sections(nodes:) end @@ -13,17 +13,18 @@ class ViewableChamp::SectionComponent < ApplicationComponent end def header_section - if @nodes.first.is_a?(Champs::HeaderSectionChamp) - @nodes.first + maybe_header_section = @nodes.first + if maybe_header_section.is_a?(TypeDeChamp) && maybe_header_section.header_section? + champ_for_type_de_champ(maybe_header_section) end end def champs - tail.filter { _1.is_a?(Champ) } + tail.filter_map { _1.is_a?(TypeDeChamp) ? champ_for_type_de_champ(_1) : nil } end def sections - tail.filter { !_1.is_a?(Champ) } + tail.filter { _1.is_a?(ViewableChamp::SectionComponent) } end def tail @@ -48,6 +49,10 @@ class ViewableChamp::SectionComponent < ApplicationComponent private def to_sections(nodes:) - nodes.map { _1.is_a?(Array) ? ViewableChamp::SectionComponent.new(nodes: _1, demande_seen_at: @demande_seen_at, profile: @profile) : _1 } + nodes.map { _1.is_a?(Array) ? ViewableChamp::SectionComponent.new(nodes: _1, demande_seen_at: @demande_seen_at, profile: @profile, champs_by_stable_id_with_row: @champs_by_stable_id_with_row) : _1 } + end + + def champ_for_type_de_champ(type_de_champ) + @champs_by_stable_id_with_row[[@row_id, type_de_champ.stable_id].compact] end end diff --git a/app/models/concerns/treeable_concern.rb b/app/models/concerns/treeable_concern.rb index 81592eff7..174a54304 100644 --- a/app/models/concerns/treeable_concern.rb +++ b/app/models/concerns/treeable_concern.rb @@ -5,30 +5,30 @@ module TreeableConcern # but a repetition can be nested an header_section, so 3+3=6=MAX_DEPTH included do - # as we progress in the list of ordered champs + # as we progress in the list of ordered types_de_champ # we keep a reference to each level of nesting (walk) # when we encounter an header_section, it depends of its own depth of nesting minus 1, ie: # h1 belongs to prior (rooted_tree) # h2 belongs to prior h1 # h3 belongs to prior h2 # h1 belongs to prior (rooted_tree) - # then, each and every champs which are not an header_section + # then, each and every types_de_champ which are not an header_section # are added to the current_tree # given a root_depth at 0, we build a full tree # given a root_depth > 0, we build a partial tree (aka, a repetition) - def to_tree(champs:) + def to_tree(types_de_champ:) rooted_tree = [] walk = Array.new(MAX_DEPTH) walk[0] = rooted_tree current_tree = rooted_tree - champs.each do |champ| - if champ.header_section? - new_tree = [champ] - walk[champ.header_section_level_value - 1].push(new_tree) - current_tree = walk[champ.header_section_level_value] = new_tree + types_de_champ.each do |type_de_champ| + if type_de_champ.header_section? + new_tree = [type_de_champ] + walk[type_de_champ.header_section_level_value - 1].push(new_tree) + current_tree = walk[type_de_champ.header_section_level_value] = new_tree else - current_tree.push(champ) + current_tree.push(type_de_champ) end end rooted_tree From f7758d00339ae9fe78f67374cd35970e61fe4acb Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Tue, 28 Nov 2023 16:29:32 +0000 Subject: [PATCH 03/13] refactor(dossier): remove old section code --- app/models/champ.rb | 4 ---- app/models/champs/header_section_champ.rb | 4 ---- app/models/concerns/dossier_sections_concern.rb | 8 ++++---- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/app/models/champ.rb b/app/models/champ.rb index 22fe4640c..8b65d6ba4 100644 --- a/app/models/champ.rb +++ b/app/models/champ.rb @@ -99,10 +99,6 @@ class Champ < ApplicationRecord [row_id, stable_id].compact end - def sections - @sections ||= dossier.sections_for(self) - end - # used for the `required` html attribute # check visibility to avoid hidden required input # which prevent the form from being sent. diff --git a/app/models/champs/header_section_champ.rb b/app/models/champs/header_section_champ.rb index 69ac6d54c..27e0c128b 100644 --- a/app/models/champs/header_section_champ.rb +++ b/app/models/champs/header_section_champ.rb @@ -16,8 +16,4 @@ class Champs::HeaderSectionChamp < Champ def libelle_with_section_index? libelle =~ /^\d/ end - - def section_index - sections.index(self) + 1 - end end diff --git a/app/models/concerns/dossier_sections_concern.rb b/app/models/concerns/dossier_sections_concern.rb index c36a1d619..e14f4b61b 100644 --- a/app/models/concerns/dossier_sections_concern.rb +++ b/app/models/concerns/dossier_sections_concern.rb @@ -6,11 +6,11 @@ module DossierSectionsConcern @sections = Hash.new do |hash, parent| case parent when :public - hash[parent] = champs_public.filter(&:header_section?) + hash[parent] = champs_for_revision(:public, true).filter(&:header_section?) when :private - hash[parent] = champs_private.filter(&:header_section?) + hash[parent] = champs_for_revision(:private, true).filter(&:header_section?) else - hash[parent] = parent.champs.filter(&:header_section?) + hash[parent] = champs_for_revision(parent.type_de_champ).filter(&:header_section?) end end @sections[champ.parent || (champ.public? ? :public : :private)] @@ -23,7 +23,7 @@ module DossierSectionsConcern end def index_for_section_header(champ) - champs = champ.private? ? champs_private : champs_public + champs = champ.private? ? champs_for_revision(:private, true) : champs_for_revision(:public, true) index = 1 champs.each do |c| if c.repetition? From 3e296fc75c830d3aa431dde0eb8d7e2525fd74dd Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Tue, 28 Nov 2023 16:30:23 +0000 Subject: [PATCH 04/13] refactor(graphql): use new champs methods on API --- .../types/champs/repetition_champ_type.rb | 16 +++++++++------- app/graphql/types/dossier_type.rb | 7 ++----- app/serializers/champ_serializer.rb | 15 ++++++++++++++- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/app/graphql/types/champs/repetition_champ_type.rb b/app/graphql/types/champs/repetition_champ_type.rb index 7ccd3106e..fdcdfab18 100644 --- a/app/graphql/types/champs/repetition_champ_type.rb +++ b/app/graphql/types/champs/repetition_champ_type.rb @@ -11,16 +11,18 @@ module Types::Champs field :rows, [Row], null: false def champs - Loaders::Association.for(object.class, champs: :type_de_champ).load(object).then do |champs| - champs.filter(&:visible?) - end + object.rows.flat_map { _1.filter(&:visible?) } end def rows - Loaders::Association.for(object.class, champs: :type_de_champ).load(object).then do |champs| - object.association(:champs).target = champs.filter(&:visible?) - object.rows.map { { champs: _1, id: GraphQL::Schema::UniqueWithinType.encode('Row', _1.first.row_id) } } - end + object + .rows + .map do + { + id: GraphQL::Schema::UniqueWithinType.encode('Row', _1.first.row_id), + champs: _1.filter(&:visible?) + } + end end end end diff --git a/app/graphql/types/dossier_type.rb b/app/graphql/types/dossier_type.rb index ac0d80bce..dde9b8f52 100644 --- a/app/graphql/types/dossier_type.rb +++ b/app/graphql/types/dossier_type.rb @@ -157,10 +157,7 @@ module Types .for(object, private: false) .load(ApplicationRecord.id_from_typed_id(id)) else - Loaders::Association - .for(object.class, champs_public: :type_de_champ) - .load(object) - .then { |champs| champs.filter(&:visible?) } + object.champs_for_revision(:public, true).filter(&:visible?) end end @@ -170,7 +167,7 @@ module Types .for(object, private: true) .load(ApplicationRecord.id_from_typed_id(id)) else - Loaders::Association.for(object.class, champs_private: :type_de_champ).load(object) + object.champs_for_revision(:private, true).filter(&:visible?) end end diff --git a/app/serializers/champ_serializer.rb b/app/serializers/champ_serializer.rb index 670979c81..bc92a8264 100644 --- a/app/serializers/champ_serializer.rb +++ b/app/serializers/champ_serializer.rb @@ -36,8 +36,21 @@ class ChampSerializer < ActiveModel::Serializer object.etablissement&.entreprise end + class Row < Hashie::Dash + property :index + property :champs + + def read_attribute_for_serialization(attribute) + self[attribute] + end + end + def rows - object.rows_for_export + object.dossier + .champs_for_revision(object.type_de_champ) + .group_by(&:row_id) + .values + .map.with_index(1) { |champs, index| Row.new(index:, champs:) } end def include_etablissement? From 1850d80b821df783f482a80ba860b0cf5238afd0 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Tue, 28 Nov 2023 16:33:29 +0000 Subject: [PATCH 05/13] refactor(dossier): use new champs_for_revision method --- .../champs/repetition_controller.rb | 4 ++- app/controllers/root_controller.rb | 6 ++--- app/models/attestation_template.rb | 4 +-- app/models/champs/repetition_champ.rb | 16 +++++++++--- app/models/concerns/dossier_clone_concern.rb | 1 + app/models/dossier.rb | 17 +++++++------ app/models/type_de_champ.rb | 4 +++ app/services/procedure_archive_service.rb | 25 ------------------- app/services/procedure_export_service.rb | 6 ++--- .../champs/repetition/add.turbo_stream.haml | 9 +++---- .../instructeurs/dossiers/print.html.haml | 12 ++++----- app/views/root/patron.html.haml | 4 +-- app/views/shared/dossiers/_champs.html.haml | 2 +- app/views/shared/dossiers/_demande.html.haml | 6 ++--- app/views/shared/dossiers/_edit.html.haml | 2 +- .../dossiers/_edit_annotations.html.haml | 2 +- 16 files changed, 54 insertions(+), 66 deletions(-) diff --git a/app/controllers/champs/repetition_controller.rb b/app/controllers/champs/repetition_controller.rb index b13979716..aabfa3601 100644 --- a/app/controllers/champs/repetition_controller.rb +++ b/app/controllers/champs/repetition_controller.rb @@ -3,7 +3,9 @@ class Champs::RepetitionController < ApplicationController def add @champ = policy_scope(Champ).includes(:champs).find(params[:champ_id]) - @champs = @champ.add_row(@champ.dossier.revision) + row = @champ.add_row(@champ.dossier.revision) + @first_champ_id = row.map(&:focusable_input_id).compact.first + @row_id = row.first&.row_id end def remove diff --git a/app/controllers/root_controller.rb b/app/controllers/root_controller.rb index 6deb4cf13..62cd4ab87 100644 --- a/app/controllers/root_controller.rb +++ b/app/controllers/root_controller.rb @@ -26,7 +26,7 @@ class RootController < ApplicationController description = "Allez voir le super site : #{APPLICATION_BASE_URL}" all_champs = TypeDeChamp.type_champs - .map { |name, _| TypeDeChamp.new(type_champ: name, private: false, libelle: name.humanize, description: description, mandatory: true) } + .map.with_index { |(name, _), i| TypeDeChamp.new(type_champ: name, private: false, libelle: name.humanize, description:, mandatory: true, stable_id: i) } .map.with_index { |type_de_champ, i| type_de_champ.champ.build(id: i) } all_champs @@ -76,7 +76,7 @@ class RootController < ApplicationController .each { |champ| champ.value = value } end - @dossier = Dossier.new(champs_public: all_champs) + @dossier = Dossier.new(champs: all_champs) @dossier.association(:procedure).target = Procedure.new all_champs.each do |champ| champ.association(:dossier).target = @dossier @@ -85,7 +85,7 @@ class RootController < ApplicationController end end - draft_revision = @dossier.procedure.build_draft_revision(types_de_champ_public: all_champs.map(&:type_de_champ)) + draft_revision = @dossier.procedure.build_draft_revision(types_de_champ: all_champs.map(&:type_de_champ)) @dossier.association(:revision).target = draft_revision @dossier.champs_public.map(&:type_de_champ).map do |tdc| tdc.association(:revision_type_de_champ).target = tdc.build_revision_type_de_champ(revision: draft_revision) diff --git a/app/models/attestation_template.rb b/app/models/attestation_template.rb index 52d32b166..a2e80ea57 100644 --- a/app/models/attestation_template.rb +++ b/app/models/attestation_template.rb @@ -79,10 +79,10 @@ class AttestationTemplate < ApplicationRecord end def unspecified_champs_for_dossier(dossier) - all_champs_with_libelle_index = (dossier.champs_public + dossier.champs_private).index_by { |champ| "tdc#{champ.stable_id}" } + champs_by_stable_id = dossier.champs_for_revision(nil, true).index_by { "tdc#{_1.stable_id}" } used_tags.filter_map do |used_tag| - corresponding_champ = all_champs_with_libelle_index[used_tag] + corresponding_champ = champs_by_stable_id[used_tag] if corresponding_champ && corresponding_champ.blank? corresponding_champ diff --git a/app/models/champs/repetition_champ.rb b/app/models/champs/repetition_champ.rb index ed9d91c00..bb8b123b6 100644 --- a/app/models/champs/repetition_champ.rb +++ b/app/models/champs/repetition_champ.rb @@ -3,7 +3,13 @@ class Champs::RepetitionChamp < Champ delegate :libelle_for_export, to: :type_de_champ def rows - champs.group_by(&:row_id).values + dossier + .champs_for_revision(type_de_champ) + .group_by(&:row_id).values + end + + def row_ids + rows.map { _1.first.row_id } end def add_row(revision) @@ -35,13 +41,15 @@ class Champs::RepetitionChamp < Champ end def rows_for_export - rows.each.with_index(1).map do |champs, index| - Champs::RepetitionChamp::Row.new(index: index, dossier_id: dossier_id.to_s, champs: champs) + champs = dossier.champs_by_stable_id_with_row + row_ids.each.with_index(1).map do |row_id, index| + Champs::RepetitionChamp::Row.new(index: index, row_id:, dossier_id: dossier_id.to_s, champs:) end end class Row < Hashie::Dash property :index + property :row_id property :dossier_id property :champs @@ -53,7 +61,7 @@ class Champs::RepetitionChamp < Champ [ ['Dossier ID', :dossier_id], ['Ligne', :index] - ] + Dossier.champs_for_export(champs, types_de_champ) + ] + Dossier.champs_for_export(types_de_champ, champs, row_id) end end end diff --git a/app/models/concerns/dossier_clone_concern.rb b/app/models/concerns/dossier_clone_concern.rb index b828b2071..4abb11b05 100644 --- a/app/models/concerns/dossier_clone_concern.rb +++ b/app/models/concerns/dossier_clone_concern.rb @@ -42,6 +42,7 @@ module DossierCloneConcern end def make_diff(editing_fork) + # TODO: remove champs_public_all usage origin_champs_index = champs_public_all.index_by(&:stable_id_with_row) forked_champs_index = editing_fork.champs_public_all.index_by(&:stable_id_with_row) updated_champs_index = editing_fork diff --git a/app/models/dossier.rb b/app/models/dossier.rb index e707b06ea..cbe452315 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -610,7 +610,7 @@ class Dossier < ApplicationRecord def any_etablissement_as_degraded_mode? return true if etablissement&.as_degraded_mode? - return true if champs_public_all.any? { _1.etablissement&.as_degraded_mode? } + return true if champs_for_revision(:public).any? { _1.etablissement&.as_degraded_mode? } false end @@ -1165,7 +1165,8 @@ class Dossier < ApplicationRecord end def check_mandatory_and_visible_champs - (champs_public + champs_public.filter(&:block?).filter(&:visible?).flat_map(&:champs)) + champs_for_revision(:public) + .filter { _1.child? ? _1.parent.visible? : true } .filter(&:visible?) .filter(&:mandatory_blank?) .map do |champ| @@ -1268,15 +1269,15 @@ class Dossier < ApplicationRecord if procedure.routing_enabled? columns << ['Groupe instructeur', groupe_instructeur.label] end - columns + self.class.champs_for_export(champs_public + champs_private, types_de_champ) + columns + self.class.champs_for_export(types_de_champ, champs_by_stable_id_with_row) end # Get all the champs values for the types de champ in the final list. # Dossier might not have corresponding champ – display nil. # To do so, we build a virtual champ when there is no value so we can call for_export with all indexes - def self.champs_for_export(champs, types_de_champ) + def self.champs_for_export(types_de_champ, champs, row_id = nil) types_de_champ.flat_map do |type_de_champ| - champ = champs.find { |champ| champ.stable_id == type_de_champ.stable_id } + champ = champs[[row_id, type_de_champ.stable_id].compact] exported_values = if champ.nil? || !champ.visible? # some champs export multiple columns @@ -1299,7 +1300,7 @@ class Dossier < ApplicationRecord end def linked_dossiers_for(instructeur_or_expert) - dossier_ids = champs_public.filter(&:dossier_link?).filter_map(&:value) + dossier_ids = champs_for_revision.filter(&:dossier_link?).filter_map(&:value) instructeur_or_expert.dossiers.where(id: dossier_ids) end @@ -1308,7 +1309,7 @@ class Dossier < ApplicationRecord end def geo_data? - GeoArea.exists?(champ_id: champs_public.ids + champs_private.ids) + GeoArea.exists?(champ_id: champs_for_revision) end def to_feature_collection @@ -1426,7 +1427,7 @@ class Dossier < ApplicationRecord end def geo_areas - champs_public.flat_map(&:geo_areas) + champs_private.flat_map(&:geo_areas) + champs_for_revision.flat_map(&:geo_areas) end def bounding_box diff --git a/app/models/type_de_champ.rb b/app/models/type_de_champ.rb index 5b60124f0..87c5acba4 100644 --- a/app/models/type_de_champ.rb +++ b/app/models/type_de_champ.rb @@ -357,6 +357,10 @@ class TypeDeChamp < ApplicationRecord type_champ == TypeDeChamp.type_champs.fetch(:drop_down_list) end + def multiple_drop_down_list? + type_champ == TypeDeChamp.type_champs.fetch(:multiple_drop_down_list) + end + def linked_drop_down_list? type_champ == TypeDeChamp.type_champs.fetch(:linked_drop_down_list) end diff --git a/app/services/procedure_archive_service.rb b/app/services/procedure_archive_service.rb index 1025b1241..3ad452ebf 100644 --- a/app/services/procedure_archive_service.rb +++ b/app/services/procedure_archive_service.rb @@ -23,16 +23,6 @@ class ProcedureArchiveService end end - def self.procedure_files_size(procedure) - dossiers_files_size(procedure.dossiers) - end - - def self.dossiers_files_size(dossiers) - dossiers.map do |dossier| - liste_pieces_justificatives_for_archive(dossier).sum(&:byte_size) - end.sum - end - private def zip_root_folder(archive) @@ -43,19 +33,4 @@ class ProcedureArchiveService archive.id ].join("-") end - - def self.attachments_from_champs_piece_justificative(champs) - champs - .filter { |c| c.type_champ == TypeDeChamp.type_champs.fetch(:piece_justificative) } - .map(&:piece_justificative_file) - .filter(&:attached?) - end - - def self.liste_pieces_justificatives_for_archive(dossier) - champs_blocs_repetables = dossier.champs_public - .filter { |c| c.type_champ == TypeDeChamp.type_champs.fetch(:repetition) } - .flat_map(&:champs) - - attachments_from_champs_piece_justificative(champs_blocs_repetables + dossier.champs_public) - end end diff --git a/app/services/procedure_export_service.rb b/app/services/procedure_export_service.rb index 41390b740..75eab89e8 100644 --- a/app/services/procedure_export_service.rb +++ b/app/services/procedure_export_service.rb @@ -90,9 +90,7 @@ class ProcedureExportService def etablissements @etablissements ||= dossiers.flat_map do |dossier| - [dossier.champs_public, dossier.champs_private] - .flatten - .filter { |champ| champ.is_a?(Champs::SiretChamp) } + dossier.champs.filter { _1.is_a?(Champs::SiretChamp) } end.filter_map(&:etablissement) + dossiers.filter_map(&:etablissement) end @@ -102,7 +100,7 @@ class ProcedureExportService def champs_repetables_options champs_by_stable_id = dossiers - .flat_map { |dossier| (dossier.champs_public + dossier.champs_private).filter(&:repetition?) } + .flat_map { _1.champs.filter(&:repetition?) } .group_by(&:stable_id) procedure diff --git a/app/views/champs/repetition/add.turbo_stream.haml b/app/views/champs/repetition/add.turbo_stream.haml index 33ceb8aee..c2c01cabc 100644 --- a/app/views/champs/repetition/add.turbo_stream.haml +++ b/app/views/champs/repetition/add.turbo_stream.haml @@ -1,6 +1,5 @@ -- if @champs.present? +- if @row_id.present? = fields_for @champ.input_name, @champ do |form| - = turbo_stream.append dom_id(@champ, :rows), render(EditableChamp::RepetitionRowComponent.new(form: form, champ: @champ, row: @champs)) - - first_champ_id = @champs.map(&:focusable_input_id).compact.first - - if first_champ_id - = turbo_stream.focus(first_champ_id) + = turbo_stream.append dom_id(@champ, :rows), render(EditableChamp::RepetitionRowComponent.new(form: form, champ: @champ, row_id: @row_id)) + - if @first_champ_id + = turbo_stream.focus(@first_champ_id) diff --git a/app/views/instructeurs/dossiers/print.html.haml b/app/views/instructeurs/dossiers/print.html.haml index e653ee157..57815b73f 100644 --- a/app/views/instructeurs/dossiers/print.html.haml +++ b/app/views/instructeurs/dossiers/print.html.haml @@ -13,15 +13,15 @@ %h2 Formulaire -- champs = @dossier.champs_public -- if champs.any? || @dossier.procedure.routing_enabled? - = render partial: "shared/dossiers/champs", locals: { champs: champs, dossier: @dossier, demande_seen_at: nil, profile: 'instructeur' } +- types_de_champ_public = @dossier.revision.types_de_champ_public +- if types_de_champ_public.any? || @dossier.procedure.routing_enabled? + = render partial: "shared/dossiers/champs", locals: { types_de_champ: types_de_champ_public, dossier: @dossier, demande_seen_at: nil, profile: 'instructeur' } %h2 Annotations privées -- champs_annotations_privees = @dossier.champs_private -- if champs_annotations_privees.any? - = render partial: "shared/dossiers/champs", locals: { champs: champs_annotations_privees, dossier: @dossier, demande_seen_at: nil, profile: 'instructeur' } +- types_de_champ_private = @dossier.revision.types_de_champ_private +- if types_de_champ_private.any? + = render partial: "shared/dossiers/champs", locals: { types_de_champ: types_de_champ_private, dossier: @dossier, demande_seen_at: nil, profile: 'instructeur' } - else Aucune annotation privée diff --git a/app/views/root/patron.html.haml b/app/views/root/patron.html.haml index c9450f713..4316dfcef 100644 --- a/app/views/root/patron.html.haml +++ b/app/views/root/patron.html.haml @@ -33,7 +33,7 @@ %form.form = form_for @dossier, url: '', html: { class: 'form' } do |f| - = render EditableChamp::SectionComponent.new(champs: @dossier.champs_public) + = render EditableChamp::SectionComponent.new(types_de_champ: @dossier.revision.types_de_champ_public, champs_by_stable_id_with_row: @dossier.champs_by_stable_id_with_row) .editable-champ.editable-champ-text %label Mot de passe @@ -331,7 +331,7 @@ %h1.fr-mt-4w Attachment::EditComponent %span.fr-hint-text Note: direct upload, suppression ne marchent pas comme attendu ici. - - champ = @dossier.champs_public.find { _1.type_champ == TypeDeChamp.type_champs.fetch(:piece_justificative) } + - champ = @dossier.champs.find { _1.type_champ == TypeDeChamp.type_champs.fetch(:piece_justificative) } - tdc = champ.type_de_champ - avis = Avis.new diff --git a/app/views/shared/dossiers/_champs.html.haml b/app/views/shared/dossiers/_champs.html.haml index 80f2cebed..3320f7ca8 100644 --- a/app/views/shared/dossiers/_champs.html.haml +++ b/app/views/shared/dossiers/_champs.html.haml @@ -1 +1 @@ -= render ViewableChamp::SectionComponent.new(champs: champs, demande_seen_at: demande_seen_at, profile: profile) += render ViewableChamp::SectionComponent.new(types_de_champ:, champs_by_stable_id_with_row: dossier.champs_by_stable_id_with_row, demande_seen_at:, profile:) diff --git a/app/views/shared/dossiers/_demande.html.haml b/app/views/shared/dossiers/_demande.html.haml index 5ec17bd28..0ded9b01d 100644 --- a/app/views/shared/dossiers/_demande.html.haml +++ b/app/views/shared/dossiers/_demande.html.haml @@ -51,6 +51,6 @@ %h2.fr-h6.fr-background-alt--grey.fr-mb-0.flex .flex-grow.fr-py-3v.fr-px-2w= t('views.shared.dossiers.demande.form') - - champs = dossier.champs_public - - if champs.any? || dossier.procedure.routing_enabled? - = render partial: "shared/dossiers/champs", locals: { champs: champs, dossier: dossier, demande_seen_at: demande_seen_at, profile: profile } + - types_de_champ = dossier.revision.types_de_champ_public + - if types_de_champ.any? || dossier.procedure.routing_enabled? + = render partial: "shared/dossiers/champs", locals: { types_de_champ:, dossier:, demande_seen_at:, profile: } diff --git a/app/views/shared/dossiers/_edit.html.haml b/app/views/shared/dossiers/_edit.html.haml index 11d21382c..14a526b7c 100644 --- a/app/views/shared/dossiers/_edit.html.haml +++ b/app/views/shared/dossiers/_edit.html.haml @@ -22,7 +22,7 @@ = render Procedure::NoticeComponent.new(procedure: dossier.procedure) - = render EditableChamp::SectionComponent.new(champs: dossier_for_editing.champs_public) + = render EditableChamp::SectionComponent.new(types_de_champ: dossier_for_editing.revision.types_de_champ_public, champs_by_stable_id_with_row: dossier_for_editing.champs_by_stable_id_with_row) = render Dossiers::PendingCorrectionCheckboxComponent.new(dossier: dossier) diff --git a/app/views/shared/dossiers/_edit_annotations.html.haml b/app/views/shared/dossiers/_edit_annotations.html.haml index 5dfaefff0..48644a34e 100644 --- a/app/views/shared/dossiers/_edit_annotations.html.haml +++ b/app/views/shared/dossiers/_edit_annotations.html.haml @@ -3,7 +3,7 @@ %section.counter-start-header-section = render NestedForms::FormOwnerComponent.new = form_for dossier, url: annotations_instructeur_dossier_path(dossier.procedure, dossier), html: { class: 'form', multipart: true } do |f| - = render EditableChamp::SectionComponent.new(champs: dossier.champs_private) + = render EditableChamp::SectionComponent.new(types_de_champ: dossier.revision.types_de_champ_private, champs_by_stable_id_with_row: dossier.champs_by_stable_id_with_row) = render Dossiers::EditFooterComponent.new(dossier: dossier, annotation: true) - else From deb19177f72cc513b24018a2a89578ed2f84d988 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Tue, 28 Nov 2023 16:33:43 +0000 Subject: [PATCH 06/13] refactor(test): update tests --- .../editable_champ/section_component_spec.rb | 5 +- spec/factories/dossier.rb | 16 +++- spec/factories/procedure.rb | 8 +- spec/models/champ_spec.rb | 5 - .../champs/header_section_champ_spec.rb | 38 -------- spec/models/concern/treeable_concern_spec.rb | 46 ++++----- spec/models/dossier_spec.rb | 15 +-- .../prefill_repetition_type_de_champ_spec.rb | 7 +- .../shared/dossiers/_champs.html.haml_spec.rb | 89 +++++++++++------- .../shared/dossiers/_edit.html.haml_spec.rb | 93 +++++++++---------- 10 files changed, 160 insertions(+), 162 deletions(-) delete mode 100644 spec/models/champs/header_section_champ_spec.rb diff --git a/spec/components/editable_champ/section_component_spec.rb b/spec/components/editable_champ/section_component_spec.rb index da5283530..f3f474297 100644 --- a/spec/components/editable_champ/section_component_spec.rb +++ b/spec/components/editable_champ/section_component_spec.rb @@ -1,6 +1,8 @@ describe EditableChamp::SectionComponent, type: :component do include TreeableConcern - let(:component) { described_class.new(champs: champs) } + let(:types_de_champ) { champs.map(&:type_de_champ) } + let(:champs_by_stable_id_with_row) { champs.index_by(&:stable_id_with_row) } + let(:component) { described_class.new(types_de_champ:, champs_by_stable_id_with_row:) } before { render_inline(component).to_html } context 'list of champs without an header_section' do @@ -98,6 +100,7 @@ describe EditableChamp::SectionComponent, type: :component do end let(:dossier) { create(:dossier, :with_populated_champs, procedure: procedure) } let(:champs) { dossier.champs_public } + let(:champs_by_stable_id_with_row) { dossier.champs_by_stable_id_with_row } it 'render nested fieldsets, increase heading level for repetition header_section' do expect(page).to have_selector("fieldset") diff --git a/spec/factories/dossier.rb b/spec/factories/dossier.rb index f934364bf..bf387fe6c 100644 --- a/spec/factories/dossier.rb +++ b/spec/factories/dossier.rb @@ -280,7 +280,13 @@ FactoryBot.define do after(:create) do |dossier, _evaluator| dossier.champs_to_destroy.where(private: false).destroy_all dossier.types_de_champ.each do |type_de_champ| - create(:"champ_#{type_de_champ.type_champ}", dossier:, type_de_champ:) + if type_de_champ.simple_drop_down_list? + create(:"champ_#{type_de_champ.type_champ}", dossier:, type_de_champ:, value: type_de_champ.drop_down_list_enabled_non_empty_options.first) + elsif type_de_champ.multiple_drop_down_list? + create(:"champ_#{type_de_champ.type_champ}", dossier:, type_de_champ:, value: type_de_champ.drop_down_list_enabled_non_empty_options.first(2).to_json) + else + create(:"champ_#{type_de_champ.type_champ}", dossier:, type_de_champ:) + end end dossier.reload end @@ -290,7 +296,13 @@ FactoryBot.define do after(:create) do |dossier, _evaluator| dossier.champs_to_destroy.where(private: true).destroy_all dossier.types_de_champ_private.each do |type_de_champ| - create(:"champ_#{type_de_champ.type_champ}", private: true, dossier:, type_de_champ:) + if type_de_champ.simple_drop_down_list? + create(:"champ_#{type_de_champ.type_champ}", private: true, dossier:, type_de_champ:, value: type_de_champ.drop_down_list_enabled_non_empty_options.first) + elsif type_de_champ.multiple_drop_down_list? + create(:"champ_#{type_de_champ.type_champ}", private: true, dossier:, type_de_champ:, value: type_de_champ.drop_down_list_enabled_non_empty_options.first(2).to_json) + else + create(:"champ_#{type_de_champ.type_champ}", private: true, dossier:, type_de_champ:) + end end dossier.reload end diff --git a/spec/factories/procedure.rb b/spec/factories/procedure.rb index 6d3b76c6e..10dbffecf 100644 --- a/spec/factories/procedure.rb +++ b/spec/factories/procedure.rb @@ -300,7 +300,13 @@ def build_types_de_champ(types_de_champ, revision:, scope: :public, parent: nil) end if type.in?([:drop_down_list, :multiple_drop_down_list, :linked_drop_down_list]) - type_de_champ_attributes[:drop_down_list_value] = options.join("\r\n") + if options.delete(:short).present? + type_de_champ_attributes[:drop_down_list_value] = "val1\r\nval2\r\n--separateur--\r\nval3" + elsif options.delete(:long).present? + type_de_champ_attributes[:drop_down_list_value] = "alpha\r\nbravo\r\n--separateur--\r\ncharly\r\ndelta\r\necho\r\nfox-trot\r\ngolf" + else + type_de_champ_attributes[:drop_down_list_value] = options.join("\r\n") + end end end diff --git a/spec/models/champ_spec.rb b/spec/models/champ_spec.rb index 9ba045034..ca7e32bc2 100644 --- a/spec/models/champ_spec.rb +++ b/spec/models/champ_spec.rb @@ -83,11 +83,6 @@ describe Champ do expect(public_sections).not_to be_empty expect(private_sections).not_to be_empty expect(sections_in_repetition).not_to be_empty - - expect(public_champ.sections).to eq(public_sections) - expect(private_champ.sections).to eq(private_sections) - expect(champ_in_repetition.sections).to eq(sections_in_repetition) - expect(standalone_champ.sections).to eq([]) end end diff --git a/spec/models/champs/header_section_champ_spec.rb b/spec/models/champs/header_section_champ_spec.rb deleted file mode 100644 index 7e3cbee4c..000000000 --- a/spec/models/champs/header_section_champ_spec.rb +++ /dev/null @@ -1,38 +0,0 @@ -describe Champs::HeaderSectionChamp do - describe '#section_index' do - let(:types_de_champ) do - [ - { type: :header_section }, - { type: :civilite }, - { type: :text }, - { type: :header_section }, - { type: :email } - ] - end - let(:types_de_champ_public) { types_de_champ } - let(:procedure) { create(:procedure, types_de_champ_public: types_de_champ_public) } - let(:dossier) { create(:dossier, procedure: procedure) } - - context 'for root-level champs' do - let(:first_header) { dossier.champs_public.first } - let(:second_header) { dossier.champs_public.fourth } - - it 'returns the index of the section (starting from 1)' do - expect(first_header.section_index).to eq 1 - expect(second_header.section_index).to eq 2 - end - end - - context 'for repetition champs' do - let(:types_de_champ_public) { [{ type: :repetition, mandatory: true, children: types_de_champ }] } - - let(:first_header) { dossier.champs_public.first.champs.first } - let(:second_header) { dossier.champs_public.first.champs.fourth } - - it 'returns the index of the section in the repetition (starting from 1)' do - expect(first_header.section_index).to eq 1 - expect(second_header.section_index).to eq 2 - end - end - end -end diff --git a/spec/models/concern/treeable_concern_spec.rb b/spec/models/concern/treeable_concern_spec.rb index d3f12d5d9..674720e92 100644 --- a/spec/models/concern/treeable_concern_spec.rb +++ b/spec/models/concern/treeable_concern_spec.rb @@ -3,35 +3,35 @@ describe TreeableConcern do include TreeableConcern attr_reader :root - def initialize(champs:) - @root = to_tree(champs:) + def initialize(types_de_champ:) + @root = to_tree(types_de_champ:) end end - subject { ChampsToTree.new(champs: champs).root } + subject { ChampsToTree.new(types_de_champ:).root } describe "to_tree" do - let(:header_1) { build(:champ_header_section_level_1) } - let(:header_1_2) { build(:champ_header_section_level_2) } - let(:header_2) { build(:champ_header_section_level_1) } - let(:champ_text) { build(:champ_text) } - let(:champ_textarea) { build(:champ_textarea) } - let(:champ_explication) { build(:champ_explication) } - let(:champ_communes) { build(:champ_communes) } + let(:header_1) { build(:champ_header_section_level_1).type_de_champ } + let(:header_1_2) { build(:champ_header_section_level_2).type_de_champ } + let(:header_2) { build(:champ_header_section_level_1).type_de_champ } + let(:champ_text) { build(:champ_text).type_de_champ } + let(:champ_textarea) { build(:champ_textarea).type_de_champ } + let(:champ_explication) { build(:champ_explication).type_de_champ } + let(:champ_communes) { build(:champ_communes).type_de_champ } context 'without section' do - let(:champs) do + let(:types_de_champ) do [ champ_text, champ_textarea ] end it 'inlines champs at root level' do - expect(subject.size).to eq(champs.size) - expect(subject).to eq(champs) + expect(subject.size).to eq(types_de_champ.size) + expect(subject).to eq(types_de_champ) end end context 'with header_section and champs' do - let(:champs) do + let(:types_de_champ) do [ header_1, champ_explication, @@ -51,7 +51,7 @@ describe TreeableConcern do end context 'leading champs, and in between sections only' do - let(:champs) do + let(:types_de_champ) do [ champ_text, champ_textarea, @@ -74,7 +74,7 @@ describe TreeableConcern do end context 'with one sub sections' do - let(:champs) do + let(:types_de_champ) do [ header_1, champ_explication, @@ -94,11 +94,11 @@ describe TreeableConcern do end context 'with consecutive subsection' do - let(:header_1) { build(:champ_header_section_level_1) } - let(:header_1_2_1) { build(:champ_header_section_level_2) } - let(:header_1_2_2) { build(:champ_header_section_level_2) } - let(:header_1_2_3) { build(:champ_header_section_level_2) } - let(:champs) do + let(:header_1) { build(:champ_header_section_level_1).type_de_champ } + let(:header_1_2_1) { build(:champ_header_section_level_2).type_de_champ } + let(:header_1_2_2) { build(:champ_header_section_level_2).type_de_champ } + let(:header_1_2_3) { build(:champ_header_section_level_2).type_de_champ } + let(:types_de_champ) do [ header_1, header_1_2_1, @@ -123,9 +123,9 @@ describe TreeableConcern do end context 'with one sub sections and one subsub section' do - let(:header_1_2_3) { build(:champ_header_section_level_3) } + let(:header_1_2_3) { build(:champ_header_section_level_3).type_de_champ } - let(:champs) do + let(:types_de_champ) do [ header_1, champ_explication, diff --git a/spec/models/dossier_spec.rb b/spec/models/dossier_spec.rb index 466c5aba4..170cf02b1 100644 --- a/spec/models/dossier_spec.rb +++ b/spec/models/dossier_spec.rb @@ -1540,10 +1540,10 @@ describe Dossier, type: :model do context "with mandatory SIRET champ" do let(:type_de_champ) { { type: :siret, mandatory: true } } - let(:champ_siret) { dossier.champs_public.first } + let(:champ_siret) { dossier.champs.first } before do - champ_siret.value = '44011762001530' + champ_siret.update(value: '44011762001530') end it 'should not have errors' do @@ -1776,6 +1776,7 @@ describe Dossier, type: :model do it "should solve N+1 problem" do dossier.champs_public << create_list(:champ_carte, 3, type_de_champ: type_de_champ_carte, geo_areas: [create(:geo_area)]) + dossier.champs_for_revision count = 0 @@ -1928,9 +1929,9 @@ describe Dossier, type: :model do let(:repetition_second_revision_champ) { dossier_second_revision.champs_public.find(&:repetition?) } let(:dossier) { create(:dossier, procedure: procedure) } let(:dossier_second_revision) { create(:dossier, procedure: procedure) } - let(:dossier_champs_for_export) { Dossier.champs_for_export(dossier.champs_public, procedure.types_de_champ_for_procedure_presentation.not_repetition) } - let(:dossier_second_revision_champs_for_export) { Dossier.champs_for_export(dossier_second_revision.champs_public, procedure.types_de_champ_for_procedure_presentation.not_repetition) } - let(:repetition_second_revision_champs_for_export) { Dossier.champs_for_export(repetition_second_revision_champ.champs, procedure.types_de_champ_for_procedure_presentation.repetition) } + let(:dossier_champs_for_export) { Dossier.champs_for_export(procedure.types_de_champ_for_procedure_presentation.not_repetition, dossier.champs_by_stable_id_with_row) } + let(:dossier_second_revision_champs_for_export) { Dossier.champs_for_export(procedure.types_de_champ_for_procedure_presentation.not_repetition, dossier_second_revision.champs_by_stable_id_with_row) } + let(:repetition_second_revision_champs_for_export) { Dossier.champs_for_export(procedure.types_de_champ_for_procedure_presentation.repetition, dossier.champs_by_stable_id_with_row) } context "when procedure published" do before do @@ -1968,7 +1969,7 @@ describe Dossier, type: :model do repetition = proc_test.types_de_champ_for_procedure_presentation.repetition.first type_champs = proc_test.types_de_champ_for_procedure_presentation(repetition).to_a expect(type_champs.size).to eq(1) - expect(Dossier.champs_for_export(dossier.champs_public, type_champs).size).to eq(3) + expect(Dossier.champs_for_export(type_champs, dossier.champs_by_stable_id_with_row).size).to eq(3) end end end @@ -1991,7 +1992,7 @@ describe Dossier, type: :model do let(:text_tdc) { procedure.active_revision.types_de_champ_public.second } let(:tdcs) { dossier.champs_public.map(&:type_de_champ) } - subject { Dossier.champs_for_export(dossier.champs_public, tdcs) } + subject { Dossier.champs_for_export(tdcs, dossier.champs_by_stable_id_with_row) } before do text_tdc.update(condition: ds_eq(champ_value(yes_no_tdc.stable_id), constant(true))) diff --git a/spec/models/types_de_champ/prefill_repetition_type_de_champ_spec.rb b/spec/models/types_de_champ/prefill_repetition_type_de_champ_spec.rb index c02a7cf15..123b9f099 100644 --- a/spec/models/types_de_champ/prefill_repetition_type_de_champ_spec.rb +++ b/spec/models/types_de_champ/prefill_repetition_type_de_champ_spec.rb @@ -1,9 +1,10 @@ # frozen_string_literal: true RSpec.describe TypesDeChamp::PrefillRepetitionTypeDeChamp, type: :model do - let(:procedure) { create(:procedure) } - let(:type_de_champ) { build(:type_de_champ_repetition, :with_types_de_champ, :with_region_types_de_champ, procedure: procedure) } - let(:champ) { create(:champ_repetition, type_de_champ: type_de_champ) } + let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :repetition, children: [{}, { type: :integer_number }, { type: :regions }] }]) } + let(:dossier) { create(:dossier, procedure: procedure) } + let(:type_de_champ) { champ.type_de_champ } + let(:champ) { dossier.champs.first } let(:prefillable_subchamps) { TypesDeChamp::PrefillRepetitionTypeDeChamp.new(type_de_champ, procedure.active_revision).send(:prefillable_subchamps) } let(:text_repetition) { prefillable_subchamps.first } let(:integer_repetition) { prefillable_subchamps.second } diff --git a/spec/views/shared/dossiers/_champs.html.haml_spec.rb b/spec/views/shared/dossiers/_champs.html.haml_spec.rb index 441ab1a4c..322c80db0 100644 --- a/spec/views/shared/dossiers/_champs.html.haml_spec.rb +++ b/spec/views/shared/dossiers/_champs.html.haml_spec.rb @@ -2,6 +2,9 @@ describe 'shared/dossiers/champs', type: :view do let(:instructeur) { create(:instructeur) } let(:demande_seen_at) { nil } let(:profile) { "instructeur" } + let(:procedure) { create(:procedure, types_de_champ_public:) } + let(:dossier) { create(:dossier, :with_populated_champs, procedure:) } + let(:types_de_champ) { dossier.revision.types_de_champ_public } before do view.extend DossierHelper @@ -12,17 +15,23 @@ describe 'shared/dossiers/champs', type: :view do end end - subject { render 'shared/dossiers/champs', champs:, dossier:, demande_seen_at:, profile: } + subject { render 'shared/dossiers/champs', types_de_champ:, dossier:, demande_seen_at:, profile: } context "there are some champs" do - let(:dossier) { create(:dossier) } - let(:champ1) { create(:champ_checkbox, dossier: dossier, value: 'true') } - let(:champ2) { create(:champ_header_section, dossier: dossier, value: "Section") } - let(:champ3) { create(:champ_explication, dossier: dossier, value: "mazette") } - let(:champ4) { create(:champ_dossier_link, dossier: dossier, value: dossier.id) } - let(:champ5) { create(:champ_textarea, dossier: dossier, value: "Some long text in a textarea.") } - let(:champ6) { create(:champ_rna, value: "W173847273") } - let(:champs) { [champ1, champ2, champ3, champ4, champ5, champ6] } + let(:types_de_champ_public) { [{ type: :checkbox }, { type: :header_section }, { type: :explication }, { type: :dossier_link }, { type: :textarea }, { type: :rna }] } + let(:champ1) { dossier.champs[0] } + let(:champ2) { dossier.champs[1] } + let(:champ3) { dossier.champs[2] } + let(:champ4) { dossier.champs[3] } + let(:champ5) { dossier.champs[4] } + let(:champ6) { dossier.champs[5] } + + before do + champ1.update(value: 'true') + champ4.update(value: dossier.id) + champ5.update(value: "Some long text in a textarea.") + champ6.update(value: "W173847273") + end it "renders titles and values of champs" do expect(subject).to include(champ1.libelle) @@ -41,26 +50,34 @@ describe 'shared/dossiers/champs', type: :view do it "doesn't render explication champs" do expect(subject).not_to include(champ3.libelle) - expect(subject).not_to include(champ3.value) + end + end + + context "with auto-link" do + let(:types_de_champ_public) { [{ type: :text }, { type: :textarea }] } + let(:champ1) { dossier.champs[0] } + let(:champ2) { dossier.champs[1] } + + before do + champ1.update(value: 'https://github.com/tchak') + champ2.update(value: "https://github.com/LeSim") end - context "with auto-link" do - let(:champ1) { create(:champ_text, value: "https://github.com/tchak") } - let(:champ2) { create(:champ_textarea, value: "https://github.com/LeSim") } - let(:link1) { 'https://github.com/tchak' } - let(:link2) { 'https://github.com/LeSim' } + let(:link1) { 'https://github.com/tchak' } + let(:link2) { 'https://github.com/LeSim' } - it "render links" do - expect(subject).to include(link1) - expect(subject).to include(link2) - end + it "render links" do + expect(subject).to include(link1) + expect(subject).to include(link2) end end context "with a dossier champ, but we are not authorized to acces the dossier" do - let(:dossier) { create(:dossier) } - let(:champ) { create(:champ_dossier_link, dossier: dossier, value: dossier.id) } - let(:champs) { [champ] } + let(:types_de_champ_public) { [{ type: :dossier_link }] } + + before do + dossier.champs.first.update(value: dossier.id) + end it { is_expected.not_to have_link("Dossier nº #{dossier.id}") } it { is_expected.to include("Dossier nº #{dossier.id}") } @@ -68,9 +85,11 @@ describe 'shared/dossiers/champs', type: :view do end context "with a dossier_link champ but without value" do - let(:dossier) { create(:dossier) } - let(:champ) { create(:champ_dossier_link, dossier: dossier, value: nil) } - let(:champs) { [champ] } + let(:types_de_champ_public) { [{ type: :dossier_link }] } + + before do + dossier.champs.first.update(value: nil) + end it { is_expected.not_to include("non saisi") } @@ -81,9 +100,11 @@ describe 'shared/dossiers/champs', type: :view do end context "with a piece justificative without value" do - let(:dossier) { create(:dossier) } - let(:champ) { create(:champ_without_piece_justificative, dossier:) } - let(:champs) { [champ] } + let(:types_de_champ_public) { [{ type: :piece_justificative }] } + + before do + dossier.champs.first.piece_justificative_file.purge + end it { is_expected.not_to include("pièce justificative non saisie") } @@ -94,9 +115,9 @@ describe 'shared/dossiers/champs', type: :view do end context "with seen_at" do - let(:dossier) { create(:dossier, :en_construction, depose_at: 1.day.ago) } - let(:champ1) { create(:champ_checkbox, dossier: dossier, value: 'true') } - let(:champs) { [champ1] } + let(:types_de_champ_public) { [{ type: :checkbox }] } + let(:dossier) { create(:dossier, :en_construction, :with_populated_champs, procedure:, depose_at: 1.day.ago) } + let(:champ1) { dossier.champs[0] } context "with a demande_seen_at after champ updated_at" do let(:demande_seen_at) { champ1.updated_at + 1.hour } @@ -105,9 +126,13 @@ describe 'shared/dossiers/champs', type: :view do end context "with champ updated_at at depose_at" do - let(:champ1) { create(:champ_checkbox, dossier: dossier, value: 'true', updated_at: dossier.depose_at) } + let(:champ1) { dossier.champs[0] } let(:demande_seen_at) { champ1.updated_at - 1.hour } + before do + champ1.update(value: 'false', updated_at: dossier.depose_at) + end + it { is_expected.not_to have_css(".fr-badge--new") } end diff --git a/spec/views/shared/dossiers/_edit.html.haml_spec.rb b/spec/views/shared/dossiers/_edit.html.haml_spec.rb index 7814425fd..67f35ed3d 100644 --- a/spec/views/shared/dossiers/_edit.html.haml_spec.rb +++ b/spec/views/shared/dossiers/_edit.html.haml_spec.rb @@ -6,29 +6,34 @@ describe 'shared/dossiers/edit', type: :view do subject { render 'shared/dossiers/edit', dossier: dossier, apercu: false } - context 'when there are some champs' do - let(:dossier) { create(:dossier) } - let(:champ_checkbox) { create(:champ_checkbox, dossier: dossier, value: 'true') } - let(:champ_header_section) { create(:champ_header_section, dossier: dossier, value: 'Section') } - let(:champ_explication) { create(:champ_explication, dossier: dossier, value: 'mazette') } - let(:champ_dossier_link) { create(:champ_dossier_link, dossier: dossier, value: dossier.id) } - let(:champ_textarea) { create(:champ_textarea, dossier: dossier, value: 'Some long text in a textarea.') } - let(:champs) { [champ_checkbox, champ_header_section, champ_explication, champ_dossier_link, champ_textarea] } + let(:procedure) { create(:procedure, types_de_champ_public:) } + let(:dossier) { create(:dossier, :with_populated_champs, procedure:) } - before { dossier.champs_public << champs } + context 'when there are some champs' do + let(:champs_by_stable_id_with_row) { dossier.champs_by_stable_id_with_row } + + let(:type_de_champ_header_section) { procedure.draft_types_de_champ_public.find(&:header_section?) } + let(:type_de_champ_explication) { procedure.draft_types_de_champ_public.find(&:explication?) } + let(:type_de_champ_dossier_link) { procedure.draft_types_de_champ_public.find(&:dossier_link?) } + let(:type_de_champ_checkbox) { procedure.draft_types_de_champ_public.find(&:checkbox?) } + let(:type_de_champ_textarea) { procedure.draft_types_de_champ_public.find(&:textarea?) } + + let(:champ_checkbox) { champs_by_stable_id_with_row[[type_de_champ_checkbox.stable_id]] } + let(:champ_dossier_link) { champs_by_stable_id_with_row[[type_de_champ_dossier_link.stable_id]] } + let(:champ_textarea) { champs_by_stable_id_with_row[[type_de_champ_textarea.stable_id]] } + + let(:types_de_champ_public) { [{ type: :checkbox }, { type: :header_section }, { type: :explication }, { type: :dossier_link }, { type: :textarea }] } it 'renders labels and editable values of champs' do expect(subject).to have_field(champ_checkbox.libelle, checked: true) - expect(subject).to have_css(".header-section", text: champ_header_section.libelle) - expect(subject).to have_text(champ_explication.libelle) - expect(subject).to have_field(champ_dossier_link.libelle, with: champ_dossier_link.value) + expect(subject).to have_css(".header-section", text: type_de_champ_header_section.libelle) + expect(subject).to have_text(type_de_champ_explication.libelle) + expect(subject).to have_field(type_de_champ_dossier_link.libelle, with: champ_dossier_link.value) expect(subject).to have_field(champ_textarea.libelle, with: champ_textarea.value) end context "with standard champs" do - let(:champ_email) { create(:champ_email, dossier: dossier) } - let(:champ_phone) { create(:champ_phone, dossier: dossier) } - let(:champs) { [champ_email, champ_phone] } + let(:types_de_champ_public) { [{ type: :email }, { type: :phone }] } it "does not render basic placeholders" do expect(subject).not_to have_css('input[type="email"][placeholder$="exemple.fr"]') @@ -38,17 +43,18 @@ describe 'shared/dossiers/edit', type: :view do end context 'with a single-value list' do - let(:dossier) { create(:dossier) } - let(:type_de_champ) { create(:type_de_champ_drop_down_list, mandatory: mandatory, procedure: dossier.procedure) } - let(:champ) { create(:champ_drop_down_list, dossier: dossier, type_de_champ: type_de_champ, value: value) } + let(:types_de_champ_public) { [{ type: :drop_down_list, options:, mandatory: }] } + let(:champ) { dossier.champs_public.first } + let(:type_de_champ) { champ.type_de_champ } let(:enabled_options) { type_de_champ.drop_down_list_enabled_non_empty_options } let(:mandatory) { true } - - before { dossier.champs_public << champ } + let(:options) { nil } context 'when the list is short' do let(:value) { 'val1' } + before { champ.update(value:) } + it 'renders the list as radio buttons' do expect(subject).to have_selector('input[type=radio]', count: enabled_options.count) end @@ -65,7 +71,9 @@ describe 'shared/dossiers/edit', type: :view do context 'when the list is long' do let(:value) { 'alpha' } - let(:type_de_champ) { create(:type_de_champ_drop_down_list, :long, procedure: dossier.procedure) } + let(:options) { [:long] } + + before { champ.update(value:) } it 'renders the list as a dropdown' do expect(subject).to have_select(type_de_champ.libelle, options: enabled_options + ['']) @@ -74,21 +82,18 @@ describe 'shared/dossiers/edit', type: :view do end context 'with a multiple-values list' do - let(:dossier) { create(:dossier) } - let(:type_de_champ) { create(:type_de_champ_multiple_drop_down_list, procedure: dossier.procedure, drop_down_list_value: drop_down_list_value) } - let(:champ) { create(:champ_multiple_drop_down_list, dossier: dossier, type_de_champ: type_de_champ, value: champ_value) } + let(:types_de_champ_public) { [{ type: :multiple_drop_down_list, options: }] } + let(:champ) { dossier.champs.first } + let(:type_de_champ) { champ.type_de_champ } let(:options) { type_de_champ.drop_down_list_options } let(:enabled_options) { type_de_champ.drop_down_list_enabled_non_empty_options } - before { dossier.champs_public << champ } - context 'when the list is short' do - let(:drop_down_list_value) { ['valid', 'invalid', 'not sure yet'].join("\r\n") } - let(:champ_value) { ['invalid'].to_json } + let(:options) { ['valid', 'invalid', 'not sure yet'] } it 'renders the list as checkboxes' do expect(subject).to have_selector('input[type=checkbox]', count: enabled_options.count) - expect(subject).to have_selector('input[type=checkbox][checked=checked]', count: 1) + expect(subject).to have_selector('input[type=checkbox][checked=checked]', count: 2) end it 'adds an extra hidden input, to send a blank value even when all checkboxes are unchecked' do @@ -97,8 +102,7 @@ describe 'shared/dossiers/edit', type: :view do end context 'when the list is long' do - let(:drop_down_list_value) { ['peach', 'banana', 'pear', 'apricot', 'apple', 'grapefruit'].join("\r\n") } - let(:champ_value) { ['banana', 'grapefruit'].to_json } + let(:options) { ['peach', 'banana', 'pear', 'apricot', 'apple', 'grapefruit'] } it 'renders the list as a multiple-selection dropdown' do expect(subject).to have_selector('select') @@ -107,13 +111,11 @@ describe 'shared/dossiers/edit', type: :view do end context 'with a mandatory piece justificative' do - let(:dossier) { create(:dossier) } - let(:type_de_champ) { create(:type_de_champ_piece_justificative, procedure: dossier.procedure, mandatory: true) } - let(:champ) { create(:champ_piece_justificative, dossier: dossier, type_de_champ: type_de_champ) } + let(:types_de_champ_public) { [{ type: :piece_justificative, mandatory: true }] } + let(:champ) { dossier.champs.first } context 'when dossier is en construction' do - let(:dossier) { create(:dossier, :en_construction) } - before { dossier.champs_public << champ } + let(:dossier) { create(:dossier, :en_construction, :with_populated_champs, procedure:) } it 'can delete a piece justificative' do expect(subject).to have_selector("[title='Supprimer le fichier #{champ.piece_justificative_file.attachments[0].filename}']") @@ -121,10 +123,6 @@ describe 'shared/dossiers/edit', type: :view do end context 'when dossier is brouillon' do - before do - dossier.champs_public << champ - end - it 'can delete a piece justificative' do expect(subject).to have_selector("[title='Supprimer le fichier #{champ.piece_justificative_file.attachments[0].filename}']") end @@ -132,16 +130,11 @@ describe 'shared/dossiers/edit', type: :view do end context 'with a routed procedure' do - let(:procedure_routee) do - create(:procedure, :routee) - end - let!(:drop_down_tdc) { create(:type_de_champ_drop_down_list, procedure: procedure_routee, drop_down_options: options) } - let(:options) { procedure_routee.groupe_instructeurs.pluck(:label) } - let(:dossier) { create(:dossier, procedure: procedure_routee) } - let(:champs) { [champ_drop_down] } - let(:champ_drop_down) { create(:champ_drop_down_list, dossier: dossier, type_de_champ: drop_down_tdc, value: options.first) } - - before { dossier.champs_public << champs } + let(:groupe_instructeur) { create(:groupe_instructeur) } + let(:procedure) { create(:procedure, :routee, groupe_instructeurs: [groupe_instructeur], types_de_champ_public: [{ type: :drop_down_list, options: }]) } + let(:options) { [groupe_instructeur.label] } + let(:dossier) { create(:dossier, procedure:) } + let(:champ_drop_down) { dossier.champs.first } it 'renders the libelle of the type de champ used for routing' do expect(subject).to include(champ_drop_down.libelle) From 8687d73a73fe364f095bf053c646c915133b5d0e Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Fri, 1 Dec 2023 16:00:03 +0000 Subject: [PATCH 07/13] refactor(dossier): clone only current revision champs --- app/models/concerns/dossier_clone_concern.rb | 11 ++++---- .../concern/dossier_clone_concern_spec.rb | 25 +++++++++++-------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/app/models/concerns/dossier_clone_concern.rb b/app/models/concerns/dossier_clone_concern.rb index 4abb11b05..9a0437d99 100644 --- a/app/models/concerns/dossier_clone_concern.rb +++ b/app/models/concerns/dossier_clone_concern.rb @@ -42,11 +42,10 @@ module DossierCloneConcern end def make_diff(editing_fork) - # TODO: remove champs_public_all usage - origin_champs_index = champs_public_all.index_by(&:stable_id_with_row) - forked_champs_index = editing_fork.champs_public_all.index_by(&:stable_id_with_row) + origin_champs_index = champs_for_revision(:public).index_by(&:stable_id_with_row) + forked_champs_index = editing_fork.champs_for_revision(:public).index_by(&:stable_id_with_row) updated_champs_index = editing_fork - .champs_public_all + .champs_for_revision(:public) .filter { _1.updated_at > editing_fork.created_at } .index_by(&:stable_id_with_row) @@ -81,7 +80,7 @@ module DossierCloneConcern dossier_attributes += [:groupe_instructeur_id] if fork relationships = [:individual, :etablissement] - cloned_champs = champs + cloned_champs = champs_for_revision .index_by(&:id) .transform_values { [_1, _1.clone(fork)] } @@ -143,7 +142,7 @@ module DossierCloneConcern end def apply_diff(diff) - champs_index = (champs_public_all + diff[:added]).index_by(&:stable_id_with_row) + champs_index = (champs_for_revision(:public) + diff[:added]).index_by(&:stable_id_with_row) diff[:added].each do |champ| if champ.child? diff --git a/spec/models/concern/dossier_clone_concern_spec.rb b/spec/models/concern/dossier_clone_concern_spec.rb index e91674b51..6d6c6beb2 100644 --- a/spec/models/concern/dossier_clone_concern_spec.rb +++ b/spec/models/concern/dossier_clone_concern_spec.rb @@ -1,20 +1,23 @@ RSpec.describe DossierCloneConcern do let(:procedure) do - create(:procedure, types_de_champ_public: [ + create(:procedure, types_de_champ_public:, types_de_champ_private:).tap(&:publish!) + end + let(:types_de_champ_public) do + [ { type: :text, libelle: "Un champ text", stable_id: 99 }, { type: :text, libelle: "Un autre champ text", stable_id: 991 }, { type: :yes_no, libelle: "Un champ yes no", stable_id: 992 }, { type: :repetition, libelle: "Un champ répétable", stable_id: 993, mandatory: true, children: [{ type: :text, libelle: 'Nom', stable_id: 994 }] } - ]) + ] end + let(:types_de_champ_private) { [] } let(:dossier) { create(:dossier, :en_construction, procedure:) } let(:forked_dossier) { dossier.find_or_create_editing_fork(dossier.user) } - before { procedure.publish! } - describe '#clone' do - let(:procedure) { create(:procedure, :with_type_de_champ, :with_type_de_champ_private) } - let(:dossier) { create(:dossier, procedure: procedure) } + let(:dossier) { create(:dossier, :en_construction, :with_populated_champs, procedure:) } + let(:types_de_champ_public) { [{}] } + let(:types_de_champ_private) { [{}] } let(:fork) { false } let(:new_dossier) { dossier.clone(fork:) } @@ -137,9 +140,9 @@ RSpec.describe DossierCloneConcern do end context 'for Champs::PieceJustificative, original_champ.piece_justificative_file is duped' do - let(:dossier) { create(:dossier) } - let(:champ_piece_justificative) { create(:champ_piece_justificative, dossier_id: dossier.id) } - before { dossier.champs_public << champ_piece_justificative } + let(:types_de_champ_public) { [{ type: :piece_justificative }] } + let(:champ_piece_justificative) { dossier.champs_public.first } + it { expect(Champs::PieceJustificativeChamp.where(dossier: new_dossier).first.piece_justificative_file.first.blob).to eq(champ_piece_justificative.piece_justificative_file.first.blob) } end @@ -181,8 +184,8 @@ RSpec.describe DossierCloneConcern do it { expect(new_dossier.champs_public[0].updated_at).to eq(dossier.champs_public[0].updated_at) } context "piece justificative champ" do - let(:champ_pj) { create(:champ_piece_justificative, dossier_id: dossier.id) } - before { dossier.champs_public << champ_pj.reload } + let(:types_de_champ_public) { [{ type: :piece_justificative }] } + let(:champ_pj) { dossier.champs_public.first } it { champ_pj_fork = Champs::PieceJustificativeChamp.where(dossier: new_dossier).first From 2eb1f54aa1a1851b5fe77955af57c950713bdc3e Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Fri, 1 Dec 2023 16:00:23 +0000 Subject: [PATCH 08/13] refactor(dossier_loader): ignore champs from old revisions --- app/models/dossier_preloader.rb | 6 +++--- .../recovery/align_champ_with_dossier_revision_spec.rb | 8 ++++---- spec/lib/recovery/revision_life_cycle_spec.rb | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/models/dossier_preloader.rb b/app/models/dossier_preloader.rb index f9a5b02ce..2f541ad93 100644 --- a/app/models/dossier_preloader.rb +++ b/app/models/dossier_preloader.rb @@ -118,9 +118,9 @@ class DossierPreloader dossier.association(:champs_private_all).target += champs end - parent.association(name).target = champs.sort_by do |champ| - [champ.row_id, positions[dossier.revision_id][champ.type_de_champ_id]] - end + parent.association(name).target = champs + .filter { positions[dossier.revision_id][_1.type_de_champ_id].present? } + .sort_by { [_1.row_id, positions[dossier.revision_id][_1.type_de_champ_id]] } # Load children champs champs.filter(&:block?).each do |parent_champ| diff --git a/spec/lib/recovery/align_champ_with_dossier_revision_spec.rb b/spec/lib/recovery/align_champ_with_dossier_revision_spec.rb index 67f966787..44e31a54a 100644 --- a/spec/lib/recovery/align_champ_with_dossier_revision_spec.rb +++ b/spec/lib/recovery/align_champ_with_dossier_revision_spec.rb @@ -26,14 +26,14 @@ describe Recovery::AlignChampWithDossierRevision do expect(bad_dossier.revision).to eq(procedure.published_revision) expect(bad_dossier.champs.size).to eq(2) expect(bad_dossier.champs_public.size).to eq(1) - expect { DossierPreloader.load_one(bad_dossier) }.to raise_error(ArgumentError) + expect { DossierPreloader.load_one(bad_dossier) }.not_to raise_error(ArgumentError) fixer = Recovery::AlignChampWithDossierRevision.new(Dossier) fixer.run expect(fixer.logs.size).to eq(1) expect(fixer.logs.first.fetch(:status)).to eq(:updated) - expect { DossierPreloader.load_one(bad_dossier) }.not_to raise_error + expect { DossierPreloader.load_one(bad_dossier) }.not_to raise_error(ArgumentError) expect(bad_dossier.champs.size).to eq(2) expect(bad_dossier.champs_public.size).to eq(2) end @@ -53,14 +53,14 @@ describe Recovery::AlignChampWithDossierRevision do expect(bad_dossier.revision).to eq(procedure.published_revision) expect(bad_dossier.champs.size).to eq(2) expect(bad_dossier.champs_public.size).to eq(2) - expect { DossierPreloader.load_one(bad_dossier) }.to raise_error(ArgumentError) + expect { DossierPreloader.load_one(bad_dossier) }.not_to raise_error(ArgumentError) fixer = Recovery::AlignChampWithDossierRevision.new(Dossier) fixer.run(destroy_extra_champs: true) expect(fixer.logs.size).to eq(1) expect(fixer.logs.first.fetch(:status)).to eq(:not_found) - expect { DossierPreloader.load_one(bad_dossier) }.not_to raise_error + expect { DossierPreloader.load_one(bad_dossier) }.not_to raise_error(ArgumentError) expect(bad_dossier.champs.size).to eq(1) expect(bad_dossier.champs_public.size).to eq(1) end diff --git a/spec/lib/recovery/revision_life_cycle_spec.rb b/spec/lib/recovery/revision_life_cycle_spec.rb index eb4c5e966..35cf0f9f3 100644 --- a/spec/lib/recovery/revision_life_cycle_spec.rb +++ b/spec/lib/recovery/revision_life_cycle_spec.rb @@ -26,11 +26,11 @@ describe 'Recovery::Revision::LifeCycle' do end it do - expect { DossierPreloader.load_one(dossier) }.to raise_error(ArgumentError) + expect { DossierPreloader.load_one(dossier) }.not_to raise_error(ArgumentError) expect(dossier.champs_public.size).to eq(1) expect(dossier.champs.size).to eq(2) importer.load - expect { DossierPreloader.load_one(dossier) }.not_to raise_error + expect { DossierPreloader.load_one(dossier) }.not_to raise_error(ArgumentError) expect(dossier.champs_public.size).to eq(2) end end From 436caa230541a34e852a3ce1a5f4050ffb3d510a Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 8 Feb 2024 16:41:24 +0100 Subject: [PATCH 09/13] fix(repetition): re-add repetition style and indexing --- .../champs_rows_show_component.html.haml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/components/dossiers/champs_rows_show_component/champs_rows_show_component.html.haml b/app/components/dossiers/champs_rows_show_component/champs_rows_show_component.html.haml index 11382ad4a..83c462630 100644 --- a/app/components/dossiers/champs_rows_show_component/champs_rows_show_component.html.haml +++ b/app/components/dossiers/champs_rows_show_component/champs_rows_show_component.html.haml @@ -2,8 +2,10 @@ - if champ.repetition? - types_de_champ = champ.dossier.revision.children_of(champ.type_de_champ) - champs_by_stable_id_with_row = champ.dossier.champs_by_stable_id_with_row - - champ.row_ids.each do |row_id| - = render ViewableChamp::SectionComponent.new(types_de_champ:, champs_by_stable_id_with_row:, row_id:, demande_seen_at: seen_at, profile:) + - champ.row_ids.each.with_index do |row_id, i| + .fr-background-alt--grey.fr-p-2w.fr-my-3w.fr-ml-2w.champ-repetition + %p.font-weight-bold= "#{champ.libelle} #{i + 1} :" + = render ViewableChamp::SectionComponent.new(types_de_champ:, champs_by_stable_id_with_row:, row_id:, demande_seen_at: seen_at, profile:) - else = render Dossiers::RowShowComponent.new(label: champ.libelle, seen_at:, profile:, content_class: champ.type_champ, updated_at: updated_after_deposer?(champ) ? champ.updated_at : nil) do |c| From 9b26dedab4f331d86ba73e41e40feb911e023135 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Thu, 8 Feb 2024 18:28:15 +0100 Subject: [PATCH 10/13] refactor(dossier): make new methods arguments named --- app/graphql/types/dossier_type.rb | 4 ++-- app/models/attestation_template.rb | 2 +- app/models/champs/repetition_champ.rb | 2 +- app/models/concerns/dossier_clone_concern.rb | 8 ++++---- app/models/concerns/dossier_sections_concern.rb | 8 ++++---- app/models/dossier.rb | 8 ++++---- app/models/procedure_revision.rb | 2 +- app/serializers/champ_serializer.rb | 2 +- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/app/graphql/types/dossier_type.rb b/app/graphql/types/dossier_type.rb index dde9b8f52..f608b4d0b 100644 --- a/app/graphql/types/dossier_type.rb +++ b/app/graphql/types/dossier_type.rb @@ -157,7 +157,7 @@ module Types .for(object, private: false) .load(ApplicationRecord.id_from_typed_id(id)) else - object.champs_for_revision(:public, true).filter(&:visible?) + object.champs_for_revision(scope: :public, root: true).filter(&:visible?) end end @@ -167,7 +167,7 @@ module Types .for(object, private: true) .load(ApplicationRecord.id_from_typed_id(id)) else - object.champs_for_revision(:private, true).filter(&:visible?) + object.champs_for_revision(scope: :private, root: true).filter(&:visible?) end end diff --git a/app/models/attestation_template.rb b/app/models/attestation_template.rb index a2e80ea57..eea88355c 100644 --- a/app/models/attestation_template.rb +++ b/app/models/attestation_template.rb @@ -79,7 +79,7 @@ class AttestationTemplate < ApplicationRecord end def unspecified_champs_for_dossier(dossier) - champs_by_stable_id = dossier.champs_for_revision(nil, true).index_by { "tdc#{_1.stable_id}" } + champs_by_stable_id = dossier.champs_for_revision(root: true).index_by { "tdc#{_1.stable_id}" } used_tags.filter_map do |used_tag| corresponding_champ = champs_by_stable_id[used_tag] diff --git a/app/models/champs/repetition_champ.rb b/app/models/champs/repetition_champ.rb index bb8b123b6..ad6a58159 100644 --- a/app/models/champs/repetition_champ.rb +++ b/app/models/champs/repetition_champ.rb @@ -4,7 +4,7 @@ class Champs::RepetitionChamp < Champ def rows dossier - .champs_for_revision(type_de_champ) + .champs_for_revision(scope: type_de_champ) .group_by(&:row_id).values end diff --git a/app/models/concerns/dossier_clone_concern.rb b/app/models/concerns/dossier_clone_concern.rb index 9a0437d99..000e6a6e5 100644 --- a/app/models/concerns/dossier_clone_concern.rb +++ b/app/models/concerns/dossier_clone_concern.rb @@ -42,10 +42,10 @@ module DossierCloneConcern end def make_diff(editing_fork) - origin_champs_index = champs_for_revision(:public).index_by(&:stable_id_with_row) - forked_champs_index = editing_fork.champs_for_revision(:public).index_by(&:stable_id_with_row) + origin_champs_index = champs_for_revision(scope: :public).index_by(&:stable_id_with_row) + forked_champs_index = editing_fork.champs_for_revision(scope: :public).index_by(&:stable_id_with_row) updated_champs_index = editing_fork - .champs_for_revision(:public) + .champs_for_revision(scope: :public) .filter { _1.updated_at > editing_fork.created_at } .index_by(&:stable_id_with_row) @@ -142,7 +142,7 @@ module DossierCloneConcern end def apply_diff(diff) - champs_index = (champs_for_revision(:public) + diff[:added]).index_by(&:stable_id_with_row) + champs_index = (champs_for_revision(scope: :public) + diff[:added]).index_by(&:stable_id_with_row) diff[:added].each do |champ| if champ.child? diff --git a/app/models/concerns/dossier_sections_concern.rb b/app/models/concerns/dossier_sections_concern.rb index e14f4b61b..4b3c9b646 100644 --- a/app/models/concerns/dossier_sections_concern.rb +++ b/app/models/concerns/dossier_sections_concern.rb @@ -6,11 +6,11 @@ module DossierSectionsConcern @sections = Hash.new do |hash, parent| case parent when :public - hash[parent] = champs_for_revision(:public, true).filter(&:header_section?) + hash[parent] = champs_for_revision(scope: :public, root: true).filter(&:header_section?) when :private - hash[parent] = champs_for_revision(:private, true).filter(&:header_section?) + hash[parent] = champs_for_revision(scope: :private, root: true).filter(&:header_section?) else - hash[parent] = champs_for_revision(parent.type_de_champ).filter(&:header_section?) + hash[parent] = champs_for_revision(scope: parent.type_de_champ).filter(&:header_section?) end end @sections[champ.parent || (champ.public? ? :public : :private)] @@ -23,7 +23,7 @@ module DossierSectionsConcern end def index_for_section_header(champ) - champs = champ.private? ? champs_for_revision(:private, true) : champs_for_revision(:public, true) + champs = champ.private? ? champs_for_revision(scope: :private, root: true) : champs_for_revision(scope: :public, root: true) index = 1 champs.each do |c| if c.repetition? diff --git a/app/models/dossier.rb b/app/models/dossier.rb index cbe452315..c643ddc14 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -610,7 +610,7 @@ class Dossier < ApplicationRecord def any_etablissement_as_degraded_mode? return true if etablissement&.as_degraded_mode? - return true if champs_for_revision(:public).any? { _1.etablissement&.as_degraded_mode? } + return true if champs_for_revision(scope: :public).any? { _1.etablissement&.as_degraded_mode? } false end @@ -1165,7 +1165,7 @@ class Dossier < ApplicationRecord end def check_mandatory_and_visible_champs - champs_for_revision(:public) + champs_for_revision(scope: :public) .filter { _1.child? ? _1.parent.visible? : true } .filter(&:visible?) .filter(&:mandatory_blank?) @@ -1396,13 +1396,13 @@ class Dossier < ApplicationRecord champs_for_revision.index_by(&:stable_id_with_row) end - def champs_for_revision(scope = nil, root = false) + def champs_for_revision(scope: nil, root: false) champs_index = champs.group_by(&:stable_id) if scope.is_a?(TypeDeChamp) revision.children_of(scope) else - revision.types_de_champ_for(scope, root) + revision.types_de_champ_for(scope:, root:) end.flat_map { champs_index[_1.stable_id] || [] } end diff --git a/app/models/procedure_revision.rb b/app/models/procedure_revision.rb index 8e1d07811..ff9959f1e 100644 --- a/app/models/procedure_revision.rb +++ b/app/models/procedure_revision.rb @@ -155,7 +155,7 @@ class ProcedureRevision < ApplicationRecord dossier end - def types_de_champ_for(scope = nil, root = false) + def types_de_champ_for(scope: nil, root: false) # We return an unordered collection return types_de_champ if !root && scope.nil? return types_de_champ.filter { scope == :public ? _1.public? : _1.private? } if !root diff --git a/app/serializers/champ_serializer.rb b/app/serializers/champ_serializer.rb index bc92a8264..003f611a5 100644 --- a/app/serializers/champ_serializer.rb +++ b/app/serializers/champ_serializer.rb @@ -47,7 +47,7 @@ class ChampSerializer < ActiveModel::Serializer def rows object.dossier - .champs_for_revision(object.type_de_champ) + .champs_for_revision(scope: object.type_de_champ) .group_by(&:row_id) .values .map.with_index(1) { |champs, index| Row.new(index:, champs:) } From 1193c866bf269c16a86a0e4b8b3527380494aab0 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Thu, 8 Feb 2024 19:20:38 +0100 Subject: [PATCH 11/13] refactor(dossier): has_annotations? --- app/models/dossier.rb | 4 ++++ app/views/administrateurs/procedures/apercu.html.haml | 2 +- app/views/dossiers/show.pdf.prawn | 2 +- app/views/shared/dossiers/_edit_annotations.html.haml | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/models/dossier.rb b/app/models/dossier.rb index c643ddc14..8f345fb4f 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -1406,6 +1406,10 @@ class Dossier < ApplicationRecord end.flat_map { champs_index[_1.stable_id] || [] } end + def has_annotations? + revision.revision_types_de_champ_private.present? + end + private def create_missing_traitemets diff --git a/app/views/administrateurs/procedures/apercu.html.haml b/app/views/administrateurs/procedures/apercu.html.haml index aa55c2b81..5941fdbb4 100644 --- a/app/views/administrateurs/procedures/apercu.html.haml +++ b/app/views/administrateurs/procedures/apercu.html.haml @@ -7,7 +7,7 @@ = tab_item('le dossier', apercu_admin_procedure_path(@dossier.procedure, tab: 'dossier'), active: @tab == 'dossier') - - if @dossier.champs_private.size > 0 + - if @dossier.has_annotations? = tab_item('les annotations privées', apercu_admin_procedure_path(@dossier.procedure, tab: 'annotations-privees'), active: @tab == 'annotations-privees') diff --git a/app/views/dossiers/show.pdf.prawn b/app/views/dossiers/show.pdf.prawn index cde96074b..eaf32db3b 100644 --- a/app/views/dossiers/show.pdf.prawn +++ b/app/views/dossiers/show.pdf.prawn @@ -349,7 +349,7 @@ prawn_document(page_size: "A4") do |pdf| add_title(pdf, 'Formulaire') add_champs(pdf, @dossier.champs_public) - if @acls[:include_infos_administration] && @dossier.champs_private.present? + if @acls[:include_infos_administration] && @dossier.has_annotations? add_title(pdf, "Annotations privées") add_champs(pdf, @dossier.champs_private) end diff --git a/app/views/shared/dossiers/_edit_annotations.html.haml b/app/views/shared/dossiers/_edit_annotations.html.haml index 48644a34e..7cef69996 100644 --- a/app/views/shared/dossiers/_edit_annotations.html.haml +++ b/app/views/shared/dossiers/_edit_annotations.html.haml @@ -1,5 +1,5 @@ .container.dossier-edit - - if dossier.champs_private.present? + - if dossier.has_annotations? %section.counter-start-header-section = render NestedForms::FormOwnerComponent.new = form_for dossier, url: annotations_instructeur_dossier_path(dossier.procedure, dossier), html: { class: 'form', multipart: true } do |f| From 662917799bb7c5885b56845a0c5b9a1471aa5682 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Mon, 12 Feb 2024 17:05:25 +0100 Subject: [PATCH 12/13] refactor(dossier): remove champ ordered scope --- app/models/champ.rb | 10 +--------- app/models/dossier.rb | 4 ++-- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/app/models/champ.rb b/app/models/champ.rb index 8b65d6ba4..3fcd43563 100644 --- a/app/models/champ.rb +++ b/app/models/champ.rb @@ -10,7 +10,7 @@ class Champ < ApplicationRecord # here because otherwise we can't easily use includes in our queries. has_many :geo_areas, -> { order(:created_at) }, dependent: :destroy, inverse_of: :champ belongs_to :etablissement, optional: true, dependent: :destroy - has_many :champs, -> { ordered }, foreign_key: :parent_id, inverse_of: :parent + has_many :champs, foreign_key: :parent_id, inverse_of: :parent delegate :procedure, to: :dossier @@ -70,14 +70,6 @@ class Champ < ApplicationRecord scope :updated_since?, -> (date) { where('champs.updated_at > ?', date) } scope :public_only, -> { where(private: false) } scope :private_only, -> { where(private: true) } - scope :ordered, -> do - includes(:type_de_champ) - .joins(dossier: { revision: :revision_types_de_champ }) - .where('procedure_revision_types_de_champ.type_de_champ_id = champs.type_de_champ_id') - .order(:row_id, :position) - end - scope :public_ordered, -> { public_only.ordered } - scope :private_ordered, -> { private_only.ordered } scope :root, -> { where(parent_id: nil) } scope :prefilled, -> { where(prefilled: true) } diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 8f345fb4f..936b5e9a9 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -45,8 +45,8 @@ class Dossier < ApplicationRecord # We have to remove champs in a particular order - champs with a reference to a parent have to be # removed first, otherwise we get a foreign key constraint error. has_many :champs_to_destroy, -> { order(:parent_id) }, class_name: 'Champ', inverse_of: false, dependent: :destroy - has_many :champs_public, -> { root.public_ordered }, class_name: 'Champ', inverse_of: false - has_many :champs_private, -> { root.private_ordered }, class_name: 'Champ', inverse_of: false + has_many :champs_public, -> { root.public_only }, class_name: 'Champ', inverse_of: false + has_many :champs_private, -> { root.private_only }, class_name: 'Champ', inverse_of: false has_many :champs_public_all, -> { public_only }, class_name: 'Champ', inverse_of: false has_many :champs_private_all, -> { private_only }, class_name: 'Champ', inverse_of: false has_many :prefilled_champs_public, -> { root.public_only.prefilled }, class_name: 'Champ', inverse_of: false From f3a97876bd7e8e9f0f35b412f390ea834d8607e7 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Fri, 16 Feb 2024 12:02:24 +0100 Subject: [PATCH 13/13] fix(champ): champs are not ordered anymore --- .../align_champ_with_dossier_revision_spec.rb | 10 +++++----- .../concern/dossier_rebase_concern_spec.rb | 9 +++++---- spec/models/dossier_spec.rb | 20 ------------------- 3 files changed, 10 insertions(+), 29 deletions(-) diff --git a/spec/lib/recovery/align_champ_with_dossier_revision_spec.rb b/spec/lib/recovery/align_champ_with_dossier_revision_spec.rb index 44e31a54a..a708b2916 100644 --- a/spec/lib/recovery/align_champ_with_dossier_revision_spec.rb +++ b/spec/lib/recovery/align_champ_with_dossier_revision_spec.rb @@ -25,15 +25,15 @@ describe Recovery::AlignChampWithDossierRevision do expect(procedure.revisions.size).to eq(3) expect(bad_dossier.revision).to eq(procedure.published_revision) expect(bad_dossier.champs.size).to eq(2) - expect(bad_dossier.champs_public.size).to eq(1) - expect { DossierPreloader.load_one(bad_dossier) }.not_to raise_error(ArgumentError) + expect(bad_dossier.champs_public.size).to eq(2) + expect { DossierPreloader.load_one(bad_dossier) }.not_to raise_error fixer = Recovery::AlignChampWithDossierRevision.new(Dossier) fixer.run expect(fixer.logs.size).to eq(1) expect(fixer.logs.first.fetch(:status)).to eq(:updated) - expect { DossierPreloader.load_one(bad_dossier) }.not_to raise_error(ArgumentError) + expect { DossierPreloader.load_one(bad_dossier) }.not_to raise_error expect(bad_dossier.champs.size).to eq(2) expect(bad_dossier.champs_public.size).to eq(2) end @@ -53,14 +53,14 @@ describe Recovery::AlignChampWithDossierRevision do expect(bad_dossier.revision).to eq(procedure.published_revision) expect(bad_dossier.champs.size).to eq(2) expect(bad_dossier.champs_public.size).to eq(2) - expect { DossierPreloader.load_one(bad_dossier) }.not_to raise_error(ArgumentError) + expect { DossierPreloader.load_one(bad_dossier) }.not_to raise_error fixer = Recovery::AlignChampWithDossierRevision.new(Dossier) fixer.run(destroy_extra_champs: true) expect(fixer.logs.size).to eq(1) expect(fixer.logs.first.fetch(:status)).to eq(:not_found) - expect { DossierPreloader.load_one(bad_dossier) }.not_to raise_error(ArgumentError) + expect { DossierPreloader.load_one(bad_dossier) }.not_to raise_error expect(bad_dossier.champs.size).to eq(1) expect(bad_dossier.champs_public.size).to eq(1) end diff --git a/spec/models/concern/dossier_rebase_concern_spec.rb b/spec/models/concern/dossier_rebase_concern_spec.rb index 795e8afc4..18ae80782 100644 --- a/spec/models/concern/dossier_rebase_concern_spec.rb +++ b/spec/models/concern/dossier_rebase_concern_spec.rb @@ -577,7 +577,7 @@ describe DossierRebaseConcern do end let!(:dossier) { create(:dossier, procedure: procedure) } - def champ_libelles = dossier.champs_public.map(&:libelle) + def champ_libelles = dossier.revision.types_de_champ_public.map(&:libelle) context 'when a tdc is added in the middle' do before do @@ -639,7 +639,7 @@ describe DossierRebaseConcern do tdc_to_update.update(type_champ: :integer_number) end - it { expect { subject }.to change { dossier.champs_public.map(&:type_champ) }.from(['text', 'text']).to(['integer_number', 'text']) } + it { expect { subject }.to change { dossier.revision.types_de_champ_public.map(&:type_champ) }.from(['text', 'text']).to(['integer_number', 'text']) } it { expect { subject }.to change { first_champ.class }.from(Champs::TextChamp).to(Champs::IntegerNumberChamp) } it { expect { subject }.to change { first_champ.value }.from('v1').to(nil) } it { expect { subject }.to change { first_champ.external_id }.from('123').to(nil) } @@ -667,7 +667,8 @@ describe DossierRebaseConcern do let!(:dossier) { create(:dossier, procedure: procedure) } let(:repetition) { procedure.draft_revision.types_de_champ.find(&:repetition?) } - def child_libelles = dossier.champs_public.first.champs.map(&:libelle) + def child_libelles = dossier.revision.revision_types_de_champ_public.first.revision_types_de_champ.map(&:libelle) + def child_types_champ = dossier.revision.revision_types_de_champ_public.first.revision_types_de_champ.map(&:type_champ) context 'when a child tdc is added in the middle' do before do @@ -708,7 +709,7 @@ describe DossierRebaseConcern do tdc_to_update.update(type_champ: :integer_number) end - it { expect { subject }.to change { dossier.champs_public.first.champs.map(&:type_champ) }.from(['text', 'text']).to(['integer_number', 'text']) } + it { expect { subject }.to change { child_types_champ }.from(['text', 'text']).to(['integer_number', 'text']) } end context 'when the parents type is changed' do diff --git a/spec/models/dossier_spec.rb b/spec/models/dossier_spec.rb index 170cf02b1..94d205a90 100644 --- a/spec/models/dossier_spec.rb +++ b/spec/models/dossier_spec.rb @@ -357,26 +357,6 @@ describe Dossier, type: :model do end end - describe '#champs' do - let(:procedure) { create(:procedure) } - let!(:tdc_1) { create(:type_de_champ, libelle: 'l1', position: 1, procedure: procedure) } - let!(:tdc_3) { create(:type_de_champ, libelle: 'l3', position: 3, procedure: procedure) } - let!(:tdc_2) { create(:type_de_champ, libelle: 'l2', position: 2, procedure: procedure) } - let(:dossier) { create(:dossier, procedure: procedure) } - - it { expect(dossier.champs_public.pluck(:libelle)).to match(['l1', 'l2', 'l3']) } - end - - describe '#champs_private' do - let(:procedure) { create(:procedure) } - let!(:tdc_1) { create(:type_de_champ, :private, libelle: 'l1', position: 1, procedure: procedure) } - let!(:tdc_3) { create(:type_de_champ, :private, libelle: 'l3', position: 3, procedure: procedure) } - let!(:tdc_2) { create(:type_de_champ, :private, libelle: 'l2', position: 2, procedure: procedure) } - let(:dossier) { create(:dossier, procedure: procedure) } - - it { expect(dossier.champs_private.pluck(:libelle)).to match(['l1', 'l2', 'l3']) } - end - describe "#text_summary" do let(:service) { create(:service, nom: 'nom du service') } let(:procedure) { create(:procedure, libelle: "Démarche", organisation: "Organisme", service: service) }