feat(ProcedureRevision.ineligibilites_rules): keep track of changes and show it to admin for republication

This commit is contained in:
mfo 2024-06-05 17:30:33 +02:00
parent aca3e38859
commit 5de4ce889f
No known key found for this signature in database
GPG key ID: 7CE3E1F5B794A8EC
11 changed files with 458 additions and 258 deletions

View file

@ -1,9 +1,13 @@
class Procedure::RevisionChangesComponent < ApplicationComponent class Procedure::RevisionChangesComponent < ApplicationComponent
def initialize(changes:, previous_revision:) def initialize(new_revision:, previous_revision:)
@changes = changes
@previous_revision = previous_revision @previous_revision = previous_revision
@public_move_changes, @private_move_changes = changes.filter { _1.op == :move }.partition { !_1.private? } @new_revision = new_revision
@delete_champ_warning = !total_dossiers.zero? && !@changes.all?(&:can_rebase?)
@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 end
private private

View file

@ -80,3 +80,10 @@ fr:
update_expression_reguliere_exemple_text: Lexemple dexpression régulière de lannotation privée « %{label} » a été modifiée. Le nouvel exemple est « %{to} ». update_expression_reguliere_exemple_text: Lexemple dexpression régulière de lannotation privée « %{label} » a été modifiée. Le nouvel exemple est « %{to} ».
remove_expression_reguliere_error_message: Le message derreur de lexpression régulière de lannotation privée « %{label} » a été supprimé. remove_expression_reguliere_error_message: Le message derreur de lexpression régulière de lannotation privée « %{label} » a été supprimé.
update_expression_reguliere_error_message: Le message derreur de lexpression régulière de lannotation privée « %{label} » a été modifiée. Le nouveau message est « %{to} ». update_expression_reguliere_error_message: Le message derreur de lexpression régulière de lannotation privée « %{label} » a été modifiée. Le nouveau message est « %{to} ».
ineligibilite_rules:
add: La condition dinéligibilité « %{new_condition} » a été ajoutée.
remove: La condition dinéligibilité « %{previous_condition} » a été supprimée
update: La conditon dinéligibilité « %{previous_condition} » a été changée pour « %{new_condition} »
enabled: "Linéligibilité des dossiers a été activée"
disabled: "Linéligibilité des dossiers a été désactivée"
message_updated: "Le message dinéligibilité a été changé pour « %{ineligibilite_message} »"

View file

@ -2,7 +2,7 @@
- list.with_empty do - list.with_empty do
= t('.no_changes') = t('.no_changes')
- @changes.each do |change| - @tdc_changes.each do |change|
- prefix = change.private? ? 'private' : 'public' - prefix = change.private? ? 'private' : 'public'
- case change.op - case change.op
- when :add - when :add
@ -176,3 +176,7 @@
- list.with_item do - list.with_item do
.fr-alert.fr-alert--warning.fr-mt-1v .fr-alert.fr-alert--warning.fr-mt-1v
= t(".invalid_routing_rules_alert") = t(".invalid_routing_rules_alert")
- @ineligibilite_rules_changes.each do |change|
- list.with_item do
= t(".ineligibilite_rules.#{change.op}", **change.i18n_params)

View file

@ -22,7 +22,7 @@ module DossierRebaseConcern
end end
def pending_changes 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 end
def can_rebase_mandatory_change?(stable_id) def can_rebase_mandatory_change?(stable_id)

View file

@ -431,11 +431,15 @@ class Procedure < ApplicationRecord
def draft_changed? def draft_changed?
preload_draft_and_published_revisions 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 end
def revision_changes def types_de_champ_revision_changes
published_revision.compare(draft_revision) published_revision.compare_types_de_champ(draft_revision)
end
def ineligibilite_rules_revision_changes
published_revision.compare_ineligibilite_rules(draft_revision)
end end
def preload_draft_and_published_revisions def preload_draft_and_published_revisions

View file

@ -148,16 +148,18 @@ class ProcedureRevision < ApplicationRecord
!draft? !draft?
end end
def different_from?(revision) def compare_types_de_champ(revision)
revision_types_de_champ != revision.revision_types_de_champ
end
def compare(revision)
changes = [] changes = []
changes += compare_revision_types_de_champ(revision_types_de_champ, revision.revision_types_de_champ) changes += compare_revision_types_de_champ(revision_types_de_champ, revision.revision_types_de_champ)
changes changes
end end
def compare_ineligibilite_rules(revision)
changes = []
changes += compare_revision_ineligibilite_rules(revision)
changes
end
def dossier_for_preview(user) def dossier_for_preview(user)
dossier = Dossier dossier = Dossier
.create_with(autorisation_donnees: true) .create_with(autorisation_donnees: true)
@ -334,6 +336,29 @@ class ProcedureRevision < ApplicationRecord
end end
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) def compare_type_de_champ(from_type_de_champ, to_type_de_champ, from_coordinates, to_coordinates)
changes = [] changes = []
if from_type_de_champ.type_champ != to_type_de_champ.type_champ if from_type_de_champ.type_champ != to_type_de_champ.type_champ

View file

@ -1,17 +1,19 @@
class ProcedureRevisionChange class ProcedureRevisionChange
attr_reader :type_de_champ class TypeDeChange
def initialize(type_de_champ) attr_reader :type_de_champ
@type_de_champ = 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 end
def label = @type_de_champ.libelle class AddChamp < TypeDeChange
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
def initialize(type_de_champ) def initialize(type_de_champ)
super(type_de_champ) super(type_de_champ)
end end
@ -23,7 +25,7 @@ class ProcedureRevisionChange
def to_h = super.merge(mandatory: mandatory?) def to_h = super.merge(mandatory: mandatory?)
end end
class RemoveChamp < ProcedureRevisionChange class RemoveChamp < TypeDeChange
def initialize(type_de_champ) def initialize(type_de_champ)
super(type_de_champ) super(type_de_champ)
end end
@ -32,7 +34,7 @@ class ProcedureRevisionChange
def can_rebase?(dossier = nil) = true def can_rebase?(dossier = nil) = true
end end
class MoveChamp < ProcedureRevisionChange class MoveChamp < TypeDeChange
attr_reader :from, :to attr_reader :from, :to
def initialize(type_de_champ, from, to) def initialize(type_de_champ, from, to)
@ -46,7 +48,7 @@ class ProcedureRevisionChange
def to_h = super.merge(from:, to:) def to_h = super.merge(from:, to:)
end end
class UpdateChamp < ProcedureRevisionChange class UpdateChamp < TypeDeChange
attr_reader :attribute, :from, :to attr_reader :attribute, :from, :to
def initialize(type_de_champ, attribute, from, to) def initialize(type_de_champ, attribute, from, to)
@ -75,4 +77,48 @@ class ProcedureRevisionChange
end end
end 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 end

View file

@ -8,7 +8,7 @@
%p.mb-2= t('.draft_changed_procedure_alert') %p.mb-2= t('.draft_changed_procedure_alert')
= render Dsfr::AlertComponent.new(state: :info, size: :sm, extra_class_names: 'fr-mb-2w') do |c| = render Dsfr::AlertComponent.new(state: :info, size: :sm, extra_class_names: 'fr-mb-2w') do |c|
- c.with_body do - 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? - if procedure.close?
= render partial: 'publication_form_inputs', locals: { procedure: procedure, closed_procedures: @closed_procedures, form: f } = render partial: 'publication_form_inputs', locals: { procedure: procedure, closed_procedures: @closed_procedures, form: f }
- elsif @procedure.brouillon? && @procedure.missing_steps.empty? - elsif @procedure.brouillon? && @procedure.missing_steps.empty?

View file

@ -13,7 +13,6 @@
- previous_revision = nil - previous_revision = nil
- @procedure.revisions.each do |revision| - @procedure.revisions.each do |revision|
- if previous_revision.present? && !revision.draft? - if previous_revision.present? && !revision.draft?
- changes = previous_revision.compare(revision)
- dossiers = revision.dossiers.visible_by_administration - dossiers = revision.dossiers.visible_by_administration
- dossiers_en_construction_count = dossiers.state_en_construction.count - dossiers_en_construction_count = dossiers.state_en_construction.count
- dossiers_en_instruction_count = dossiers.state_en_instruction.count - dossiers_en_instruction_count = dossiers.state_en_instruction.count
@ -31,7 +30,7 @@
%p= t('.dossiers_en_construction', count: dossiers_en_construction_count) %p= t('.dossiers_en_construction', count: dossiers_en_construction_count)
- elsif !dossiers_en_instruction_count.zero? - elsif !dossiers_en_instruction_count.zero?
%p= t('.dossiers_en_instruction', count: dossiers_en_instruction_count) %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 - previous_revision = revision
= render Procedure::FixedFooterComponent.new(procedure: @procedure) = render Procedure::FixedFooterComponent.new(procedure: @procedure)

View file

@ -30,8 +30,8 @@
- if @procedure.draft_changed? - if @procedure.draft_changed?
= render Dsfr::CalloutComponent.new(title: t(:has_changes, scope: [:administrateurs, :revision_changes]), icon: "fr-fi-information-line") do |c| = render Dsfr::CalloutComponent.new(title: t(:has_changes, scope: [:administrateurs, :revision_changes]), icon: "fr-fi-information-line") do |c|
- c.with_body do - 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::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 - c.with_bottom do
%ul.fr-mt-2w.fr-btns-group.fr-btns-group--inline %ul.fr-mt-2w.fr-btns-group.fr-btns-group--inline

View file

@ -347,306 +347,417 @@ describe ProcedureRevision do
end end
end end
describe '#compare' do describe '#compare_types_de_champ' do
include Logic 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 } 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 context 'with a procedure with 2 tdcs' do
let(:procedure) do let(:procedure) do
create(:procedure, types_de_champ_public: [ create(:procedure, types_de_champ_public: [
{ type: :integer_number, libelle: 'l1' }, { type: :integer_number, libelle: 'l1' },
{ type: :text, libelle: 'l2' } { 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 end
context 'when a condition is added' do context 'when a type de champ is added' do
before do let(:procedure) { create(:procedure) }
second = new_draft.find_and_ensure_exclusive_use(second_tdc.stable_id) let(:new_tdc) do
second.update(condition: ds_eq(champ_value(first_tdc.stable_id), constant(3))) new_draft.add_type_de_champ(
type_champ: TypeDeChamp.type_champs.fetch(:text),
libelle: "Un champ text"
)
end end
before { new_tdc }
it do it do
is_expected.to eq([ is_expected.to eq([
{ {
attribute: :condition, op: :add,
from: nil, label: "Un champ text",
label: "l2",
op: :update,
private: false, private: false,
stable_id: second_tdc.stable_id, mandatory: false,
to: "(l1 == 3)" stable_id: new_tdc.stable_id
} }
]) ])
end end
end end
context 'when a condition is removed' do context 'when a type de champ is changed' do
before do context 'when libelle, description, and mandatory are changed' do
second_tdc.update(condition: ds_eq(champ_value(first_tdc.stable_id), constant(2))) let(:procedure) { create(:procedure, :with_type_de_champ) }
draft.reload
second = new_draft.find_and_ensure_exclusive_use(second_tdc.stable_id) before do
second.update(condition: nil) 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 end
it do it do
is_expected.to eq([ is_expected.to eq([
{ {
attribute: :condition, op: :move,
from: "(l1 == 2)", label: new_draft_third_tdc.libelle,
label: "l2",
op: :update,
private: false, private: false,
stable_id: second_tdc.stable_id, from: 2,
to: nil to: 1,
} stable_id: new_draft_third_tdc.stable_id
]) },
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, op: :move,
from: "(l1 == 2)", label: new_draft_second_tdc.libelle,
label: "l2",
op: :update,
private: false, private: false,
stable_id: second_tdc.stable_id, from: 1,
to: "(l1 == 3)" to: 2,
stable_id: new_draft_second_tdc.stable_id
} }
]) ])
end end
end end
end
context 'when a type de champ is added' do context 'when a type de champ is removed' 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
let(:procedure) { create(:procedure, :with_type_de_champ) } let(:procedure) { create(:procedure, :with_type_de_champ) }
before do before do
updated_tdc = new_draft.find_and_ensure_exclusive_use(first_tdc.stable_id) new_draft.remove_type_de_champ(first_tdc.stable_id)
updated_tdc.update(libelle: 'modifier le libelle', description: 'une description', mandatory: !updated_tdc.mandatory)
end end
it do it do
is_expected.to eq([ is_expected.to eq([
{ {
op: :update, op: :remove,
attribute: :libelle,
label: first_tdc.libelle, label: first_tdc.libelle,
private: false, 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 stable_id: first_tdc.stable_id
} }
]) ])
end end
end end
context 'when collapsible_explanation_enabled and collapsible_explanation_text are changed' do context 'when a child type de champ is transformed into a drop_down_list' do
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :explication }]) } let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :repetition, children: [{ type: :text, libelle: 'sub type de champ' }, { type: :integer_number }] }]) }
before do before do
updated_tdc = new_draft.find_and_ensure_exclusive_use(first_tdc.stable_id) 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'])
updated_tdc.update(collapsible_explanation_enabled: "1", collapsible_explanation_text: 'afficher au clique')
end end
it do it do
is_expected.to eq([ is_expected.to eq([
{ {
op: :update, op: :update,
attribute: :collapsible_explanation_enabled, attribute: :type_champ,
label: first_tdc.libelle, label: "sub type de champ",
private: first_tdc.private?, private: false,
from: false, from: "text",
to: true, to: "drop_down_list",
stable_id: first_tdc.stable_id stable_id: new_draft.children_of(new_draft.types_de_champ_public.last).first.stable_id
}, },
{ {
op: :update, op: :update,
attribute: :collapsible_explanation_text, attribute: :drop_down_options,
label: first_tdc.libelle, label: "sub type de champ",
private: first_tdc.private?, private: false,
from: nil, from: [],
to: 'afficher au clique', to: ["one", "two"],
stable_id: first_tdc.stable_id 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
end end
end
context 'when a type de champ is moved' do describe 'compare_ineligibilite_rules' do
let(:procedure) { create(:procedure, types_de_champ_public: Array.new(3) { { type: :text } }) } include Logic
let(:new_draft_second_tdc) { new_draft.types_de_champ_public.second } let(:new_draft) { procedure.create_new_revision }
let(:new_draft_third_tdc) { new_draft.types_de_champ_public.third } subject { procedure.active_revision.compare_ineligibilite_rules(new_draft.reload) }
before do context 'when ineligibilite_rules changes' do
new_draft_second_tdc let(:procedure) { create(:procedure, :published, types_de_champ_public:) }
new_draft_third_tdc let(:types_de_champ_public) { [{ type: :yes_no }] }
new_draft.move_type_de_champ(new_draft_second_tdc.stable_id, 2) let(:yes_no_tdc) { new_draft.types_de_champ_public.first }
context 'when nothing changed' do
it { is_expected.to be_empty }
end end
it do context 'when ineligibilite_rules added' do
is_expected.to eq([ before do
{ new_draft.update!(ineligibilite_rules: ds_eq(champ_value(yes_no_tdc.stable_id), constant(true)))
op: :move, end
label: new_draft_third_tdc.libelle,
private: false, it { is_expected.to include(an_instance_of(ProcedureRevisionChange::AddEligibiliteRuleChange)) }
from: 2, end
to: 1,
stable_id: new_draft_third_tdc.stable_id 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)))
op: :move, end
label: new_draft_second_tdc.libelle,
private: false, it { is_expected.to include(an_instance_of(ProcedureRevisionChange::RemoveEligibiliteRuleChange)) }
from: 1, end
to: 2,
stable_id: new_draft_second_tdc.stable_id 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 end
end
context 'when a type de champ is removed' do describe 'ineligibilite_rules_are_valid?' do
let(:procedure) { create(:procedure, :with_type_de_champ) } include Logic
let(:procedure) { create(:procedure) }
before do let(:draft_revision) { procedure.draft_revision }
new_draft.remove_type_de_champ(first_tdc.stable_id) let(:ineligibilite_message) { 'ok' }
end let(:ineligibilite_enabled) { true }
before do
it do procedure.draft_revision.update(ineligibilite_rules:, ineligibilite_message:, ineligibilite_enabled:)
is_expected.to eq([
{
op: :remove,
label: first_tdc.libelle,
private: false,
stable_id: first_tdc.stable_id
}
])
end
end end
context 'when a child type de champ is transformed into a drop_down_list' do context 'when ineligibilite_rules are valid' do
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :repetition, children: [{ type: :text, libelle: 'sub type de champ' }, { type: :integer_number }] }]) } let(:ineligibilite_rules) { ds_eq(constant(true), constant(true)) }
it 'is valid' do
before do expect(draft_revision.validate(:publication)).to be_truthy
child = new_draft.children_of(new_draft.types_de_champ_public.last).first expect(draft_revision.validate(:ineligibilite_rules_editor)).to be_truthy
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
}
])
end end
end end
context 'when ineligibilite_rules are invalid on simple champ' do
context 'when a child type de champ is transformed into a map' do let(:ineligibilite_rules) { ds_eq(constant(true), constant(1)) }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :repetition, children: [{ type: :text, libelle: 'sub type de champ' }, { type: :integer_number }] }]) } it 'is invalid' do
expect(draft_revision.validate(:publication)).to be_falsey
before do expect(draft_revision.validate(:ineligibilite_rules_editor)).to be_falsey
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 end
end
it do context 'when ineligibilite_rules are invalid on repetition champ' do
is_expected.to eq([ let(:ineligibilite_rules) { ds_eq(constant(true), constant(1)) }
{ let(:procedure) { create(:procedure, types_de_champ_public:) }
op: :update, let(:types_de_champ_public) { [{ type: :repetition, children: [{ type: :integer_number }] }] }
attribute: :type_champ, let(:tdc_number) { draft_revision.types_de_champ_for(scope: :public).find { _1.type_champ == 'integer_number' } }
label: "sub type de champ", let(:ineligibilite_rules) do
private: false, ds_eq(champ_value(tdc_number.stable_id), constant(true))
from: "text", end
to: "carte", it 'is invalid' do
stable_id: new_draft.children_of(new_draft.types_de_champ_public.last).first.stable_id expect(draft_revision.validate(:publication)).to be_falsey
}, expect(draft_revision.validate(:ineligibilite_rules_editor)).to be_falsey
{
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
end end