diff --git a/app/components/types_de_champ_editor/block_component.rb b/app/components/types_de_champ_editor/block_component.rb index 22cf4bdaa..e0010ee48 100644 --- a/app/components/types_de_champ_editor/block_component.rb +++ b/app/components/types_de_champ_editor/block_component.rb @@ -1,7 +1,8 @@ class TypesDeChampEditor::BlockComponent < ApplicationComponent - def initialize(block:, coordinates:) + def initialize(block:, coordinates:, upper_coordinates: []) @block = block @coordinates = coordinates + @upper_coordinates = upper_coordinates end private diff --git a/app/components/types_de_champ_editor/block_component/block_component.html.haml b/app/components/types_de_champ_editor/block_component/block_component.html.haml index 59d07c80a..770b47671 100644 --- a/app/components/types_de_champ_editor/block_component/block_component.html.haml +++ b/app/components/types_de_champ_editor/block_component/block_component.html.haml @@ -1,3 +1,3 @@ %ul.types-de-champ-block{ id: block_id, data: sortable_options } - @coordinates.each.with_index do |coordinate, i| - = render TypesDeChampEditor::ChampComponent.new(coordinate: coordinate, upper_coordinates: @coordinates.take(i)) + = render TypesDeChampEditor::ChampComponent.new(coordinate: coordinate, upper_coordinates: @upper_coordinates + @coordinates.take(i)) diff --git a/app/components/types_de_champ_editor/champ_component.rb b/app/components/types_de_champ_editor/champ_component.rb index 6160d7790..856d0d06a 100644 --- a/app/components/types_de_champ_editor/champ_component.rb +++ b/app/components/types_de_champ_editor/champ_component.rb @@ -118,6 +118,6 @@ class TypesDeChampEditor::ChampComponent < ApplicationComponent end def conditional_enabled? - !type_de_champ.private? && !coordinate.child? + !type_de_champ.private? end end diff --git a/app/components/types_de_champ_editor/champ_component/champ_component.html.haml b/app/components/types_de_champ_editor/champ_component/champ_component.html.haml index 830bb3aab..5a8d36080 100644 --- a/app/components/types_de_champ_editor/champ_component/champ_component.html.haml +++ b/app/components/types_de_champ_editor/champ_component/champ_component.html.haml @@ -87,7 +87,7 @@ - if type_de_champ.block? .flex.justify-start.section.ml-1 .editor-block.flex-grow.cell - = render TypesDeChampEditor::BlockComponent.new(block: coordinate, coordinates: coordinate.revision_types_de_champ) + = render TypesDeChampEditor::BlockComponent.new(block: coordinate, coordinates: coordinate.revision_types_de_champ, upper_coordinates: @upper_coordinates) = render TypesDeChampEditor::AddChampButtonComponent.new(revision: coordinate.revision, parent: coordinate, is_annotation: coordinate.private?) - if conditional_enabled? diff --git a/app/controllers/administrateurs/conditions_controller.rb b/app/controllers/administrateurs/conditions_controller.rb index 42cb328c1..91bd49eb9 100644 --- a/app/controllers/administrateurs/conditions_controller.rb +++ b/app/controllers/administrateurs/conditions_controller.rb @@ -56,7 +56,13 @@ module Administrateurs def retrieve_coordinate_and_uppers @tdc = draft_revision.find_and_ensure_exclusive_use(params[:stable_id]) @coordinate = draft_revision.coordinate_for(@tdc) - @upper_tdcs = @coordinate.upper_siblings.map(&:type_de_champ) + + upper_coordinates = @coordinate.upper_siblings + if @coordinate.child? + upper_coordinates += @coordinate.parent.upper_siblings + end + + @upper_tdcs = upper_coordinates.map(&:type_de_champ) end def draft_revision diff --git a/app/models/champ.rb b/app/models/champ.rb index 7d95b352d..5b9f0b4e6 100644 --- a/app/models/champ.rb +++ b/app/models/champ.rb @@ -239,7 +239,7 @@ class Champ < ApplicationRecord private def champs_for_condition - private? ? dossier.champs_private : dossier.champs_public + dossier.champs.filter { _1.row.nil? || _1.row == row } end def html_id diff --git a/spec/controllers/administrateurs/conditions_controller_spec.rb b/spec/controllers/administrateurs/conditions_controller_spec.rb index 2b34060ab..3552a848b 100644 --- a/spec/controllers/administrateurs/conditions_controller_spec.rb +++ b/spec/controllers/administrateurs/conditions_controller_spec.rb @@ -1,118 +1,159 @@ describe Administrateurs::ConditionsController, type: :controller do include Logic - let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :integer_number }] * 3) } - let(:first_coordinate) { procedure.draft_revision.revision_types_de_champ.first } - let(:second_coordinate) { procedure.draft_revision.revision_types_de_champ.first } - let(:third_coordinate) { procedure.draft_revision.revision_types_de_champ.first } - let(:first_tdc) { procedure.draft_revision.types_de_champ.first } - let(:second_tdc) { procedure.draft_revision.types_de_champ.second } - let(:third_tdc) { procedure.draft_revision.types_de_champ.third } - before { sign_in(procedure.administrateurs.first.user) } - let(:default_params) do - { - procedure_id: procedure.id, - stable_id: third_tdc.stable_id - } - end + context 'without bloc repetition' do + let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :integer_number }] * 3) } + let(:first_tdc) { procedure.draft_revision.types_de_champ.first } + let(:second_tdc) { procedure.draft_revision.types_de_champ.second } + let(:third_tdc) { procedure.draft_revision.types_de_champ.third } - describe '#update' do - before { post :update, params: params, format: :turbo_stream } + before { sign_in(procedure.administrateurs.first.user) } - let(:params) { default_params.merge(type_de_champ: { condition_form: condition_form }) } - - let(:condition_form) do + let(:default_params) do { - rows: [ - { - targeted_champ: champ_value(first_tdc.stable_id).to_json, - operator_name: Logic::Eq.name, - value: '2' - } - ] + procedure_id: procedure.id, + stable_id: third_tdc.stable_id } end - it do - expect(third_tdc.reload.condition).to eq(ds_eq(champ_value(first_tdc.stable_id), constant(2))) - expect(assigns(:coordinate)).to eq(procedure.draft_revision.coordinate_for(third_tdc)) - expect(assigns(:upper_tdcs)).to eq([first_tdc, second_tdc]) + describe '#update' do + before { post :update, params: params, format: :turbo_stream } + + let(:params) { default_params.merge(type_de_champ: { condition_form: condition_form }) } + + let(:condition_form) do + { + rows: [ + { + targeted_champ: champ_value(first_tdc.stable_id).to_json, + operator_name: Logic::Eq.name, + value: '2' + } + ] + } + end + + it do + expect(third_tdc.reload.condition).to eq(ds_eq(champ_value(first_tdc.stable_id), constant(2))) + expect(assigns(:coordinate)).to eq(procedure.draft_revision.coordinate_for(third_tdc)) + expect(assigns(:upper_tdcs)).to eq([first_tdc, second_tdc]) + end + end + + describe '#add_row' do + before { post :add_row, params: default_params, format: :turbo_stream } + + it do + expect(third_tdc.reload.condition).to eq(empty_operator(empty, empty)) + expect(assigns(:coordinate)).to eq(procedure.draft_revision.coordinate_for(third_tdc)) + expect(assigns(:upper_tdcs)).to eq([first_tdc, second_tdc]) + end + end + + describe '#delete_row' do + before { delete :delete_row, params: params.merge(row_index: 0), format: :turbo_stream } + + let(:params) { default_params.merge(type_de_champ: { condition_form: condition_form }) } + + let(:condition_form) do + { + rows: [ + { + targeted_champ: champ_value(1).to_json, + operator_name: Logic::Eq.name, + value: '2' + } + ] + } + end + + it do + expect(third_tdc.reload.condition).to eq(nil) + expect(assigns(:coordinate)).to eq(procedure.draft_revision.coordinate_for(third_tdc)) + expect(assigns(:upper_tdcs)).to eq([first_tdc, second_tdc]) + end + end + + describe '#destroy' do + before do + second_tdc.update(condition: empty_operator(empty, empty)) + delete :destroy, params: default_params, format: :turbo_stream + end + + it do + expect(third_tdc.reload.condition).to eq(nil) + expect(assigns(:coordinate)).to eq(procedure.draft_revision.coordinate_for(third_tdc)) + expect(assigns(:upper_tdcs)).to eq([first_tdc, second_tdc]) + end + end + + describe '#change_targeted_champ' do + before do + second_tdc.update(condition: empty_operator(empty, empty)) + patch :change_targeted_champ, params: params, format: :turbo_stream + end + + let(:params) { default_params.merge(type_de_champ: { condition_form: condition_form }) } + + let(:condition_form) do + { + rows: [ + { + targeted_champ: champ_value(second_tdc.stable_id).to_json, + operator_name: Logic::EmptyOperator.name, + value: empty.to_json + } + ] + } + end + + it do + expect(third_tdc.reload.condition).to eq(ds_eq(champ_value(second_tdc.stable_id), constant(0))) + expect(assigns(:coordinate)).to eq(procedure.draft_revision.coordinate_for(third_tdc)) + expect(assigns(:upper_tdcs)).to eq([first_tdc, second_tdc]) + end end end - describe '#add_row' do - before { post :add_row, params: default_params, format: :turbo_stream } - - it do - expect(third_tdc.reload.condition).to eq(empty_operator(empty, empty)) - expect(assigns(:coordinate)).to eq(procedure.draft_revision.coordinate_for(third_tdc)) - expect(assigns(:upper_tdcs)).to eq([first_tdc, second_tdc]) + context 'with a repetiton bloc' do + let(:procedure) do + create(:procedure, types_de_champ_public: [ + { type: :integer_number, libelle: 'top_1' }, + { + type: :repetition, + libelle: 'repetition', + children: [ + { type: :integer_number, libelle: 'child_1' }, + { type: :integer_number, libelle: 'child_2' } + ] + } + ]) end - end + let(:tdcs) { procedure.draft_revision.types_de_champ } + let(:top) { tdcs.find_by(libelle: 'top_1') } + let(:repetition) { tdcs.find_by(libelle: 'repetition') } + let(:child_1) { tdcs.find_by(libelle: 'child_1') } + let(:child_2) { tdcs.find_by(libelle: 'child_2') } - describe '#delete_row' do - before { delete :delete_row, params: params.merge(row_index: 0), format: :turbo_stream } - - let(:params) { default_params.merge(type_de_champ: { condition_form: condition_form }) } - - let(:condition_form) do + let(:default_params) do { - rows: [ - { - targeted_champ: champ_value(1).to_json, - operator_name: Logic::Eq.name, - value: '2' - } - ] + procedure_id: procedure.id, + stable_id: child_2.stable_id } end - it do - expect(third_tdc.reload.condition).to eq(nil) - expect(assigns(:coordinate)).to eq(procedure.draft_revision.coordinate_for(third_tdc)) - expect(assigns(:upper_tdcs)).to eq([first_tdc, second_tdc]) - end - end + describe '#add_row' do + before do + post :add_row, params: default_params, format: :turbo_stream + end - describe '#destroy' do - before do - second_tdc.update(condition: empty_operator(empty, empty)) - delete :destroy, params: default_params, format: :turbo_stream - end - - it do - expect(third_tdc.reload.condition).to eq(nil) - expect(assigns(:coordinate)).to eq(procedure.draft_revision.coordinate_for(third_tdc)) - expect(assigns(:upper_tdcs)).to eq([first_tdc, second_tdc]) - end - end - - describe '#change_targeted_champ' do - before do - second_tdc.update(condition: empty_operator(empty, empty)) - patch :change_targeted_champ, params: params, format: :turbo_stream - end - - let(:params) { default_params.merge(type_de_champ: { condition_form: condition_form }) } - - let(:condition_form) do - { - rows: [ - { - targeted_champ: champ_value(second_tdc.stable_id).to_json, - operator_name: Logic::EmptyOperator.name, - value: empty.to_json - } - ] - } - end - - it do - expect(third_tdc.reload.condition).to eq(ds_eq(champ_value(second_tdc.stable_id), constant(0))) - expect(assigns(:coordinate)).to eq(procedure.draft_revision.coordinate_for(third_tdc)) - expect(assigns(:upper_tdcs)).to eq([first_tdc, second_tdc]) + it do + expect(child_2.reload.condition).to eq(empty_operator(empty, empty)) + expect(assigns(:coordinate)).to eq(procedure.draft_revision.coordinate_for(child_2)) + expect(assigns(:upper_tdcs)).to eq([child_1, top]) + end end end end diff --git a/spec/system/users/brouillon_spec.rb b/spec/system/users/brouillon_spec.rb index 8714a0ce9..9d810530a 100644 --- a/spec/system/users/brouillon_spec.rb +++ b/spec/system/users/brouillon_spec.rb @@ -289,52 +289,87 @@ describe 'The user' do include Logic context 'with a repetition' do + let(:stable_id) { 999 } + let(:condition) { greater_than_eq(champ_value(stable_id), constant(18)) } let(:procedure) do - procedure = create(:procedure, :published, :for_individual, - types_de_champ_public: [ - { type: :integer_number, libelle: 'age' }, - { - type: :repetition, libelle: 'repetition', children: [ - { type: :text, libelle: 'nom', mandatory: true } - ] - } - ]) - - age = procedure.published_revision.types_de_champ.where(libelle: 'age').first - repetition = procedure.published_revision.types_de_champ.repetition.first - repetition.update(condition: greater_than_eq(champ_value(age.stable_id), constant(18))) - - procedure + create(:procedure, :published, :for_individual, + types_de_champ_public: [ + { type: :integer_number, libelle: 'age', stable_id: }, + { + type: :repetition, libelle: 'repetition', condition:, children: [ + { type: :text, libelle: 'nom', mandatory: true } + ] + } + ]) end scenario 'submit a dossier with an hidden mandatory champ within a repetition', js: true do log_in(user, procedure) fill_individual + fill_in('age', with: 10) click_on 'Déposer le dossier' expect(page).to have_current_path(merci_dossier_path(user_dossier)) end end - context 'with a required conditionnal champ' do + context 'with a condition inside repetition' do + let(:a_stable_id) { 999 } + let(:b_stable_id) { 9999 } + let(:a_condition) { ds_eq(champ_value(a_stable_id), constant(true)) } + let(:b_condition) { ds_eq(champ_value(b_stable_id), constant(true)) } + let(:condition) { ds_or([a_condition, b_condition]) } let(:procedure) do - procedure = create(:procedure, :published, :for_individual, - types_de_champ_public: [ - { type: :integer_number, libelle: 'age' }, - { type: :text, libelle: 'nom', mandatory: true } - ]) + create(:procedure, :published, :for_individual, + types_de_champ_public: [ + { type: :checkbox, libelle: 'champ_a', stable_id: a_stable_id }, + { + type: :repetition, libelle: 'repetition', children: [ + { type: :checkbox, libelle: 'champ_b', stable_id: b_stable_id }, + { type: :text, libelle: 'champ_c', condition: } + ] + } + ]) + end - age, nom = procedure.draft_revision.types_de_champ.all - nom.update(condition: greater_than_eq(champ_value(age.stable_id), constant(18))) + scenario 'fill a dossier', js: true do + log_in(user, procedure) - procedure + fill_individual + + expect(page).to have_no_css('label', text: 'champ_c', visible: true) + check('champ_a') + wait_for_autosave + + expect(page).to have_css('label', text: 'champ_c', visible: true) + uncheck('champ_a') + wait_for_autosave + + expect(page).to have_no_css('label', text: 'champ_c', visible: true) + check('champ_b') + wait_for_autosave + + expect(page).to have_css('label', text: 'champ_c', visible: true) + end + end + + context 'with a required conditionnal champ' do + let(:stable_id) { 999 } + let(:condition) { greater_than_eq(champ_value(stable_id), constant(18)) } + let(:procedure) do + create(:procedure, :published, :for_individual, + types_de_champ_public: [ + { type: :integer_number, libelle: 'age', stable_id: }, + { type: :text, libelle: 'nom', mandatory: true, condition: } + ]) end scenario 'submit a dossier with an hidden mandatory champ ', js: true do log_in(user, procedure) fill_individual + click_on 'Déposer le dossier' expect(page).to have_current_path(merci_dossier_path(user_dossier)) end @@ -353,23 +388,21 @@ describe 'The user' do end context 'with a visibilite in cascade' do + let(:age_stable_id) { 999 } + let(:permis_stable_id) { 9999 } + let(:tonnage_stable_id) { 99999 } + let(:permis_condition) { greater_than_eq(champ_value(age_stable_id), constant(18)) } + let(:tonnage_condition) { ds_eq(champ_value(permis_stable_id), constant(true)) } + let(:parking_condition) { less_than_eq(champ_value(tonnage_stable_id), constant(20)) } + let(:procedure) do - procedure = create(:procedure, :for_individual).tap do |p| - p.draft_revision.add_type_de_champ(type_champ: :integer_number, libelle: 'age') - p.draft_revision.add_type_de_champ(type_champ: :yes_no, libelle: 'permis de conduire') - p.draft_revision.add_type_de_champ(type_champ: :integer_number, libelle: 'tonnage') - p.draft_revision.add_type_de_champ(type_champ: :text, libelle: 'parking') - end - - age, permis, tonnage, parking = procedure.draft_revision.types_de_champ.all - - permis.update(condition: greater_than_eq(champ_value(age.stable_id), constant(18))) - tonnage.update(condition: ds_eq(champ_value(permis.stable_id), constant(true))) - parking.update(condition: less_than_eq(champ_value(tonnage.stable_id), constant(20))) - - procedure.publish! - - procedure + create(:procedure, :published, :for_individual, + types_de_champ_public: [ + { type: :integer_number, libelle: 'age', stable_id: age_stable_id }, + { type: :yes_no, libelle: 'permis de conduire', stable_id: permis_stable_id, condition: permis_condition }, + { type: :integer_number, libelle: 'tonnage', stable_id: tonnage_stable_id, condition: tonnage_condition }, + { type: :text, libelle: 'parking', condition: parking_condition } + ]) end scenario 'fill a dossier', js: true do