diff --git a/app/components/procedure/one_groupe_management_component.rb b/app/components/procedure/one_groupe_management_component.rb index f1b8aa0e1..4fc003628 100644 --- a/app/components/procedure/one_groupe_management_component.rb +++ b/app/components/procedure/one_groupe_management_component.rb @@ -17,6 +17,10 @@ class Procedure::OneGroupeManagementComponent < ApplicationComponent @groupe_instructeur.routing_rule&.right || empty end + def operator_name + @groupe_instructeur.routing_rule&.class&.name || empty + end + def targeted_champ_tag select_tag( 'targeted_champ', @@ -39,6 +43,21 @@ class Procedure::OneGroupeManagementComponent < ApplicationComponent .map { |tdc| [tdc.libelle, champ_value(tdc.stable_id).to_json] } end + def operator_tag + select_tag('operator_name', + options_for_select( + options_for_operator_tag, + selected: operator_name + ), + class: 'fr-select') + end + + def options_for_operator_tag + [Eq, NotEq] + .map(&:name) + .map { |name| [t(name, scope: 'logic.operators'), name] } + end + def value_tag select_tag( 'value', diff --git a/app/components/procedure/one_groupe_management_component/one_groupe_management_component.html.haml b/app/components/procedure/one_groupe_management_component/one_groupe_management_component.html.haml index 3df93fb48..0aa12ca39 100644 --- a/app/components/procedure/one_groupe_management_component/one_groupe_management_component.html.haml +++ b/app/components/procedure/one_groupe_management_component/one_groupe_management_component.html.haml @@ -40,7 +40,8 @@ .fr-mr-2w.no-wrap si le champ .target.fr-mr-2w = targeted_champ_tag - .operator.fr-mr-2w.no-wrap est égal à + .operator.fr-mr-2w.no-wrap + = operator_tag .value = value_tag .fr-hint-text diff --git a/app/controllers/administrateurs/routing_controller.rb b/app/controllers/administrateurs/routing_controller.rb index 0e87ede8b..3e047cdbf 100644 --- a/app/controllers/administrateurs/routing_controller.rb +++ b/app/controllers/administrateurs/routing_controller.rb @@ -9,7 +9,13 @@ module Administrateurs right = targeted_champ_changed? ? empty : value - groupe_instructeur.update!(routing_rule: ds_eq(left, right)) + new_routing_rule = case operator_name + when Eq.name + ds_eq(left, right) + when NotEq.name + ds_not_eq(left, right) + end + groupe_instructeur.update!(routing_rule: new_routing_rule) end def update_defaut_groupe_instructeur @@ -27,6 +33,10 @@ module Administrateurs Logic.from_json(params[:targeted_champ]) end + def operator_name + params[:operator_name] + end + def value Logic.from_json(params[:value]) end diff --git a/app/models/groupe_instructeur.rb b/app/models/groupe_instructeur.rb index 59b3bcb14..0e48fb391 100644 --- a/app/models/groupe_instructeur.rb +++ b/app/models/groupe_instructeur.rb @@ -73,9 +73,12 @@ class GroupeInstructeur < ApplicationRecord end def invalid_rule? - rule = routing_rule - return true if !(rule.is_a?(Logic::Eq) && rule.left.is_a?(Logic::ChampValue) && rule.right.is_a?(Logic::Constant)) - return true if !routing_rule_matches_tdc? + !valid_rule? + end + + def valid_rule? + return false if routing_rule.nil? + ([routing_rule.left, routing_rule, routing_rule.right] in [ChampValue, Eq | NotEq, Constant]) && routing_rule_matches_tdc? end def non_unique_rule? diff --git a/spec/controllers/administrateurs/routing_controller_spec.rb b/spec/controllers/administrateurs/routing_controller_spec.rb index 03eb31d36..17eff6874 100644 --- a/spec/controllers/administrateurs/routing_controller_spec.rb +++ b/spec/controllers/administrateurs/routing_controller_spec.rb @@ -11,40 +11,82 @@ describe Administrateurs::RoutingController, type: :controller do { procedure_id: procedure.id, targeted_champ: champ_value(drop_down_tdc.stable_id).to_json, + operator_name: operator_name, value: empty.to_json, groupe_instructeur_id: gi_2.id } end - before do - post :update, params: params, format: :turbo_stream - end - - it do - expect(gi_2.reload.routing_rule).to eq(ds_eq(champ_value(drop_down_tdc.stable_id), empty)) - end - - context '#update value' do - let(:value_updated_params) { params.merge(value: constant('Lyon').to_json) } + context 'with Eq operator' do + let(:operator_name) { Logic::Eq.name } before do - post :update, params: value_updated_params, format: :turbo_stream + post :update, params: params, format: :turbo_stream end it do - expect(gi_2.reload.routing_rule).to eq(ds_eq(champ_value(drop_down_tdc.stable_id), constant('Lyon'))) + expect(gi_2.reload.routing_rule).to eq(ds_eq(champ_value(drop_down_tdc.stable_id), empty)) end - context 'targeted champ changed' do - let(:last_tdc) { procedure.draft_revision.types_de_champ.last } + context '#update value' do + let(:value_updated_params) { params.merge(value: constant('Lyon').to_json) } before do - targeted_champ_updated_params = value_updated_params.merge(targeted_champ: champ_value(last_tdc.stable_id).to_json) - post :update, params: targeted_champ_updated_params, format: :turbo_stream + post :update, params: value_updated_params, format: :turbo_stream end it do - expect(gi_2.reload.routing_rule).to eq(ds_eq(champ_value(last_tdc.stable_id), empty)) + expect(gi_2.reload.routing_rule).to eq(ds_eq(champ_value(drop_down_tdc.stable_id), constant('Lyon'))) + end + + context 'targeted champ changed' do + let(:last_tdc) { procedure.draft_revision.types_de_champ.last } + + before do + targeted_champ_updated_params = value_updated_params.merge(targeted_champ: champ_value(last_tdc.stable_id).to_json) + post :update, params: targeted_champ_updated_params, format: :turbo_stream + end + + it do + expect(gi_2.reload.routing_rule).to eq(ds_eq(champ_value(last_tdc.stable_id), empty)) + end + end + end + end + + context 'with NotEq operator' do + let(:operator_name) { Logic::NotEq.name } + + before do + post :update, params: params, format: :turbo_stream + end + + it do + expect(gi_2.reload.routing_rule).to eq(ds_not_eq(champ_value(drop_down_tdc.stable_id), empty)) + end + + context '#update value' do + let(:value_updated_params) { params.merge(value: constant('Lyon').to_json) } + + before do + post :update, params: value_updated_params, format: :turbo_stream + end + + it do + expect(gi_2.reload.routing_rule).to eq(ds_not_eq(champ_value(drop_down_tdc.stable_id), constant('Lyon'))) + end + + context 'targeted champ changed' do + let(:last_tdc) { procedure.draft_revision.types_de_champ.last } + + before do + targeted_champ_updated_params = value_updated_params.merge(targeted_champ: champ_value(last_tdc.stable_id).to_json) + post :update, params: targeted_champ_updated_params, format: :turbo_stream + end + + it do + expect(gi_2.reload.routing_rule).to eq(ds_not_eq(champ_value(last_tdc.stable_id), empty)) + end end end end diff --git a/spec/models/routing_engine_spec.rb b/spec/models/routing_engine_spec.rb index 3095da91f..38468ebb6 100644 --- a/spec/models/routing_engine_spec.rb +++ b/spec/models/routing_engine_spec.rb @@ -4,7 +4,7 @@ describe RoutingEngine, type: :model do describe '.compute' do let(:dossier) { create(:dossier, procedure:) } let(:defaut_groupe) { procedure.defaut_groupe_instructeur } - let(:gi_2) { procedure.groupe_instructeurs.find_by(label: 'a second group') } + let(:gi_2) { procedure.groupe_instructeurs.create(label: 'a second group') } subject do RoutingEngine.compute(dossier) @@ -15,7 +15,6 @@ describe RoutingEngine, type: :model do let(:procedure) do create(:procedure, types_de_champ_public: [{ type: :drop_down_list, libelle: 'Votre ville', options: ['Paris', 'Lyon', 'Marseille'] }]).tap do |p| - p.groupe_instructeurs.create(label: 'a second group') p.groupe_instructeurs.create(label: 'a third group') end end @@ -29,7 +28,7 @@ describe RoutingEngine, type: :model do context 'without any matching rules' do before do procedure.groupe_instructeurs.each do |gi| - gi.update(routing_rule: constant(false)) + gi.update(routing_rule: ds_eq(constant(false), constant(false))) end end @@ -60,12 +59,23 @@ describe RoutingEngine, type: :model do it { is_expected.to eq(defaut_groupe) } end + + context 'with a non equals rule' do + before do + gi_2.update(routing_rule: ds_not_eq(champ_value(drop_down_tdc.stable_id), constant('Lyon'))) + dossier.champs.first.update(value: 'Paris') + end + + it do + is_expected.not_to eq(defaut_groupe) + is_expected.to eq(gi_2) + end + end end context 'with a departements type de champ' do let(:procedure) do create(:procedure, types_de_champ_public: [{ type: :departements }]).tap do |p| - p.groupe_instructeurs.create(label: 'a second group') p.groupe_instructeurs.create(label: 'a third group') end end @@ -81,5 +91,31 @@ describe RoutingEngine, type: :model do it { is_expected.to eq(gi_2) } end end + + context 'routing rules priorities' do + let(:procedure) do + create(:procedure, + types_de_champ_public: [{ type: :drop_down_list, libelle: 'Ville', options: ['Paris', 'Lyon', 'Marseille'] }]).tap do |p| + p.groupe_instructeurs.create(label: 'c') + end + end + + let(:drop_down_tdc) { procedure.draft_revision.types_de_champ.first } + + let!(:gi_3) { procedure.groupe_instructeurs.find_by(label: 'c') } + + context 'not eq rule coming first' do + before do + defaut_groupe.update(label: 'a') + gi_2.update(label: 'b', routing_rule: ds_not_eq(champ_value(drop_down_tdc.stable_id), constant('Lyon'))) + gi_3.update(routing_rule: ds_eq(champ_value(drop_down_tdc.stable_id), constant('Marseille'))) + dossier.champs.first.update(value: 'Marseille') + end + + it 'computes by groups label order' do + is_expected.to eq(gi_2) + end + end + end end end