Merge pull request #10873 from tchak/refactor-repetition-remove-parent_id
ETQ dev, je ne veux plus avoir de relation entre les champs et leur parent
This commit is contained in:
commit
8ddec687e6
25 changed files with 72 additions and 222 deletions
|
@ -4,19 +4,17 @@ class Champ < ApplicationRecord
|
||||||
include ChampConditionalConcern
|
include ChampConditionalConcern
|
||||||
include ChampsValidateConcern
|
include ChampsValidateConcern
|
||||||
|
|
||||||
self.ignored_columns += [:type_de_champ_id]
|
self.ignored_columns += [:type_de_champ_id, :parent_id]
|
||||||
|
|
||||||
attr_readonly :stable_id
|
attr_readonly :stable_id
|
||||||
|
|
||||||
belongs_to :dossier, inverse_of: false, touch: true, optional: false
|
belongs_to :dossier, inverse_of: false, touch: true, optional: false
|
||||||
belongs_to :parent, class_name: 'Champ', optional: true
|
|
||||||
has_many_attached :piece_justificative_file
|
has_many_attached :piece_justificative_file
|
||||||
|
|
||||||
# We declare champ specific relationships (Champs::CarteChamp, Champs::SiretChamp and Champs::RepetitionChamp)
|
# We declare champ specific relationships (Champs::CarteChamp, Champs::SiretChamp and Champs::RepetitionChamp)
|
||||||
# here because otherwise we can't easily use includes in our queries.
|
# here because otherwise we can't easily use includes in our queries.
|
||||||
has_many :geo_areas, -> { order(:created_at) }, dependent: :destroy, inverse_of: :champ
|
has_many :geo_areas, -> { order(:created_at) }, dependent: :destroy, inverse_of: :champ
|
||||||
belongs_to :etablissement, optional: true, dependent: :destroy
|
belongs_to :etablissement, optional: true, dependent: :destroy
|
||||||
has_many :champs, foreign_key: :parent_id, inverse_of: :parent
|
|
||||||
|
|
||||||
delegate :procedure, to: :dossier
|
delegate :procedure, to: :dossier
|
||||||
|
|
||||||
|
@ -79,13 +77,8 @@ class Champ < ApplicationRecord
|
||||||
delegate :used_by_routing_rules?, to: :type_de_champ
|
delegate :used_by_routing_rules?, to: :type_de_champ
|
||||||
|
|
||||||
scope :updated_since?, -> (date) { where('champs.updated_at > ?', date) }
|
scope :updated_since?, -> (date) { where('champs.updated_at > ?', date) }
|
||||||
scope :public_only, -> { where(private: false) }
|
|
||||||
scope :private_only, -> { where(private: true) }
|
|
||||||
scope :root, -> { where(parent_id: nil) }
|
|
||||||
scope :prefilled, -> { where(prefilled: true) }
|
scope :prefilled, -> { where(prefilled: true) }
|
||||||
|
|
||||||
before_create :set_dossier_id, if: :needs_dossier_id?
|
|
||||||
before_validation :set_dossier_id, if: :needs_dossier_id?
|
|
||||||
before_save :cleanup_if_empty
|
before_save :cleanup_if_empty
|
||||||
before_save :normalize
|
before_save :normalize
|
||||||
after_update_commit :fetch_external_data_later
|
after_update_commit :fetch_external_data_later
|
||||||
|
@ -232,7 +225,7 @@ class Champ < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def clone(fork = false)
|
def clone(fork = false)
|
||||||
champ_attributes = [:parent_id, :private, :row_id, :type, :stable_id, :stream]
|
champ_attributes = [:private, :row_id, :type, :stable_id, :stream]
|
||||||
value_attributes = fork || !private? ? [:value, :value_json, :data, :external_id] : []
|
value_attributes = fork || !private? ? [:value, :value_json, :data, :external_id] : []
|
||||||
relationships = fork || !private? ? [:etablissement, :geo_areas] : []
|
relationships = fork || !private? ? [:etablissement, :geo_areas] : []
|
||||||
|
|
||||||
|
@ -265,14 +258,6 @@ class Champ < ApplicationRecord
|
||||||
type_de_champ.html_id(row_id)
|
type_de_champ.html_id(row_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def needs_dossier_id?
|
|
||||||
!dossier_id && parent_id
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_dossier_id
|
|
||||||
self.dossier_id = parent.dossier_id
|
|
||||||
end
|
|
||||||
|
|
||||||
def cleanup_if_empty
|
def cleanup_if_empty
|
||||||
if fetch_external_data? && persisted? && external_id_changed?
|
if fetch_external_data? && persisted? && external_id_changed?
|
||||||
self.data = nil
|
self.data = nil
|
||||||
|
|
|
@ -12,11 +12,7 @@ class Champs::RepetitionChamp < Champ
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_row(updated_by:)
|
def add_row(updated_by:)
|
||||||
# TODO: clean this up when parent_id is deprecated
|
dossier.repetition_add_row(type_de_champ, updated_by:)
|
||||||
row_id, added_champs = dossier.repetition_add_row(type_de_champ, updated_by:)
|
|
||||||
self.champs << added_champs
|
|
||||||
dossier.champs.reload if dossier.persisted?
|
|
||||||
row_id
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_row(row_id, updated_by:)
|
def remove_row(row_id, updated_by:)
|
||||||
|
|
|
@ -40,7 +40,7 @@ module DossierChampsConcern
|
||||||
end
|
end
|
||||||
|
|
||||||
def project_rows_for(type_de_champ)
|
def project_rows_for(type_de_champ)
|
||||||
[] if !type_de_champ.repetition?
|
return [] if !type_de_champ.repetition?
|
||||||
|
|
||||||
children = revision.children_of(type_de_champ)
|
children = revision.children_of(type_de_champ)
|
||||||
row_ids = repetition_row_ids(type_de_champ)
|
row_ids = repetition_row_ids(type_de_champ)
|
||||||
|
@ -84,7 +84,7 @@ module DossierChampsConcern
|
||||||
end
|
end
|
||||||
|
|
||||||
def repetition_row_ids(type_de_champ)
|
def repetition_row_ids(type_de_champ)
|
||||||
[] if !type_de_champ.repetition?
|
return [] if !type_de_champ.repetition?
|
||||||
|
|
||||||
stable_ids = revision.children_of(type_de_champ).map(&:stable_id)
|
stable_ids = revision.children_of(type_de_champ).map(&:stable_id)
|
||||||
champs.filter { _1.stable_id.in?(stable_ids) && _1.row_id.present? }
|
champs.filter { _1.stable_id.in?(stable_ids) && _1.row_id.present? }
|
||||||
|
@ -98,10 +98,10 @@ module DossierChampsConcern
|
||||||
|
|
||||||
row_id = ULID.generate
|
row_id = ULID.generate
|
||||||
types_de_champ = revision.children_of(type_de_champ)
|
types_de_champ = revision.children_of(type_de_champ)
|
||||||
# TODO: clean this up when parent_id is deprecated
|
self.champs += types_de_champ.map { _1.build_champ(row_id:, updated_by:) }
|
||||||
added_champs = types_de_champ.map { _1.build_champ(row_id:, updated_by:) }
|
champs.reload if persisted?
|
||||||
@champs_by_public_id = nil
|
@champs_by_public_id = nil
|
||||||
[row_id, added_champs]
|
row_id
|
||||||
end
|
end
|
||||||
|
|
||||||
def repetition_remove_row(type_de_champ, row_id, updated_by:)
|
def repetition_remove_row(type_de_champ, row_id, updated_by:)
|
||||||
|
@ -158,13 +158,6 @@ module DossierChampsConcern
|
||||||
attributes[:data] = nil
|
attributes[:data] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
parent = revision.parent_of(type_de_champ)
|
|
||||||
if parent.present?
|
|
||||||
attributes[:parent] = champs.find { _1.stable_id == parent.stable_id }
|
|
||||||
else
|
|
||||||
attributes[:parent] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
@champs_by_public_id = nil
|
@champs_by_public_id = nil
|
||||||
|
|
||||||
[champ, attributes]
|
[champ, attributes]
|
||||||
|
|
|
@ -101,7 +101,6 @@ module DossierCloneConcern
|
||||||
kopy.state = Dossier.states.fetch(:brouillon)
|
kopy.state = Dossier.states.fetch(:brouillon)
|
||||||
kopy.champs = cloned_champs.values.map do |(_, champ)|
|
kopy.champs = cloned_champs.values.map do |(_, champ)|
|
||||||
champ.dossier = kopy
|
champ.dossier = kopy
|
||||||
champ.parent = cloned_champs[champ.parent_id].second if champ.child?
|
|
||||||
champ
|
champ
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -152,33 +151,16 @@ module DossierCloneConcern
|
||||||
def apply_diff(diff)
|
def apply_diff(diff)
|
||||||
champs_index = (champs_for_revision(scope: :public) + diff[:added]).index_by(&:public_id)
|
champs_index = (champs_for_revision(scope: :public) + diff[:added]).index_by(&:public_id)
|
||||||
|
|
||||||
diff[:added].each do |champ|
|
diff[:added].each { _1.update_column(:dossier_id, id) }
|
||||||
if champ.child?
|
|
||||||
champ.update_columns(dossier_id: id, parent_id: champs_index.fetch(champ.parent.public_id).id)
|
|
||||||
else
|
|
||||||
champ.update_column(:dossier_id, id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
champs_to_remove = []
|
# a bit of a hack to work around unicity index
|
||||||
|
remove_group_id = ULID.generate
|
||||||
diff[:updated].each do |champ|
|
diff[:updated].each do |champ|
|
||||||
old_champ = champs_index.fetch(champ.public_id)
|
champs_index.fetch(champ.public_id).update(row_id: remove_group_id)
|
||||||
champs_to_remove << old_champ
|
champ.update_column(:dossier_id, id)
|
||||||
|
|
||||||
if champ.child?
|
|
||||||
# we need to do that in order to avoid a foreign key constraint
|
|
||||||
old_champ.update(row_id: nil)
|
|
||||||
champ.update_columns(dossier_id: id, parent_id: champs_index.fetch(champ.parent.public_id).id)
|
|
||||||
else
|
|
||||||
champ.update_column(:dossier_id, id)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
champs_to_remove += diff[:removed]
|
Champ.where(row_id: remove_group_id).destroy_all
|
||||||
children_champs_to_remove, root_champs_to_remove = champs_to_remove.partition(&:child?)
|
diff[:removed].each(&:destroy!)
|
||||||
|
|
||||||
children_champs_to_remove.each(&:destroy!)
|
|
||||||
Champ.where(parent_id: root_champs_to_remove.map(&:id)).destroy_all
|
|
||||||
root_champs_to_remove.each(&:destroy!)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -65,9 +65,7 @@ module DossierRebaseConcern
|
||||||
.tap { _1.default = Champ.none }
|
.tap { _1.default = Champ.none }
|
||||||
|
|
||||||
# remove champ
|
# remove champ
|
||||||
children_champ, root_champ = changes_by_op[:remove].partition(&:child?)
|
changes_by_op[:remove].each { champs_by_stable_id[_1.stable_id].destroy_all }
|
||||||
children_champ.each { champs_by_stable_id[_1.stable_id].destroy_all }
|
|
||||||
root_champ.each { champs_by_stable_id[_1.stable_id].destroy_all }
|
|
||||||
|
|
||||||
# update champ
|
# update champ
|
||||||
changes_by_op[:update].each { apply(_1, champs_by_stable_id[_1.stable_id]) }
|
changes_by_op[:update].each { apply(_1, champs_by_stable_id[_1.stable_id]) }
|
||||||
|
@ -78,8 +76,6 @@ module DossierRebaseConcern
|
||||||
# add champ (after changing dossier revision to avoid errors)
|
# add champ (after changing dossier revision to avoid errors)
|
||||||
changes_by_op[:add]
|
changes_by_op[:add]
|
||||||
.map { target_coordinates_by_stable_id[_1.stable_id] }
|
.map { target_coordinates_by_stable_id[_1.stable_id] }
|
||||||
# add parent champs first so we can then add children
|
|
||||||
.sort_by { _1.child? ? 1 : 0 }
|
|
||||||
.each { add_new_champs_for_revision(_1) }
|
.each { add_new_champs_for_revision(_1) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -119,28 +115,24 @@ module DossierRebaseConcern
|
||||||
|
|
||||||
def add_new_champs_for_revision(target_coordinate)
|
def add_new_champs_for_revision(target_coordinate)
|
||||||
if target_coordinate.child?
|
if target_coordinate.child?
|
||||||
# If this type de champ is a child, we create a new champ for each row of the parent
|
row_ids = repetition_row_ids(target_coordinate.parent.type_de_champ)
|
||||||
parent_stable_id = target_coordinate.parent.stable_id
|
|
||||||
|
|
||||||
champs.filter { _1.stable_id == parent_stable_id }.each do |champ_repetition|
|
if row_ids.present?
|
||||||
if champ_repetition.champs.present?
|
row_ids.each do |row_id|
|
||||||
champ_repetition.champs.map(&:row_id).uniq.each do |row_id|
|
create_champ(target_coordinate, row_id:)
|
||||||
champs << create_champ(target_coordinate, champ_repetition, row_id:)
|
|
||||||
end
|
|
||||||
elsif target_coordinate.parent.mandatory?
|
|
||||||
champs << create_champ(target_coordinate, champ_repetition, row_id: ULID.generate)
|
|
||||||
end
|
end
|
||||||
|
elsif target_coordinate.parent.mandatory?
|
||||||
|
create_champ(target_coordinate, row_id: ULID.generate)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
create_champ(target_coordinate, self)
|
create_champ(target_coordinate)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_champ(target_coordinate, parent, row_id: nil)
|
def create_champ(target_coordinate, row_id: nil)
|
||||||
target_coordinate
|
self.champs << target_coordinate
|
||||||
.type_de_champ
|
.type_de_champ
|
||||||
.build_champ(rebased_at: Time.zone.now, row_id:)
|
.build_champ(rebased_at: Time.zone.now, row_id:)
|
||||||
.tap { parent.champs << _1 }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def purge_piece_justificative_file(champ)
|
def purge_piece_justificative_file(champ)
|
||||||
|
|
|
@ -45,11 +45,7 @@ class Dossier < ApplicationRecord
|
||||||
|
|
||||||
has_one_attached :justificatif_motivation
|
has_one_attached :justificatif_motivation
|
||||||
|
|
||||||
has_many :champs
|
has_many :champs, dependent: :destroy
|
||||||
# We have to remove champs in a particular order - champs with a reference to a parent have to be
|
|
||||||
# removed first, otherwise we get a foreign key constraint error.
|
|
||||||
has_many :champs_to_destroy, -> { order(:parent_id) }, class_name: 'Champ', inverse_of: false, dependent: :destroy
|
|
||||||
|
|
||||||
has_many :commentaires, inverse_of: :dossier, dependent: :destroy
|
has_many :commentaires, inverse_of: :dossier, dependent: :destroy
|
||||||
has_many :preloaded_commentaires, -> { includes(:dossier_correction, piece_jointe_attachments: :blob) }, class_name: 'Commentaire', inverse_of: :dossier
|
has_many :preloaded_commentaires, -> { includes(:dossier_correction, piece_jointe_attachments: :blob) }, class_name: 'Commentaire', inverse_of: :dossier
|
||||||
|
|
||||||
|
@ -1160,12 +1156,12 @@ class Dossier < ApplicationRecord
|
||||||
|
|
||||||
def build_default_champs_for(types_de_champ)
|
def build_default_champs_for(types_de_champ)
|
||||||
self.champs << types_de_champ.flat_map do |type_de_champ|
|
self.champs << types_de_champ.flat_map do |type_de_champ|
|
||||||
|
champ = type_de_champ.build_champ(dossier: self)
|
||||||
if type_de_champ.repetition? && (type_de_champ.private? || type_de_champ.mandatory?)
|
if type_de_champ.repetition? && (type_de_champ.private? || type_de_champ.mandatory?)
|
||||||
row_id = ULID.generate
|
row_id = ULID.generate
|
||||||
parent = type_de_champ.build_champ(dossier: self)
|
[champ] + revision.children_of(type_de_champ).map { _1.build_champ(dossier: self, row_id:) }
|
||||||
[parent] + revision.children_of(type_de_champ).map { _1.build_champ(dossier: self, parent:, row_id:) }
|
|
||||||
else
|
else
|
||||||
type_de_champ.build_champ(dossier: self)
|
champ
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -81,20 +81,8 @@ class DossierPreloader
|
||||||
end
|
end
|
||||||
dossier.association(:champs).target = champs
|
dossier.association(:champs).target = champs
|
||||||
|
|
||||||
# remove once parent_id is deprecated
|
|
||||||
champs_by_parent_id = champs.group_by(&:parent_id)
|
|
||||||
|
|
||||||
champs.each do |champ|
|
champs.each do |champ|
|
||||||
champ.association(:dossier).target = dossier
|
champ.association(:dossier).target = dossier
|
||||||
|
|
||||||
# remove once parent_id is deprecated
|
|
||||||
if champ.repetition?
|
|
||||||
children = champs_by_parent_id.fetch(champ.id, [])
|
|
||||||
children.each do |child|
|
|
||||||
child.association(:parent).target = champ
|
|
||||||
end
|
|
||||||
champ.association(:champs).target = children
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# We need to do this because of the check on `Etablissement#champ` in
|
# We need to do this because of the check on `Etablissement#champ` in
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Maintenance
|
|
||||||
class RescueDossierWithInvalidRepetitionTask < MaintenanceTasks::Task
|
|
||||||
INVALID_RELEASE_DATETIME = DateTime.new(2024, 8, 30, 12)
|
|
||||||
def collection
|
|
||||||
Dossier.where("last_champ_updated_at > ?", INVALID_RELEASE_DATETIME).pluck(:id) # heure de l'incident
|
|
||||||
end
|
|
||||||
|
|
||||||
def process(dossier_id)
|
|
||||||
Dossier.find(dossier_id)
|
|
||||||
.champs
|
|
||||||
.filter { _1.row_id.present? && _1.parent_id.blank? }
|
|
||||||
.each(&:destroy!)
|
|
||||||
rescue ActiveRecord::RecordNotFound
|
|
||||||
# some dossier had already been destroyed
|
|
||||||
end
|
|
||||||
|
|
||||||
def count
|
|
||||||
# Optionally, define the number of rows that will be iterated over
|
|
||||||
# This is used to track the task's progress
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
8
db/migrate/20240321081721_remove_champs_foreign_key.rb
Normal file
8
db/migrate/20240321081721_remove_champs_foreign_key.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class RemoveChampsForeignKey < ActiveRecord::Migration[7.0]
|
||||||
|
def change
|
||||||
|
remove_foreign_key :champs, column: :parent_id
|
||||||
|
remove_index :champs, :parent_id
|
||||||
|
end
|
||||||
|
end
|
|
@ -270,7 +270,6 @@ ActiveRecord::Schema[7.0].define(version: 2024_09_29_141825) do
|
||||||
t.index ["dossier_id", "stream", "stable_id", "row_id"], name: "index_champs_on_dossier_id_and_stream_and_stable_id_and_row_id", unique: true
|
t.index ["dossier_id", "stream", "stable_id", "row_id"], name: "index_champs_on_dossier_id_and_stream_and_stable_id_and_row_id", unique: true
|
||||||
t.index ["dossier_id"], name: "index_champs_on_dossier_id"
|
t.index ["dossier_id"], name: "index_champs_on_dossier_id"
|
||||||
t.index ["etablissement_id"], name: "index_champs_on_etablissement_id"
|
t.index ["etablissement_id"], name: "index_champs_on_etablissement_id"
|
||||||
t.index ["parent_id"], name: "index_champs_on_parent_id"
|
|
||||||
t.index ["row_id"], name: "index_champs_on_row_id"
|
t.index ["row_id"], name: "index_champs_on_row_id"
|
||||||
t.index ["stable_id"], name: "index_champs_on_stable_id"
|
t.index ["stable_id"], name: "index_champs_on_stable_id"
|
||||||
t.index ["type"], name: "index_champs_on_type"
|
t.index ["type"], name: "index_champs_on_type"
|
||||||
|
@ -1264,7 +1263,6 @@ ActiveRecord::Schema[7.0].define(version: 2024_09_29_141825) do
|
||||||
add_foreign_key "avis", "experts_procedures"
|
add_foreign_key "avis", "experts_procedures"
|
||||||
add_foreign_key "batch_operations", "instructeurs"
|
add_foreign_key "batch_operations", "instructeurs"
|
||||||
add_foreign_key "bulk_messages", "procedures"
|
add_foreign_key "bulk_messages", "procedures"
|
||||||
add_foreign_key "champs", "champs", column: "parent_id"
|
|
||||||
add_foreign_key "champs", "dossiers"
|
add_foreign_key "champs", "dossiers"
|
||||||
add_foreign_key "champs", "etablissements"
|
add_foreign_key "champs", "etablissements"
|
||||||
add_foreign_key "champs", "types_de_champ"
|
add_foreign_key "champs", "types_de_champ"
|
||||||
|
|
|
@ -5,7 +5,7 @@ describe EditableChamp::EditableChampComponent, type: :component do
|
||||||
let(:types_de_champ_public) { [] }
|
let(:types_de_champ_public) { [] }
|
||||||
let(:types_de_champ_private) { [] }
|
let(:types_de_champ_private) { [] }
|
||||||
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
|
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
|
||||||
let(:champ) { dossier.champs.first }
|
let(:champ) { (dossier.project_champs_public + dossier.project_champs_private).first }
|
||||||
|
|
||||||
let(:component) { described_class.new(form: nil, champ:) }
|
let(:component) { described_class.new(form: nil, champ:) }
|
||||||
|
|
||||||
|
|
|
@ -1037,7 +1037,7 @@ describe Instructeurs::DossiersController, type: :controller do
|
||||||
primary_value: 'primary',
|
primary_value: 'primary',
|
||||||
secondary_value: 'secondary'
|
secondary_value: 'secondary'
|
||||||
},
|
},
|
||||||
champ_repetition.champs.first.public_id => {
|
champ_repetition.rows.first.first.public_id => {
|
||||||
value: 'text'
|
value: 'text'
|
||||||
},
|
},
|
||||||
champ_drop_down_list.public_id => {
|
champ_drop_down_list.public_id => {
|
||||||
|
@ -1054,7 +1054,7 @@ describe Instructeurs::DossiersController, type: :controller do
|
||||||
expect(champ_linked_drop_down_list.primary_value).to eq('primary')
|
expect(champ_linked_drop_down_list.primary_value).to eq('primary')
|
||||||
expect(champ_linked_drop_down_list.secondary_value).to eq('secondary')
|
expect(champ_linked_drop_down_list.secondary_value).to eq('secondary')
|
||||||
expect(champ_datetime.value).to eq(Time.zone.parse('2019-12-21T13:17:00').iso8601)
|
expect(champ_datetime.value).to eq(Time.zone.parse('2019-12-21T13:17:00').iso8601)
|
||||||
expect(champ_repetition.champs.first.value).to eq('text')
|
expect(champ_repetition.rows.first.first.value).to eq('text')
|
||||||
expect(champ_drop_down_list.value).to eq('other value')
|
expect(champ_drop_down_list.value).to eq('other value')
|
||||||
expect(dossier.reload.last_champ_private_updated_at).to eq(now)
|
expect(dossier.reload.last_champ_private_updated_at).to eq(now)
|
||||||
expect(response).to have_http_status(200)
|
expect(response).to have_http_status(200)
|
||||||
|
|
|
@ -196,8 +196,8 @@ FactoryBot.define do
|
||||||
|
|
||||||
evaluator.rows.times do
|
evaluator.rows.times do
|
||||||
row_id = ULID.generate
|
row_id = ULID.generate
|
||||||
champ_repetition.champs << types_de_champ.map do |type_de_champ|
|
champ_repetition.dossier.champs << types_de_champ.map do |type_de_champ|
|
||||||
attrs = { dossier: champ_repetition.dossier, parent: champ_repetition, private: champ_repetition.private?, stable_id: type_de_champ.stable_id, row_id: }
|
attrs = { dossier: champ_repetition.dossier, private: champ_repetition.private?, stable_id: type_de_champ.stable_id, row_id: }
|
||||||
build(:"champ_do_not_use_#{type_de_champ.type_champ}", **attrs)
|
build(:"champ_do_not_use_#{type_de_champ.type_champ}", **attrs)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -48,14 +48,14 @@ RSpec.describe Mutations::DossierModifierAnnotation, type: :graphql do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'add row' do
|
it 'add row' do
|
||||||
expect(annotation.champs.size).to eq(4)
|
expect(annotation.row_ids.size).to eq(2)
|
||||||
expect(data).to eq(dossierModifierAnnotationAjouterLigne: {
|
expect(data).to eq(dossierModifierAnnotationAjouterLigne: {
|
||||||
annotation: {
|
annotation: {
|
||||||
id: annotation.to_typed_id
|
id: annotation.to_typed_id
|
||||||
},
|
},
|
||||||
errors: nil
|
errors: nil
|
||||||
})
|
})
|
||||||
expect(annotation.reload.champs.size).to eq(6)
|
expect(annotation.reload.row_ids.size).to eq(3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -83,7 +83,7 @@ describe 'Dossier::Recovery::LifeCycle' do
|
||||||
|
|
||||||
expect(reloaded_dossier.champs.count).not_to be(0)
|
expect(reloaded_dossier.champs.count).not_to be(0)
|
||||||
|
|
||||||
expect(repetition(reloaded_dossier).champs.map(&:type)).to match_array(["Champs::PieceJustificativeChamp"])
|
expect(repetition(reloaded_dossier).rows.flatten.map(&:type)).to match_array(["Champs::PieceJustificativeChamp"])
|
||||||
expect(pj_champ(reloaded_dossier).piece_justificative_file).to be_attached
|
expect(pj_champ(reloaded_dossier).piece_justificative_file).to be_attached
|
||||||
expect(carte(reloaded_dossier).geo_areas).to be_present
|
expect(carte(reloaded_dossier).geo_areas).to be_present
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ describe ChampPresentations::RepetitionPresentation do
|
||||||
stars.update(value: 4)
|
stars.update(value: 4)
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:representation) { described_class.new(libelle, dossier.champs[0].reload.rows) }
|
let(:representation) { described_class.new(libelle, champ_repetition.rows) }
|
||||||
|
|
||||||
describe '#to_s' do
|
describe '#to_s' do
|
||||||
it 'returns a key-value representation' do
|
it 'returns a key-value representation' do
|
||||||
|
|
|
@ -75,44 +75,27 @@ describe Champ do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#public?' do
|
describe 'public and private' do
|
||||||
let(:champ) { Champ.new }
|
let(:champ) { Champ.new }
|
||||||
|
|
||||||
it { expect(champ.public?).to be_truthy }
|
|
||||||
it { expect(champ.private?).to be_falsey }
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#public_only' do
|
|
||||||
let(:dossier) { create(:dossier) }
|
let(:dossier) { create(:dossier) }
|
||||||
|
|
||||||
it 'partition public and private' do
|
it 'partition public and private' do
|
||||||
expect(dossier.project_champs_public.count).to eq(1)
|
expect(dossier.project_champs_public.count).to eq(1)
|
||||||
expect(dossier.project_champs_private.count).to eq(1)
|
expect(dossier.project_champs_private.count).to eq(1)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
describe '#public_ordered' do
|
it { expect(champ.public?).to be_truthy }
|
||||||
let(:procedure) { create(:simple_procedure) }
|
it { expect(champ.private?).to be_falsey }
|
||||||
let(:dossier) { create(:dossier, procedure: procedure) }
|
|
||||||
|
|
||||||
context 'when a procedure has 2 revisions' do
|
context 'when a procedure has 2 revisions' do
|
||||||
it 'does not duplicate the champs' do
|
it { expect(dossier.procedure.revisions.count).to eq(2) }
|
||||||
|
|
||||||
|
it 'does not duplicate public champs' do
|
||||||
expect(dossier.project_champs_public.count).to eq(1)
|
expect(dossier.project_champs_public.count).to eq(1)
|
||||||
expect(procedure.revisions.count).to eq(2)
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#private_ordered' do
|
it 'does not duplicate private champs' do
|
||||||
let(:procedure) { create(:procedure, :with_type_de_champ_private) }
|
|
||||||
let(:dossier) { create(:dossier, procedure: procedure) }
|
|
||||||
|
|
||||||
context 'when a procedure has 2 revisions' do
|
|
||||||
before { procedure.publish }
|
|
||||||
|
|
||||||
it 'does not duplicate the champs private' do
|
|
||||||
expect(dossier.project_champs_private.count).to eq(1)
|
expect(dossier.project_champs_private.count).to eq(1)
|
||||||
expect(procedure.revisions.count).to eq(2)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -598,16 +581,6 @@ describe Champ do
|
||||||
let(:champ) { Champs::TextChamp.new(private: true) }
|
let(:champ) { Champs::TextChamp.new(private: true) }
|
||||||
it { expect(champ.input_name).to eq "dossier[champs_private_attributes][#{champ.public_id}]" }
|
it { expect(champ.input_name).to eq "dossier[champs_private_attributes][#{champ.public_id}]" }
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when has parent" do
|
|
||||||
let(:champ) { Champs::TextChamp.new(parent: Champs::TextChamp.new) }
|
|
||||||
it { expect(champ.input_name).to eq "dossier[champs_public_attributes][#{champ.public_id}]" }
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when has private parent" do
|
|
||||||
let(:champ) { Champs::TextChamp.new(private: true, parent: Champs::TextChamp.new(private: true)) }
|
|
||||||
it { expect(champ.input_name).to eq "dossier[champs_private_attributes][#{champ.public_id}]" }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'dom_id' do
|
describe 'dom_id' do
|
||||||
|
|
|
@ -11,7 +11,7 @@ describe Champs::RepetitionChamp do
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
let(:dossier) { create(:dossier, procedure:) }
|
let(:dossier) { create(:dossier, procedure:) }
|
||||||
let(:champ) { dossier.champs.first }
|
let(:champ) { dossier.champs.find(&:repetition?) }
|
||||||
|
|
||||||
describe "#for_tag" do
|
describe "#for_tag" do
|
||||||
before do
|
before do
|
||||||
|
|
|
@ -49,7 +49,6 @@ RSpec.describe DossierChampsConcern do
|
||||||
it {
|
it {
|
||||||
expect(subject.persisted?).to be_truthy
|
expect(subject.persisted?).to be_truthy
|
||||||
expect(subject.row_id).to eq(row_id)
|
expect(subject.row_id).to eq(row_id)
|
||||||
expect(subject.parent_id).not_to be_nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
context "invalid row_id" do
|
context "invalid row_id" do
|
||||||
|
@ -137,12 +136,7 @@ RSpec.describe DossierChampsConcern do
|
||||||
describe '#repetition_add_row' do
|
describe '#repetition_add_row' do
|
||||||
let(:type_de_champ_repetition) { dossier.find_type_de_champ_by_stable_id(993) }
|
let(:type_de_champ_repetition) { dossier.find_type_de_champ_by_stable_id(993) }
|
||||||
let(:row_ids) { dossier.repetition_row_ids(type_de_champ_repetition) }
|
let(:row_ids) { dossier.repetition_row_ids(type_de_champ_repetition) }
|
||||||
subject do
|
subject { dossier.repetition_add_row(type_de_champ_repetition, updated_by: 'test') }
|
||||||
# TODO: clean this up when parent_id is deprecated
|
|
||||||
row_id, added_champs = dossier.repetition_add_row(type_de_champ_repetition, updated_by: 'test')
|
|
||||||
dossier.champs << added_champs
|
|
||||||
row_id
|
|
||||||
end
|
|
||||||
|
|
||||||
it { expect { subject }.to change { dossier.repetition_row_ids(type_de_champ_repetition).size }.by(1) }
|
it { expect { subject }.to change { dossier.repetition_row_ids(type_de_champ_repetition).size }.by(1) }
|
||||||
it { expect(subject).to be_in(row_ids) }
|
it { expect(subject).to be_in(row_ids) }
|
||||||
|
@ -206,7 +200,6 @@ RSpec.describe DossierChampsConcern do
|
||||||
it {
|
it {
|
||||||
expect(subject.persisted?).to be_truthy
|
expect(subject.persisted?).to be_truthy
|
||||||
expect(subject.row_id).to eq(row_id)
|
expect(subject.row_id).to eq(row_id)
|
||||||
expect(subject.parent_id).not_to be_nil
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -226,7 +219,6 @@ RSpec.describe DossierChampsConcern do
|
||||||
expect(subject.persisted?).to be_truthy
|
expect(subject.persisted?).to be_truthy
|
||||||
expect(subject.is_a?(Champs::TextChamp)).to be_truthy
|
expect(subject.is_a?(Champs::TextChamp)).to be_truthy
|
||||||
expect(subject.row_id).to eq(row_id)
|
expect(subject.row_id).to eq(row_id)
|
||||||
expect(subject.parent_id).not_to be_nil
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -135,12 +135,13 @@ RSpec.describe DossierCloneConcern do
|
||||||
|
|
||||||
context 'for Champs::Repetition with rows, original_champ.repetition and rows are duped' do
|
context 'for Champs::Repetition with rows, original_champ.repetition and rows are duped' do
|
||||||
let(:types_de_champ_public) { [{ type: :repetition, children: [{}, {}] }] }
|
let(:types_de_champ_public) { [{ type: :repetition, children: [{}, {}] }] }
|
||||||
let(:champ_repetition) { dossier.champs.first }
|
let(:champ_repetition) { dossier.champs.find(&:repetition?) }
|
||||||
let(:cloned_champ_repetition) { new_dossier.champs.first }
|
let(:cloned_champ_repetition) { new_dossier.champs.find(&:repetition?) }
|
||||||
|
|
||||||
it do
|
it do
|
||||||
expect(cloned_champ_repetition.champs.count).to eq(4)
|
expect(cloned_champ_repetition.rows.flatten.count).to eq(4)
|
||||||
expect(cloned_champ_repetition.champs.ids).not_to eq(champ_repetition.champs.ids)
|
expect(cloned_champ_repetition.rows.flatten.map(&:id)).not_to eq(champ_repetition.rows.flatten.map(&:id))
|
||||||
|
expect(cloned_champ_repetition.row_ids).to eq(champ_repetition.row_ids)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -407,9 +408,7 @@ RSpec.describe DossierCloneConcern do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with old revision having repetition' do
|
context 'with old revision having repetition' do
|
||||||
let(:added_champ) { nil }
|
|
||||||
let(:removed_champ) { dossier.champs.find(&:repetition?) }
|
let(:removed_champ) { dossier.champs.find(&:repetition?) }
|
||||||
let(:updated_champ) { nil }
|
|
||||||
|
|
||||||
before do
|
before do
|
||||||
dossier.champs.each do |champ|
|
dossier.champs.each do |champ|
|
||||||
|
|
|
@ -349,7 +349,7 @@ describe DossierRebaseConcern do
|
||||||
# Add two rows then remove previous to last row in order to create a "hole" in the sequence
|
# Add two rows then remove previous to last row in order to create a "hole" in the sequence
|
||||||
repetition_champ.add_row(updated_by: 'test')
|
repetition_champ.add_row(updated_by: 'test')
|
||||||
repetition_champ.add_row(updated_by: 'test')
|
repetition_champ.add_row(updated_by: 'test')
|
||||||
repetition_champ.champs.where(row_id: repetition_champ.row_ids[-2]).destroy_all
|
repetition_champ.dossier.champs.where(row_id: repetition_champ.row_ids[-2]).destroy_all
|
||||||
dossier.reload
|
dossier.reload
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -728,7 +728,7 @@ describe DossierRebaseConcern do
|
||||||
parent.update(type_champ: :integer_number)
|
parent.update(type_champ: :integer_number)
|
||||||
end
|
end
|
||||||
|
|
||||||
it { expect { subject }.to change { dossier.project_champs_public.first.champs.count }.from(2).to(0) }
|
it { expect { subject }.to change { dossier.champs.filter(&:child?).count }.from(2).to(0) }
|
||||||
it { expect { subject }.to change { Champ.count }.from(3).to(1) }
|
it { expect { subject }.to change { Champ.count }.from(3).to(1) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,7 +12,7 @@ describe DossierPreloader do
|
||||||
let(:dossier) { create(:dossier, procedure: procedure) }
|
let(:dossier) { create(:dossier, procedure: procedure) }
|
||||||
let(:repetition) { subject.project_champs_public.second }
|
let(:repetition) { subject.project_champs_public.second }
|
||||||
let(:repetition_optional) { subject.project_champs_public.third }
|
let(:repetition_optional) { subject.project_champs_public.third }
|
||||||
let(:first_child) { subject.project_champs_public.second.champs.first }
|
let(:first_child) { repetition.rows.first.first }
|
||||||
|
|
||||||
describe 'all' do
|
describe 'all' do
|
||||||
subject { DossierPreloader.load_one(dossier, pj_template: true) }
|
subject { DossierPreloader.load_one(dossier, pj_template: true) }
|
||||||
|
@ -40,9 +40,8 @@ describe DossierPreloader do
|
||||||
expect(subject.champs.find(&:public?).conditional?).to eq(false)
|
expect(subject.champs.find(&:public?).conditional?).to eq(false)
|
||||||
expect(subject.project_champs_public.first.conditional?).to eq(false)
|
expect(subject.project_champs_public.first.conditional?).to eq(false)
|
||||||
|
|
||||||
expect(first_child.parent).to eq(repetition)
|
expect(repetition.rows.first.first).to eq(first_child)
|
||||||
expect(repetition.champs.first).to eq(first_child)
|
expect(repetition_optional.row_ids).to be_empty
|
||||||
expect(repetition_optional.champs).to be_empty
|
|
||||||
end
|
end
|
||||||
|
|
||||||
expect(count).to eq(0)
|
expect(count).to eq(0)
|
||||||
|
|
|
@ -52,8 +52,8 @@ describe PiecesJustificativesService do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with a repetition' do
|
context 'with a repetition' do
|
||||||
let(:first_champ) { repetition(dossier).champs.first }
|
let(:first_champ) { repetition(dossier).rows.first.first }
|
||||||
let(:second_champ) { repetition(dossier).champs.second }
|
let(:second_champ) { repetition(dossier).rows.second.first }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
repetition(dossier).add_row(updated_by: 'test')
|
repetition(dossier).add_row(updated_by: 'test')
|
||||||
|
@ -65,8 +65,8 @@ describe PiecesJustificativesService do
|
||||||
end
|
end
|
||||||
|
|
||||||
it do
|
it do
|
||||||
first_child_attachments = attachments(repetition(dossier).champs.first)
|
first_child_attachments = attachments(repetition(dossier).rows.first.first)
|
||||||
second_child_attachments = attachments(repetition(dossier).champs.second)
|
second_child_attachments = attachments(repetition(dossier).rows.second.first)
|
||||||
|
|
||||||
expect(export_template).to receive(:attachment_path)
|
expect(export_template).to receive(:attachment_path)
|
||||||
.with(dossier, first_child_attachments.first, index: 0, row_index: 0, champ: first_champ)
|
.with(dossier, first_child_attachments.first, index: 0, row_index: 0, champ: first_champ)
|
||||||
|
|
|
@ -16,11 +16,11 @@ describe ProcedureExportService do
|
||||||
attach_file_to_champ(pj_champ(dossier))
|
attach_file_to_champ(pj_champ(dossier))
|
||||||
|
|
||||||
repetition(dossier).add_row(updated_by: 'test')
|
repetition(dossier).add_row(updated_by: 'test')
|
||||||
attach_file_to_champ(repetition(dossier).champs.first)
|
attach_file_to_champ(repetition(dossier).rows.first.first)
|
||||||
attach_file_to_champ(repetition(dossier).champs.first)
|
attach_file_to_champ(repetition(dossier).rows.first.first)
|
||||||
|
|
||||||
repetition(dossier).add_row(updated_by: 'test')
|
repetition(dossier).add_row(updated_by: 'test')
|
||||||
attach_file_to_champ(repetition(dossier).champs.second)
|
attach_file_to_champ(repetition(dossier).rows.second.first)
|
||||||
end
|
end
|
||||||
|
|
||||||
allow_any_instance_of(ActiveStorage::Attachment).to receive(:url).and_return("https://opengraph.githubassets.com/d0e7862b24d8026a3c03516d865b28151eb3859029c6c6c2e86605891fbdcd7a/socketry/async-io")
|
allow_any_instance_of(ActiveStorage::Attachment).to receive(:url).and_return("https://opengraph.githubassets.com/d0e7862b24d8026a3c03516d865b28151eb3859029c6c6c2e86605891fbdcd7a/socketry/async-io")
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require "rails_helper"
|
|
||||||
|
|
||||||
module Maintenance
|
|
||||||
RSpec.describe RescueDossierWithInvalidRepetitionTask do
|
|
||||||
describe "#process" do
|
|
||||||
let(:procedure) { create(:procedure, types_de_champ_public:) }
|
|
||||||
let(:types_de_champ_public) do
|
|
||||||
[
|
|
||||||
{ type: :repetition, children: [{ type: :text, mandatory: true }] },
|
|
||||||
{ type: :checkbox }
|
|
||||||
]
|
|
||||||
end
|
|
||||||
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
|
|
||||||
let(:invalid_champ) { dossier.champs.find(&:checkbox?) }
|
|
||||||
|
|
||||||
# reproduce bad data
|
|
||||||
before { invalid_champ.update!(row_id: dossier.champs[1].row_id) }
|
|
||||||
|
|
||||||
it "dissociate champ having a row_id without a parent_id" do
|
|
||||||
expect { described_class.process(dossier.id) }
|
|
||||||
.to change { Champ.exists?(invalid_champ.id) }.from(true).to(false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
Reference in a new issue