Skip to content

Commit

Permalink
feat(Users/Dossiers#submit_brouillon_or_en_construction): prevent tra…
Browse files Browse the repository at this point in the history
…nsition to en_construction if eligibilite_rules returns false. pop error nicely
  • Loading branch information
mfo committed May 23, 2024
1 parent 546de52 commit 705aead
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 30 deletions.
7 changes: 3 additions & 4 deletions app/components/dossiers/errors_full_messages_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
class Dossiers::ErrorsFullMessagesComponent < ApplicationComponent
ErrorDescriptor = Data.define(:anchor, :label, :error_message)

def initialize(dossier:, errors:)
def initialize(dossier:)
@dossier = dossier
@errors = errors
end

def dedup_and_partitioned_errors
formated_errors = @errors.to_enum # ActiveModel::Errors.to_a is an alias to full_messages, we don't want that
formated_errors = @dossier.errors.to_enum # ActiveModel::Errors.to_a is an alias to full_messages, we don't want that
.to_a # but enum.to_a gives back an array
.uniq { |error| [error.inner_error.base] } # dedup cumulated errors from dossier.champs, dossier.champs_public, dossier.champs_private which run the validator one time per association
.map { |error| to_error_descriptor(error) }
Expand All @@ -27,6 +26,6 @@ def to_error_descriptor(error)
end

def render?
!@errors.empty?
!@dossier.errors.empty?
end
end
22 changes: 8 additions & 14 deletions app/controllers/users/dossiers_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,9 @@ def brouillon

def submit_brouillon
@dossier = dossier_with_champs(pj_template: false)
@errors = submit_dossier_and_compute_errors
submit_dossier_and_compute_errors

if @errors.blank?
if @dossier.errors.blank? && @dossier.can_passer_en_construction?
@dossier.passer_en_construction!
@dossier.process_declarative!
@dossier.process_sva_svr!
Expand All @@ -240,6 +240,7 @@ def submit_brouillon
end
redirect_to merci_dossier_path(@dossier)
else
@dossier.check_in_eligibilite_rules_and_visible_champs if !@dossier.can_passer_en_construction?
respond_to do |format|
format.html { render :brouillon }
format.turbo_stream
Expand Down Expand Up @@ -276,17 +277,17 @@ def submit_en_construction
editing_fork_origin.resolve_pending_correction
end

@errors = submit_dossier_and_compute_errors
submit_dossier_and_compute_errors

if @errors.blank?
if @dossier.errors.blank? && @dossier.can_passer_en_construction?
editing_fork_origin.merge_fork(@dossier)
editing_fork_origin.submit_en_construction!

redirect_to dossier_path(editing_fork_origin)
else
@dossier.check_in_eligibilite_rules_and_visible_champs if !@dossier.can_passer_en_construction?
respond_to do |format|
format.html do
@dossier = editing_fork_origin
render :modifier
end

Expand Down Expand Up @@ -574,21 +575,14 @@ def update_dossier_and_compute_errors

def submit_dossier_and_compute_errors
@dossier.validate(:champs_public_value)

errors = @dossier.errors
@dossier.check_mandatory_and_visible_champs.each do |error_on_champ|
errors.import(error_on_champ)
end
@dossier.check_mandatory_and_visible_champs

if @dossier.editing_fork_origin&.pending_correction?
@dossier.editing_fork_origin.validate(:champs_public_value)
@dossier.editing_fork_origin.errors.where(:pending_correction).each do |error|
errors.import(error)
@dossier.errors.import(error)

Check warning on line 583 in app/controllers/users/dossiers_controller.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/users/dossiers_controller.rb#L583

Added line #L583 was not covered by tests
end

end

errors
end

def ensure_ownership!
Expand Down
4 changes: 4 additions & 0 deletions app/models/champ.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ def mandatory_blank?
mandatory? && blank?
end

def in_eligibilite_rules?
blank? && stable_id.in?(dossier.revision&.eligibilite_rules&.sources || [])
end

def blank?
value.blank?
end
Expand Down
14 changes: 13 additions & 1 deletion app/models/dossier.rb
Original file line number Diff line number Diff line change
Expand Up @@ -935,13 +935,25 @@ def remove_titres_identite!
end

def check_mandatory_and_visible_champs
champs_for_revision(scope: :public)
champ_errors = champs_for_revision(scope: :public)
.filter { _1.child? ? _1.parent.visible? : true }
.filter(&:visible?)
.filter(&:mandatory_blank?)
.map do |champ|
champ.errors.add(:value, :missing)
end
champ_errors.each { errors.import(_1) }
end

def check_in_eligibilite_rules_and_visible_champs
ineligible_errors = champs_for_revision(scope: :public)

Check warning on line 949 in app/models/dossier.rb

View check run for this annotation

Codecov / codecov/patch

app/models/dossier.rb#L949

Added line #L949 was not covered by tests
.filter { _1.child? ? _1.parent.visible? : true }
.filter(&:visible?)
.filter(&:in_eligibilite_rules?)
.map do |champ|
champ.errors.add(:value, :needed_for_eligibilite)

Check warning on line 954 in app/models/dossier.rb

View check run for this annotation

Codecov / codecov/patch

app/models/dossier.rb#L954

Added line #L954 was not covered by tests
end
ineligible_errors.each { errors.import(_1) }

Check warning on line 956 in app/models/dossier.rb

View check run for this annotation

Codecov / codecov/patch

app/models/dossier.rb#L956

Added line #L956 was not covered by tests
end

def eligibilite_rules_computable?
Expand Down
2 changes: 1 addition & 1 deletion app/views/shared/dossiers/_edit.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
= render NestedForms::FormOwnerComponent.new
= form_for dossier_for_editing, url: brouillon_dossier_url(dossier), method: :patch, html: { id: 'dossier-edit-form', class: 'form', multipart: true, novalidate: 'novalidate' } do |f|

= render Dossiers::ErrorsFullMessagesComponent.new(dossier: @dossier, errors: @errors || [])
= render Dossiers::ErrorsFullMessagesComponent.new(dossier: dossier)
%header.mb-6
.fr-highlight
%p.fr-text--sm
Expand Down
2 changes: 1 addition & 1 deletion config/locales/models/champs/champs.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ fr:
value:
format: '%{message}'
missing: must be filled

needed_for_eligibilite: must be filled in order to know if your file is elegible
2 changes: 1 addition & 1 deletion config/locales/models/champs/champs.fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ fr:
value:
format: '%{message}'
missing: doit être rempli

needed_for_eligibilite: doît être rempli pour determiner si votre dossier est éligible
67 changes: 59 additions & 8 deletions spec/controllers/users/dossiers_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,9 @@

describe '#submit_brouillon' do
before { sign_in(user) }
let!(:dossier) { create(:dossier, user: user) }
let(:procedure) { create(:procedure, :published, types_de_champ_public:) }
let(:types_de_champ_public) { [{ type: :text }] }
let!(:dossier) { create(:dossier, user:, procedure:) }
let(:first_champ) { dossier.champs_public.first }
let(:anchor_to_first_champ) { controller.helpers.link_to first_champ.libelle, brouillon_dossier_path(anchor: first_champ.labelledby_id), class: 'error-anchor' }
let(:value) { 'beautiful value' }
Expand Down Expand Up @@ -438,9 +440,9 @@
render_views
let(:error_message) { 'nop' }
before do
expect_any_instance_of(Dossier).to receive(:validate).and_return(false)
expect_any_instance_of(Dossier).to receive(:errors).and_return(
[double(inner_error: double(base: first_champ), message: 'nop')]
allow_any_instance_of(Dossier).to receive(:validate).and_return(false)
allow_any_instance_of(Dossier).to receive(:errors).and_return(
[instance_double(ActiveModel::NestedError, inner_error: double(base: first_champ), message: 'nop')]
)
subject
end
Expand All @@ -460,15 +462,38 @@
render_views

let(:value) { nil }
let(:types_de_champ_public) { [{ type: :text, mandatory: true, libelle: 'l' }] }
before { subject }

it { expect(response).to render_template(:brouillon) }
it { expect(response.body).to have_link(first_champ.libelle, href: "##{first_champ.labelledby_id}") }
it { expect(response.body).to have_content("doit être rempli") }
end

context 'when a eligible champ is missing' do
include Logic
render_views

let(:value) { nil }
let(:types_de_champ_public) { [{ type: :yes_no, libelle: 'l1' }, { type: :integer_number, libelle: 'l2' }] }
let(:yes_no_tdc) { dossier.champs_public.first }
let(:integer_tdc) { dossier.champs_public.last }

let(:initial_condition) do
ds_or([
ds_eq(champ_value(yes_no_tdc.stable_id), constant(true)),
ds_eq(champ_value(integer_tdc.stable_id), constant(1))
])
end

before do
first_champ.type_de_champ.update(mandatory: true, libelle: 'l')
procedure.published_revision.update(eligibilite_enabled: true, eligibilite_rules: initial_condition, eligibilite_message: :ko)
subject
end

it { expect(response).to render_template(:brouillon) }
it { expect(response.body).to have_link(first_champ.libelle, href: "##{first_champ.labelledby_id}") }
it { expect(response.body).to have_content("doit être rempli") }
it { expect(response.body).to have_content("doît être rempli pour determiner si votre dossier est éligible") }
end

context 'when dossier has no champ' do
Expand Down Expand Up @@ -547,8 +572,8 @@
render_views

before do
expect_any_instance_of(Dossier).to receive(:validate).and_return(false)
expect_any_instance_of(Dossier).to receive(:errors).and_return(
allow_any_instance_of(Dossier).to receive(:validate).and_return(false)
allow_any_instance_of(Dossier).to receive(:errors).and_return(
[double(inner_error: double(base: first_champ), message: 'nop')]
)

Expand All @@ -569,6 +594,32 @@
it { expect(response.body).to have_link(first_champ.libelle, href: "##{first_champ.labelledby_id}") }
end

context 'when a eligible champ is missing' do
include Logic
render_views

let(:value) { nil }
let(:types_de_champ_public) { [{ type: :yes_no, libelle: 'l1' }, { type: :integer_number, libelle: 'l2' }] }
let(:yes_no_tdc) { dossier.champs_public.first }
let(:integer_tdc) { dossier.champs_public.last }

let(:initial_condition) do
ds_or([
ds_eq(champ_value(yes_no_tdc.stable_id), constant(true)),
ds_eq(champ_value(integer_tdc.stable_id), constant(1))
])
end

before do
procedure.published_revision.update(eligibilite_enabled: true, eligibilite_rules: initial_condition, eligibilite_message: :ko)
subject
end

it { expect(response).to render_template(:modifier) }
it { expect(response.body).to have_link(first_champ.libelle, href: "##{first_champ.labelledby_id}") }
it { expect(response.body).to have_content("doît être rempli pour determiner si votre dossier est éligible") }
end

context 'when dossier has no champ' do
let(:submit_payload) { { id: dossier.id } }

Expand Down

0 comments on commit 705aead

Please sign in to comment.