From 5a4cefc5c50aff187eb73cd08efc165160679170 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Fri, 6 Jan 2023 12:57:15 +0100 Subject: [PATCH] fix(dossier): fix rebase with drop down options --- app/models/champs/drop_down_list_champ.rb | 8 ++ .../champs/linked_drop_down_list_champ.rb | 8 ++ .../champs/multiple_drop_down_list_champ.rb | 8 ++ app/models/concerns/dossier_rebase_concern.rb | 28 ++-- app/models/procedure_revision_change.rb | 2 +- spec/models/dossier_rebase_concern_spec.rb | 122 +++++++++++++++++- 6 files changed, 160 insertions(+), 16 deletions(-) diff --git a/app/models/champs/drop_down_list_champ.rb b/app/models/champs/drop_down_list_champ.rb index 8e0342efd..b8b367bb9 100644 --- a/app/models/champs/drop_down_list_champ.rb +++ b/app/models/champs/drop_down_list_champ.rb @@ -71,4 +71,12 @@ class Champs::DropDownListChamp < Champ def value_other other_value_present? ? value : "" end + + def in?(options) + options.include?(value) + end + + def remove_option(options) + update_column(:value, nil) + end end diff --git a/app/models/champs/linked_drop_down_list_champ.rb b/app/models/champs/linked_drop_down_list_champ.rb index 3861a7325..c292fd7a0 100644 --- a/app/models/champs/linked_drop_down_list_champ.rb +++ b/app/models/champs/linked_drop_down_list_champ.rb @@ -89,6 +89,14 @@ class Champs::LinkedDropDownListChamp < Champ primary_value.present? && secondary_options[primary_value]&.any?(&:present?) end + def in?(options) + options.include?(primary_value) || options.include?(secondary_value) + end + + def remove_option(options) + update_column(:value, nil) + end + private def pack_value(primary, secondary) diff --git a/app/models/champs/multiple_drop_down_list_champ.rb b/app/models/champs/multiple_drop_down_list_champ.rb index 96c442193..b6533d956 100644 --- a/app/models/champs/multiple_drop_down_list_champ.rb +++ b/app/models/champs/multiple_drop_down_list_champ.rb @@ -70,6 +70,14 @@ class Champs::MultipleDropDownListChamp < Champ selected_options.blank? end + def in?(options) + (selected_options - options).size != selected_options.size + end + + def remove_option(options) + update_column(:value, (selected_options - options).to_json) + end + private def format_before_save diff --git a/app/models/concerns/dossier_rebase_concern.rb b/app/models/concerns/dossier_rebase_concern.rb index 8e4c192ca..e5b7b65fa 100644 --- a/app/models/concerns/dossier_rebase_concern.rb +++ b/app/models/concerns/dossier_rebase_concern.rb @@ -26,6 +26,10 @@ module DossierRebaseConcern !champs.filter { _1.stable_id == stable_id }.any?(&:blank?) end + def can_rebase_drop_down_options_change?(stable_id, options) + !champs.filter { _1.stable_id == stable_id }.any? { _1.in?(options) } + end + private def accepted_en_construction_changes? @@ -60,9 +64,11 @@ module DossierRebaseConcern changes_by_op[:remove] .each { delete_champs_for_revision(_1.stable_id) } - changes_by_op[:update] - .map { |change| [change, champs.joins(:type_de_champ).where(type_de_champ: { stable_id: change.stable_id })] } - .each { |change, champs| apply(change, champs) } + if brouillon? + changes_by_op[:update] + .map { |change| [change, champs.joins(:type_de_champ).where(type_de_champ: { stable_id: change.stable_id })] } + .each { |change, champs| apply(change, champs) } + end # due to repetition tdc clone on update or erase # we must reassign tdc to the latest version @@ -82,25 +88,23 @@ module DossierRebaseConcern champs.each { purge_piece_justificative_file(_1) } GeoArea.where(champ: champs).destroy_all Etablissement.where(champ: champs).destroy_all - - { - type: "Champs::#{change.to.classify}Champ", + champs.update_all(type: "Champs::#{change.to.classify}Champ", value: nil, value_json: nil, external_id: nil, - data: nil - } + data: nil) when :drop_down_options - { value: nil } + # we are removing options, we need to remove the value if it contains one of the removed options + removed_options = change.from - change.to + if removed_options.present? && champs.any? { _1.in?(removed_options) } + champs.filter { _1.in?(removed_options) }.each { _1.remove_option(removed_options) } + end when :carte_layers # if we are removing cadastres layer, we need to remove cadastre geo areas if change.from.include?(:cadastres) && !change.to.include?(:cadastres) champs.each { _1.cadastres.each(&:destroy) } end - - nil end - &.then { champs.update_all(_1) } end def add_new_champs_for_revision(target_coordinate) diff --git a/app/models/procedure_revision_change.rb b/app/models/procedure_revision_change.rb index 88f7b494f..46f762f32 100644 --- a/app/models/procedure_revision_change.rb +++ b/app/models/procedure_revision_change.rb @@ -62,7 +62,7 @@ class ProcedureRevisionChange case attribute when :drop_down_options - (from - to).empty? + (from - to).empty? || dossier&.can_rebase_drop_down_options_change?(stable_id, from - to) when :drop_down_other !from && to when :mandatory diff --git a/spec/models/dossier_rebase_concern_spec.rb b/spec/models/dossier_rebase_concern_spec.rb index 700bbe361..596cf9096 100644 --- a/spec/models/dossier_rebase_concern_spec.rb +++ b/spec/models/dossier_rebase_concern_spec.rb @@ -323,23 +323,139 @@ describe Dossier do context 'with a procedure with a dropdown tdc' do let!(:procedure) do create(:procedure).tap do |p| - p.draft_revision.add_type_de_champ(type_champ: :drop_down_list, libelle: 'l1', drop_down_list_value: 'option') + p.draft_revision.add_type_de_champ(type_champ: :drop_down_list, libelle: 'l1', drop_down_list_value: "option\nv1\n") p.publish! end end let!(:dossier) { create(:dossier, procedure: procedure) } - context 'when a dropdown option is changed' do + context 'when a dropdown option is added' do before do dossier.champs_public.first.update(value: 'v1') stable_id = procedure.draft_revision.types_de_champ.find_by(libelle: 'l1') tdc_to_update = procedure.draft_revision.find_and_ensure_exclusive_use(stable_id) - tdc_to_update.update(drop_down_list_value: 'option updated') + tdc_to_update.update(drop_down_list_value: "option\nupdated\nv1") + end + + it { expect { subject }.not_to change { dossier.champs_public.first.value } } + end + + context 'when a dropdown option is removed' do + before do + dossier.champs_public.first.update(value: 'v1') + + stable_id = procedure.draft_revision.types_de_champ.find_by(libelle: 'l1') + tdc_to_update = procedure.draft_revision.find_and_ensure_exclusive_use(stable_id) + tdc_to_update.update(drop_down_list_value: "option\nupdated") end it { expect { subject }.to change { dossier.champs_public.first.value }.from('v1').to(nil) } end + + context 'when a dropdown unused option is removed' do + before do + dossier.champs_public.first.update(value: 'v1') + + stable_id = procedure.draft_revision.types_de_champ.find_by(libelle: 'l1') + tdc_to_update = procedure.draft_revision.find_and_ensure_exclusive_use(stable_id) + tdc_to_update.update(drop_down_list_value: "v1\nupdated") + end + + it { expect { subject }.not_to change { dossier.champs_public.first.value } } + end + end + + context 'with a procedure with a multiple dropdown tdc' do + let!(:procedure) do + create(:procedure).tap do |p| + p.draft_revision.add_type_de_champ(type_champ: :multiple_drop_down_list, libelle: 'l1', drop_down_list_value: "option\nv1\n") + p.publish! + end + end + let!(:dossier) { create(:dossier, procedure: procedure) } + + context 'when a dropdown option is added' do + before do + dossier.champs_public.first.update(value: '["v1"]') + + stable_id = procedure.draft_revision.types_de_champ.find_by(libelle: 'l1') + tdc_to_update = procedure.draft_revision.find_and_ensure_exclusive_use(stable_id) + tdc_to_update.update(drop_down_list_value: "option\nupdated\nv1") + end + + it { expect { subject }.not_to change { dossier.champs_public.first.value } } + end + + context 'when a dropdown option is removed' do + before do + dossier.champs_public.first.update(value: '["v1", "option"]') + + stable_id = procedure.draft_revision.types_de_champ.find_by(libelle: 'l1') + tdc_to_update = procedure.draft_revision.find_and_ensure_exclusive_use(stable_id) + tdc_to_update.update(drop_down_list_value: "option\nupdated") + end + + it { expect { subject }.to change { dossier.champs_public.first.value }.from('["v1", "option"]').to('["option"]') } + end + + context 'when a dropdown unused option is removed' do + before do + dossier.champs_public.first.update(value: '["v1"]') + + stable_id = procedure.draft_revision.types_de_champ.find_by(libelle: 'l1') + tdc_to_update = procedure.draft_revision.find_and_ensure_exclusive_use(stable_id) + tdc_to_update.update(drop_down_list_value: "v1\nupdated") + end + + it { expect { subject }.not_to change { dossier.champs_public.first.value } } + end + end + + context 'with a procedure with a linked dropdown tdc' do + let!(:procedure) do + create(:procedure).tap do |p| + p.draft_revision.add_type_de_champ(type_champ: :linked_drop_down_list, libelle: 'l1', drop_down_list_value: "--titre1--\noption\nv1\n--titre2--\noption2\nv2\n") + p.publish! + end + end + let!(:dossier) { create(:dossier, procedure: procedure) } + + context 'when a dropdown option is added' do + before do + dossier.champs_public.first.update(value: '["v1",""]') + + stable_id = procedure.draft_revision.types_de_champ.find_by(libelle: 'l1') + tdc_to_update = procedure.draft_revision.find_and_ensure_exclusive_use(stable_id) + tdc_to_update.update(drop_down_list_value: "--titre1--\noption\nv1\nupdated\n--titre2--\noption2\nv2\n") + end + + it { expect { subject }.not_to change { dossier.champs_public.first.value } } + end + + context 'when a dropdown option is removed' do + before do + dossier.champs_public.first.update(value: '["v1","option2"]') + + stable_id = procedure.draft_revision.types_de_champ.find_by(libelle: 'l1') + tdc_to_update = procedure.draft_revision.find_and_ensure_exclusive_use(stable_id) + tdc_to_update.update(drop_down_list_value: "--titre1--\noption\nupdated\n--titre2--\noption2\nv2\n") + end + + it { expect { subject }.to change { dossier.champs_public.first.value }.from('["v1","option2"]').to(nil) } + end + + context 'when a dropdown unused option is removed' do + before do + dossier.champs_public.first.update(value: '["v1",""]') + + stable_id = procedure.draft_revision.types_de_champ.find_by(libelle: 'l1') + tdc_to_update = procedure.draft_revision.find_and_ensure_exclusive_use(stable_id) + tdc_to_update.update(drop_down_list_value: "--titre1--\nv1\nupdated\n--titre2--\noption2\nv2\n") + end + + it { expect { subject }.not_to change { dossier.champs_public.first.value } } + end end context 'with a procedure with a carte tdc' do