feat(ProcedureRevision.ineligibilites_rules): keep track of changes and show it to admin for republication
This commit is contained in:
parent
aca3e38859
commit
5de4ce889f
11 changed files with 458 additions and 258 deletions
|
@ -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
|
||||||
|
|
|
@ -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} ».
|
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é.
|
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} ».
|
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} »"
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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?
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue