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
|
||||
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
|
||||
|
|
|
@ -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} »"
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
class ProcedureRevisionChange
|
||||
class TypeDeChange
|
||||
attr_reader :type_de_champ
|
||||
def initialize(type_de_champ)
|
||||
@type_de_champ = type_de_champ
|
||||
|
@ -10,8 +11,9 @@ class ProcedureRevisionChange
|
|||
def child? = @type_de_champ.child?
|
||||
|
||||
def to_h = { op:, stable_id:, label:, private: private? }
|
||||
end
|
||||
|
||||
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
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -347,14 +347,14 @@ describe ProcedureRevision do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#compare' do
|
||||
describe '#compare_types_de_champ' do
|
||||
include Logic
|
||||
let(:new_draft) { procedure.create_new_revision }
|
||||
subject { procedure.active_revision.compare_types_de_champ(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 }
|
||||
let(:new_draft) { procedure.create_new_revision }
|
||||
|
||||
subject { procedure.active_revision.compare(new_draft.reload).map(&:to_h) }
|
||||
|
||||
context 'with a procedure with 2 tdcs' do
|
||||
let(:procedure) do
|
||||
|
@ -650,6 +650,117 @@ describe ProcedureRevision do
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'compare_ineligibilite_rules' do
|
||||
include Logic
|
||||
let(:new_draft) { procedure.create_new_revision }
|
||||
subject { procedure.active_revision.compare_ineligibilite_rules(new_draft.reload) }
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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 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 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
|
||||
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
|
||||
|
||||
describe 'children_of' do
|
||||
context 'with a simple tdc' do
|
||||
|
|
Loading…
Reference in a new issue