Merge pull request #10902 from tchak/refactor-champs-on-demande
ETQ dev, je ne veux pas créer les champs inutiles (et je veux pouvoir supprimer un row d'une répétition sans supprimer de champs)
This commit is contained in:
commit
7603b7c1f1
58 changed files with 689 additions and 412 deletions
|
@ -17,7 +17,7 @@ class EditableChamp::SectionComponent < ApplicationComponent
|
|||
|
||||
def header_section
|
||||
node = @nodes.first
|
||||
@dossier.project_champ(node, @row_id) if node.is_a?(TypeDeChamp) && node.header_section?
|
||||
@dossier.project_champ(node, row_id: @row_id) if node.is_a?(TypeDeChamp) && node.header_section?
|
||||
end
|
||||
|
||||
def splitted_tail
|
||||
|
@ -40,7 +40,7 @@ class EditableChamp::SectionComponent < ApplicationComponent
|
|||
when EditableChamp::SectionComponent
|
||||
[node, nil]
|
||||
else
|
||||
[nil, @dossier.project_champ(node, @row_id)]
|
||||
[nil, @dossier.project_champ(node, row_id: @row_id)]
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -18,11 +18,11 @@ class ViewableChamp::SectionComponent < ApplicationComponent
|
|||
|
||||
def header_section
|
||||
node = @nodes.first
|
||||
@dossier.project_champ(node, @row_id) if node.is_a?(TypeDeChamp) && node.header_section?
|
||||
@dossier.project_champ(node, row_id: @row_id) if node.is_a?(TypeDeChamp) && node.header_section?
|
||||
end
|
||||
|
||||
def champs
|
||||
tail.filter_map { _1.is_a?(TypeDeChamp) ? @dossier.project_champ(_1, @row_id) : nil }
|
||||
tail.filter_map { _1.is_a?(TypeDeChamp) ? @dossier.project_champ(_1, row_id: @row_id) : nil }
|
||||
end
|
||||
|
||||
def sections
|
||||
|
|
|
@ -10,9 +10,9 @@ class Champs::ChampController < ApplicationController
|
|||
dossier = policy_scope(Dossier).includes(:champs, revision: [:types_de_champ]).find(params[:dossier_id])
|
||||
type_de_champ = dossier.find_type_de_champ_by_stable_id(params[:stable_id])
|
||||
if type_de_champ.repetition?
|
||||
dossier.project_champ(type_de_champ, nil)
|
||||
dossier.project_champ(type_de_champ)
|
||||
else
|
||||
dossier.champ_for_update(type_de_champ, params_row_id, updated_by: current_user.email)
|
||||
dossier.champ_for_update(type_de_champ, row_id: params_row_id, updated_by: current_user.email)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -308,7 +308,7 @@ module Instructeurs
|
|||
|
||||
respond_to do |format|
|
||||
format.turbo_stream do
|
||||
@to_show, @to_hide, @to_update = champs_to_turbo_update(champs_private_attributes_params, dossier.champs.filter(&:private?))
|
||||
@to_show, @to_hide, @to_update = champs_to_turbo_update(champs_private_attributes_params, dossier.project_champs_private_all)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -321,7 +321,7 @@ module Instructeurs
|
|||
def annotation
|
||||
@dossier = dossier_with_champs(pj_template: false)
|
||||
type_de_champ = @dossier.find_type_de_champ_by_stable_id(params[:stable_id], :private)
|
||||
annotation = @dossier.project_champ(type_de_champ, params[:row_id])
|
||||
annotation = @dossier.project_champ(type_de_champ, row_id: params[:row_id])
|
||||
|
||||
respond_to do |format|
|
||||
format.turbo_stream do
|
||||
|
|
|
@ -286,7 +286,7 @@ module Users
|
|||
|
||||
respond_to do |format|
|
||||
format.turbo_stream do
|
||||
@to_show, @to_hide, @to_update = champs_to_turbo_update(champs_public_attributes_params, dossier.champs.filter(&:public?))
|
||||
@to_show, @to_hide, @to_update = champs_to_turbo_update(champs_public_attributes_params, dossier.project_champs_public_all)
|
||||
render :update, layout: false
|
||||
end
|
||||
end
|
||||
|
@ -299,7 +299,7 @@ module Users
|
|||
def champ
|
||||
@dossier = dossier_with_champs(pj_template: false)
|
||||
type_de_champ = dossier.find_type_de_champ_by_stable_id(params[:stable_id], :public)
|
||||
champ = dossier.project_champ(type_de_champ, params[:row_id])
|
||||
champ = dossier.project_champ(type_de_champ, row_id: params[:row_id])
|
||||
|
||||
respond_to do |format|
|
||||
format.turbo_stream do
|
||||
|
|
|
@ -46,7 +46,7 @@ module Mutations
|
|||
.find_by(type_champ: annotation_type_champ, stable_id:)
|
||||
|
||||
return nil if type_de_champ.nil?
|
||||
dossier.champ_for_update(type_de_champ, row_id, updated_by: current_administrateur.email)
|
||||
dossier.champ_for_update(type_de_champ, row_id:, updated_by: current_administrateur.email)
|
||||
end
|
||||
|
||||
def annotation_type_champ
|
||||
|
|
|
@ -34,7 +34,7 @@ module Mutations
|
|||
.find_by(type_champ: TypeDeChamp.type_champs.fetch(:repetition), stable_id:)
|
||||
|
||||
return nil if type_de_champ.nil?
|
||||
dossier.project_champ(type_de_champ, nil)
|
||||
dossier.project_champ(type_de_champ)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -92,7 +92,7 @@ class AttestationTemplate < ApplicationRecord
|
|||
used_tags.filter_map do |used_tag|
|
||||
corresponding_type_de_champ = types_de_champ_by_tag_id[used_tag]
|
||||
|
||||
if corresponding_type_de_champ && dossier.project_champ(corresponding_type_de_champ, nil).blank?
|
||||
if corresponding_type_de_champ && dossier.project_champ(corresponding_type_de_champ).blank?
|
||||
corresponding_type_de_champ
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
class Champ < ApplicationRecord
|
||||
include ChampConditionalConcern
|
||||
include ChampsValidateConcern
|
||||
include ChampValidateConcern
|
||||
include ChampRevisionConcern
|
||||
|
||||
self.ignored_columns += [:type_de_champ_id, :parent_id]
|
||||
|
||||
|
@ -69,7 +70,11 @@ class Champ < ApplicationRecord
|
|||
end
|
||||
|
||||
def child?
|
||||
row_id.present?
|
||||
row_id.present? && !is_type?(TypeDeChamp.type_champs.fetch(:repetition))
|
||||
end
|
||||
|
||||
def row?
|
||||
row_id.present? && is_type?(TypeDeChamp.type_champs.fetch(:repetition))
|
||||
end
|
||||
|
||||
# used for the `required` html attribute
|
||||
|
|
|
@ -23,6 +23,14 @@ class Champs::RepetitionChamp < Champ
|
|||
rows.last&.first&.focusable_input_id
|
||||
end
|
||||
|
||||
def discarded?
|
||||
discarded_at.present?
|
||||
end
|
||||
|
||||
def discard!
|
||||
touch(:discarded_at)
|
||||
end
|
||||
|
||||
def search_terms
|
||||
# The user cannot enter any information here so it doesn’t make much sense to search
|
||||
end
|
||||
|
|
23
app/models/concerns/champ_revision_concern.rb
Normal file
23
app/models/concerns/champ_revision_concern.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module ChampRevisionConcern
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
protected
|
||||
|
||||
def is_same_type_as_revision?
|
||||
is_type?(type_de_champ.type_champ)
|
||||
end
|
||||
|
||||
def in_dossier_revision?
|
||||
dossier.stable_id_in_revision?(stable_id)
|
||||
end
|
||||
|
||||
def in_discarded_row?
|
||||
if child?
|
||||
repetition_type_de_champ = dossier.revision.parent_of(type_de_champ)
|
||||
row_ids = dossier.repetition_row_ids(repetition_type_de_champ)
|
||||
!row_id.in?(row_ids)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module ChampsValidateConcern
|
||||
module ChampValidateConcern
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
|
@ -16,20 +16,20 @@ module ChampsValidateConcern
|
|||
def validate_champ_value?
|
||||
case validation_context
|
||||
when :champs_public_value
|
||||
public? && in_dossier_revision? && visible?
|
||||
public? && can_validate? && visible?
|
||||
when :champs_private_value
|
||||
private? && in_dossier_revision? && visible?
|
||||
private? && can_validate? && visible?
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def can_validate?
|
||||
in_dossier_revision? && is_same_type_as_revision? && !row? && !in_discarded_row?
|
||||
end
|
||||
|
||||
def validate_champ_value_or_prefill?
|
||||
validate_champ_value? || validation_context == :prefill
|
||||
end
|
||||
|
||||
def in_dossier_revision?
|
||||
dossier.revision.in_revision?(stable_id) && is_type?(type_de_champ.type_champ)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,8 +3,8 @@
|
|||
module DossierChampsConcern
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def project_champ(type_de_champ, row_id)
|
||||
check_valid_row_id?(type_de_champ, row_id)
|
||||
def project_champ(type_de_champ, row_id: nil)
|
||||
check_valid_row_id_on_read?(type_de_champ, row_id)
|
||||
champ = champs_by_public_id[type_de_champ.public_id(row_id)]
|
||||
if champ.nil? || !champ.is_type?(type_de_champ.type_champ)
|
||||
value = type_de_champ.champ_blank?(champ) ? nil : champ.value
|
||||
|
@ -17,11 +17,11 @@ module DossierChampsConcern
|
|||
end
|
||||
|
||||
def project_champs_public
|
||||
@project_champs_public ||= revision.types_de_champ_public.map { project_champ(_1, nil) }
|
||||
@project_champs_public ||= revision.types_de_champ_public.map { project_champ(_1) }
|
||||
end
|
||||
|
||||
def project_champs_private
|
||||
@project_champs_private ||= revision.types_de_champ_private.map { project_champ(_1, nil) }
|
||||
@project_champs_private ||= revision.types_de_champ_private.map { project_champ(_1) }
|
||||
end
|
||||
|
||||
def filled_champs_public
|
||||
|
@ -52,6 +52,28 @@ module DossierChampsConcern
|
|||
filled_champs_public + filled_champs_private
|
||||
end
|
||||
|
||||
def project_champs_public_all
|
||||
revision.types_de_champ_public.flat_map do |type_de_champ|
|
||||
champ = project_champ(type_de_champ)
|
||||
if type_de_champ.repetition?
|
||||
[champ] + project_rows_for(type_de_champ).flatten
|
||||
else
|
||||
champ
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def project_champs_private_all
|
||||
revision.types_de_champ_private.flat_map do |type_de_champ|
|
||||
champ = project_champ(type_de_champ)
|
||||
if type_de_champ.repetition?
|
||||
[champ] + project_rows_for(type_de_champ).flatten
|
||||
else
|
||||
champ
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def project_rows_for(type_de_champ)
|
||||
return [] if !type_de_champ.repetition?
|
||||
|
||||
|
@ -59,7 +81,7 @@ module DossierChampsConcern
|
|||
row_ids = repetition_row_ids(type_de_champ)
|
||||
|
||||
row_ids.map do |row_id|
|
||||
children.map { project_champ(_1, row_id) }
|
||||
children.map { project_champ(_1, row_id:) }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -79,17 +101,21 @@ module DossierChampsConcern
|
|||
.types_de_champ
|
||||
.filter { _1.stable_id.in?(stable_ids) }
|
||||
.filter { !_1.child?(revision) }
|
||||
.map { _1.repetition? ? project_champ(_1, nil) : champ_for_update(_1, nil, updated_by: nil) }
|
||||
.map { _1.repetition? ? project_champ(_1) : champ_for_update(_1, updated_by: nil) }
|
||||
end
|
||||
|
||||
def champ_value_for_tag(type_de_champ, path = :value)
|
||||
champ = filled_champ(type_de_champ, nil)
|
||||
champ = if type_de_champ.repetition?
|
||||
project_champ(type_de_champ)
|
||||
else
|
||||
filled_champ(type_de_champ)
|
||||
end
|
||||
type_de_champ.champ_value_for_tag(champ, path)
|
||||
end
|
||||
|
||||
def champ_for_update(type_de_champ, row_id, updated_by:)
|
||||
champ, attributes = champ_with_attributes_for_update(type_de_champ, row_id, updated_by:)
|
||||
champ.assign_attributes(attributes)
|
||||
def champ_for_update(type_de_champ, row_id: nil, updated_by:)
|
||||
champ = champ_upsert_by!(type_de_champ, row_id)
|
||||
champ.updated_by = updated_by
|
||||
champ
|
||||
end
|
||||
|
||||
|
@ -109,30 +135,42 @@ module DossierChampsConcern
|
|||
|
||||
def repetition_row_ids(type_de_champ)
|
||||
return [] if !type_de_champ.repetition?
|
||||
return [] if !revision.in_revision?(type_de_champ.stable_id)
|
||||
return [] unless stable_id_in_revision?(type_de_champ.stable_id)
|
||||
@repetition_row_ids ||= {}
|
||||
@repetition_row_ids[type_de_champ.stable_id] ||= begin
|
||||
rows = champs_in_revision.filter { _1.row? && _1.stable_id == type_de_champ.stable_id }
|
||||
row_ids = rows.reject(&:discarded?).map(&:row_id)
|
||||
|
||||
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
|
||||
# Legacy rows are rows that have been created before the introduction of the discarded_at column
|
||||
# TODO migrate and clean
|
||||
children_stable_ids = revision.children_of(type_de_champ).map(&:stable_id)
|
||||
discarded_row_ids = rows.filter(&:discarded?).map(&:row_id)
|
||||
legacy_row_ids = champs_in_revision.filter { _1.stable_id.in?(children_stable_ids) && _1.row_id.present? }.map(&:row_id).uniq
|
||||
row_ids += (legacy_row_ids - discarded_row_ids)
|
||||
row_ids.uniq.sort
|
||||
end
|
||||
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)
|
||||
self.champs += types_de_champ.map { _1.build_champ(row_id:, updated_by:) }
|
||||
reload_champs_cache
|
||||
champ = champ_for_update(type_de_champ, row_id:, updated_by:)
|
||||
champ.save!
|
||||
reset_champ_cache(champ)
|
||||
row_id
|
||||
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
|
||||
reload_champs_cache
|
||||
champ = champ_for_update(type_de_champ, row_id:, updated_by:)
|
||||
champ.discard!
|
||||
reset_champ_cache(champ)
|
||||
end
|
||||
|
||||
def stable_id_in_revision?(stable_id)
|
||||
revision_stable_ids.member?(stable_id.to_i)
|
||||
end
|
||||
|
||||
def reload
|
||||
|
@ -142,10 +180,18 @@ module DossierChampsConcern
|
|||
private
|
||||
|
||||
def champs_by_public_id
|
||||
@champs_by_public_id ||= champs.sort_by(&:id).index_by(&:public_id)
|
||||
@champs_by_public_id ||= champs_in_revision.index_by(&:public_id)
|
||||
end
|
||||
|
||||
def filled_champ(type_de_champ, row_id)
|
||||
def revision_stable_ids
|
||||
@revision_stable_ids ||= revision.types_de_champ.map(&:stable_id).to_set
|
||||
end
|
||||
|
||||
def champs_in_revision
|
||||
champs.filter { stable_id_in_revision?(_1.stable_id) }
|
||||
end
|
||||
|
||||
def filled_champ(type_de_champ, row_id: nil)
|
||||
champ = champs_by_public_id[type_de_champ.public_id(row_id)]
|
||||
if type_de_champ.champ_blank?(champ) || !champ.visible?
|
||||
nil
|
||||
|
@ -157,41 +203,51 @@ module DossierChampsConcern
|
|||
def champ_attributes_by_public_id(public_id, attributes, scope, updated_by:)
|
||||
stable_id, row_id = public_id.split('-')
|
||||
type_de_champ = find_type_de_champ_by_stable_id(stable_id, scope)
|
||||
champ_with_attributes_for_update(type_de_champ, row_id, updated_by:).last.merge(attributes)
|
||||
champ = champ_upsert_by!(type_de_champ, row_id)
|
||||
attributes.merge(id: champ.id, updated_by:)
|
||||
end
|
||||
|
||||
def champ_with_attributes_for_update(type_de_champ, row_id, updated_by:)
|
||||
check_valid_row_id?(type_de_champ, row_id)
|
||||
attributes = type_de_champ.params_for_champ
|
||||
def champ_upsert_by!(type_de_champ, row_id)
|
||||
check_valid_row_id_on_write?(type_de_champ, row_id)
|
||||
champ_attributes = type_de_champ.params_for_champ
|
||||
# TODO: Once we have the right index in place, we should change this to use `create_or_find_by` instead of `find_or_create_by`
|
||||
champ = champs
|
||||
.create_with(**attributes)
|
||||
.create_with(**champ_attributes)
|
||||
.find_or_create_by!(stable_id: type_de_champ.stable_id, row_id:)
|
||||
|
||||
attributes[:id] = champ.id
|
||||
attributes[:updated_by] = updated_by
|
||||
|
||||
# Needed when a revision change the champ type in this case, we reset the champ data
|
||||
if champ.type != attributes[:type]
|
||||
attributes[:value] = nil
|
||||
attributes[:value_json] = nil
|
||||
attributes[:external_id] = nil
|
||||
attributes[:data] = nil
|
||||
champ = champ.becomes!(attributes[:type].constantize)
|
||||
champ.save!
|
||||
if champ.type != champ_attributes[:type]
|
||||
champ_attributes[:value] = nil
|
||||
champ_attributes[:value_json] = nil
|
||||
champ_attributes[:external_id] = nil
|
||||
champ_attributes[:data] = nil
|
||||
champ = champ.becomes!(champ_attributes[:type].constantize)
|
||||
end
|
||||
|
||||
champ.assign_attributes(champ_attributes)
|
||||
champ.save!
|
||||
|
||||
reset_champ_cache(champ)
|
||||
|
||||
[champ, attributes]
|
||||
champ
|
||||
end
|
||||
|
||||
def check_valid_row_id?(type_de_champ, row_id)
|
||||
def check_valid_row_id_on_write?(type_de_champ, row_id)
|
||||
if type_de_champ.repetition?
|
||||
if row_id.blank?
|
||||
raise "type_de_champ #{type_de_champ.stable_id} in revision #{revision_id} must have a row_id because it represents a row in a repetition"
|
||||
end
|
||||
else
|
||||
check_valid_row_id_on_read?(type_de_champ, row_id)
|
||||
end
|
||||
end
|
||||
|
||||
def check_valid_row_id_on_read?(type_de_champ, row_id)
|
||||
if type_de_champ.child?(revision)
|
||||
if row_id.blank?
|
||||
raise "type_de_champ #{type_de_champ.stable_id} in revision #{revision_id} must have a row_id because it is part of a repetition"
|
||||
end
|
||||
elsif row_id.present? && type_de_champ.in_revision?(revision)
|
||||
elsif row_id.present? && stable_id_in_revision?(type_de_champ.stable_id)
|
||||
raise "type_de_champ #{type_de_champ.stable_id} in revision #{revision_id} can not have a row_id because it is not part of a repetition"
|
||||
end
|
||||
end
|
||||
|
@ -202,15 +258,12 @@ module DossierChampsConcern
|
|||
@filled_champs_private = nil
|
||||
@project_champs_public = nil
|
||||
@project_champs_private = nil
|
||||
@repetition_row_ids = nil
|
||||
@revision_stable_ids = nil
|
||||
end
|
||||
|
||||
def reset_champ_cache(champ)
|
||||
champs_by_public_id[champ.public_id]&.reload
|
||||
reset_champs_cache
|
||||
end
|
||||
|
||||
def reload_champs_cache
|
||||
champs.reload if persisted?
|
||||
reset_champs_cache
|
||||
end
|
||||
end
|
||||
|
|
|
@ -89,7 +89,11 @@ module DossierCloneConcern
|
|||
dossier_attributes += [:groupe_instructeur_id] if fork
|
||||
relationships = [:individual, :etablissement]
|
||||
|
||||
cloned_champs = champs
|
||||
discarded_row_ids = champs_in_revision
|
||||
.filter { _1.row? && _1.discarded? }
|
||||
.to_set(&:row_id)
|
||||
cloned_champs = champs_in_revision
|
||||
.reject { discarded_row_ids.member?(_1.row_id) }
|
||||
.index_by(&:id)
|
||||
.transform_values { [_1, _1.clone(fork)] }
|
||||
|
||||
|
@ -143,34 +147,38 @@ module DossierCloneConcern
|
|||
end
|
||||
|
||||
def apply_diff(diff)
|
||||
champs_added = diff[:added].filter(&:persisted?)
|
||||
champs_updated = diff[:updated].filter(&:persisted?)
|
||||
champs_removed = diff[:removed].filter(&:persisted?)
|
||||
added_row_ids = {}
|
||||
diff[:added].each do |champ|
|
||||
next if !champ.child?
|
||||
next if added_row_ids.key?(champ.row_id)
|
||||
added_row_ids[champ.row_id] = revision.parent_of(champ.type_de_champ)
|
||||
end
|
||||
|
||||
champs_added.each { _1.update_column(:dossier_id, id) }
|
||||
removed_row_ids = {}
|
||||
diff[:removed].each do |champ|
|
||||
next if !champ.child?
|
||||
next if removed_row_ids.key?(champ.row_id)
|
||||
removed_row_ids[champ.row_id] = revision.parent_of(champ.type_de_champ)
|
||||
end
|
||||
|
||||
if champs_updated.present?
|
||||
added_champs = diff[:added].filter { _1.persisted? && _1.fillable? }
|
||||
updated_champs = diff[:updated].filter { _1.persisted? && _1.fillable? }
|
||||
|
||||
added_champs.each { _1.update_column(:dossier_id, id) }
|
||||
|
||||
if updated_champs.present?
|
||||
champs_index = filled_champs_public.index_by(&:public_id)
|
||||
champs_updated.each do |champ|
|
||||
updated_champs.each do |champ|
|
||||
champs_index[champ.public_id]&.destroy!
|
||||
champ.update_column(:dossier_id, id)
|
||||
end
|
||||
end
|
||||
|
||||
champs_removed.each(&:destroy!)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# This is a temporary method that is only used by diff/merge algorithm. Once it's gone, this method should be removed.
|
||||
def project_champs_public_all
|
||||
revision.types_de_champ_public.flat_map do |type_de_champ|
|
||||
champ = project_champ(type_de_champ, nil)
|
||||
if type_de_champ.repetition?
|
||||
[champ] + project_rows_for(type_de_champ).flatten
|
||||
else
|
||||
champ
|
||||
end
|
||||
added_row_ids.each do |row_id, repetition_type_de_champ|
|
||||
champ_for_update(repetition_type_de_champ, row_id:, updated_by: user.email).save!
|
||||
end
|
||||
removed_row_ids.each do |row_id, repetition_type_de_champ|
|
||||
champ_for_update(repetition_type_de_champ, row_id:, updated_by: user.email).discard!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,7 +17,7 @@ module DossierExportConcern
|
|||
|
||||
def champ_values_for_export(types_de_champ, row_id: nil, export_template: nil, format:)
|
||||
types_de_champ.flat_map do |type_de_champ|
|
||||
champ = filled_champ(type_de_champ, row_id)
|
||||
champ = filled_champ(type_de_champ, row_id:)
|
||||
if export_template.present?
|
||||
export_template
|
||||
.columns_for_stable_id(type_de_champ.stable_id)
|
||||
|
|
|
@ -49,59 +49,27 @@ module DossierRebaseConcern
|
|||
# revision we are rebasing to
|
||||
target_revision = procedure.published_revision
|
||||
|
||||
# index published types de champ coordinates by stable_id
|
||||
target_coordinates_by_stable_id = target_revision
|
||||
.revision_types_de_champ
|
||||
.includes(:parent)
|
||||
.index_by(&:stable_id)
|
||||
|
||||
changes_by_op = pending_changes
|
||||
changed_stable_ids_by_op = pending_changes
|
||||
.group_by(&:op)
|
||||
.tap { _1.default = [] }
|
||||
|
||||
champs_by_stable_id = champs
|
||||
.group_by(&:stable_id)
|
||||
.transform_values { Champ.where(id: _1) }
|
||||
.tap { _1.default = Champ.none }
|
||||
|
||||
# remove champ
|
||||
changes_by_op[:remove].each { champs_by_stable_id[_1.stable_id].destroy_all }
|
||||
|
||||
# update champ
|
||||
changes_by_op[:update].each { champs_by_stable_id[_1.stable_id].update_all(rebased_at: Time.zone.now) }
|
||||
.transform_values { _1.map(&:stable_id) }
|
||||
updated_stable_ids = changed_stable_ids_by_op.fetch(:update, [])
|
||||
added_stable_ids = changed_stable_ids_by_op.fetch(:add, [])
|
||||
|
||||
# update dossier revision
|
||||
update_column(:revision_id, target_revision.id)
|
||||
|
||||
# add champ (after changing dossier revision to avoid errors)
|
||||
changes_by_op[:add]
|
||||
.map { target_coordinates_by_stable_id[_1.stable_id] }
|
||||
.each { add_new_champs_for_revision(_1) }
|
||||
end
|
||||
# mark updated champs as rebased
|
||||
champs.where(stable_id: updated_stable_ids).update_all(rebased_at: Time.zone.now)
|
||||
|
||||
def add_new_champs_for_revision(target_coordinate)
|
||||
if target_coordinate.child?
|
||||
row_ids = repetition_row_ids(target_coordinate.parent.type_de_champ)
|
||||
|
||||
if row_ids.present?
|
||||
row_ids.each do |row_id|
|
||||
create_champ(target_coordinate, row_id:)
|
||||
end
|
||||
elsif target_coordinate.parent.mandatory?
|
||||
create_champ(target_coordinate, row_id: ULID.generate)
|
||||
# add rows for new repetitions
|
||||
repetition_types_de_champ = target_revision
|
||||
.types_de_champ
|
||||
.repetition
|
||||
.where(stable_id: added_stable_ids)
|
||||
repetition_types_de_champ.mandatory
|
||||
.or(repetition_types_de_champ.private_only)
|
||||
.find_each do |type_de_champ|
|
||||
self.champs << type_de_champ.build_champ(row_id: ULID.generate, rebased_at: Time.zone.now)
|
||||
end
|
||||
else
|
||||
create_champ(target_coordinate)
|
||||
end
|
||||
end
|
||||
|
||||
def create_champ(target_coordinate, row_id: nil)
|
||||
self.champs << target_coordinate
|
||||
.type_de_champ
|
||||
.build_champ(rebased_at: Time.zone.now, row_id:)
|
||||
end
|
||||
|
||||
def purge_piece_justificative_file(champ)
|
||||
ActiveStorage::Attachment.where(id: champ.piece_justificative_file.ids).delete_all
|
||||
end
|
||||
end
|
||||
|
|
|
@ -33,7 +33,7 @@ module DossierSectionsConcern
|
|||
return "#{index}.#{index_in_repetition + 1}" if index_in_repetition
|
||||
else
|
||||
return index if tdc.stable_id == type_de_champ.stable_id
|
||||
next unless project_champ(tdc, nil).visible?
|
||||
next unless project_champ(tdc).visible?
|
||||
|
||||
index += 1 if tdc.header_section?
|
||||
end
|
||||
|
|
|
@ -11,9 +11,7 @@ module DossierStateConcern
|
|||
|
||||
resolve_pending_correction!
|
||||
process_sva_svr!
|
||||
remove_piece_justificative_file_not_visible!
|
||||
|
||||
editing_forks.each(&:destroy_editing_fork!)
|
||||
clean_champs_after_submit!
|
||||
end
|
||||
|
||||
def after_passer_en_construction
|
||||
|
@ -41,7 +39,8 @@ module DossierStateConcern
|
|||
groupe_instructeur.instructeurs.with_instant_email_dossier_notifications.each do |instructeur|
|
||||
DossierMailer.notify_new_dossier_depose_to_instructeur(self, instructeur.email).deliver_later
|
||||
end
|
||||
remove_piece_justificative_file_not_visible!
|
||||
|
||||
clean_champs_after_submit!
|
||||
end
|
||||
|
||||
def after_passer_en_instruction(h)
|
||||
|
@ -154,7 +153,7 @@ module DossierStateConcern
|
|||
end
|
||||
|
||||
send_dossier_decision_to_experts(self)
|
||||
remove_titres_identite!
|
||||
clean_champs_after_instruction!
|
||||
end
|
||||
|
||||
def after_accepter_automatiquement
|
||||
|
@ -189,7 +188,7 @@ module DossierStateConcern
|
|||
NotificationMailer.send_notification_for_tiers(self).deliver_later if self.for_tiers?
|
||||
|
||||
send_dossier_decision_to_experts(self)
|
||||
remove_titres_identite!
|
||||
clean_champs_after_instruction!
|
||||
end
|
||||
|
||||
def after_refuser(h)
|
||||
|
@ -226,7 +225,7 @@ module DossierStateConcern
|
|||
end
|
||||
|
||||
send_dossier_decision_to_experts(self)
|
||||
remove_titres_identite!
|
||||
clean_champs_after_instruction!
|
||||
end
|
||||
|
||||
def after_refuser_automatiquement
|
||||
|
@ -254,7 +253,7 @@ module DossierStateConcern
|
|||
NotificationMailer.send_notification_for_tiers(self).deliver_later if self.for_tiers?
|
||||
|
||||
send_dossier_decision_to_experts(self)
|
||||
remove_titres_identite!
|
||||
clean_champs_after_instruction!
|
||||
end
|
||||
|
||||
def after_classer_sans_suite(h)
|
||||
|
@ -291,7 +290,7 @@ module DossierStateConcern
|
|||
end
|
||||
|
||||
send_dossier_decision_to_experts(self)
|
||||
remove_titres_identite!
|
||||
clean_champs_after_instruction!
|
||||
end
|
||||
|
||||
def after_repasser_en_instruction(h)
|
||||
|
@ -329,4 +328,58 @@ module DossierStateConcern
|
|||
|
||||
rebase_later
|
||||
end
|
||||
|
||||
def clean_champs_after_submit!
|
||||
remove_discarded_rows!
|
||||
remove_not_visible_rows!
|
||||
remove_not_visible_or_empty_champs!
|
||||
editing_forks.each(&:destroy_editing_fork!)
|
||||
end
|
||||
|
||||
def clean_champs_after_instruction!
|
||||
remove_discarded_rows!
|
||||
remove_titres_identite!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def remove_discarded_rows!
|
||||
row_to_remove_ids = champs.filter { _1.row? && _1.discarded? }.map(&:row_id)
|
||||
|
||||
return if row_to_remove_ids.empty?
|
||||
champs.where(row_id: row_to_remove_ids).destroy_all
|
||||
end
|
||||
|
||||
def remove_not_visible_or_empty_champs!
|
||||
repetition_to_keep_stable_ids, champ_to_keep_public_ids = project_champs_public_all
|
||||
.reject { _1.blank? || !_1.visible? }
|
||||
.partition(&:repetition?)
|
||||
.then { |(repetitions, champs)| [repetitions.to_set(&:stable_id), champs.to_set(&:public_id)] }
|
||||
|
||||
rows_public, champs_public = champs
|
||||
.filter(&:public?)
|
||||
.partition(&:row?)
|
||||
|
||||
champs_to_remove = champs_public.reject { champ_to_keep_public_ids.member?(_1.public_id) }
|
||||
champs_to_remove += rows_public.reject { repetition_to_keep_stable_ids.member?(_1.stable_id) }
|
||||
|
||||
return if champs_to_remove.empty?
|
||||
champs.where(id: champs_to_remove).destroy_all
|
||||
end
|
||||
|
||||
def remove_not_visible_rows!
|
||||
row_to_remove_ids = project_champs_public
|
||||
.filter { _1.repetition? && !_1.visible? }
|
||||
.flat_map(&:row_ids)
|
||||
|
||||
return if row_to_remove_ids.empty?
|
||||
champs.where(row_id: row_to_remove_ids).destroy_all
|
||||
end
|
||||
|
||||
def remove_titres_identite!
|
||||
champ_to_remove_ids = filled_champs.filter(&:titre_identite?).map(&:id)
|
||||
|
||||
return if champ_to_remove_ids.empty?
|
||||
champs.where(id: champ_to_remove_ids).destroy_all
|
||||
end
|
||||
end
|
||||
|
|
|
@ -905,19 +905,6 @@ class Dossier < ApplicationRecord
|
|||
traitements.any?(&:termine?)
|
||||
end
|
||||
|
||||
def remove_titres_identite!
|
||||
champs.filter(&:titre_identite?).map(&:piece_justificative_file).each(&:purge_later)
|
||||
end
|
||||
|
||||
def remove_piece_justificative_file_not_visible!
|
||||
champs.each do |champ|
|
||||
next unless champ.piece_justificative_file.attached?
|
||||
next if champ.visible?
|
||||
|
||||
champ.piece_justificative_file.purge_later
|
||||
end
|
||||
end
|
||||
|
||||
def check_mandatory_and_visible_champs
|
||||
project_champs_public.filter(&:visible?).each do |champ|
|
||||
if champ.mandatory_blank?
|
||||
|
@ -1058,13 +1045,13 @@ class Dossier < ApplicationRecord
|
|||
end
|
||||
|
||||
def build_default_champs_for(types_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?)
|
||||
row_id = ULID.generate
|
||||
[champ] + revision.children_of(type_de_champ).map { _1.build_champ(dossier: self, row_id:) }
|
||||
self.champs << types_de_champ.filter(&:fillable?).filter_map do |type_de_champ|
|
||||
if type_de_champ.repetition?
|
||||
if type_de_champ.private? || type_de_champ.mandatory?
|
||||
type_de_champ.build_champ(dossier: self, row_id: ULID.generate)
|
||||
end
|
||||
else
|
||||
champ
|
||||
type_de_champ.build_champ(dossier: self)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -203,10 +203,6 @@ class ProcedureRevision < ApplicationRecord
|
|||
coordinate_for(tdc).parent_type_de_champ
|
||||
end
|
||||
|
||||
def in_revision?(stable_id)
|
||||
types_de_champ.any? { _1.stable_id == stable_id }
|
||||
end
|
||||
|
||||
def dependent_conditions(tdc)
|
||||
stable_id = tdc.stable_id
|
||||
|
||||
|
|
|
@ -171,6 +171,7 @@ class TypeDeChamp < ApplicationRecord
|
|||
scope :not_condition, -> { where(condition: nil) }
|
||||
scope :fillable, -> { where.not(type_champ: [type_champs.fetch(:header_section), type_champs.fetch(:explication)]) }
|
||||
scope :with_header_section, -> { where.not(type_champ: TypeDeChamp.type_champs[:explication]) }
|
||||
scope :mandatory, -> { where(mandatory: true) }
|
||||
|
||||
scope :dubious, -> {
|
||||
where("unaccent(types_de_champ.libelle) ~* unaccent(?)", DubiousProcedure.forbidden_regexp)
|
||||
|
@ -331,10 +332,6 @@ class TypeDeChamp < ApplicationRecord
|
|||
!private?
|
||||
end
|
||||
|
||||
def in_revision?(revision)
|
||||
revision.types_de_champ.any? { _1.stable_id == stable_id }
|
||||
end
|
||||
|
||||
def child?(revision)
|
||||
revision.coordinate_for(self)&.child?
|
||||
end
|
||||
|
|
|
@ -65,7 +65,7 @@ class TypesDeChamp::PrefillRepetitionTypeDeChamp < TypesDeChamp::PrefillTypeDeCh
|
|||
type_de_champ = revision.types_de_champ.find { _1.stable_id == stable_id }
|
||||
next unless type_de_champ
|
||||
|
||||
subchamp = champ.dossier.champ_for_update(type_de_champ, row_id, updated_by: nil)
|
||||
subchamp = champ.dossier.champ_for_update(type_de_champ, row_id:, updated_by: nil)
|
||||
TypesDeChamp::PrefillTypeDeChamp.build(subchamp.type_de_champ, revision).to_assignable_attributes(subchamp, value)
|
||||
end.compact
|
||||
end
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Maintenance
|
||||
class RemovePieceJustificativeFileNotVisibleTask < MaintenanceTasks::Task
|
||||
attribute :procedure_id, :string
|
||||
validates :procedure_id, presence: true
|
||||
|
||||
def collection
|
||||
procedure = Procedure.with_discarded.find(procedure_id.strip.to_i)
|
||||
procedure.dossiers.state_not_brouillon
|
||||
end
|
||||
|
||||
def process(dossier)
|
||||
dossier.remove_piece_justificative_file_not_visible!
|
||||
end
|
||||
end
|
||||
end
|
7
db/migrate/20241007174301_add_discarded_at_to_champs.rb
Normal file
7
db/migrate/20241007174301_add_discarded_at_to_champs.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddDiscardedAtToChamps < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
add_column :champs, :discarded_at, :datetime
|
||||
end
|
||||
end
|
|
@ -250,6 +250,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_11_26_145420) do
|
|||
create_table "champs", id: :serial, force: :cascade do |t|
|
||||
t.datetime "created_at", precision: nil
|
||||
t.jsonb "data"
|
||||
t.datetime "discarded_at"
|
||||
t.integer "dossier_id"
|
||||
t.integer "etablissement_id"
|
||||
t.string "external_id"
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
describe EditableChamp::ExplicationComponent, type: :component do
|
||||
let(:procedure) { create(:procedure, :published, types_de_champ_public:) }
|
||||
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
|
||||
let(:champ) { dossier.champs.first }
|
||||
let(:dossier) { create(:dossier, procedure:) }
|
||||
let(:champ) { dossier.project_champs_public.first }
|
||||
|
||||
let(:component) {
|
||||
described_class.new(form: instance_double(ActionView::Helpers::FormBuilder, object_name: "dossier[champs_public_attributes]"), champ:)
|
||||
|
|
|
@ -40,20 +40,7 @@ describe Champs::PieceJustificativeController, type: :controller do
|
|||
end
|
||||
|
||||
context 'when the file is invalid' do
|
||||
let(:file) { fixture_file_upload('spec/fixtures/files/invalid_file_format.json', 'application/json') }
|
||||
|
||||
# TODO: for now there are no validators on the champ piece_justificative_file,
|
||||
# so we have to mock a failing validation.
|
||||
# Once the validators will be enabled, remove those mocks, and let the usual
|
||||
# validation fail naturally.
|
||||
#
|
||||
# See https://github.com/betagouv/demarches-simplifiees.fr/issues/4926
|
||||
before do
|
||||
champ
|
||||
expect_any_instance_of(Champs::PieceJustificativeChamp).to receive(:save).and_return(false)
|
||||
expect_any_instance_of(Champs::PieceJustificativeChamp).to receive(:errors)
|
||||
.and_return(double(full_messages: ['La pièce justificative n’est pas d’un type accepté']))
|
||||
end
|
||||
let(:file) { fixture_file_upload('spec/fixtures/files/invalid_file_format.json', 'bad/bad') }
|
||||
|
||||
it 'doesn’t attach the file' do
|
||||
subject
|
||||
|
@ -64,7 +51,7 @@ describe Champs::PieceJustificativeController, type: :controller do
|
|||
subject
|
||||
expect(response.status).to eq(422)
|
||||
expect(response.header['Content-Type']).to include('application/json')
|
||||
expect(response.parsed_body).to eq({ 'errors' => ['La pièce justificative n’est pas d’un type accepté'] })
|
||||
expect(response.parsed_body).to eq({ 'errors' => ['Le champ « Piece justificative file » n’est pas d’un type accepté'] })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,14 +3,18 @@
|
|||
describe Champs::RepetitionController, type: :controller do
|
||||
describe '#remove' do
|
||||
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :repetition, mandatory: true, children: [{ libelle: 'Nom' }, { type: :integer_number, libelle: 'Age' }] }]) }
|
||||
let(:dossier) { create(:dossier, procedure: procedure) }
|
||||
let(:dossier) { create(:dossier, procedure:) }
|
||||
let(:repetition) { dossier.project_champs_public.find(&:repetition?) }
|
||||
let(:row) { dossier.champs.find(&:row?) }
|
||||
|
||||
before { sign_in dossier.user }
|
||||
it 'removes repetition' do
|
||||
rows, repetitions = dossier.champs.partition(&:child?)
|
||||
repetition = repetitions.first
|
||||
expect { delete :remove, params: { dossier_id: dossier, stable_id: repetition.stable_id, row_id: rows.first.row_id }, format: :turbo_stream }
|
||||
.to change { dossier.reload.champs.size }.from(3).to(1)
|
||||
|
||||
subject { delete :remove, params: { dossier_id: dossier, stable_id: repetition.stable_id, row_id: row.row_id }, format: :turbo_stream }
|
||||
|
||||
context 'removes repetition' do
|
||||
it { expect { subject }.not_to change { dossier.reload.champs.size } }
|
||||
it { expect { subject }.to change { dossier.reload; dossier.project_champs_public.find(&:repetition?).row_ids.size }.from(1).to(0) }
|
||||
it { expect { subject }.to change { row.reload.discarded_at }.from(nil).to(Time) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1002,10 +1002,11 @@ describe Instructeurs::DossiersController, type: :controller do
|
|||
let(:another_instructeur) { create(:instructeur) }
|
||||
let(:now) { Time.zone.parse('01/01/2100') }
|
||||
|
||||
let(:champ_repetition) { dossier.project_champs_private.fourth }
|
||||
let(:champ_text) { champ_repetition.rows.first.first }
|
||||
let(:champ_multiple_drop_down_list) { dossier.project_champs_private.first }
|
||||
let(:champ_linked_drop_down_list) { dossier.project_champs_private.second }
|
||||
let(:champ_datetime) { dossier.project_champs_private.third }
|
||||
let(:champ_repetition) { dossier.project_champs_private.fourth }
|
||||
let(:champ_drop_down_list) { dossier.project_champs_private.fifth }
|
||||
|
||||
context 'when no invalid champs_public' do
|
||||
|
@ -1015,12 +1016,12 @@ describe Instructeurs::DossiersController, type: :controller do
|
|||
another_instructeur.follow(dossier)
|
||||
Timecop.freeze(now)
|
||||
patch :update_annotations, params: params, format: :turbo_stream
|
||||
|
||||
dossier.reload
|
||||
champ_multiple_drop_down_list.reload
|
||||
champ_linked_drop_down_list.reload
|
||||
champ_datetime.reload
|
||||
champ_repetition.reload
|
||||
champ_drop_down_list.reload
|
||||
champ_text.reload
|
||||
end
|
||||
|
||||
after do
|
||||
|
@ -1059,9 +1060,9 @@ describe Instructeurs::DossiersController, type: :controller do
|
|||
expect(champ_linked_drop_down_list.primary_value).to eq('primary')
|
||||
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_repetition.rows.first.first.value).to eq('text')
|
||||
expect(champ_text.value).to eq('text')
|
||||
expect(champ_drop_down_list.value).to eq('other value')
|
||||
expect(dossier.reload.last_champ_private_updated_at).to eq(now)
|
||||
expect(dossier.last_champ_private_updated_at).to eq(now)
|
||||
expect(response).to have_http_status(200)
|
||||
assert_enqueued_jobs(1, only: DossierIndexSearchTermsJob)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
describe Users::CommencerController, type: :controller do
|
||||
let(:user) { create(:user) }
|
||||
let(:published_procedure) { create(:procedure, :for_individual, :published) }
|
||||
let(:published_procedure) { create(:procedure, :for_individual, :published, types_de_champ_public:) }
|
||||
let(:types_de_champ_public) { [] }
|
||||
let(:draft_procedure) { create(:procedure, :with_path) }
|
||||
|
||||
describe '#commencer' do
|
||||
|
@ -160,7 +161,8 @@ describe Users::CommencerController, type: :controller do
|
|||
end
|
||||
|
||||
context 'when a dossier is being prefilled by GET' do
|
||||
let(:type_de_champ_text) { create(:type_de_champ_text, procedure: published_procedure) }
|
||||
let(:types_de_champ_public) { [{}] }
|
||||
let(:type_de_champ_text) { published_procedure.published_revision.types_de_champ.first }
|
||||
let(:path) { published_procedure.path }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
|
|
|
@ -594,11 +594,11 @@ describe Users::DossiersController, type: :controller do
|
|||
let(:dossier) { create(:dossier, :en_construction, :with_populated_champs, procedure:, user:) }
|
||||
let(:types_de_champ_public) { [{ type: :repetition, libelle: 'repetition', children: [{ type: :text, libelle: 'child' }] }] }
|
||||
let(:editing_fork) { dossier.owner_editing_fork }
|
||||
let(:champ_repetition) { editing_fork.champs.find(&:repetition?) }
|
||||
let(:champ_repetition) { editing_fork.project_champs_public.find(&:repetition?) }
|
||||
before do
|
||||
editing_fork
|
||||
|
||||
procedure.draft_revision.remove_type_de_champ(editing_fork.champs.find(&:repetition?).stable_id)
|
||||
procedure.draft_revision.remove_type_de_champ(champ_repetition.stable_id)
|
||||
procedure.publish_revision!
|
||||
|
||||
editing_fork.reload
|
||||
|
@ -611,7 +611,7 @@ describe Users::DossiersController, type: :controller do
|
|||
|
||||
context 'when dossier was already submitted' do
|
||||
before do
|
||||
expect_any_instance_of(Dossier).to receive(:remove_piece_justificative_file_not_visible!)
|
||||
expect_any_instance_of(Dossier).to receive(:remove_not_visible_or_empty_champs!)
|
||||
post :submit_en_construction, params: payload
|
||||
end
|
||||
|
||||
|
|
|
@ -94,14 +94,6 @@ FactoryBot.define do
|
|||
external_id { '200071991' }
|
||||
end
|
||||
|
||||
factory :champ_do_not_use_header_section, class: 'Champs::HeaderSectionChamp' do
|
||||
value { 'une section' }
|
||||
end
|
||||
|
||||
factory :champ_do_not_use_explication, class: 'Champs::ExplicationChamp' do
|
||||
value { '' }
|
||||
end
|
||||
|
||||
factory :champ_do_not_use_dossier_link, class: 'Champs::DossierLinkChamp' do
|
||||
value { create(:dossier, :en_construction).id }
|
||||
end
|
||||
|
@ -188,25 +180,5 @@ FactoryBot.define do
|
|||
|
||||
factory :champ_do_not_use_expression_reguliere, class: 'Champs::ExpressionReguliereChamp' do
|
||||
end
|
||||
|
||||
factory :champ_do_not_use_repetition, class: 'Champs::RepetitionChamp' do
|
||||
transient do
|
||||
rows { 2 }
|
||||
end
|
||||
|
||||
after(:build) do |champ_repetition, evaluator|
|
||||
revision = champ_repetition.procedure.active_revision
|
||||
parent = revision.revision_types_de_champ.find { _1.type_de_champ == champ_repetition.type_de_champ }
|
||||
types_de_champ = revision.revision_types_de_champ.filter { _1.parent == parent }.map(&:type_de_champ)
|
||||
|
||||
evaluator.rows.times do
|
||||
row_id = ULID.generate
|
||||
champ_repetition.dossier.champs << types_de_champ.map do |type_de_champ|
|
||||
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)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -24,25 +24,13 @@ FactoryBot.define do
|
|||
after(:create) do |dossier, evaluator|
|
||||
if evaluator.populate_champs
|
||||
dossier.revision.types_de_champ_public.each do |type_de_champ|
|
||||
value = if type_de_champ.drop_down_list?
|
||||
type_de_champ.drop_down_options.first
|
||||
elsif type_de_champ.multiple_drop_down_list?
|
||||
type_de_champ.drop_down_options.first(2).to_json
|
||||
end
|
||||
attrs = { stable_id: type_de_champ.stable_id, dossier:, value: }.compact
|
||||
create(:"champ_do_not_use_#{type_de_champ.type_champ}", **attrs)
|
||||
dossier_factory_create_champ_or_repetition(type_de_champ, dossier)
|
||||
end
|
||||
end
|
||||
|
||||
if evaluator.populate_annotations
|
||||
dossier.revision.types_de_champ_private.each do |type_de_champ|
|
||||
value = if type_de_champ.drop_down_list?
|
||||
type_de_champ.drop_down_options.first
|
||||
elsif type_de_champ.multiple_drop_down_list?
|
||||
type_de_champ.drop_down_options.first(2).to_json
|
||||
end
|
||||
attrs = { stable_id: type_de_champ.stable_id, dossier:, private: true, value: }.compact
|
||||
create(:"champ_do_not_use_#{type_de_champ.type_champ}", **attrs)
|
||||
dossier_factory_create_champ_or_repetition(type_de_champ, dossier)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -301,3 +289,30 @@ FactoryBot.define do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
def dossier_factory_create_champ_or_repetition(type_de_champ, dossier)
|
||||
if type_de_champ.repetition?
|
||||
types_de_champ = dossier.revision.children_of(type_de_champ)
|
||||
2.times do
|
||||
row_id = ULID.generate
|
||||
type_de_champ.build_champ(dossier:, row_id:).save!
|
||||
types_de_champ.each do |type_de_champ|
|
||||
dossier_factory_create_champ(type_de_champ, dossier, row_id:)
|
||||
end
|
||||
end
|
||||
else
|
||||
dossier_factory_create_champ(type_de_champ, dossier)
|
||||
end
|
||||
end
|
||||
|
||||
def dossier_factory_create_champ(type_de_champ, dossier, row_id: nil)
|
||||
return unless type_de_champ.fillable?
|
||||
|
||||
value = if type_de_champ.drop_down_list?
|
||||
type_de_champ.drop_down_options.first
|
||||
elsif type_de_champ.multiple_drop_down_list?
|
||||
type_de_champ.drop_down_options.first(2).to_json
|
||||
end
|
||||
attrs = { stable_id: type_de_champ.stable_id, private: type_de_champ.private?, row_id:, dossier:, value: }.compact
|
||||
create(:"champ_do_not_use_#{type_de_champ.type_champ}", **attrs)
|
||||
end
|
||||
|
|
|
@ -55,7 +55,8 @@ RSpec.describe Mutations::DossierModifierAnnotation, type: :graphql do
|
|||
},
|
||||
errors: nil
|
||||
})
|
||||
expect(annotation.reload.row_ids.size).to eq(3)
|
||||
dossier.reload
|
||||
expect(annotation.row_ids.size).to eq(3)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ describe 'Dossier::Recovery::LifeCycle' do
|
|||
let(:geo_area) { build(:geo_area, :selection_utilisateur, :polygon) }
|
||||
let(:fp) { Rails.root.join('spec', 'fixtures', 'export.dump') }
|
||||
let(:dossier) do
|
||||
d = create(:dossier, procedure:)
|
||||
d = create(:dossier, :with_populated_champs, procedure:)
|
||||
|
||||
repetition(d).add_row(updated_by: 'test')
|
||||
pj_champ(d).piece_justificative_file.attach(some_file)
|
||||
|
@ -49,7 +49,7 @@ describe 'Dossier::Recovery::LifeCycle' do
|
|||
d
|
||||
end
|
||||
|
||||
def repetition(d) = d.champs.find_by(type: "Champs::RepetitionChamp")
|
||||
def repetition(d) = d.project_champs_public.find(&:repetition?)
|
||||
def pj_champ(d) = d.champs.find_by(type: "Champs::PieceJustificativeChamp")
|
||||
def carte(d) = d.champs.find_by(type: "Champs::CarteChamp")
|
||||
def siret(d) = d.champs.find_by(type: "Champs::SiretChamp")
|
||||
|
@ -83,7 +83,7 @@ describe 'Dossier::Recovery::LifeCycle' do
|
|||
|
||||
expect(reloaded_dossier.champs.count).not_to be(0)
|
||||
|
||||
expect(repetition(reloaded_dossier).rows.flatten.map(&:type)).to match_array(["Champs::PieceJustificativeChamp"])
|
||||
expect(repetition(reloaded_dossier).rows.flatten.map(&:type)).to match_array(["Champs::PieceJustificativeChamp", "Champs::PieceJustificativeChamp", "Champs::PieceJustificativeChamp"])
|
||||
expect(pj_champ(reloaded_dossier).piece_justificative_file).to be_attached
|
||||
expect(carte(reloaded_dossier).geo_areas).to be_present
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ describe ChampPresentations::RepetitionPresentation do
|
|||
}
|
||||
|
||||
let(:dossier) { create(:dossier, procedure:) }
|
||||
let(:champ_repetition) { dossier.champs.find(&:repetition?) }
|
||||
let(:champ_repetition) { dossier.project_champs_public.first }
|
||||
|
||||
before do
|
||||
champ_repetition.add_row(updated_by: 'test')
|
||||
|
@ -23,15 +23,15 @@ describe ChampPresentations::RepetitionPresentation do
|
|||
row1, row2, row3 = champ_repetition.rows
|
||||
|
||||
nom, stars = row1
|
||||
nom.update(value: "ruby")
|
||||
stars.update(value: 5)
|
||||
champ_for_update(nom).update(value: "ruby")
|
||||
champ_for_update(stars).update(value: 5)
|
||||
|
||||
nom = row2.first
|
||||
nom.update(value: "js")
|
||||
champ_for_update(nom).update(value: "js")
|
||||
|
||||
nom, stars = row3
|
||||
nom.update(value: "rust")
|
||||
stars.update(value: 4)
|
||||
champ_for_update(nom).update(value: "rust")
|
||||
champ_for_update(stars).update(value: 4)
|
||||
end
|
||||
|
||||
let(:representation) { described_class.new(libelle, champ_repetition.rows) }
|
||||
|
|
|
@ -58,7 +58,7 @@ describe Champ do
|
|||
context 'when repetition not blank' do
|
||||
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :repetition, children: [{ type: :text }] }]) }
|
||||
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
|
||||
let(:champ) { dossier.champs.find(&:repetition?) }
|
||||
let(:champ) { dossier.project_champs_public.find(&:repetition?) }
|
||||
|
||||
it { expect(champ.blank?).to be(false) }
|
||||
end
|
||||
|
@ -108,11 +108,10 @@ describe Champ do
|
|||
let(:dossier) { create(:dossier, procedure: procedure) }
|
||||
let(:public_champ) { dossier.project_champs_public.first }
|
||||
let(:private_champ) { dossier.project_champs_private.first }
|
||||
let(:champ_in_repetition) { dossier.project_champs_public.find(&:repetition?).champs.first }
|
||||
let(:standalone_champ) { build(:champ, type_de_champ: build(:type_de_champ), dossier: build(:dossier)) }
|
||||
let(:public_sections) { dossier.project_champs_public.filter(&:header_section?) }
|
||||
let(:private_sections) { dossier.project_champs_private.filter(&:header_section?) }
|
||||
let(:sections_in_repetition) { dossier.champs.filter(&:child?).filter(&:header_section?) }
|
||||
let(:sections_in_repetition) { dossier.project_champs_public.find(&:repetition?).rows.flatten.filter(&:header_section?) }
|
||||
|
||||
it 'returns the sibling sections of a champ' do
|
||||
expect(public_sections).not_to be_empty
|
||||
|
|
|
@ -8,7 +8,7 @@ describe Champs::EpciChamp, type: :model do
|
|||
let(:champ) { Champs::EpciChamp.new(code_departement: code_departement, dossier: build(:dossier)) }
|
||||
before do
|
||||
allow(champ).to receive(:visible?).and_return(true)
|
||||
allow(champ).to receive(:in_dossier_revision?).and_return(true)
|
||||
allow(champ).to receive(:can_validate?).and_return(true)
|
||||
end
|
||||
context 'when nil' do
|
||||
let(:code_departement) { nil }
|
||||
|
|
|
@ -4,7 +4,7 @@ describe Champs::IntegerNumberChamp do
|
|||
let(:champ) { Champs::IntegerNumberChamp.new(value:, dossier: build(:dossier)) }
|
||||
before do
|
||||
allow(champ).to receive(:visible?).and_return(true)
|
||||
allow(champ).to receive(:in_dossier_revision?).and_return(true)
|
||||
allow(champ).to receive(:can_validate?).and_return(true)
|
||||
end
|
||||
subject { champ.validate(:champs_public_value) }
|
||||
|
||||
|
|
|
@ -11,11 +11,11 @@ describe Champs::RepetitionChamp do
|
|||
])
|
||||
}
|
||||
let(:dossier) { create(:dossier, procedure:) }
|
||||
let(:champ) { dossier.champs.find(&:repetition?) }
|
||||
let(:champ) { dossier.project_champs_public.find(&:repetition?) }
|
||||
|
||||
describe "#for_tag" do
|
||||
before do
|
||||
champ.rows[0][0].update(value: "rb")
|
||||
champ_for_update(champ.rows.first.first).update(value: "rb")
|
||||
end
|
||||
|
||||
it "can render as string" do
|
||||
|
|
|
@ -155,13 +155,13 @@ describe Columns::ChampColumn do
|
|||
|
||||
def expect_type_de_champ_values(type, assertion)
|
||||
type_de_champ = types_de_champ.find { _1.type_champ == type }
|
||||
champ = dossier.send(:filled_champ, type_de_champ, nil)
|
||||
champ = dossier.send(:filled_champ, type_de_champ)
|
||||
columns = type_de_champ.columns(procedure:)
|
||||
expect(columns.map { _1.value(champ) }).to assertion
|
||||
end
|
||||
|
||||
def retrieve_champ(type)
|
||||
type_de_champ = types_de_champ.find { _1.type_champ == type }
|
||||
dossier.send(:filled_champ, type_de_champ, nil)
|
||||
dossier.send(:filled_champ, type_de_champ)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe ChampsValidateConcern do
|
||||
RSpec.describe ChampValidateConcern do
|
||||
let(:procedure) { create(:procedure, :published, types_de_champ_public:) }
|
||||
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
|
||||
let(:type_de_champ) { dossier.revision.types_de_champ_public.first }
|
||||
|
||||
let(:public_id) { type_de_champ.public_id(nil) }
|
||||
let(:types_de_champ_public) { [{ type: :email }] }
|
||||
|
||||
def update_champ(value)
|
||||
dossier.update_champs_attributes({
|
||||
type_de_champ.stable_id.to_s => { value: }
|
||||
public_id => { value: }
|
||||
}, :public, updated_by: 'test')
|
||||
dossier.save
|
||||
end
|
||||
|
@ -68,4 +68,45 @@ RSpec.describe ChampsValidateConcern do
|
|||
}
|
||||
end
|
||||
end
|
||||
|
||||
context 'when in a row' do
|
||||
let(:types_de_champ_public) { [{ type: :repetition, children: [{ type: :email }], mandatory: true }] }
|
||||
let(:type_de_champ_in_repetition) { dossier.revision.children_of(type_de_champ).first }
|
||||
let(:row_id) { dossier.repetition_row_ids(type_de_champ).first }
|
||||
let(:public_id) { type_de_champ_in_repetition.public_id(row_id) }
|
||||
|
||||
context 'valid' do
|
||||
before {
|
||||
update_champ('test@test.com')
|
||||
dossier.validate(:champs_public_value)
|
||||
}
|
||||
it {
|
||||
expect(dossier.champs).not_to be_empty
|
||||
expect(dossier.errors).to be_empty
|
||||
}
|
||||
end
|
||||
|
||||
context 'invalid' do
|
||||
before {
|
||||
update_champ('test')
|
||||
dossier.validate(:champs_public_value)
|
||||
}
|
||||
it {
|
||||
expect(dossier.champs).not_to be_empty
|
||||
expect(dossier.errors).not_to be_empty
|
||||
}
|
||||
end
|
||||
|
||||
context 'do not validate when in discarded row' do
|
||||
before {
|
||||
update_champ('test')
|
||||
dossier.repetition_remove_row(type_de_champ, row_id, updated_by: 'test')
|
||||
dossier.validate(:champs_public_value)
|
||||
}
|
||||
it {
|
||||
expect(dossier.champs).not_to be_empty
|
||||
expect(dossier.errors).to be_empty
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,9 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe DossierChampsConcern do
|
||||
let(:procedure) do
|
||||
create(:procedure, types_de_champ_public:, types_de_champ_private:)
|
||||
end
|
||||
let(:procedure) { create(:procedure, types_de_champ_public:, types_de_champ_private:) }
|
||||
let(:types_de_champ_public) do
|
||||
[
|
||||
{ type: :text, libelle: "Un champ text", stable_id: 99 },
|
||||
|
@ -38,16 +36,16 @@ RSpec.describe DossierChampsConcern do
|
|||
|
||||
context "public champ" do
|
||||
let(:row_id) { nil }
|
||||
subject { dossier.project_champ(type_de_champ_public, row_id) }
|
||||
subject { dossier.project_champ(type_de_champ_public, row_id:) }
|
||||
|
||||
it { expect(subject.persisted?).to be_truthy }
|
||||
|
||||
context "in repetition" do
|
||||
let(:type_de_champ_public) { dossier.find_type_de_champ_by_stable_id(994) }
|
||||
let(:row_id) { dossier.project_champ(type_de_champ_repetition, nil).row_ids.first }
|
||||
let(:row_id) { dossier.project_champ(type_de_champ_repetition).row_ids.first }
|
||||
|
||||
it {
|
||||
expect(subject.persisted?).to be_truthy
|
||||
expect(subject.new_record?).to be_truthy
|
||||
expect(subject.row_id).to eq(row_id)
|
||||
}
|
||||
|
||||
|
@ -90,7 +88,7 @@ RSpec.describe DossierChampsConcern do
|
|||
end
|
||||
|
||||
context "private champ" do
|
||||
subject { dossier.project_champ(type_de_champ_private, nil) }
|
||||
subject { dossier.project_champ(type_de_champ_private) }
|
||||
|
||||
it { expect(subject.persisted?).to be_truthy }
|
||||
|
||||
|
@ -130,10 +128,11 @@ RSpec.describe DossierChampsConcern do
|
|||
{ type: :explication }
|
||||
]
|
||||
end
|
||||
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
|
||||
subject { dossier.filled_champs_public }
|
||||
|
||||
it { expect(subject.size).to eq(4) }
|
||||
it { expect(subject.find { _1.libelle == 'Nom' }).to be_truthy }
|
||||
it { expect(subject.size).to eq(5) }
|
||||
it { expect(subject.filter { _1.libelle == 'Nom' }.size).to eq(2) }
|
||||
end
|
||||
|
||||
describe '#filled_champs_private' do
|
||||
|
@ -156,14 +155,8 @@ RSpec.describe DossierChampsConcern do
|
|||
it { expect(subject.size).to eq(1) }
|
||||
|
||||
context 'given a type de champ repetition in another revision' do
|
||||
let(:procedure) { create(:procedure, :published, types_de_champ_public:, types_de_champ_private:) }
|
||||
let(:draft) { procedure.draft_revision }
|
||||
let(:errored_stable_id) { 666 }
|
||||
let(:type_de_champ_repetition) { procedure.active_revision.types_de_champ.find { _1.stable_id == errored_stable_id } }
|
||||
before do
|
||||
dossier
|
||||
tdc_repetition = draft.add_type_de_champ(type_champ: :repetition, libelle: "repetition", stable_id: errored_stable_id)
|
||||
draft.add_type_de_champ(type_champ: :text, libelle: "t1", parent_stable_id: tdc_repetition.stable_id)
|
||||
procedure.draft_revision.remove_type_de_champ(type_de_champ_repetition.stable_id)
|
||||
procedure.publish_revision!
|
||||
end
|
||||
|
||||
|
@ -232,7 +225,7 @@ RSpec.describe DossierChampsConcern do
|
|||
let(:row_id) { nil }
|
||||
|
||||
context "public champ" do
|
||||
subject { dossier.champ_for_update(type_de_champ_public, row_id, updated_by: dossier.user.email) }
|
||||
subject { dossier.champ_for_update(type_de_champ_public, row_id:, updated_by: dossier.user.email) }
|
||||
|
||||
it {
|
||||
expect(subject.persisted?).to be_truthy
|
||||
|
@ -290,7 +283,7 @@ RSpec.describe DossierChampsConcern do
|
|||
end
|
||||
|
||||
context "private champ" do
|
||||
subject { dossier.champ_for_update(type_de_champ_private, row_id, updated_by: dossier.user.email) }
|
||||
subject { dossier.champ_for_update(type_de_champ_private, row_id:, updated_by: dossier.user.email) }
|
||||
|
||||
it {
|
||||
expect(subject.persisted?).to be_truthy
|
||||
|
@ -311,9 +304,9 @@ RSpec.describe DossierChampsConcern do
|
|||
}
|
||||
end
|
||||
|
||||
let(:champ_99) { dossier.project_champ(dossier.find_type_de_champ_by_stable_id(99), nil) }
|
||||
let(:champ_991) { dossier.project_champ(dossier.find_type_de_champ_by_stable_id(991), nil) }
|
||||
let(:champ_994) { dossier.project_champ(dossier.find_type_de_champ_by_stable_id(994), row_id) }
|
||||
let(:champ_99) { dossier.project_champ(dossier.find_type_de_champ_by_stable_id(99)) }
|
||||
let(:champ_991) { dossier.project_champ(dossier.find_type_de_champ_by_stable_id(991)) }
|
||||
let(:champ_994) { dossier.project_champ(dossier.find_type_de_champ_by_stable_id(994), row_id:) }
|
||||
|
||||
subject { dossier.update_champs_attributes(attributes, :public, updated_by: dossier.user.email) }
|
||||
|
||||
|
@ -372,7 +365,7 @@ RSpec.describe DossierChampsConcern do
|
|||
}
|
||||
end
|
||||
|
||||
let(:annotation_995) { dossier.project_champ(dossier.find_type_de_champ_by_stable_id(995), nil) }
|
||||
let(:annotation_995) { dossier.project_champ(dossier.find_type_de_champ_by_stable_id(995)) }
|
||||
|
||||
subject { dossier.update_champs_attributes(attributes, :private, updated_by: dossier.user.email) }
|
||||
|
||||
|
|
|
@ -135,8 +135,8 @@ RSpec.describe DossierCloneConcern do
|
|||
|
||||
context 'for Champs::Repetition with rows, original_champ.repetition and rows are duped' do
|
||||
let(:types_de_champ_public) { [{ type: :repetition, children: [{}, {}] }] }
|
||||
let(:champ_repetition) { dossier.champs.find(&:repetition?) }
|
||||
let(:cloned_champ_repetition) { new_dossier.champs.find(&:repetition?) }
|
||||
let(:champ_repetition) { dossier.project_champs_public.find(&:repetition?) }
|
||||
let(:cloned_champ_repetition) { new_dossier.project_champs_public.find(&:repetition?) }
|
||||
|
||||
it do
|
||||
expect(cloned_champ_repetition.rows.flatten.count).to eq(4)
|
||||
|
@ -308,7 +308,7 @@ RSpec.describe DossierCloneConcern do
|
|||
end
|
||||
|
||||
context 'with new revision' do
|
||||
let(:added_champ) { forked_dossier.champs.find { _1.libelle == "Un nouveau champ text" } }
|
||||
let(:added_champ) { forked_dossier.project_champs_public.find { _1.libelle == "Un nouveau champ text" } }
|
||||
let(:removed_champ) { dossier.champs.find { _1.stable_id == 99 } }
|
||||
let(:new_dossier) { dossier.clone }
|
||||
|
||||
|
@ -325,12 +325,16 @@ RSpec.describe DossierCloneConcern do
|
|||
expect(dossier.revision_id).to eq(procedure.revisions.first.id)
|
||||
expect(new_dossier.revision_id).to eq(procedure.published_revision.id)
|
||||
expect(forked_dossier.revision_id).to eq(procedure.published_revision_id)
|
||||
is_expected.to eq(added: [added_champ], updated: [], removed: [removed_champ])
|
||||
expect(subject[:added].map(&:stable_id)).to eq([added_champ.stable_id])
|
||||
expect(subject[:added].first.new_record?).to be_truthy
|
||||
expect(subject[:updated]).to be_empty
|
||||
expect(subject[:removed]).to eq([removed_champ])
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe '#merge_fork' do
|
||||
let(:dossier) { create(:dossier, :en_construction, :with_populated_champs, procedure:) }
|
||||
subject { dossier.merge_fork(forked_dossier) }
|
||||
|
||||
context 'with updated champ' do
|
||||
|
@ -348,11 +352,11 @@ RSpec.describe DossierCloneConcern do
|
|||
dossier.debounce_index_search_terms_flag.remove
|
||||
end
|
||||
|
||||
it { expect { subject }.to change { dossier.champs.size }.by(0) }
|
||||
it { expect { subject }.to change { dossier.champs.reload.size }.by(0) }
|
||||
it { expect { subject }.not_to change { dossier.champs.order(:created_at).reject { _1.stable_id.in?([99, 994]) }.map(&:value) } }
|
||||
it { expect { subject }.to have_enqueued_job(DossierIndexSearchTermsJob).with(dossier) }
|
||||
it { expect { subject }.to change { dossier.champs.find { _1.stable_id == 99 }.value }.from('old value').to('new value') }
|
||||
it { expect { subject }.to change { dossier.champs.find { _1.stable_id == 994 }.value }.from('old value').to('new value in repetition') }
|
||||
it { expect { subject }.to change { dossier.reload.champs.find { _1.stable_id == 994 }.value }.from('old value').to('new value in repetition') }
|
||||
|
||||
it 'fork is hidden after merge' do
|
||||
subject
|
||||
|
@ -362,8 +366,16 @@ RSpec.describe DossierCloneConcern do
|
|||
end
|
||||
|
||||
context 'with new revision' do
|
||||
let(:added_champ) { forked_dossier.champs.find { _1.libelle == "Un nouveau champ text" } }
|
||||
let(:added_repetition_champ) { forked_dossier.champs.find { _1.libelle == "Texte en répétition" } }
|
||||
let(:added_champ) {
|
||||
tdc = forked_dossier.revision.types_de_champ.find { _1.libelle == "Un nouveau champ text" }
|
||||
forked_dossier.champ_for_update(tdc, updated_by: 'test')
|
||||
}
|
||||
let(:added_repetition_champ) {
|
||||
tdc_repetition = forked_dossier.revision.types_de_champ.find { _1.stable_id == 993 }
|
||||
tdc = forked_dossier.revision.types_de_champ.find { _1.libelle == "Texte en répétition" }
|
||||
row_id = forked_dossier.repetition_row_ids(tdc_repetition).first
|
||||
forked_dossier.champ_for_update(tdc, row_id:, updated_by: 'test')
|
||||
}
|
||||
let(:removed_champ) { dossier.champs.find { _1.stable_id == 99 } }
|
||||
let(:updated_champ) { dossier.champs.find { _1.stable_id == 991 } }
|
||||
|
||||
|
@ -393,8 +405,8 @@ RSpec.describe DossierCloneConcern do
|
|||
super()
|
||||
}
|
||||
|
||||
it { expect { subject }.to change { dossier.champs.size }.by(1) }
|
||||
it { expect { subject }.to change { dossier.champs.order(:created_at).map(&:to_s) }.from(['old value', 'old value', 'Non', 'old value', 'old value']).to(['new value for updated champ', 'Non', 'old value', 'old value', 'new value for added champ', 'new value in repetition champ']) }
|
||||
it { expect { subject }.to change { dossier.filled_champs.size }.by(1) }
|
||||
it { expect { subject }.to change { dossier.filled_champs.sort_by(&:created_at).map(&:to_s) }.from(['old value', 'old value', 'Non', 'old value', 'old value']).to(['new value for updated champ', 'Non', 'old value', 'old value', 'new value for added champ', 'new value in repetition champ']) }
|
||||
|
||||
it "dossier after merge should be on last published revision" do
|
||||
expect(dossier.revision_id).to eq(procedure.revisions.first.id)
|
||||
|
@ -404,13 +416,13 @@ RSpec.describe DossierCloneConcern do
|
|||
perform_enqueued_jobs only: DestroyRecordLaterJob
|
||||
|
||||
expect(dossier.revision_id).to eq(procedure.published_revision_id)
|
||||
expect(dossier.champs.all? { dossier.revision.in?(_1.type_de_champ.revisions) }).to be_truthy
|
||||
expect(dossier.filled_champs.all? { dossier.revision.in?(_1.type_de_champ.revisions) }).to be_truthy
|
||||
expect(Dossier.exists?(forked_dossier.id)).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
context 'with old revision having repetition' do
|
||||
let(:removed_champ) { dossier.champs.find(&:repetition?) }
|
||||
let(:removed_champ) { dossier.project_champs_public.find(&:repetition?) }
|
||||
|
||||
before do
|
||||
dossier.champs.each do |champ|
|
||||
|
@ -423,5 +435,42 @@ RSpec.describe DossierCloneConcern do
|
|||
expect { subject }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
context 'with added row' do
|
||||
let(:repetition_champ) { forked_dossier.project_champs_public.find(&:repetition?) }
|
||||
|
||||
def dossier_rows(dossier) = dossier.champs.filter(&:row?)
|
||||
|
||||
before do
|
||||
repetition_champ.add_row(updated_by: 'test')
|
||||
end
|
||||
|
||||
it {
|
||||
expect(dossier_rows(dossier).size).to eq(2)
|
||||
expect { subject }.to change { dossier_rows(dossier).size }.by(1)
|
||||
}
|
||||
end
|
||||
|
||||
context 'with removed row' do
|
||||
let(:repetition_champ) { forked_dossier.project_champs_public.find(&:repetition?) }
|
||||
let(:row_id) { repetition_champ.row_ids.first }
|
||||
|
||||
def dossier_rows(dossier) = dossier.champs.filter(&:row?)
|
||||
def dossier_discarded_rows(dossier) = dossier_rows(dossier).filter(&:discarded?)
|
||||
|
||||
before do
|
||||
repetition_champ.remove_row(row_id, updated_by: 'test')
|
||||
end
|
||||
|
||||
it {
|
||||
expect(dossier_rows(dossier).size).to eq(2)
|
||||
expect { subject }.to change { dossier_rows(dossier).size }.by(0)
|
||||
}
|
||||
|
||||
it {
|
||||
expect(dossier_discarded_rows(dossier).size).to eq(0)
|
||||
expect { subject }.to change { dossier_discarded_rows(dossier).size }.by(1)
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -356,7 +356,7 @@ describe DossierRebaseConcern do
|
|||
it "updates the brouillon champs with the latest revision changes" do
|
||||
expect(dossier.revision).to eq(procedure.published_revision)
|
||||
expect(dossier.project_champs_public.size).to eq(5)
|
||||
expect(dossier.champs.count(&:public?)).to eq(7)
|
||||
expect(dossier.champs.count(&:public?)).to eq(6)
|
||||
expect(repetition_champ.rows.size).to eq(2)
|
||||
expect(repetition_champ.rows[0].size).to eq(1)
|
||||
expect(repetition_champ.rows[1].size).to eq(1)
|
||||
|
@ -369,7 +369,7 @@ describe DossierRebaseConcern do
|
|||
expect(procedure.revisions.size).to eq(3)
|
||||
expect(dossier.revision).to eq(procedure.published_revision)
|
||||
expect(dossier.project_champs_public.size).to eq(7)
|
||||
expect(dossier.champs.count(&:public?)).to eq(13)
|
||||
expect(dossier.champs.count(&:public?)).to eq(7)
|
||||
expect(rebased_text_champ.value).to eq(text_champ.value)
|
||||
expect(rebased_text_champ.type_de_champ).not_to eq(text_champ.type_de_champ)
|
||||
expect(rebased_datetime_champ.type_champ).to eq(TypeDeChamp.type_champs.fetch(:date))
|
||||
|
@ -381,7 +381,6 @@ describe DossierRebaseConcern do
|
|||
expect(rebased_datetime_champ.rebased_at).not_to be_nil
|
||||
expect(rebased_number_champ.rebased_at).to be_nil
|
||||
expect(rebased_new_repetition_champ).not_to be_nil
|
||||
expect(rebased_new_repetition_champ.rebased_at).not_to be_nil
|
||||
expect(rebased_new_repetition_champ.rows.size).to eq(1)
|
||||
expect(rebased_new_repetition_champ.rows[0].size).to eq(2)
|
||||
|
||||
|
@ -728,9 +727,8 @@ describe DossierRebaseConcern do
|
|||
parent.update(type_champ: :integer_number)
|
||||
end
|
||||
|
||||
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 { dossier.project_champs_public.find(&:repetition?)&.libelle }.from('p1').to(nil) }
|
||||
it { expect { subject }.not_to change { Champ.count } }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -72,7 +72,7 @@ describe DossierSectionsConcern do
|
|||
context "when there are invisible sections" do
|
||||
it "index accordingly header sections" do
|
||||
expect(dossier.index_for_section_header(headers[0])).to eq(1)
|
||||
expect(dossier.project_champ(headers[1], nil)).not_to be_visible
|
||||
expect(dossier.project_champ(headers[1])).not_to be_visible
|
||||
expect(dossier.index_for_section_header(headers[2])).to eq(2)
|
||||
end
|
||||
end
|
||||
|
@ -81,7 +81,7 @@ describe DossierSectionsConcern do
|
|||
let(:number_value) { 5 }
|
||||
it "index accordingly header sections" do
|
||||
expect(dossier.index_for_section_header(headers[0])).to eq(1)
|
||||
expect(dossier.project_champ(headers[1], nil)).to be_visible
|
||||
expect(dossier.project_champ(headers[1])).to be_visible
|
||||
expect(dossier.index_for_section_header(headers[1])).to eq(2)
|
||||
expect(dossier.index_for_section_header(headers[2])).to eq(3)
|
||||
end
|
||||
|
|
149
spec/models/concerns/dossier_state_concern_spec.rb
Normal file
149
spec/models/concerns/dossier_state_concern_spec.rb
Normal file
|
@ -0,0 +1,149 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe DossierStateConcern do
|
||||
include Logic
|
||||
|
||||
let(:procedure) { create(:procedure, :published, :for_individual, types_de_champ_public:, declarative_with_state:) }
|
||||
let(:types_de_champ_public) do
|
||||
[
|
||||
{ type: :text, stable_id: 90 },
|
||||
{ type: :text, stable_id: 91 },
|
||||
{ type: :piece_justificative, stable_id: 92, condition: ds_eq(constant(true), constant(false)) },
|
||||
{ type: :titre_identite, stable_id: 93, condition: ds_eq(constant(true), constant(false)) },
|
||||
{ type: :repetition, stable_id: 94, children: [{ type: :text, stable_id: 941 }, { type: :text, stable_id: 942 }] },
|
||||
{ type: :repetition, stable_id: 95, children: [{ type: :text, stable_id: 951 }] },
|
||||
{ type: :repetition, stable_id: 96, children: [{ type: :text, stable_id: 961 }], condition: ds_eq(constant(true), constant(false)) },
|
||||
{ type: :text, stable_id: 97, condition: ds_eq(constant(true), constant(false)) },
|
||||
{ type: :titre_identite, stable_id: 98 }
|
||||
]
|
||||
end
|
||||
let(:declarative_with_state) { nil }
|
||||
let(:dossier_state) { :brouillon }
|
||||
let(:dossier) do
|
||||
create(:dossier, dossier_state, :with_individual, :with_populated_champs, procedure:).tap do |dossier|
|
||||
procedure.draft_revision.remove_type_de_champ(91)
|
||||
procedure.draft_revision.remove_type_de_champ(95)
|
||||
procedure.draft_revision.remove_type_de_champ(942)
|
||||
procedure.publish_revision!
|
||||
perform_enqueued_jobs
|
||||
dossier.reload
|
||||
champ_repetition = dossier.project_champs_public.find { _1.stable_id == 94 }
|
||||
row_id = champ_repetition.row_ids.first
|
||||
champ_repetition.remove_row(row_id, updated_by: 'test')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'submit brouillon' do
|
||||
it do
|
||||
expect(dossier.champs.size).to eq(20)
|
||||
expect(dossier.champs.filter { _1.row? && _1.stable_id == 94 }.size).to eq(2)
|
||||
expect(dossier.champs.filter { _1.row? && _1.discarded? }.size).to eq(1)
|
||||
expect(dossier.champs.filter { _1.row? && _1.stable_id.in?([95, 96]) }.size).to eq(4)
|
||||
expect(dossier.champs.filter { _1.stable_id.in?([90, 92, 93, 97, 961, 951]) }.size).to eq(8)
|
||||
|
||||
champ_text = dossier.project_champs_public.find { _1.stable_id == 90 }
|
||||
champ_text.update(value: '')
|
||||
|
||||
dossier.passer_en_construction!
|
||||
dossier.reload
|
||||
|
||||
expect(dossier.champs.size).to eq(3)
|
||||
expect(dossier.champs.filter { _1.row? && _1.stable_id == 94 }.size).to eq(1)
|
||||
expect(dossier.champs.filter { _1.row? && _1.discarded? }.size).to eq(0)
|
||||
expect(dossier.champs.filter { _1.row? && _1.stable_id.in?([95, 96]) }.size).to eq(0)
|
||||
expect(dossier.champs.filter { _1.stable_id.in?([90, 92, 93, 97, 961, 951]) }.size).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'submit en construction' do
|
||||
let(:dossier_state) { :en_construction }
|
||||
|
||||
it do
|
||||
expect(dossier.champs.size).to eq(20)
|
||||
expect(dossier.champs.filter { _1.row? && _1.stable_id == 94 }.size).to eq(2)
|
||||
expect(dossier.champs.filter { _1.row? && _1.discarded? }.size).to eq(1)
|
||||
expect(dossier.champs.filter { _1.row? && _1.stable_id.in?([95, 96]) }.size).to eq(4)
|
||||
expect(dossier.champs.filter { _1.stable_id.in?([92, 93, 97, 961, 951]) }.size).to eq(7)
|
||||
|
||||
dossier.submit_en_construction!
|
||||
dossier.reload
|
||||
|
||||
expect(dossier.champs.size).to eq(4)
|
||||
expect(dossier.champs.filter { _1.row? && _1.stable_id == 94 }.size).to eq(1)
|
||||
expect(dossier.champs.filter { _1.row? && _1.discarded? }.size).to eq(0)
|
||||
expect(dossier.champs.filter { _1.row? && _1.stable_id.in?([95, 96]) }.size).to eq(0)
|
||||
expect(dossier.champs.filter { _1.stable_id.in?([92, 93, 97, 961, 951]) }.size).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'accepter' do
|
||||
let(:dossier_state) { :en_instruction }
|
||||
|
||||
it do
|
||||
expect(dossier.champs.size).to eq(20)
|
||||
expect(dossier.champs.filter { _1.row? && _1.stable_id == 94 }.size).to eq(2)
|
||||
expect(dossier.champs.filter { _1.stable_id.in?([93, 98]) }.size).to eq(2)
|
||||
|
||||
dossier.accepter!(motivation: 'test')
|
||||
dossier.reload
|
||||
|
||||
expect(dossier.champs.size).to eq(15)
|
||||
expect(dossier.champs.filter { _1.row? && _1.stable_id == 94 }.size).to eq(1)
|
||||
expect(dossier.champs.filter { _1.stable_id.in?([93, 98]) }.size).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'refuser' do
|
||||
let(:dossier_state) { :en_instruction }
|
||||
|
||||
it do
|
||||
expect(dossier.champs.size).to eq(20)
|
||||
expect(dossier.champs.filter { _1.row? && _1.stable_id == 94 }.size).to eq(2)
|
||||
expect(dossier.champs.filter { _1.stable_id.in?([93, 98]) }.size).to eq(2)
|
||||
|
||||
dossier.refuser!(motivation: 'test')
|
||||
dossier.reload
|
||||
|
||||
expect(dossier.champs.size).to eq(15)
|
||||
expect(dossier.champs.filter { _1.row? && _1.stable_id == 94 }.size).to eq(1)
|
||||
expect(dossier.champs.filter { _1.stable_id.in?([93, 98]) }.size).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'classer_sans_suite' do
|
||||
let(:dossier_state) { :en_instruction }
|
||||
|
||||
it do
|
||||
expect(dossier.champs.size).to eq(20)
|
||||
expect(dossier.champs.filter { _1.row? && _1.stable_id == 94 }.size).to eq(2)
|
||||
expect(dossier.champs.filter { _1.stable_id.in?([93, 98]) }.size).to eq(2)
|
||||
|
||||
dossier.classer_sans_suite!(motivation: 'test')
|
||||
dossier.reload
|
||||
|
||||
expect(dossier.champs.size).to eq(15)
|
||||
expect(dossier.champs.filter { _1.row? && _1.stable_id == 94 }.size).to eq(1)
|
||||
expect(dossier.champs.filter { _1.stable_id.in?([93, 98]) }.size).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'automatiquement' do
|
||||
let(:dossier_state) { :en_construction }
|
||||
let(:declarative_with_state) { Dossier.states.fetch(:accepte) }
|
||||
|
||||
describe 'accepter' do
|
||||
it do
|
||||
expect(dossier.champs.size).to eq(20)
|
||||
expect(dossier.champs.filter { _1.row? && _1.stable_id == 94 }.size).to eq(2)
|
||||
expect(dossier.champs.filter { _1.stable_id.in?([93, 98]) }.size).to eq(2)
|
||||
|
||||
dossier.accepter_automatiquement!
|
||||
dossier.reload
|
||||
|
||||
expect(dossier.champs.size).to eq(15)
|
||||
expect(dossier.champs.filter { _1.row? && _1.stable_id == 94 }.size).to eq(1)
|
||||
expect(dossier.champs.filter { _1.stable_id.in?([93, 98]) }.size).to eq(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -237,11 +237,11 @@ describe TagsSubstitutionConcern, type: :model do
|
|||
repetition.add_row(updated_by: 'test')
|
||||
paul_champs, pierre_champs = repetition.rows
|
||||
|
||||
paul_champs.first.update(value: 'Paul')
|
||||
paul_champs.last.update(value: 'Chavard')
|
||||
champ_for_update(paul_champs.first).update(value: 'Paul')
|
||||
champ_for_update(paul_champs.last).update(value: 'Chavard')
|
||||
|
||||
pierre_champs.first.update(value: 'Pierre')
|
||||
pierre_champs.last.update(value: 'de La Morinerie')
|
||||
champ_for_update(pierre_champs.first).update(value: 'Pierre')
|
||||
champ_for_update(pierre_champs.last).update(value: 'de La Morinerie')
|
||||
end
|
||||
|
||||
it { is_expected.to eq("Répétition\n\nNom : Paul\nPrénom : Chavard\n\nNom : Pierre\nPrénom : de La Morinerie") }
|
||||
|
@ -258,7 +258,7 @@ describe TagsSubstitutionConcern, type: :model do
|
|||
|
||||
context 'and the champ has a primary value' do
|
||||
before do
|
||||
dossier.champs.find_by(stable_id: type_de_champ.stable_id).update(primary_value: 'primo')
|
||||
dossier.champ_for_update(type_de_champ, updated_by: 'test').update(primary_value: 'primo')
|
||||
dossier.reload
|
||||
end
|
||||
|
||||
|
@ -266,7 +266,7 @@ describe TagsSubstitutionConcern, type: :model do
|
|||
|
||||
context 'and the champ has a secondary value' do
|
||||
before do
|
||||
dossier.champs.find_by(stable_id: type_de_champ.stable_id).update(secondary_value: 'secundo')
|
||||
dossier.champ_for_update(type_de_champ, updated_by: 'test').update(secondary_value: 'secundo')
|
||||
dossier.reload
|
||||
end
|
||||
|
||||
|
@ -481,7 +481,7 @@ describe TagsSubstitutionConcern, type: :model do
|
|||
before do
|
||||
draft_type_de_champ.update(libelle: 'mon nouveau libellé')
|
||||
dossier.project_champs_public.first.update(value: 'valeur')
|
||||
procedure.update!(draft_revision: procedure.create_new_revision, published_revision: procedure.draft_revision)
|
||||
procedure.publish_revision!
|
||||
end
|
||||
|
||||
context "when using the champ's original label" do
|
||||
|
|
|
@ -29,7 +29,7 @@ describe DossierPreloader do
|
|||
expect(subject.changed?).to be false
|
||||
|
||||
expect(first_child.type).to eq('Champs::TextChamp')
|
||||
expect(repetition.id).not_to eq(first_child.id)
|
||||
expect(repetition).not_to eq(first_child)
|
||||
expect(subject.champs.first.dossier).to eq(subject)
|
||||
expect(subject.champs.find(&:public?).dossier).to eq(subject)
|
||||
expect(subject.project_champs_public.first.dossier).to eq(subject)
|
||||
|
@ -40,7 +40,7 @@ describe DossierPreloader do
|
|||
expect(subject.champs.find(&:public?).conditional?).to eq(false)
|
||||
expect(subject.project_champs_public.first.conditional?).to eq(false)
|
||||
|
||||
expect(repetition.rows.first.first).to eq(first_child)
|
||||
expect(repetition.rows.first.first.public_id).to eq(first_child.public_id)
|
||||
expect(repetition_optional.row_ids).to be_empty
|
||||
end
|
||||
|
||||
|
|
|
@ -501,15 +501,13 @@ describe Dossier, type: :model do
|
|||
let(:procedure) { create(:procedure, types_de_champ_public:) }
|
||||
let(:dossier) { create(:dossier, :brouillon, :with_populated_champs, procedure:) }
|
||||
|
||||
before { expect(champ).to receive(:visible?).and_return(visible) }
|
||||
|
||||
context 'when piece_justificative' do
|
||||
let(:types_de_champ_public) { [{ type: :piece_justificative }] }
|
||||
let(:types_de_champ_public) { [{ type: :piece_justificative, condition: ds_eq(constant(true), constant(visible)) }] }
|
||||
let(:champ) { dossier.project_champs_public.find(&:piece_justificative?) }
|
||||
|
||||
context 'when not visible' do
|
||||
let(:visible) { false }
|
||||
it { expect { subject }.to change { champ.reload.piece_justificative_file.attached? } }
|
||||
it { expect { subject }.to change { Champ.exists?(champ.id) } }
|
||||
end
|
||||
|
||||
context 'when visible' do
|
||||
|
@ -519,12 +517,12 @@ describe Dossier, type: :model do
|
|||
end
|
||||
|
||||
context 'when titre identite' do
|
||||
let(:types_de_champ_public) { [{ type: :titre_identite }] }
|
||||
let(:types_de_champ_public) { [{ type: :titre_identite, condition: ds_eq(constant(true), constant(visible)) }] }
|
||||
let(:champ) { dossier.project_champs_public.find(&:titre_identite?) }
|
||||
|
||||
context 'when not visible' do
|
||||
let(:visible) { false }
|
||||
it { expect { subject }.to change { champ.reload.piece_justificative_file.attached? } }
|
||||
it { expect { subject }.to change { Champ.exists?(champ.id) } }
|
||||
end
|
||||
|
||||
context 'when visible' do
|
||||
|
@ -1986,13 +1984,12 @@ describe Dossier, type: :model do
|
|||
context 'with integer_number' do
|
||||
let(:procedure) { create(:procedure, :published, types_de_champ_public: [{ type: :integer_number, libelle: 'c1' }]) }
|
||||
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
|
||||
let(:integer_number_type_de_champ) { procedure.active_revision.types_de_champ_public.find { |type_de_champ| type_de_champ.type_champ == TypeDeChamp.type_champs.fetch(:integer_number) } }
|
||||
let(:integer_number_type_de_champ) { procedure.active_revision.types_de_champ_public.find(&:integer_number?) }
|
||||
|
||||
it 'give me back my decimal number' do
|
||||
dossier
|
||||
expect {
|
||||
integer_number_type_de_champ.update(type_champ: :decimal_number)
|
||||
procedure.update(published_revision: procedure.draft_revision, draft_revision: procedure.create_new_revision)
|
||||
}.to change { dossier.reload.champ_values_for_export(procedure.all_revisions_types_de_champ.not_repetition.to_a, format: :xlsx) }
|
||||
.from([["c1", 42]]).to([["c1", 42.0]])
|
||||
end
|
||||
|
@ -2032,7 +2029,7 @@ describe Dossier, type: :model do
|
|||
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(repetition_type_de_champ.stable_id).update(libelle: 'Repetition')
|
||||
procedure.update(published_revision: procedure.draft_revision, draft_revision: procedure.create_new_revision)
|
||||
procedure.publish_revision!
|
||||
dossier.reload
|
||||
procedure.reload
|
||||
end
|
||||
|
|
|
@ -17,7 +17,7 @@ describe TypeDeChamp do
|
|||
|
||||
it do
|
||||
dossier.revision.types_de_champ_public.each do |type_de_champ|
|
||||
champ = dossier.project_champ(type_de_champ, nil)
|
||||
champ = dossier.project_champ(type_de_champ)
|
||||
expect(type_de_champ.dynamic_type.class.name).to match(/^TypesDeChamp::/)
|
||||
expect(champ.class.name).to match(/^Champs::/)
|
||||
end
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
RSpec.describe TypesDeChamp::PrefillRepetitionTypeDeChamp, type: :model do
|
||||
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :repetition, children: [{}, { type: :integer_number }, { type: :regions }] }]) }
|
||||
let(:dossier) { create(:dossier, procedure: procedure) }
|
||||
let(:champ) { dossier.project_champs_public.first }
|
||||
let(:type_de_champ) { champ.type_de_champ }
|
||||
let(:champ) { dossier.champs.first }
|
||||
let(:prefillable_subchamps) { TypesDeChamp::PrefillRepetitionTypeDeChamp.new(type_de_champ, procedure.active_revision).send(:prefillable_subchamps) }
|
||||
let(:text_repetition) { prefillable_subchamps.first }
|
||||
let(:integer_repetition) { prefillable_subchamps.second }
|
||||
let(:region_repetition) { prefillable_subchamps.third }
|
||||
let(:text_repetition_champs) { dossier.champs.where(stable_id: text_repetition.stable_id) }
|
||||
let(:integer_repetition_champs) { dossier.champs.where(stable_id: integer_repetition.stable_id) }
|
||||
let(:text_repetition_champs) { champ.rows.flat_map(&:first) }
|
||||
let(:integer_repetition_champs) { champ.rows.flat_map(&:second) }
|
||||
|
||||
describe 'ancestors' do
|
||||
subject { described_class.build(type_de_champ, procedure.active_revision) }
|
||||
|
|
|
@ -125,6 +125,13 @@ RSpec.configure do |config|
|
|||
end
|
||||
end
|
||||
|
||||
module SpecHelpers
|
||||
def champ_for_update(champ)
|
||||
champ.dossier.champ_for_update(champ.type_de_champ, row_id: champ.row_id, updated_by: 'test')
|
||||
end
|
||||
end
|
||||
|
||||
config.include SpecHelpers
|
||||
config.include ActiveSupport::Testing::TimeHelpers
|
||||
config.include Shoulda::Matchers::ActiveRecord, type: :model
|
||||
config.include Shoulda::Matchers::ActiveModel, type: :model
|
||||
|
|
|
@ -10,8 +10,8 @@ describe PiecesJustificativesService do
|
|||
let(:pj_service) { PiecesJustificativesService.new(user_profile:, export_template:) }
|
||||
let(:user_profile) { build(:administrateur) }
|
||||
|
||||
def pj_champ(d) = d.project_champs_public.find { _1.type == 'Champs::PieceJustificativeChamp' }
|
||||
def repetition(d) = d.champs.find_by(type: "Champs::RepetitionChamp")
|
||||
def pj_champ(d) = d.project_champs_public.find(&:piece_justificative?)
|
||||
def repetition(d) = d.project_champs_public.find(&:repetition?)
|
||||
def attachments(champ) = champ.piece_justificative_file.attachments
|
||||
|
||||
before { attach_file_to_champ(pj_champ(witness)) }
|
||||
|
@ -52,21 +52,20 @@ describe PiecesJustificativesService do
|
|||
end
|
||||
|
||||
context 'with a repetition' do
|
||||
let(:first_champ) { repetition(dossier).rows.first.first }
|
||||
let(:second_champ) { repetition(dossier).rows.second.first }
|
||||
let(:first_champ) { champ_for_update(repetition(dossier).rows.first.first) }
|
||||
let(:second_champ) { champ_for_update(repetition(dossier).rows.second.first) }
|
||||
|
||||
before do
|
||||
repetition(dossier).add_row(updated_by: 'test')
|
||||
attach_file_to_champ(first_champ)
|
||||
attach_file_to_champ(first_champ)
|
||||
|
||||
repetition(dossier).add_row(updated_by: 'test')
|
||||
attach_file_to_champ(first_champ)
|
||||
attach_file_to_champ(first_champ)
|
||||
attach_file_to_champ(second_champ)
|
||||
end
|
||||
|
||||
it do
|
||||
first_child_attachments = attachments(repetition(dossier).rows.first.first)
|
||||
second_child_attachments = attachments(repetition(dossier).rows.second.first)
|
||||
first_child_attachments = attachments(first_champ)
|
||||
second_child_attachments = attachments(second_champ)
|
||||
|
||||
expect(export_template).to receive(:attachment_path)
|
||||
.with(dossier, first_child_attachments.first, index: 0, row_index: 0, champ: first_champ)
|
||||
|
@ -103,7 +102,7 @@ describe PiecesJustificativesService do
|
|||
let(:user_profile) { build(:administrateur) }
|
||||
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :piece_justificative }]) }
|
||||
let(:witness) { create(:dossier, procedure: procedure) }
|
||||
def pj_champ(d) = d.project_champs_public.find { |c| c.type == 'Champs::PieceJustificativeChamp' }
|
||||
def pj_champ(d) = d.project_champs_public.find(&:piece_justificative?)
|
||||
|
||||
context 'with a single attachment' do
|
||||
before do
|
||||
|
@ -144,7 +143,7 @@ describe PiecesJustificativesService do
|
|||
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :titre_identite }]) }
|
||||
let(:dossier) { create(:dossier, procedure: procedure) }
|
||||
|
||||
let(:champ_identite) { dossier.project_champs_public.find { |c| c.type == 'Champs::TitreIdentiteChamp' } }
|
||||
let(:champ_identite) { dossier.project_champs_public.find(&:titre_identite?) }
|
||||
|
||||
before { attach_file_to_champ(champ_identite) }
|
||||
|
||||
|
@ -261,7 +260,7 @@ describe PiecesJustificativesService do
|
|||
let(:witness) { create(:dossier, procedure: procedure) }
|
||||
|
||||
let!(:private_pj) { create(:type_de_champ_piece_justificative, procedure: procedure, private: true) }
|
||||
def private_pj_champ(d) = d.project_champs_private.find { |c| c.type == 'Champs::PieceJustificativeChamp' }
|
||||
def private_pj_champ(d) = d.project_champs_private.find(&:piece_justificative?)
|
||||
|
||||
before do
|
||||
attach_file_to_champ(private_pj_champ(dossier))
|
||||
|
@ -501,17 +500,14 @@ describe PiecesJustificativesService do
|
|||
end
|
||||
|
||||
let(:procedure) { create(:procedure, types_de_champ_public:) }
|
||||
let(:dossier_1) { create(:dossier, procedure:) }
|
||||
let(:champs) { dossier_1.champs }
|
||||
let(:dossier_1) { create(:dossier, :with_populated_champs, procedure:) }
|
||||
let(:champs) { dossier_1.filled_champs }
|
||||
|
||||
def pj_champ(d) = d.project_champs_public.find { _1.type == 'Champs::PieceJustificativeChamp' }
|
||||
def repetition(d, index:) = d.project_champs_public.filter(&:repetition?)[index]
|
||||
|
||||
subject { PiecesJustificativesService.new(user_profile:, export_template: nil).send(:compute_champ_id_row_index, champs) }
|
||||
|
||||
before do
|
||||
pj_champ(dossier_1)
|
||||
|
||||
# repet_0 (stable_id: r0)
|
||||
# # row_0
|
||||
# # # pj_champ_0 (stable_id: 0)
|
||||
|
@ -553,7 +549,9 @@ describe PiecesJustificativesService do
|
|||
end
|
||||
|
||||
def attach_file_to_champ(champ, safe = true)
|
||||
champ = champ_for_update(champ)
|
||||
attach_file(champ.piece_justificative_file, safe)
|
||||
champ.save!
|
||||
end
|
||||
|
||||
def attach_file(attachable, safe = true)
|
||||
|
|
|
@ -7,8 +7,8 @@ describe ProcedureExportService do
|
|||
let(:export_template) { create(:export_template, :enabled_pjs, groupe_instructeur: procedure.defaut_groupe_instructeur) }
|
||||
let(:service) { ProcedureExportService.new(procedure, procedure.dossiers, instructeur, export_template) }
|
||||
|
||||
def pj_champ(d) = d.project_champs_public.find { _1.type == 'Champs::PieceJustificativeChamp' }
|
||||
def repetition(d) = d.champs.find_by(type: "Champs::RepetitionChamp")
|
||||
def pj_champ(d) = d.project_champs_public.find(&:piece_justificative?)
|
||||
def repetition(d) = d.project_champs_public.find(&:repetition?)
|
||||
def attachments(champ) = champ.piece_justificative_file.attachments
|
||||
|
||||
before do
|
||||
|
@ -69,7 +69,9 @@ describe ProcedureExportService do
|
|||
end
|
||||
|
||||
def attach_file_to_champ(champ, safe = true)
|
||||
champ = champ_for_update(champ)
|
||||
attach_file(champ.piece_justificative_file, safe)
|
||||
champ.save!
|
||||
end
|
||||
|
||||
def attach_file(attachable, safe = true)
|
||||
|
|
|
@ -171,7 +171,7 @@ describe 'The user', js: true do
|
|||
wait_until { page.all(".row").size == 1 }
|
||||
# removing a repetition means one child only, thus its button destroy is not visible
|
||||
expect(page).to have_selector(".repetition .row:first-child .utils-repetition-required-destroy-button", count: 1, visible: false)
|
||||
end.to change { Champ.count }
|
||||
end.to change { Champ.where.not(discarded_at: nil).count }
|
||||
end
|
||||
|
||||
let(:simple_procedure) {
|
||||
|
@ -569,6 +569,16 @@ describe 'The user', js: true do
|
|||
expect(page).to have_no_css('legend', text: 'permis de conduire', visible: true)
|
||||
expect(page).to have_no_css('label', text: 'tonnage', visible: true)
|
||||
|
||||
fill_in('age du candidat', with: '18')
|
||||
wait_for_autosave
|
||||
|
||||
# the champ keeps their previous value so they are all displayed
|
||||
expect(page).to have_css('legend', text: 'permis de conduire', visible: true)
|
||||
expect(page).to have_css('label', text: 'tonnage', visible: true)
|
||||
|
||||
fill_in('age du candidat', with: '2')
|
||||
wait_for_autosave
|
||||
|
||||
click_on 'Déposer le dossier'
|
||||
click_on 'Accéder à votre dossier'
|
||||
click_on 'Modifier mon dossier'
|
||||
|
@ -576,13 +586,6 @@ describe 'The user', js: true do
|
|||
expect(page).to have_css('label', text: 'age du candidat', visible: true)
|
||||
expect(page).to have_no_css('legend', text: 'permis de conduire', visible: true)
|
||||
expect(page).to have_no_css('label', text: 'tonnage', visible: true)
|
||||
|
||||
fill_in('age du candidat', with: '18')
|
||||
wait_for_autosave
|
||||
|
||||
# the champ keeps their previous value so they are all displayed
|
||||
expect(page).to have_css('legend', text: 'permis de conduire', visible: true)
|
||||
expect(page).to have_css('label', text: 'tonnage', visible: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "rails_helper"
|
||||
|
||||
module Maintenance
|
||||
RSpec.describe RemovePieceJustificativeFileNotVisibleTask do
|
||||
describe "#process" do
|
||||
subject(:process) { described_class.process(dossier) }
|
||||
|
||||
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :piece_justificative }]) }
|
||||
let(:dossier) { create(:dossier, :en_construction, :with_populated_champs, procedure:) }
|
||||
|
||||
before { expect(champ).to receive(:visible?).and_return(visible) }
|
||||
|
||||
context 'when piece_justificative' do
|
||||
let(:champ) { dossier.filled_champs_public.find(&:piece_justificative?) }
|
||||
|
||||
context 'when not visible' do
|
||||
let(:visible) { false }
|
||||
it { expect { subject }.to change { champ.reload.piece_justificative_file.attached? } }
|
||||
end
|
||||
|
||||
context 'when visible' do
|
||||
let(:visible) { true }
|
||||
it { expect { subject }.not_to change { champ.reload.piece_justificative_file.attached? } }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -19,9 +19,9 @@ describe 'shared/dossiers/edit', type: :view do
|
|||
let(:type_de_champ_checkbox) { procedure.draft_types_de_champ_public.find(&:checkbox?) }
|
||||
let(:type_de_champ_textarea) { procedure.draft_types_de_champ_public.find(&:textarea?) }
|
||||
|
||||
let(:champ_checkbox) { dossier.project_champ(type_de_champ_checkbox, nil) }
|
||||
let(:champ_dossier_link) { dossier.project_champ(type_de_champ_dossier_link, nil) }
|
||||
let(:champ_textarea) { dossier.project_champ(type_de_champ_textarea, nil) }
|
||||
let(:champ_checkbox) { dossier.project_champ(type_de_champ_checkbox) }
|
||||
let(:champ_dossier_link) { dossier.project_champ(type_de_champ_dossier_link) }
|
||||
let(:champ_textarea) { dossier.project_champ(type_de_champ_textarea) }
|
||||
|
||||
let(:types_de_champ_public) { [{ type: :checkbox }, { type: :header_section }, { type: :explication }, { type: :dossier_link }, { type: :textarea }] }
|
||||
|
||||
|
|
Loading…
Reference in a new issue