From 5de4ce889f4f7f8717388852e73bc5e1978f98ec Mon Sep 17 00:00:00 2001 From: mfo Date: Wed, 5 Jun 2024 17:30:33 +0200 Subject: [PATCH] feat(ProcedureRevision.ineligibilites_rules): keep track of changes and show it to admin for republication --- .../procedure/revision_changes_component.rb | 12 +- .../revision_changes_component.fr.yml | 7 + .../revision_changes_component.html.haml | 6 +- app/models/concerns/dossier_rebase_concern.rb | 2 +- app/models/procedure.rb | 10 +- app/models/procedure_revision.rb | 35 +- app/models/procedure_revision_change.rb | 74 ++- .../procedures/_publication_form.html.haml | 2 +- .../procedures/modifications.html.haml | 3 +- .../administrateurs/procedures/show.html.haml | 2 +- spec/models/procedure_revision_spec.rb | 563 +++++++++++------- 11 files changed, 458 insertions(+), 258 deletions(-) diff --git a/app/components/procedure/revision_changes_component.rb b/app/components/procedure/revision_changes_component.rb index e266f13e2..af786e6bc 100644 --- a/app/components/procedure/revision_changes_component.rb +++ b/app/components/procedure/revision_changes_component.rb @@ -1,9 +1,13 @@ class Procedure::RevisionChangesComponent < ApplicationComponent - def initialize(changes:, previous_revision:) - @changes = changes + def initialize(new_revision:, previous_revision:) @previous_revision = previous_revision - @public_move_changes, @private_move_changes = changes.filter { _1.op == :move }.partition { !_1.private? } - @delete_champ_warning = !total_dossiers.zero? && !@changes.all?(&:can_rebase?) + @new_revision = new_revision + + @tdc_changes = previous_revision.compare_types_de_champ(new_revision) + @public_move_changes, @private_move_changes = @tdc_changes.filter { _1.op == :move }.partition { !_1.private? } + @delete_champ_warning = !total_dossiers.zero? && !@tdc_changes.all?(&:can_rebase?) + + @ineligibilite_rules_changes = previous_revision.compare_ineligibilite_rules(new_revision) end private diff --git a/app/components/procedure/revision_changes_component/revision_changes_component.fr.yml b/app/components/procedure/revision_changes_component/revision_changes_component.fr.yml index 10009ce1e..3228c76a8 100644 --- a/app/components/procedure/revision_changes_component/revision_changes_component.fr.yml +++ b/app/components/procedure/revision_changes_component/revision_changes_component.fr.yml @@ -80,3 +80,10 @@ fr: update_expression_reguliere_exemple_text: L’exemple d’expression régulière de l’annotation privée « %{label} » a été modifiée. Le nouvel exemple est « %{to} ». remove_expression_reguliere_error_message: Le message d’erreur de l’expression régulière de l’annotation privée « %{label} » a été supprimé. update_expression_reguliere_error_message: Le message d’erreur de l’expression régulière de l’annotation privée « %{label} » a été modifiée. Le nouveau message est « %{to} ». + ineligibilite_rules: + add: La condition d’inéligibilité « %{new_condition} » a été ajoutée. + remove: La condition d’inéligibilité « %{previous_condition} » a été supprimée + update: La conditon d’inéligibilité « %{previous_condition} » a été changée pour « %{new_condition} » + enabled: "L’inéligibilité des dossiers a été activée" + disabled: "L’inéligibilité des dossiers a été désactivée" + message_updated: "Le message d’inéligibilité a été changé pour « %{ineligibilite_message} »" \ No newline at end of file diff --git a/app/components/procedure/revision_changes_component/revision_changes_component.html.haml b/app/components/procedure/revision_changes_component/revision_changes_component.html.haml index ba19a0dd9..ed7f550c8 100644 --- a/app/components/procedure/revision_changes_component/revision_changes_component.html.haml +++ b/app/components/procedure/revision_changes_component/revision_changes_component.html.haml @@ -2,7 +2,7 @@ - list.with_empty do = t('.no_changes') - - @changes.each do |change| + - @tdc_changes.each do |change| - prefix = change.private? ? 'private' : 'public' - case change.op - when :add @@ -176,3 +176,7 @@ - list.with_item do .fr-alert.fr-alert--warning.fr-mt-1v = t(".invalid_routing_rules_alert") + + - @ineligibilite_rules_changes.each do |change| + - list.with_item do + = t(".ineligibilite_rules.#{change.op}", **change.i18n_params) diff --git a/app/models/concerns/dossier_rebase_concern.rb b/app/models/concerns/dossier_rebase_concern.rb index dd6395dc2..49807793c 100644 --- a/app/models/concerns/dossier_rebase_concern.rb +++ b/app/models/concerns/dossier_rebase_concern.rb @@ -22,7 +22,7 @@ module DossierRebaseConcern end def pending_changes - procedure.published_revision.present? ? revision.compare(procedure.published_revision) : [] + procedure.published_revision.present? ? revision.compare_types_de_champ(procedure.published_revision) : [] end def can_rebase_mandatory_change?(stable_id) diff --git a/app/models/procedure.rb b/app/models/procedure.rb index af398a991..21f272372 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -431,11 +431,15 @@ class Procedure < ApplicationRecord def draft_changed? preload_draft_and_published_revisions - !brouillon? && published_revision.different_from?(draft_revision) && revision_changes.present? + !brouillon? && (types_de_champ_revision_changes.present? || ineligibilite_rules_revision_changes.present?) end - def revision_changes - published_revision.compare(draft_revision) + def types_de_champ_revision_changes + published_revision.compare_types_de_champ(draft_revision) + end + + def ineligibilite_rules_revision_changes + published_revision.compare_ineligibilite_rules(draft_revision) end def preload_draft_and_published_revisions diff --git a/app/models/procedure_revision.rb b/app/models/procedure_revision.rb index 2b56ecf80..a3e16f592 100644 --- a/app/models/procedure_revision.rb +++ b/app/models/procedure_revision.rb @@ -148,16 +148,18 @@ class ProcedureRevision < ApplicationRecord !draft? end - def different_from?(revision) - revision_types_de_champ != revision.revision_types_de_champ - end - - def compare(revision) + def compare_types_de_champ(revision) changes = [] changes += compare_revision_types_de_champ(revision_types_de_champ, revision.revision_types_de_champ) changes end + def compare_ineligibilite_rules(revision) + changes = [] + changes += compare_revision_ineligibilite_rules(revision) + changes + end + def dossier_for_preview(user) dossier = Dossier .create_with(autorisation_donnees: true) @@ -334,6 +336,29 @@ class ProcedureRevision < ApplicationRecord end end + def compare_revision_ineligibilite_rules(new_revision) + from_ineligibilite_rules = ineligibilite_rules + to_ineligibilite_rules = new_revision.ineligibilite_rules + changes = [] + + if from_ineligibilite_rules.present? && to_ineligibilite_rules.blank? + changes << ProcedureRevisionChange::RemoveEligibiliteRuleChange + end + if from_ineligibilite_rules.blank? && to_ineligibilite_rules.present? + changes << ProcedureRevisionChange::AddEligibiliteRuleChange + end + if from_ineligibilite_rules != to_ineligibilite_rules + changes << ProcedureRevisionChange::UpdateEligibiliteRuleChange + end + if ineligibilite_message != new_revision.ineligibilite_message + changes << ProcedureRevisionChange::UpdateEligibiliteMessageChange + end + if ineligibilite_enabled != new_revision.ineligibilite_enabled + changes << (new_revision.ineligibilite_enabled ? ProcedureRevisionChange::EligibiliteEnabledChange : ProcedureRevisionChange::EligibiliteDisabledChange) + end + changes.map { _1.new(self, new_revision) } + end + def compare_type_de_champ(from_type_de_champ, to_type_de_champ, from_coordinates, to_coordinates) changes = [] if from_type_de_champ.type_champ != to_type_de_champ.type_champ diff --git a/app/models/procedure_revision_change.rb b/app/models/procedure_revision_change.rb index fc412cc26..7d99f0fd2 100644 --- a/app/models/procedure_revision_change.rb +++ b/app/models/procedure_revision_change.rb @@ -1,17 +1,19 @@ class ProcedureRevisionChange - attr_reader :type_de_champ - def initialize(type_de_champ) - @type_de_champ = type_de_champ + class TypeDeChange + attr_reader :type_de_champ + def initialize(type_de_champ) + @type_de_champ = type_de_champ + end + + def label = @type_de_champ.libelle + def stable_id = @type_de_champ.stable_id + def private? = @type_de_champ.private? + def child? = @type_de_champ.child? + + def to_h = { op:, stable_id:, label:, private: private? } end - def label = @type_de_champ.libelle - def stable_id = @type_de_champ.stable_id - def private? = @type_de_champ.private? - def child? = @type_de_champ.child? - - def to_h = { op:, stable_id:, label:, private: private? } - - class AddChamp < ProcedureRevisionChange + class AddChamp < TypeDeChange def initialize(type_de_champ) super(type_de_champ) end @@ -23,7 +25,7 @@ class ProcedureRevisionChange def to_h = super.merge(mandatory: mandatory?) end - class RemoveChamp < ProcedureRevisionChange + class RemoveChamp < TypeDeChange def initialize(type_de_champ) super(type_de_champ) end @@ -32,7 +34,7 @@ class ProcedureRevisionChange def can_rebase?(dossier = nil) = true end - class MoveChamp < ProcedureRevisionChange + class MoveChamp < TypeDeChange attr_reader :from, :to def initialize(type_de_champ, from, to) @@ -46,7 +48,7 @@ class ProcedureRevisionChange def to_h = super.merge(from:, to:) end - class UpdateChamp < ProcedureRevisionChange + class UpdateChamp < TypeDeChange attr_reader :attribute, :from, :to def initialize(type_de_champ, attribute, from, to) @@ -75,4 +77,48 @@ class ProcedureRevisionChange end end end + + class EligibiliteRulesChange + attr_reader :previous_revision, :new_revision + def initialize(previous_revision, new_revision) + @previous_revision = previous_revision + @new_revision = new_revision + @previous_ineligibilite_rules = @previous_revision.ineligibilite_rules + @new_ineligibilite_rules = @new_revision.ineligibilite_rules + end + + def i18n_params + { + previous_condition: @previous_ineligibilite_rules&.to_s(previous_revision.types_de_champ.filter { @previous_ineligibilite_rules.sources.include? _1.stable_id }), + new_condition: @new_ineligibilite_rules&.to_s(new_revision.types_de_champ.filter { @new_ineligibilite_rules.sources.include? _1.stable_id }) + } + end + end + + class AddEligibiliteRuleChange < EligibiliteRulesChange + def op = :add + end + + class RemoveEligibiliteRuleChange < EligibiliteRulesChange + def op = :remove + end + + class UpdateEligibiliteRuleChange < EligibiliteRulesChange + def op = :update + end + + class EligibiliteEnabledChange < EligibiliteRulesChange + def op = :enabled + def i18n_params = {} + end + + class EligibiliteDisabledChange < EligibiliteRulesChange + def op = :disabled + def i18n_params = {} + end + + class UpdateEligibiliteMessageChange < EligibiliteRulesChange + def op = :message_updated + def i18n_params = { ineligibilite_message: @new_revision.ineligibilite_message } + end end diff --git a/app/views/administrateurs/procedures/_publication_form.html.haml b/app/views/administrateurs/procedures/_publication_form.html.haml index 0c9cc8454..d8d96870e 100644 --- a/app/views/administrateurs/procedures/_publication_form.html.haml +++ b/app/views/administrateurs/procedures/_publication_form.html.haml @@ -8,7 +8,7 @@ %p.mb-2= t('.draft_changed_procedure_alert') = render Dsfr::AlertComponent.new(state: :info, size: :sm, extra_class_names: 'fr-mb-2w') do |c| - c.with_body do - = render Procedure::RevisionChangesComponent.new changes: procedure.revision_changes, previous_revision: procedure.published_revision + = render Procedure::RevisionChangesComponent.new new_revision: procedure.draft_revision, previous_revision: procedure.published_revision - if procedure.close? = render partial: 'publication_form_inputs', locals: { procedure: procedure, closed_procedures: @closed_procedures, form: f } - elsif @procedure.brouillon? && @procedure.missing_steps.empty? diff --git a/app/views/administrateurs/procedures/modifications.html.haml b/app/views/administrateurs/procedures/modifications.html.haml index 978fee30f..73b8673bd 100644 --- a/app/views/administrateurs/procedures/modifications.html.haml +++ b/app/views/administrateurs/procedures/modifications.html.haml @@ -13,7 +13,6 @@ - previous_revision = nil - @procedure.revisions.each do |revision| - if previous_revision.present? && !revision.draft? - - changes = previous_revision.compare(revision) - dossiers = revision.dossiers.visible_by_administration - dossiers_en_construction_count = dossiers.state_en_construction.count - dossiers_en_instruction_count = dossiers.state_en_instruction.count @@ -31,7 +30,7 @@ %p= t('.dossiers_en_construction', count: dossiers_en_construction_count) - elsif !dossiers_en_instruction_count.zero? %p= t('.dossiers_en_instruction', count: dossiers_en_instruction_count) - = render Procedure::RevisionChangesComponent.new changes:, previous_revision: + = render Procedure::RevisionChangesComponent.new new_revision: revision, previous_revision: - previous_revision = revision = render Procedure::FixedFooterComponent.new(procedure: @procedure) diff --git a/app/views/administrateurs/procedures/show.html.haml b/app/views/administrateurs/procedures/show.html.haml index 4d63d909b..4463a86cf 100644 --- a/app/views/administrateurs/procedures/show.html.haml +++ b/app/views/administrateurs/procedures/show.html.haml @@ -30,8 +30,8 @@ - if @procedure.draft_changed? = render Dsfr::CalloutComponent.new(title: t(:has_changes, scope: [:administrateurs, :revision_changes]), icon: "fr-fi-information-line") do |c| - c.with_body do - = render Procedure::RevisionChangesComponent.new changes: @procedure.revision_changes, previous_revision: @procedure.published_revision = render Procedure::ErrorsSummary.new(procedure: @procedure, validation_context: :publication) + = render Procedure::RevisionChangesComponent.new new_revision: @procedure.draft_revision, previous_revision: @procedure.published_revision - c.with_bottom do %ul.fr-mt-2w.fr-btns-group.fr-btns-group--inline diff --git a/spec/models/procedure_revision_spec.rb b/spec/models/procedure_revision_spec.rb index 4c7a17ba3..434d82e44 100644 --- a/spec/models/procedure_revision_spec.rb +++ b/spec/models/procedure_revision_spec.rb @@ -347,306 +347,417 @@ describe ProcedureRevision do end end - describe '#compare' do + describe '#compare_types_de_champ' do include Logic - - let(:first_tdc) { draft.types_de_champ_public.first } - let(:second_tdc) { draft.types_de_champ_public.second } let(:new_draft) { procedure.create_new_revision } + subject { procedure.active_revision.compare_types_de_champ(new_draft.reload).map(&:to_h) } - subject { procedure.active_revision.compare(new_draft.reload).map(&:to_h) } + describe 'when tdcs changes' do + let(:first_tdc) { draft.types_de_champ_public.first } + let(:second_tdc) { draft.types_de_champ_public.second } - context 'with a procedure with 2 tdcs' do - let(:procedure) do - create(:procedure, types_de_champ_public: [ - { type: :integer_number, libelle: 'l1' }, - { type: :text, libelle: 'l2' } - ]) + context 'with a procedure with 2 tdcs' do + let(:procedure) do + create(:procedure, types_de_champ_public: [ + { type: :integer_number, libelle: 'l1' }, + { type: :text, libelle: 'l2' } + ]) + end + + context 'when a condition is added' do + before do + second = new_draft.find_and_ensure_exclusive_use(second_tdc.stable_id) + second.update(condition: ds_eq(champ_value(first_tdc.stable_id), constant(3))) + end + + it do + is_expected.to eq([ + { + attribute: :condition, + from: nil, + label: "l2", + op: :update, + private: false, + stable_id: second_tdc.stable_id, + to: "(l1 == 3)" + } + ]) + end + end + + context 'when a condition is removed' do + before do + second_tdc.update(condition: ds_eq(champ_value(first_tdc.stable_id), constant(2))) + draft.reload + + second = new_draft.find_and_ensure_exclusive_use(second_tdc.stable_id) + second.update(condition: nil) + end + + it do + is_expected.to eq([ + { + attribute: :condition, + from: "(l1 == 2)", + label: "l2", + op: :update, + private: false, + stable_id: second_tdc.stable_id, + to: nil + } + ]) + end + end + + context 'when a condition is changed' do + before do + second_tdc.update(condition: ds_eq(champ_value(first_tdc.stable_id), constant(2))) + draft.reload + + second = new_draft.find_and_ensure_exclusive_use(second_tdc.stable_id) + second.update(condition: ds_eq(champ_value(first_tdc.stable_id), constant(3))) + end + + it do + is_expected.to eq([ + { + attribute: :condition, + from: "(l1 == 2)", + label: "l2", + op: :update, + private: false, + stable_id: second_tdc.stable_id, + to: "(l1 == 3)" + } + ]) + end + end end - context 'when a condition is added' do - before do - second = new_draft.find_and_ensure_exclusive_use(second_tdc.stable_id) - second.update(condition: ds_eq(champ_value(first_tdc.stable_id), constant(3))) + context 'when a type de champ is added' do + let(:procedure) { create(:procedure) } + let(:new_tdc) do + new_draft.add_type_de_champ( + type_champ: TypeDeChamp.type_champs.fetch(:text), + libelle: "Un champ text" + ) end + before { new_tdc } + it do is_expected.to eq([ { - attribute: :condition, - from: nil, - label: "l2", - op: :update, + op: :add, + label: "Un champ text", private: false, - stable_id: second_tdc.stable_id, - to: "(l1 == 3)" + mandatory: false, + stable_id: new_tdc.stable_id } ]) end end - context 'when a condition is removed' do - before do - second_tdc.update(condition: ds_eq(champ_value(first_tdc.stable_id), constant(2))) - draft.reload + context 'when a type de champ is changed' do + context 'when libelle, description, and mandatory are changed' do + let(:procedure) { create(:procedure, :with_type_de_champ) } - second = new_draft.find_and_ensure_exclusive_use(second_tdc.stable_id) - second.update(condition: nil) + before do + updated_tdc = new_draft.find_and_ensure_exclusive_use(first_tdc.stable_id) + + updated_tdc.update(libelle: 'modifier le libelle', description: 'une description', mandatory: !updated_tdc.mandatory) + end + + it do + is_expected.to eq([ + { + op: :update, + attribute: :libelle, + label: first_tdc.libelle, + private: false, + from: first_tdc.libelle, + to: "modifier le libelle", + stable_id: first_tdc.stable_id + }, + { + op: :update, + attribute: :description, + label: first_tdc.libelle, + private: false, + from: first_tdc.description, + to: "une description", + stable_id: first_tdc.stable_id + }, + { + op: :update, + attribute: :mandatory, + label: first_tdc.libelle, + private: false, + from: false, + to: true, + stable_id: first_tdc.stable_id + } + ]) + end + end + + context 'when collapsible_explanation_enabled and collapsible_explanation_text are changed' do + let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :explication }]) } + + before do + updated_tdc = new_draft.find_and_ensure_exclusive_use(first_tdc.stable_id) + + updated_tdc.update(collapsible_explanation_enabled: "1", collapsible_explanation_text: 'afficher au clique') + end + it do + is_expected.to eq([ + { + op: :update, + attribute: :collapsible_explanation_enabled, + label: first_tdc.libelle, + private: first_tdc.private?, + from: false, + to: true, + stable_id: first_tdc.stable_id + }, + { + op: :update, + attribute: :collapsible_explanation_text, + label: first_tdc.libelle, + private: first_tdc.private?, + from: nil, + to: 'afficher au clique', + stable_id: first_tdc.stable_id + } + ]) + end + end + end + + context 'when a type de champ is moved' do + let(:procedure) { create(:procedure, types_de_champ_public: Array.new(3) { { type: :text } }) } + let(:new_draft_second_tdc) { new_draft.types_de_champ_public.second } + let(:new_draft_third_tdc) { new_draft.types_de_champ_public.third } + + before do + new_draft_second_tdc + new_draft_third_tdc + new_draft.move_type_de_champ(new_draft_second_tdc.stable_id, 2) end it do is_expected.to eq([ { - attribute: :condition, - from: "(l1 == 2)", - label: "l2", - op: :update, + op: :move, + label: new_draft_third_tdc.libelle, private: false, - stable_id: second_tdc.stable_id, - to: nil - } - ]) - end - end - - context 'when a condition is changed' do - before do - second_tdc.update(condition: ds_eq(champ_value(first_tdc.stable_id), constant(2))) - draft.reload - - second = new_draft.find_and_ensure_exclusive_use(second_tdc.stable_id) - second.update(condition: ds_eq(champ_value(first_tdc.stable_id), constant(3))) - end - - it do - is_expected.to eq([ + from: 2, + to: 1, + stable_id: new_draft_third_tdc.stable_id + }, { - attribute: :condition, - from: "(l1 == 2)", - label: "l2", - op: :update, + op: :move, + label: new_draft_second_tdc.libelle, private: false, - stable_id: second_tdc.stable_id, - to: "(l1 == 3)" + from: 1, + to: 2, + stable_id: new_draft_second_tdc.stable_id } ]) end end - end - context 'when a type de champ is added' do - let(:procedure) { create(:procedure) } - let(:new_tdc) do - new_draft.add_type_de_champ( - type_champ: TypeDeChamp.type_champs.fetch(:text), - libelle: "Un champ text" - ) - end - - before { new_tdc } - - it do - is_expected.to eq([ - { - op: :add, - label: "Un champ text", - private: false, - mandatory: false, - stable_id: new_tdc.stable_id - } - ]) - end - end - - context 'when a type de champ is changed' do - context 'when libelle, description, and mandatory are changed' do + context 'when a type de champ is removed' do let(:procedure) { create(:procedure, :with_type_de_champ) } before do - updated_tdc = new_draft.find_and_ensure_exclusive_use(first_tdc.stable_id) - - updated_tdc.update(libelle: 'modifier le libelle', description: 'une description', mandatory: !updated_tdc.mandatory) + new_draft.remove_type_de_champ(first_tdc.stable_id) end it do is_expected.to eq([ { - op: :update, - attribute: :libelle, + op: :remove, label: first_tdc.libelle, private: false, - from: first_tdc.libelle, - to: "modifier le libelle", - stable_id: first_tdc.stable_id - }, - { - op: :update, - attribute: :description, - label: first_tdc.libelle, - private: false, - from: first_tdc.description, - to: "une description", - stable_id: first_tdc.stable_id - }, - { - op: :update, - attribute: :mandatory, - label: first_tdc.libelle, - private: false, - from: false, - to: true, stable_id: first_tdc.stable_id } ]) end end - context 'when collapsible_explanation_enabled and collapsible_explanation_text are changed' do - let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :explication }]) } + context 'when a child type de champ is transformed into a drop_down_list' do + let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :repetition, children: [{ type: :text, libelle: 'sub type de champ' }, { type: :integer_number }] }]) } before do - updated_tdc = new_draft.find_and_ensure_exclusive_use(first_tdc.stable_id) - - updated_tdc.update(collapsible_explanation_enabled: "1", collapsible_explanation_text: 'afficher au clique') + child = new_draft.children_of(new_draft.types_de_champ_public.last).first + new_draft.find_and_ensure_exclusive_use(child.stable_id).update(type_champ: :drop_down_list, drop_down_options: ['one', 'two']) end + it do is_expected.to eq([ { op: :update, - attribute: :collapsible_explanation_enabled, - label: first_tdc.libelle, - private: first_tdc.private?, - from: false, - to: true, - stable_id: first_tdc.stable_id + attribute: :type_champ, + label: "sub type de champ", + private: false, + from: "text", + to: "drop_down_list", + stable_id: new_draft.children_of(new_draft.types_de_champ_public.last).first.stable_id }, { op: :update, - attribute: :collapsible_explanation_text, - label: first_tdc.libelle, - private: first_tdc.private?, - from: nil, - to: 'afficher au clique', - stable_id: first_tdc.stable_id + attribute: :drop_down_options, + label: "sub type de champ", + private: false, + from: [], + to: ["one", "two"], + stable_id: new_draft.children_of(new_draft.types_de_champ_public.last).first.stable_id + } + ]) + end + end + + context 'when a child type de champ is transformed into a map' do + let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :repetition, children: [{ type: :text, libelle: 'sub type de champ' }, { type: :integer_number }] }]) } + + before do + child = new_draft.children_of(new_draft.types_de_champ_public.last).first + new_draft.find_and_ensure_exclusive_use(child.stable_id).update(type_champ: :carte, options: { cadastres: true, znieff: true }) + end + + it do + is_expected.to eq([ + { + op: :update, + attribute: :type_champ, + label: "sub type de champ", + private: false, + from: "text", + to: "carte", + stable_id: new_draft.children_of(new_draft.types_de_champ_public.last).first.stable_id + }, + { + op: :update, + attribute: :carte_layers, + label: "sub type de champ", + private: false, + from: [], + to: [:cadastres, :znieff], + stable_id: new_draft.children_of(new_draft.types_de_champ_public.last).first.stable_id } ]) end end end + end - context 'when a type de champ is moved' do - let(:procedure) { create(:procedure, types_de_champ_public: Array.new(3) { { type: :text } }) } - let(:new_draft_second_tdc) { new_draft.types_de_champ_public.second } - let(:new_draft_third_tdc) { new_draft.types_de_champ_public.third } + describe 'compare_ineligibilite_rules' do + include Logic + let(:new_draft) { procedure.create_new_revision } + subject { procedure.active_revision.compare_ineligibilite_rules(new_draft.reload) } - before do - new_draft_second_tdc - new_draft_third_tdc - new_draft.move_type_de_champ(new_draft_second_tdc.stable_id, 2) + context 'when ineligibilite_rules changes' do + let(:procedure) { create(:procedure, :published, types_de_champ_public:) } + let(:types_de_champ_public) { [{ type: :yes_no }] } + let(:yes_no_tdc) { new_draft.types_de_champ_public.first } + + context 'when nothing changed' do + it { is_expected.to be_empty } end - it do - is_expected.to eq([ - { - op: :move, - label: new_draft_third_tdc.libelle, - private: false, - from: 2, - to: 1, - stable_id: new_draft_third_tdc.stable_id - }, - { - op: :move, - label: new_draft_second_tdc.libelle, - private: false, - from: 1, - to: 2, - stable_id: new_draft_second_tdc.stable_id - } - ]) + context 'when ineligibilite_rules added' do + before do + new_draft.update!(ineligibilite_rules: ds_eq(champ_value(yes_no_tdc.stable_id), constant(true))) + end + + it { is_expected.to include(an_instance_of(ProcedureRevisionChange::AddEligibiliteRuleChange)) } + end + + context 'when ineligibilite_rules removed' do + before do + procedure.published_revision.update!(ineligibilite_rules: ds_eq(champ_value(yes_no_tdc.stable_id), constant(true))) + end + + it { is_expected.to include(an_instance_of(ProcedureRevisionChange::RemoveEligibiliteRuleChange)) } + end + + context 'when ineligibilite_rules changed' do + before do + procedure.published_revision.update!(ineligibilite_rules: ds_eq(champ_value(yes_no_tdc.stable_id), constant(true))) + new_draft.update!(ineligibilite_rules: ds_and([ + ds_eq(champ_value(yes_no_tdc.stable_id), constant(true)), + empty_operator(empty, empty) + ])) + end + + it { is_expected.to include(an_instance_of(ProcedureRevisionChange::UpdateEligibiliteRuleChange)) } + end + + context 'when when ineligibilite_enabled changes from false to true' do + before do + procedure.published_revision.update!(ineligibilite_enabled: false, ineligibilite_message: :required) + new_draft.update!(ineligibilite_enabled: true, ineligibilite_message: :required) + end + + it { is_expected.to include(an_instance_of(ProcedureRevisionChange::EligibiliteEnabledChange)) } + end + + context 'when ineligibilite_enabled changes from true to false' do + before do + procedure.published_revision.update!(ineligibilite_enabled: true, ineligibilite_message: :required) + new_draft.update!(ineligibilite_enabled: false, ineligibilite_message: :required) + end + + it { is_expected.to include(an_instance_of(ProcedureRevisionChange::EligibiliteDisabledChange)) } + end + + context 'when ineligibilite_message changes' do + before do + procedure.published_revision.update!(ineligibilite_message: :a) + new_draft.update!(ineligibilite_message: :b) + end + + it { is_expected.to include(an_instance_of(ProcedureRevisionChange::UpdateEligibiliteMessageChange)) } end end + end - context 'when a type de champ is removed' do - let(:procedure) { create(:procedure, :with_type_de_champ) } - - before do - new_draft.remove_type_de_champ(first_tdc.stable_id) - end - - it do - is_expected.to eq([ - { - op: :remove, - label: first_tdc.libelle, - private: false, - stable_id: first_tdc.stable_id - } - ]) - end + describe 'ineligibilite_rules_are_valid?' do + include Logic + let(:procedure) { create(:procedure) } + let(:draft_revision) { procedure.draft_revision } + let(:ineligibilite_message) { 'ok' } + let(:ineligibilite_enabled) { true } + before do + procedure.draft_revision.update(ineligibilite_rules:, ineligibilite_message:, ineligibilite_enabled:) end - context 'when a child type de champ is transformed into a drop_down_list' do - let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :repetition, children: [{ type: :text, libelle: 'sub type de champ' }, { type: :integer_number }] }]) } - - before do - child = new_draft.children_of(new_draft.types_de_champ_public.last).first - new_draft.find_and_ensure_exclusive_use(child.stable_id).update(type_champ: :drop_down_list, drop_down_options: ['one', 'two']) - end - - it do - is_expected.to eq([ - { - op: :update, - attribute: :type_champ, - label: "sub type de champ", - private: false, - from: "text", - to: "drop_down_list", - stable_id: new_draft.children_of(new_draft.types_de_champ_public.last).first.stable_id - }, - { - op: :update, - attribute: :drop_down_options, - label: "sub type de champ", - private: false, - from: [], - to: ["one", "two"], - stable_id: new_draft.children_of(new_draft.types_de_champ_public.last).first.stable_id - } - ]) + context 'when ineligibilite_rules are valid' do + let(:ineligibilite_rules) { ds_eq(constant(true), constant(true)) } + it 'is valid' do + expect(draft_revision.validate(:publication)).to be_truthy + expect(draft_revision.validate(:ineligibilite_rules_editor)).to be_truthy end end - - context 'when a child type de champ is transformed into a map' do - let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :repetition, children: [{ type: :text, libelle: 'sub type de champ' }, { type: :integer_number }] }]) } - - before do - child = new_draft.children_of(new_draft.types_de_champ_public.last).first - new_draft.find_and_ensure_exclusive_use(child.stable_id).update(type_champ: :carte, options: { cadastres: true, znieff: true }) + context 'when ineligibilite_rules are invalid on simple champ' do + let(:ineligibilite_rules) { ds_eq(constant(true), constant(1)) } + it 'is invalid' do + expect(draft_revision.validate(:publication)).to be_falsey + expect(draft_revision.validate(:ineligibilite_rules_editor)).to be_falsey end - - it do - is_expected.to eq([ - { - op: :update, - attribute: :type_champ, - label: "sub type de champ", - private: false, - from: "text", - to: "carte", - stable_id: new_draft.children_of(new_draft.types_de_champ_public.last).first.stable_id - }, - { - op: :update, - attribute: :carte_layers, - label: "sub type de champ", - private: false, - from: [], - to: [:cadastres, :znieff], - stable_id: new_draft.children_of(new_draft.types_de_champ_public.last).first.stable_id - } - ]) + end + context 'when ineligibilite_rules are invalid on repetition champ' do + let(:ineligibilite_rules) { ds_eq(constant(true), constant(1)) } + let(:procedure) { create(:procedure, types_de_champ_public:) } + let(:types_de_champ_public) { [{ type: :repetition, children: [{ type: :integer_number }] }] } + let(:tdc_number) { draft_revision.types_de_champ_for(scope: :public).find { _1.type_champ == 'integer_number' } } + let(:ineligibilite_rules) do + ds_eq(champ_value(tdc_number.stable_id), constant(true)) + end + it 'is invalid' do + expect(draft_revision.validate(:publication)).to be_falsey + expect(draft_revision.validate(:ineligibilite_rules_editor)).to be_falsey end end end