Skip to content

Commit 10e55f7

Browse files
tchakclaude
andcommitted
tech: replace check_mandatory_and_visible_champs_for with champ completeness validations
Champ completeness (mandatory presence and multi-input completion) is now a regular AR validation on champs under the :champ_completeness context, run alongside :champ_value in a single pass at submit (public champs) and traitement (private champs). Also prevents autosave's errors.uniq! from collapsing same-attribute, same-type errors imported from different champs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 99871f3 commit 10e55f7

4 files changed

Lines changed: 37 additions & 39 deletions

File tree

app/models/concerns/champ_validate_concern.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,20 @@ module ChampValidateConcern
55

66
included do
77
validates_with ExternalDataChampValidator, if: :validate_external_data_response?
8+
validate :validate_completed, on: :champ_completeness, if: :visible?
9+
end
10+
11+
# Overridden by champs with multi-input completeness rules (address, linked drop down)
12+
def validate_completed
13+
errors.add(:value, :missing) if mandatory_blank?
814
end
915

1016
private
1117

1218
def should_validate_in_current_context?
13-
validation_context == :champ_value && visible?
19+
# validation_context is an array when champs are validated for completeness
20+
# ([:champ_value, :champ_completeness])
21+
Array(validation_context).include?(:champ_value) && visible?
1422
end
1523

1624
def validate_external_data_response?

app/models/concerns/dossier_correctable_concern.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ module DossierCorrectableConcern
1212

1313
scope :with_pending_corrections, -> { joins(:corrections).where(corrections: { resolved_at: nil }) }
1414

15-
validate :validate_pending_correction, on: :champs_public_value
15+
validate :validate_pending_correction, on: :champs_public_completeness
1616
end
1717

1818
def flag_as_pending_correction!(commentaire, reason = nil)

app/models/concerns/dossier_validate_concern.rb

Lines changed: 26 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,64 +3,54 @@
33
module DossierValidateConcern
44
extend ActiveSupport::Concern
55

6+
# Autosave's _ensure_no_duplicate_errors calls errors.uniq! after validations,
7+
# which would collapse same-attribute, same-type errors imported from
8+
# different champs. Include the champ in the error identity to prevent that.
9+
class ChampNestedError < ActiveModel::NestedError
10+
protected def attributes_for_hash
11+
[*super, inner_error.base]
12+
end
13+
end
14+
615
included do
716
validate :validate_champs_public_value, on: :champs_public_value
817
validate :validate_champs_private_value, on: :champs_private_value
18+
validate :validate_champs_public_completeness, on: :champs_public_completeness
19+
validate :validate_champs_private_completeness, on: :champs_private_completeness
920
end
1021

1122
def champs_public_valid?
12-
validate(:champs_public_value)
13-
check_mandatory_and_visible_champs_for(root_champs_public)
14-
errors.blank?
23+
validate(:champs_public_completeness)
1524
end
1625

1726
def champs_private_valid?
18-
validate(:champs_private_value)
19-
check_mandatory_and_visible_champs_for(root_champs_private)
20-
errors.blank?
27+
validate(:champs_private_completeness)
2128
end
2229

2330
private
2431

2532
def validate_champs_public_value
26-
validate_projected_champs(flat_champs_public)
33+
validate_projected_champs(flat_champs_public, :champ_value)
2734
end
2835

2936
def validate_champs_private_value
30-
validate_projected_champs(flat_champs_private)
37+
validate_projected_champs(flat_champs_private, :champ_value)
3138
end
3239

33-
def validate_projected_champs(champs)
34-
champs.each do |champ|
35-
next if champ.validate(:champ_value)
36-
champ.errors.each { errors.import(it) }
37-
end
40+
def validate_champs_public_completeness
41+
validate_projected_champs(flat_champs_public, [:champ_value, :champ_completeness])
3842
end
3943

40-
def check_mandatory_and_visible_champs_for(collection)
41-
collection.filter(&:visible?).each do |champ|
42-
if champ.mandatory_blank? && !champ.respond_to?(:validate_completed)
43-
error = champ.errors.add(:value, :missing)
44-
errors.import(error)
45-
end
44+
def validate_champs_private_completeness
45+
validate_projected_champs(flat_champs_private, [:champ_value, :champ_completeness])
46+
end
4647

47-
if champ.repetition?
48-
champ.rows.each do |row|
49-
row.flat_champs.filter(&:visible?).each do |champ|
50-
if champ.respond_to?(:validate_completed)
51-
champ.validate_completed
52-
champ.errors.each { errors.import(it) }
53-
elsif champ.mandatory_blank?
54-
error = champ.errors.add(:value, :missing)
55-
errors.import(error)
56-
end
57-
end
58-
end
59-
elsif champ.respond_to?(:validate_completed)
60-
champ.validate_completed
61-
champ.errors.each { errors.import(it) }
62-
end
48+
# Both contexts must be validated in a single call: champ.validate clears
49+
# previous errors, so a second pass would erase the first one's errors.
50+
def validate_projected_champs(champs, contexts)
51+
champs.each do |champ|
52+
next if champ.validate(contexts)
53+
champ.errors.each { errors.objects.append(ChampNestedError.new(self, it)) }
6354
end
64-
errors
6555
end
6656
end

app/validators/external_data_champ_validator.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# frozen_string_literal: true
22

33
class ExternalDataChampValidator < ActiveModel::Validator
4-
# Required checks are delegated to check_mandatory_and_visible_champs_public.
4+
# Required checks are delegated to Champ#validate_completed (:champ_completeness context).
55
def validate(record)
66
if record.pending?
77
# User filled the field, but background job is still running.

0 commit comments

Comments
 (0)