refactor(repetition): consolidate repetition manipulation methods
This commit is contained in:
parent
9167e9a4da
commit
084a68a121
24 changed files with 202 additions and 275 deletions
|
@ -2,14 +2,13 @@
|
||||||
|
|
||||||
class Champs::RepetitionController < Champs::ChampController
|
class Champs::RepetitionController < Champs::ChampController
|
||||||
def add
|
def add
|
||||||
row = @champ.add_row(@champ.dossier.revision)
|
@row_id = @champ.add_row(updated_by: current_user.email)
|
||||||
@first_champ_id = row.map(&:focusable_input_id).compact.first
|
@first_champ_id = @champ.focusable_input_id
|
||||||
@row_id = row.first&.row_id
|
|
||||||
@row_number = @row_id.nil? ? 0 : @champ.row_ids.find_index(@row_id) + 1
|
@row_number = @row_id.nil? ? 0 : @champ.row_ids.find_index(@row_id) + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove
|
def remove
|
||||||
@champ.remove_row(params[:row_id])
|
@champ.remove_row(params[:row_id], updated_by: current_user.email)
|
||||||
@to_remove = "safe-row-selector-#{params[:row_id]}"
|
@to_remove = "safe-row-selector-#{params[:row_id]}"
|
||||||
@to_focus = @champ.focusable_input_id || helpers.dom_id(@champ, :create_repetition)
|
@to_focus = @champ.focusable_input_id || helpers.dom_id(@champ, :create_repetition)
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,7 +16,7 @@ module Mutations
|
||||||
return { errors: ["L’annotation \"#{annotation_id}\" n’existe pas"] }
|
return { errors: ["L’annotation \"#{annotation_id}\" n’existe pas"] }
|
||||||
end
|
end
|
||||||
|
|
||||||
annotation.add_row(dossier.revision)
|
annotation.add_row(updated_by: instructeur.email)
|
||||||
|
|
||||||
{ annotation:, errors: nil }
|
{ annotation:, errors: nil }
|
||||||
end
|
end
|
||||||
|
|
|
@ -170,7 +170,7 @@ module Types
|
||||||
.for(object, private: false)
|
.for(object, private: false)
|
||||||
.load(ApplicationRecord.id_from_typed_id(id))
|
.load(ApplicationRecord.id_from_typed_id(id))
|
||||||
else
|
else
|
||||||
object.champs_for_revision(scope: :public, root: true).filter(&:visible?)
|
object.project_champs_public.filter(&:visible?)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -180,7 +180,7 @@ module Types
|
||||||
.for(object, private: true)
|
.for(object, private: true)
|
||||||
.load(ApplicationRecord.id_from_typed_id(id))
|
.load(ApplicationRecord.id_from_typed_id(id))
|
||||||
else
|
else
|
||||||
object.champs_for_revision(scope: :private, root: true).filter(&:visible?)
|
object.project_champs_private.filter(&:visible?)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,83 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
# some race condition (regarding double submit of dossier.passer_en_construction!) might remove champs
|
|
||||||
# until now we haven't decided to push a stronger fix than an UI change
|
|
||||||
# so we might have to recreate some deleted champs and notify administration
|
|
||||||
class DataFixer::DossierChampsMissing
|
|
||||||
def fix
|
|
||||||
fixed_on_origin = apply_fix(@original_dossier)
|
|
||||||
|
|
||||||
fixed_on_other = Dossier.where(editing_fork_origin_id: @original_dossier.id)
|
|
||||||
.map(&method(:apply_fix))
|
|
||||||
|
|
||||||
[fixed_on_origin, fixed_on_other.sum].sum
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
attr_reader :original_dossier
|
|
||||||
|
|
||||||
def initialize(dossier:)
|
|
||||||
@original_dossier = dossier
|
|
||||||
end
|
|
||||||
|
|
||||||
def apply_fix(dossier)
|
|
||||||
added_champs_root = fix_champs_root(dossier)
|
|
||||||
added_champs_in_repetition = fix_champs_in_repetition(dossier)
|
|
||||||
|
|
||||||
added_champs = added_champs_root + added_champs_in_repetition
|
|
||||||
if !added_champs.empty?
|
|
||||||
dossier.save!
|
|
||||||
log_champs_added(dossier, added_champs)
|
|
||||||
added_champs.size
|
|
||||||
else
|
|
||||||
0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def fix_champs_root(dossier)
|
|
||||||
champs_root, _ = dossier.champs.partition { _1.parent_id.blank? }
|
|
||||||
expected_tdcs = dossier.revision.revision_types_de_champ.filter { _1.parent.blank? }.map(&:type_de_champ)
|
|
||||||
|
|
||||||
expected_tdcs.filter { !champs_root.map(&:stable_id).include?(_1.stable_id) }
|
|
||||||
.map do |missing_tdc|
|
|
||||||
champ_root_missing = missing_tdc.build_champ
|
|
||||||
|
|
||||||
dossier.champs_public << champ_root_missing
|
|
||||||
champ_root_missing
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def fix_champs_in_repetition(dossier)
|
|
||||||
champs_repetition, _ = dossier.champs.partition(&:repetition?)
|
|
||||||
|
|
||||||
champs_repetition.flat_map do |champ_repetition|
|
|
||||||
champ_repetition_missing = champ_repetition.rows.flat_map do |row|
|
|
||||||
row_id = row.first.row_id
|
|
||||||
expected_tdcs = dossier.revision.children_of(champ_repetition.type_de_champ)
|
|
||||||
row_tdcs = row.map(&:type_de_champ)
|
|
||||||
|
|
||||||
(expected_tdcs - row_tdcs).map do |missing_tdc|
|
|
||||||
champ_repetition_missing = missing_tdc.build_champ(row_id: row_id)
|
|
||||||
champ_repetition.champs << champ_repetition_missing
|
|
||||||
champ_repetition_missing
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def log_champs_added(dossier, added_champs)
|
|
||||||
app_traces = caller.reject { _1.match?(%r{/ruby/.+/gems/}) }.map { _1.sub(Rails.root.to_s, "") }
|
|
||||||
|
|
||||||
payload = {
|
|
||||||
message: "DataFixer::DossierChampsMissing",
|
|
||||||
dossier_id: dossier.id,
|
|
||||||
champs_ids: added_champs.map(&:id).join(","),
|
|
||||||
caller: app_traces
|
|
||||||
}
|
|
||||||
|
|
||||||
logger = Lograge.logger || Rails.logger
|
|
||||||
|
|
||||||
logger.info payload.to_json
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,35 +1,26 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Champs::RepetitionChamp < Champ
|
class Champs::RepetitionChamp < Champ
|
||||||
accepts_nested_attributes_for :champs
|
delegate :libelle_for_export, to: :type_de_champ
|
||||||
|
|
||||||
def rows
|
def rows
|
||||||
dossier
|
dossier.project_rows_for(type_de_champ)
|
||||||
.champs_for_revision(scope: type_de_champ)
|
|
||||||
.group_by(&:row_id)
|
|
||||||
.sort
|
|
||||||
.map(&:second)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def row_ids
|
def row_ids
|
||||||
rows.map { _1.first.row_id }
|
dossier.repetition_row_ids(type_de_champ)
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_row(revision)
|
def add_row(updated_by:)
|
||||||
added_champs = []
|
# TODO: clean this up when parent_id is deprecated
|
||||||
transaction do
|
row_id, added_champs = dossier.repetition_add_row(type_de_champ, updated_by:)
|
||||||
row_id = ULID.generate
|
self.champs << added_champs
|
||||||
revision.children_of(type_de_champ).each do |type_de_champ|
|
dossier.champs.reload if dossier.persisted?
|
||||||
added_champs << type_de_champ.build_champ(row_id:)
|
row_id
|
||||||
end
|
|
||||||
self.champs << added_champs
|
|
||||||
end
|
|
||||||
added_champs
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_row(row_id)
|
def remove_row(row_id, updated_by:)
|
||||||
dossier.champs.where(row_id:).destroy_all
|
dossier.repetition_remove_row(type_de_champ, row_id, updated_by:)
|
||||||
dossier.champs.reload
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def focusable_input_id
|
def focusable_input_id
|
||||||
|
|
|
@ -3,21 +3,10 @@
|
||||||
module DossierChampsConcern
|
module DossierChampsConcern
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
def champs_for_revision(scope: nil, root: false)
|
def champs_for_revision(scope: nil)
|
||||||
champs_index = champs.group_by(&:stable_id)
|
champs_index = champs.group_by(&:stable_id)
|
||||||
# Due to some bad data we can have multiple copies of the same champ. Ignore extra copy.
|
revision.types_de_champ_for(scope:)
|
||||||
.transform_values { _1.sort_by(&:id).uniq(&:row_id) }
|
.flat_map { champs_index[_1.stable_id] || [] }
|
||||||
|
|
||||||
if scope.is_a?(TypeDeChamp)
|
|
||||||
revision
|
|
||||||
.children_of(scope)
|
|
||||||
.flat_map { champs_index[_1.stable_id] || [] }
|
|
||||||
.filter(&:child?) # TODO: remove once bad data (child champ without a row id) is cleaned
|
|
||||||
else
|
|
||||||
revision
|
|
||||||
.types_de_champ_for(scope:, root:)
|
|
||||||
.flat_map { champs_index[_1.stable_id] || [] }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Get all the champs values for the types de champ in the final list.
|
# Get all the champs values for the types de champ in the final list.
|
||||||
|
@ -42,6 +31,25 @@ module DossierChampsConcern
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def project_champs_public
|
||||||
|
revision.types_de_champ_public.map { project_champ(_1, nil) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def project_champs_private
|
||||||
|
revision.types_de_champ_private.map { project_champ(_1, nil) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def project_rows_for(type_de_champ)
|
||||||
|
[] if !type_de_champ.repetition?
|
||||||
|
|
||||||
|
children = revision.children_of(type_de_champ)
|
||||||
|
row_ids = repetition_row_ids(type_de_champ)
|
||||||
|
|
||||||
|
row_ids.map do |row_id|
|
||||||
|
children.map { project_champ(_1, row_id) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def find_type_de_champ_by_stable_id(stable_id, scope = nil)
|
def find_type_de_champ_by_stable_id(stable_id, scope = nil)
|
||||||
case scope
|
case scope
|
||||||
when :public
|
when :public
|
||||||
|
@ -75,6 +83,41 @@ module DossierChampsConcern
|
||||||
assign_attributes(champs_attributes:)
|
assign_attributes(champs_attributes:)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def repetition_row_ids(type_de_champ)
|
||||||
|
[] if !type_de_champ.repetition?
|
||||||
|
|
||||||
|
stable_ids = revision.children_of(type_de_champ).map(&:stable_id)
|
||||||
|
champs.filter { _1.stable_id.in?(stable_ids) && _1.row_id.present? }
|
||||||
|
.map(&:row_id)
|
||||||
|
.uniq
|
||||||
|
.sort
|
||||||
|
end
|
||||||
|
|
||||||
|
def repetition_add_row(type_de_champ, updated_by:)
|
||||||
|
raise "Can't add row to non-repetition type de champ" if !type_de_champ.repetition?
|
||||||
|
|
||||||
|
row_id = ULID.generate
|
||||||
|
types_de_champ = revision.children_of(type_de_champ)
|
||||||
|
# TODO: clean this up when parent_id is deprecated
|
||||||
|
added_champs = types_de_champ.map { _1.build_champ(row_id:, updated_by:) }
|
||||||
|
@champs_by_public_id = nil
|
||||||
|
[row_id, added_champs]
|
||||||
|
end
|
||||||
|
|
||||||
|
def repetition_remove_row(type_de_champ, row_id, updated_by:)
|
||||||
|
raise "Can't remove row from non-repetition type de champ" if !type_de_champ.repetition?
|
||||||
|
|
||||||
|
champs.where(row_id:).destroy_all
|
||||||
|
champs.reload if persisted?
|
||||||
|
@champs_by_public_id = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def reload
|
||||||
|
super.tap do
|
||||||
|
@champs_by_public_id = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def champs_by_public_id
|
def champs_by_public_id
|
||||||
|
|
|
@ -15,7 +15,7 @@ module DossierSearchableConcern
|
||||||
|
|
||||||
search_terms = [
|
search_terms = [
|
||||||
user&.email,
|
user&.email,
|
||||||
*champs_public.flat_map(&:search_terms),
|
*project_champs_public.flat_map(&:search_terms),
|
||||||
*etablissement&.search_terms,
|
*etablissement&.search_terms,
|
||||||
individual&.nom,
|
individual&.nom,
|
||||||
individual&.prenom,
|
individual&.prenom,
|
||||||
|
@ -23,7 +23,7 @@ module DossierSearchableConcern
|
||||||
mandataire_last_name
|
mandataire_last_name
|
||||||
].compact_blank.join(' ')
|
].compact_blank.join(' ')
|
||||||
|
|
||||||
private_search_terms = champs_private.flat_map(&:search_terms).compact_blank.join(' ')
|
private_search_terms = project_champs_private.flat_map(&:search_terms).compact_blank.join(' ')
|
||||||
|
|
||||||
sql = "UPDATE dossiers SET search_terms = :search_terms, private_search_terms = :private_search_terms WHERE id = :id"
|
sql = "UPDATE dossiers SET search_terms = :search_terms, private_search_terms = :private_search_terms WHERE id = :id"
|
||||||
sanitized_sql = self.class.sanitize_sql_array([sql, search_terms:, private_search_terms:, id:])
|
sanitized_sql = self.class.sanitize_sql_array([sql, search_terms:, private_search_terms:, id:])
|
||||||
|
|
|
@ -479,10 +479,10 @@ class Dossier < ApplicationRecord
|
||||||
champs_private << champ
|
champs_private << champ
|
||||||
end
|
end
|
||||||
champs_public.filter { _1.repetition? && _1.mandatory? }.each do |champ|
|
champs_public.filter { _1.repetition? && _1.mandatory? }.each do |champ|
|
||||||
champ.add_row(revision)
|
champ.add_row(updated_by: nil)
|
||||||
end
|
end
|
||||||
champs_private.filter(&:repetition?).each do |champ|
|
champs_private.filter(&:repetition?).each do |champ|
|
||||||
champ.add_row(revision)
|
champ.add_row(updated_by: nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -942,14 +942,21 @@ class Dossier < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_mandatory_and_visible_champs
|
def check_mandatory_and_visible_champs
|
||||||
champs_for_revision(scope: :public)
|
project_champs_public.filter(&:visible?).each do |champ|
|
||||||
.filter { _1.child? ? _1.parent.visible? : true }
|
if champ.mandatory_blank?
|
||||||
.filter(&:visible?)
|
error = champ.errors.add(:value, :missing)
|
||||||
.filter(&:mandatory_blank?)
|
errors.import(error)
|
||||||
.map do |champ|
|
|
||||||
champ.errors.add(:value, :missing)
|
|
||||||
end
|
end
|
||||||
.each { errors.import(_1) }
|
if champ.repetition?
|
||||||
|
champ.rows.each do |champs|
|
||||||
|
champs.filter(&:visible?).filter(&:mandatory_blank?).each do |champ|
|
||||||
|
error = champ.errors.add(:value, :missing)
|
||||||
|
errors.import(error)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
errors
|
||||||
end
|
end
|
||||||
|
|
||||||
def demander_un_avis!(avis)
|
def demander_un_avis!(avis)
|
||||||
|
|
|
@ -80,8 +80,8 @@ class DossierPreloader
|
||||||
dossier.association(:revision).target = revision
|
dossier.association(:revision).target = revision
|
||||||
end
|
end
|
||||||
dossier.association(:champs).target = champs
|
dossier.association(:champs).target = champs
|
||||||
dossier.association(:champs_public).target = dossier.champs_for_revision(scope: :public, root: true)
|
dossier.association(:champs_public).target = dossier.project_champs_public
|
||||||
dossier.association(:champs_private).target = dossier.champs_for_revision(scope: :private, root: true)
|
dossier.association(:champs_private).target = dossier.project_champs_private
|
||||||
|
|
||||||
# remove once parent_id is deprecated
|
# remove once parent_id is deprecated
|
||||||
champs_by_parent_id = champs.group_by(&:parent_id)
|
champs_by_parent_id = champs.group_by(&:parent_id)
|
||||||
|
|
|
@ -179,19 +179,14 @@ class ProcedureRevision < ApplicationRecord
|
||||||
dossier
|
dossier
|
||||||
end
|
end
|
||||||
|
|
||||||
def types_de_champ_for(scope: nil, root: false)
|
def types_de_champ_for(scope: nil)
|
||||||
# We return an unordered collection
|
|
||||||
return types_de_champ if !root && scope.nil?
|
|
||||||
return types_de_champ.filter { scope == :public ? _1.public? : _1.private? } if !root
|
|
||||||
|
|
||||||
# We return an ordered collection
|
|
||||||
case scope
|
case scope
|
||||||
when :public
|
when :public
|
||||||
types_de_champ_public
|
types_de_champ.filter(&:public?)
|
||||||
when :private
|
when :private
|
||||||
types_de_champ_private
|
types_de_champ.filter(&:private?)
|
||||||
else
|
else
|
||||||
types_de_champ_public + types_de_champ_private
|
types_de_champ
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -56,8 +56,7 @@ class TypesDeChamp::PrefillRepetitionTypeDeChamp < TypesDeChamp::PrefillTypeDeCh
|
||||||
def to_assignable_attributes
|
def to_assignable_attributes
|
||||||
return unless repetition.is_a?(Hash)
|
return unless repetition.is_a?(Hash)
|
||||||
|
|
||||||
row = champ.rows[index] || champ.add_row(revision)
|
row_id = champ.row_ids[index] || champ.add_row(updated_by: nil)
|
||||||
row_id = row.first.row_id
|
|
||||||
|
|
||||||
repetition.map do |key, value|
|
repetition.map do |key, value|
|
||||||
next unless key.is_a?(String) && key.starts_with?("champ_")
|
next unless key.is_a?(String) && key.starts_with?("champ_")
|
||||||
|
|
|
@ -48,11 +48,7 @@ class ChampSerializer < ActiveModel::Serializer
|
||||||
end
|
end
|
||||||
|
|
||||||
def rows
|
def rows
|
||||||
object.dossier
|
object.rows.map.with_index(1) { |champs, index| Row.new(index:, champs:) }
|
||||||
.champs_for_revision(scope: object.type_de_champ)
|
|
||||||
.group_by(&:row_id)
|
|
||||||
.values
|
|
||||||
.map.with_index(1) { |champs, index| Row.new(index:, champs:) }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def include_etablissement?
|
def include_etablissement?
|
||||||
|
|
|
@ -32,7 +32,7 @@ class DossierSerializer < ActiveModel::Serializer
|
||||||
has_many :champs, serializer: ChampSerializer
|
has_many :champs, serializer: ChampSerializer
|
||||||
|
|
||||||
def champs
|
def champs
|
||||||
champs = object.champs_public.reject { |c| c.type_de_champ.old_pj.present? }
|
champs = object.project_champs_public.reject { |c| c.type_de_champ.old_pj.present? }
|
||||||
|
|
||||||
if object.expose_legacy_carto_api?
|
if object.expose_legacy_carto_api?
|
||||||
champ_carte = champs.find do |champ|
|
champ_carte = champs.find do |champ|
|
||||||
|
@ -52,12 +52,16 @@ class DossierSerializer < ActiveModel::Serializer
|
||||||
champs
|
champs
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def champs_private
|
||||||
|
object.project_champs_private
|
||||||
|
end
|
||||||
|
|
||||||
def cerfa
|
def cerfa
|
||||||
[]
|
[]
|
||||||
end
|
end
|
||||||
|
|
||||||
def pieces_justificatives
|
def pieces_justificatives
|
||||||
object.champs_public.filter { |champ| champ.type_de_champ.old_pj }.map do |champ|
|
object.project_champs_public.filter { |champ| champ.type_de_champ.old_pj }.map do |champ|
|
||||||
{
|
{
|
||||||
created_at: champ.created_at&.in_time_zone('UTC'),
|
created_at: champ.created_at&.in_time_zone('UTC'),
|
||||||
type_de_piece_justificative_id: champ.type_de_champ.old_pj[:stable_id],
|
type_de_piece_justificative_id: champ.type_de_champ.old_pj[:stable_id],
|
||||||
|
|
|
@ -5,7 +5,7 @@ RSpec.describe Mutations::DossierModifierAnnotation, type: :graphql do
|
||||||
let(:procedure) { create(:procedure, :published, :for_individual, types_de_champ_private: [{ type: :repetition, children: [{ libelle: 'Nom' }, { type: :integer_number, libelle: 'Age' }] }, {}], administrateurs: [admin]) }
|
let(:procedure) { create(:procedure, :published, :for_individual, types_de_champ_private: [{ type: :repetition, children: [{ libelle: 'Nom' }, { type: :integer_number, libelle: 'Age' }] }, {}], administrateurs: [admin]) }
|
||||||
let(:dossiers) { [] }
|
let(:dossiers) { [] }
|
||||||
let(:instructeur) { create(:instructeur, followed_dossiers: dossiers) }
|
let(:instructeur) { create(:instructeur, followed_dossiers: dossiers) }
|
||||||
let(:champs_private) { dossier.champs_for_revision(scope: :private, root: true) }
|
let(:champs_private) { dossier.project_champs_private }
|
||||||
|
|
||||||
let(:query) { '' }
|
let(:query) { '' }
|
||||||
let(:context) { { administrateur_id: admin.id, procedure_ids: admin.procedure_ids, write_access: true } }
|
let(:context) { { administrateur_id: admin.id, procedure_ids: admin.procedure_ids, write_access: true } }
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
describe DataFixer::DossierChampsMissing do
|
|
||||||
describe '#fix' do
|
|
||||||
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :datetime }, { type: :dossier_link }]) }
|
|
||||||
let(:dossier) { create(:dossier, procedure:) }
|
|
||||||
|
|
||||||
context 'when dossier does not have a fork' do
|
|
||||||
before { dossier.champs_public.first.destroy }
|
|
||||||
subject { described_class.new(dossier:).fix }
|
|
||||||
|
|
||||||
it 'add missing champs to the dossier' do
|
|
||||||
expect { subject }.to change { dossier.champs_public.count }.from(1).to(2)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns number of added champs' do
|
|
||||||
expect(subject).to eq(1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when dossier have a fork' do
|
|
||||||
before { dossier.champs_public.first.destroy }
|
|
||||||
let(:create_fork) { dossier.find_or_create_editing_fork(dossier.user) }
|
|
||||||
subject do
|
|
||||||
create_fork
|
|
||||||
described_class.new(dossier:).fix
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'add missing champs to the fork too' do
|
|
||||||
expect { subject }.to change { create_fork.champs_public.count }.from(1).to(2)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'sums number of added champs for dossier and editing_fork_origin_id' do
|
|
||||||
expect(subject).to eq(2)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when dossier have missing champ on repetition' do
|
|
||||||
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :repetition, children: [{ type: :text }, { type: :decimal_number }] }]) }
|
|
||||||
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
|
|
||||||
let(:champ_repetition) { dossier.champs_public.first }
|
|
||||||
let(:initial_champ_count) { dossier.champs.count }
|
|
||||||
before do
|
|
||||||
initial_champ_count
|
|
||||||
champ_repetition.champs.first.destroy
|
|
||||||
end
|
|
||||||
subject { described_class.new(dossier:).fix }
|
|
||||||
|
|
||||||
it 'add missing champs to repetition' do
|
|
||||||
expect { subject }.to change { dossier.champs.count }.from(initial_champ_count - 1).to(initial_champ_count)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'counts number of added champs for dossier.repetitions' do
|
|
||||||
expect(subject).to eq(1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -17,7 +17,7 @@ describe 'Dossier::Recovery::LifeCycle' do
|
||||||
let(:dossier) do
|
let(:dossier) do
|
||||||
d = create(:dossier, procedure:)
|
d = create(:dossier, procedure:)
|
||||||
|
|
||||||
repetition(d).add_row(d.revision)
|
repetition(d).add_row(updated_by: 'test')
|
||||||
pj_champ(d).piece_justificative_file.attach(some_file)
|
pj_champ(d).piece_justificative_file.attach(some_file)
|
||||||
carte(d).update(geo_areas: [geo_area])
|
carte(d).update(geo_areas: [geo_area])
|
||||||
d.etablissement = create(:etablissement, :with_exercices)
|
d.etablissement = create(:etablissement, :with_exercices)
|
||||||
|
|
|
@ -15,16 +15,21 @@ describe ChampPresentations::RepetitionPresentation do
|
||||||
}
|
}
|
||||||
|
|
||||||
let(:dossier) { create(:dossier, procedure:) }
|
let(:dossier) { create(:dossier, procedure:) }
|
||||||
|
let(:champ_repetition) { dossier.champs.find(&:repetition?) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
nom, stars = dossier.champs[0].rows.first
|
champ_repetition.add_row(updated_by: 'test')
|
||||||
|
champ_repetition.add_row(updated_by: 'test')
|
||||||
|
row1, row2, row3 = champ_repetition.rows
|
||||||
|
|
||||||
|
nom, stars = row1
|
||||||
nom.update(value: "ruby")
|
nom.update(value: "ruby")
|
||||||
stars.update(value: 5)
|
stars.update(value: 5)
|
||||||
|
|
||||||
nom, stars = dossier.champs[0].add_row(dossier.procedure.active_revision)
|
nom = row2.first
|
||||||
nom.update(value: "js")
|
nom.update(value: "js")
|
||||||
|
|
||||||
nom, stars = dossier.champs[0].add_row(dossier.procedure.active_revision)
|
nom, stars = row3
|
||||||
nom.update(value: "rust")
|
nom.update(value: "rust")
|
||||||
stars.update(value: 4)
|
stars.update(value: 4)
|
||||||
end
|
end
|
||||||
|
|
|
@ -104,6 +104,57 @@ RSpec.describe DossierChampsConcern do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#project_champs_public' do
|
||||||
|
subject { dossier.project_champs_public }
|
||||||
|
|
||||||
|
it { expect(subject.size).to eq(4) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#project_champs_private' do
|
||||||
|
subject { dossier.project_champs_private }
|
||||||
|
|
||||||
|
it { expect(subject.size).to eq(1) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#repetition_row_ids' do
|
||||||
|
let(:type_de_champ_repetition) { dossier.find_type_de_champ_by_stable_id(993) }
|
||||||
|
subject { dossier.repetition_row_ids(type_de_champ_repetition) }
|
||||||
|
|
||||||
|
it { expect(subject.size).to eq(1) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#project_rows_for' do
|
||||||
|
let(:type_de_champ_repetition) { dossier.find_type_de_champ_by_stable_id(993) }
|
||||||
|
subject { dossier.project_rows_for(type_de_champ_repetition) }
|
||||||
|
|
||||||
|
it { expect(subject.size).to eq(1) }
|
||||||
|
it { expect(subject.first.size).to eq(1) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#repetition_add_row' do
|
||||||
|
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) }
|
||||||
|
subject do
|
||||||
|
# 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 be_in(row_ids) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#repetition_remove_row' do
|
||||||
|
let(:type_de_champ_repetition) { dossier.find_type_de_champ_by_stable_id(993) }
|
||||||
|
let(:row_id) { dossier.repetition_row_ids(type_de_champ_repetition).first }
|
||||||
|
let(:row_ids) { dossier.repetition_row_ids(type_de_champ_repetition) }
|
||||||
|
subject { dossier.repetition_remove_row(type_de_champ_repetition, row_id, updated_by: 'test') }
|
||||||
|
|
||||||
|
it { expect { subject }.to change { dossier.repetition_row_ids(type_de_champ_repetition).size }.by(-1) }
|
||||||
|
it { row_id; subject; expect(row_id).not_to be_in(row_ids) }
|
||||||
|
end
|
||||||
|
|
||||||
describe "#champs_for_export" do
|
describe "#champs_for_export" do
|
||||||
subject { dossier.champs_for_export(dossier.revision.types_de_champ_public) }
|
subject { dossier.champs_for_export(dossier.revision.types_de_champ_public) }
|
||||||
|
|
||||||
|
|
|
@ -346,8 +346,8 @@ describe DossierRebaseConcern do
|
||||||
datetime_champ.update(value: Time.zone.now.to_s)
|
datetime_champ.update(value: Time.zone.now.to_s)
|
||||||
text_champ.update(value: 'bonjour')
|
text_champ.update(value: 'bonjour')
|
||||||
# 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(repetition_champ.dossier.revision)
|
repetition_champ.add_row(updated_by: 'test')
|
||||||
repetition_champ.add_row(repetition_champ.dossier.revision)
|
repetition_champ.add_row(updated_by: 'test')
|
||||||
repetition_champ.champs.where(row_id: repetition_champ.rows[-2].first.row_id).destroy_all
|
repetition_champ.champs.where(row_id: repetition_champ.rows[-2].first.row_id).destroy_all
|
||||||
repetition_champ.reload
|
repetition_champ.reload
|
||||||
end
|
end
|
||||||
|
|
|
@ -233,9 +233,8 @@ describe TagsSubstitutionConcern, type: :model do
|
||||||
let(:dossier) { create(:dossier, procedure:) }
|
let(:dossier) { create(:dossier, procedure:) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
repetition = dossier.champs_public
|
repetition = dossier.project_champs_public.find(&:repetition?)
|
||||||
.find { |champ| champ.libelle == 'Répétition' }
|
repetition.add_row(updated_by: 'test')
|
||||||
repetition.add_row(dossier.revision)
|
|
||||||
paul_champs, pierre_champs = repetition.rows
|
paul_champs, pierre_champs = repetition.rows
|
||||||
|
|
||||||
paul_champs.first.update(value: 'Paul')
|
paul_champs.first.update(value: 'Paul')
|
||||||
|
|
|
@ -515,7 +515,7 @@ describe Dossier, type: :model do
|
||||||
|
|
||||||
context 'when piece_justificative' do
|
context 'when piece_justificative' do
|
||||||
let(:types_de_champ_public) { [{ type: :piece_justificative }] }
|
let(:types_de_champ_public) { [{ type: :piece_justificative }] }
|
||||||
let(:champ) { dossier.champs_for_revision(scope: :public).find(&:piece_justificative?) }
|
let(:champ) { dossier.project_champs_public.find(&:piece_justificative?) }
|
||||||
|
|
||||||
context 'when not visible' do
|
context 'when not visible' do
|
||||||
let(:visible) { false }
|
let(:visible) { false }
|
||||||
|
@ -530,7 +530,7 @@ describe Dossier, type: :model do
|
||||||
|
|
||||||
context 'when titre identite' do
|
context 'when titre identite' do
|
||||||
let(:types_de_champ_public) { [{ type: :titre_identite }] }
|
let(:types_de_champ_public) { [{ type: :titre_identite }] }
|
||||||
let(:champ) { dossier.champs_for_revision(scope: :public).find(&:piece_justificative?) }
|
let(:champ) { dossier.project_champs_public.find(&:piece_justificative?) }
|
||||||
|
|
||||||
context 'when not visible' do
|
context 'when not visible' do
|
||||||
let(:visible) { false }
|
let(:visible) { false }
|
||||||
|
@ -728,10 +728,10 @@ describe Dossier, type: :model do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#unspecified_attestation_champs" do
|
describe "#unspecified_attestation_champs" do
|
||||||
let(:procedure) { create(:procedure, attestation_template: attestation_template, types_de_champ_public: types_de_champ, types_de_champ_private: types_de_champ_private) }
|
let(:procedure) { create(:procedure, attestation_template:, types_de_champ_public:, types_de_champ_private:) }
|
||||||
let(:dossier) { create(:dossier, :en_instruction, procedure: procedure) }
|
let(:dossier) { create(:dossier, :en_instruction, procedure:) }
|
||||||
|
|
||||||
let(:types_de_champ) { [tdc_1, tdc_2, tdc_3, tdc_4] }
|
let(:types_de_champ_public) { [tdc_1, tdc_2, tdc_3, tdc_4] }
|
||||||
let(:types_de_champ_private) { [tdc_5, tdc_6, tdc_7, tdc_8] }
|
let(:types_de_champ_private) { [tdc_5, tdc_6, tdc_7, tdc_8] }
|
||||||
|
|
||||||
let(:tdc_1) { { libelle: "specified champ-in-title" } }
|
let(:tdc_1) { { libelle: "specified champ-in-title" } }
|
||||||
|
@ -744,7 +744,7 @@ describe Dossier, type: :model do
|
||||||
let(:tdc_8) { { libelle: "unspecified annotation privée-in-body" } }
|
let(:tdc_8) { { libelle: "unspecified annotation privée-in-body" } }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
(dossier.champs_public + dossier.champs_private)
|
(dossier.project_champs_public + dossier.project_champs_private)
|
||||||
.filter { |c| c.libelle.match?(/^specified/) }
|
.filter { |c| c.libelle.match?(/^specified/) }
|
||||||
.each { |c| c.update_attribute(:value, "specified") }
|
.each { |c| c.update_attribute(:value, "specified") }
|
||||||
end
|
end
|
||||||
|
@ -799,7 +799,7 @@ describe Dossier, type: :model do
|
||||||
let(:attestation_template) { build(:attestation_template, :v2) }
|
let(:attestation_template) { build(:attestation_template, :v2) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
tdc_content = (types_de_champ + types_de_champ_private).filter_map do |tdc_config|
|
tdc_content = (types_de_champ_public + types_de_champ_private).filter_map do |tdc_config|
|
||||||
next if tdc_config[:libelle].include?("in-title")
|
next if tdc_config[:libelle].include?("in-title")
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -1607,7 +1607,7 @@ describe Dossier, type: :model do
|
||||||
|
|
||||||
context "with mandatory champs" do
|
context "with mandatory champs" do
|
||||||
let(:type_de_champ) { { mandatory: true } }
|
let(:type_de_champ) { { mandatory: true } }
|
||||||
let(:champ_with_error) { dossier.champs_public.first }
|
let(:champ_with_error) { dossier.champs.first }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
champ_with_error.value = nil
|
champ_with_error.value = nil
|
||||||
|
@ -1616,7 +1616,7 @@ describe Dossier, type: :model do
|
||||||
|
|
||||||
it 'should have errors' do
|
it 'should have errors' do
|
||||||
expect(errors).not_to be_empty
|
expect(errors).not_to be_empty
|
||||||
expect(errors.first.full_message).to eq("doit être rempli")
|
expect(errors.first.full_message).to eq("Le champ « Value » doit être rempli")
|
||||||
end
|
end
|
||||||
|
|
||||||
context "conditionaly visible" do
|
context "conditionaly visible" do
|
||||||
|
@ -1649,7 +1649,7 @@ describe Dossier, type: :model do
|
||||||
|
|
||||||
it 'should have errors' do
|
it 'should have errors' do
|
||||||
expect(errors).not_to be_empty
|
expect(errors).not_to be_empty
|
||||||
expect(errors.first.full_message).to eq("doit être rempli")
|
expect(errors.first.full_message).to eq("Le champ « Value » doit être rempli")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1660,39 +1660,37 @@ describe Dossier, type: :model do
|
||||||
let(:type_de_champ_repetition) { revision.types_de_champ.first }
|
let(:type_de_champ_repetition) { revision.types_de_champ.first }
|
||||||
|
|
||||||
context "when no champs" do
|
context "when no champs" do
|
||||||
let(:champ_with_error) { dossier.champs_public.first }
|
|
||||||
|
|
||||||
it 'should have errors' do
|
it 'should have errors' do
|
||||||
dossier.champs_public.first.champs.destroy_all
|
dossier.champs.first.row_ids.each do |row_id|
|
||||||
expect(dossier.champs_public.first.rows).to be_empty
|
dossier.repetition_remove_row(type_de_champ_repetition, row_id, updated_by: 'test')
|
||||||
|
end
|
||||||
|
expect(dossier.champs.first.rows).to be_empty
|
||||||
expect(errors).not_to be_empty
|
expect(errors).not_to be_empty
|
||||||
expect(errors.first.full_message).to eq("doit être rempli")
|
expect(errors.first.full_message).to eq("Le champ « Value » doit être rempli")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when mandatory champ inside repetition" do
|
context "when mandatory champ inside repetition" do
|
||||||
let(:champ_with_error) { dossier.champs_public.first.champs.first }
|
|
||||||
|
|
||||||
it 'should have errors' do
|
it 'should have errors' do
|
||||||
expect(dossier.champs_public.first.rows).not_to be_empty
|
expect(dossier.champs.first.rows).not_to be_empty
|
||||||
expect(errors.first.full_message).to eq("doit être rempli")
|
expect(errors).not_to be_empty
|
||||||
|
expect(errors.first.full_message).to eq("Le champ « Value » doit être rempli")
|
||||||
end
|
end
|
||||||
|
|
||||||
context "conditionaly visible" do
|
context "conditionaly visible" do
|
||||||
let(:champ_with_error) { dossier.champs_public.second.champs.first }
|
|
||||||
let(:types_de_champ) { [{ type: :yes_no, stable_id: 99, mandatory: false }, type_de_champ] }
|
let(:types_de_champ) { [{ type: :yes_no, stable_id: 99, mandatory: false }, type_de_champ] }
|
||||||
let(:type_de_champ) { { type: :repetition, mandatory: true, children: [{ mandatory: true }], condition: ds_eq(champ_value(99), constant(true)) } }
|
let(:type_de_champ) { { type: :repetition, mandatory: true, children: [{ mandatory: true }], condition: ds_eq(champ_value(99), constant(true)) } }
|
||||||
|
|
||||||
it 'should not have errors' do
|
it 'should not have errors' do
|
||||||
expect(dossier.champs_public.second.rows).not_to be_empty
|
expect(dossier.champs.second.rows).not_to be_empty
|
||||||
expect(errors).to be_empty
|
expect(errors).to be_empty
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should have errors' do
|
it 'should have errors' do
|
||||||
dossier.champs_public.first.update(value: 'true')
|
dossier.champs.first.update(value: 'true')
|
||||||
expect(dossier.champs_public.second.rows).not_to be_empty
|
expect(dossier.champs.second.rows).not_to be_empty
|
||||||
expect(errors).not_to be_empty
|
expect(errors).not_to be_empty
|
||||||
expect(errors.first.full_message).to eq("doit être rempli")
|
expect(errors.first.full_message).to eq("Le champ « Value » doit être rempli")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -2040,7 +2038,7 @@ describe Dossier, type: :model do
|
||||||
procedure.publish!
|
procedure.publish!
|
||||||
dossier
|
dossier
|
||||||
procedure.draft_revision.remove_type_de_champ(text_type_de_champ.stable_id)
|
procedure.draft_revision.remove_type_de_champ(text_type_de_champ.stable_id)
|
||||||
coordinate = procedure.draft_revision.add_type_de_champ(type_champ: TypeDeChamp.type_champs.fetch(:text), libelle: 'New text field', after_stable_id: repetition_champ.stable_id)
|
coordinate = procedure.draft_revision.add_type_de_champ(type_champ: TypeDeChamp.type_champs.fetch(:text), libelle: 'New text field', after_stable_id: repetition_type_de_champ.stable_id)
|
||||||
procedure.draft_revision.find_and_ensure_exclusive_use(yes_no_type_de_champ.stable_id).update(libelle: 'Updated yes/no')
|
procedure.draft_revision.find_and_ensure_exclusive_use(yes_no_type_de_champ.stable_id).update(libelle: 'Updated yes/no')
|
||||||
procedure.draft_revision.find_and_ensure_exclusive_use(commune_type_de_champ.stable_id).update(libelle: 'Commune de naissance')
|
procedure.draft_revision.find_and_ensure_exclusive_use(commune_type_de_champ.stable_id).update(libelle: 'Commune de naissance')
|
||||||
procedure.draft_revision.find_and_ensure_exclusive_use(repetition_type_de_champ.stable_id).update(libelle: 'Repetition')
|
procedure.draft_revision.find_and_ensure_exclusive_use(repetition_type_de_champ.stable_id).update(libelle: 'Repetition')
|
||||||
|
|
|
@ -56,11 +56,11 @@ describe PiecesJustificativesService do
|
||||||
let(:second_champ) { repetition(dossier).champs.second }
|
let(:second_champ) { repetition(dossier).champs.second }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
repetition(dossier).add_row(dossier.revision)
|
repetition(dossier).add_row(updated_by: 'test')
|
||||||
attach_file_to_champ(first_champ)
|
attach_file_to_champ(first_champ)
|
||||||
attach_file_to_champ(first_champ)
|
attach_file_to_champ(first_champ)
|
||||||
|
|
||||||
repetition(dossier).add_row(dossier.revision)
|
repetition(dossier).add_row(updated_by: 'test')
|
||||||
attach_file_to_champ(second_champ)
|
attach_file_to_champ(second_champ)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -527,11 +527,11 @@ describe PiecesJustificativesService do
|
||||||
repet_0 = repetition(dossier_1, index: 0)
|
repet_0 = repetition(dossier_1, index: 0)
|
||||||
repet_1 = repetition(dossier_1, index: 1)
|
repet_1 = repetition(dossier_1, index: 1)
|
||||||
|
|
||||||
repet_0.add_row(dossier_1.revision)
|
repet_0.add_row(updated_by: 'test')
|
||||||
repet_0.add_row(dossier_1.revision)
|
repet_0.add_row(updated_by: 'test')
|
||||||
|
|
||||||
repet_1.add_row(dossier_1.revision)
|
repet_1.add_row(updated_by: 'test')
|
||||||
repet_1.add_row(dossier_1.revision)
|
repet_1.add_row(updated_by: 'test')
|
||||||
end
|
end
|
||||||
|
|
||||||
it do
|
it do
|
||||||
|
|
|
@ -15,11 +15,11 @@ describe ProcedureExportService do
|
||||||
dossiers.each do |dossier|
|
dossiers.each do |dossier|
|
||||||
attach_file_to_champ(pj_champ(dossier))
|
attach_file_to_champ(pj_champ(dossier))
|
||||||
|
|
||||||
repetition(dossier).add_row(dossier.revision)
|
repetition(dossier).add_row(updated_by: 'test')
|
||||||
attach_file_to_champ(repetition(dossier).champs.first)
|
attach_file_to_champ(repetition(dossier).champs.first)
|
||||||
attach_file_to_champ(repetition(dossier).champs.first)
|
attach_file_to_champ(repetition(dossier).champs.first)
|
||||||
|
|
||||||
repetition(dossier).add_row(dossier.revision)
|
repetition(dossier).add_row(updated_by: 'test')
|
||||||
attach_file_to_champ(repetition(dossier).champs.second)
|
attach_file_to_champ(repetition(dossier).champs.second)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require "rails_helper"
|
|
||||||
|
|
||||||
module Maintenance
|
|
||||||
RSpec.describe FixMissingChampsTask do
|
|
||||||
describe "#process" do
|
|
||||||
subject(:process) { described_class.process(dossiers) }
|
|
||||||
let(:procedure) { create(:procedure, types_de_champ_public:) }
|
|
||||||
let(:types_de_champ_public) { [{ type: :text, libelle: 'l1' }, { type: :text, libelle: 'l2' }] }
|
|
||||||
let(:dossier_1) { create(:dossier, procedure:) }
|
|
||||||
let(:dossiers) { [dossier_1] }
|
|
||||||
it "add missing champs" do
|
|
||||||
dossier_1.champs.last.destroy
|
|
||||||
expect { subject }.to change { dossier_1.champs.count }.by(1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
Reference in a new issue