Merge pull request #8659 from colinux/fix-titres-numeros-conditional
Titres de section: la numérotation automatique tient compte du conditionnel
This commit is contained in:
commit
905d868390
15 changed files with 167 additions and 31 deletions
|
@ -89,7 +89,8 @@
|
||||||
color: $light-grey;
|
color: $light-grey;
|
||||||
}
|
}
|
||||||
|
|
||||||
.conditionnel {
|
.conditionnel,
|
||||||
|
p {
|
||||||
color: $white;
|
color: $white;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
11
app/assets/stylesheets/sections.scss
Normal file
11
app/assets/stylesheets/sections.scss
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
.counter-start-header-section {
|
||||||
|
counter-reset: headerSectionCounter;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-section {
|
||||||
|
counter-increment: headerSectionCounter;
|
||||||
|
|
||||||
|
&.header-section-counter::before {
|
||||||
|
content: counter(headerSectionCounter) ". ";
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,2 +1,2 @@
|
||||||
%h2.header-section
|
%h2.header-section{ class: @champ.dossier.auto_numbering_section_headers_for?(@champ) ? "header-section-counter" : nil }
|
||||||
= @champ.libelle_with_section_index
|
= @champ.libelle
|
||||||
|
|
|
@ -29,6 +29,10 @@
|
||||||
= form.check_box :mandatory, class: 'small-margin small', id: dom_id(type_de_champ, :mandatory)
|
= form.check_box :mandatory, class: 'small-margin small', id: dom_id(type_de_champ, :mandatory)
|
||||||
= form.label :mandatory, "Champ obligatoire", for: dom_id(type_de_champ, :mandatory)
|
= form.label :mandatory, "Champ obligatoire", for: dom_id(type_de_champ, :mandatory)
|
||||||
= form.text_field :libelle, class: 'small-margin small width-100', id: dom_id(type_de_champ, :libelle), data: input_autofocus
|
= form.text_field :libelle, class: 'small-margin small width-100', id: dom_id(type_de_champ, :libelle), data: input_autofocus
|
||||||
|
- if type_de_champ.header_section?
|
||||||
|
%p
|
||||||
|
%small Nous numérotons automatiquement les titres lorsqu’aucun de vos titres ne commence par un chiffre.
|
||||||
|
|
||||||
- if !type_de_champ.header_section? && !type_de_champ.titre_identite?
|
- if !type_de_champ.header_section? && !type_de_champ.titre_identite?
|
||||||
.cell.mt-1
|
.cell.mt-1
|
||||||
= form.label :description, "Description du champ (optionnel)", for: dom_id(type_de_champ, :description)
|
= form.label :description, "Description du champ (optionnel)", for: dom_id(type_de_champ, :description)
|
||||||
|
|
|
@ -25,14 +25,6 @@ class Champs::HeaderSectionChamp < Champ
|
||||||
# The user cannot enter any information here so it doesn’t make much sense to search
|
# The user cannot enter any information here so it doesn’t make much sense to search
|
||||||
end
|
end
|
||||||
|
|
||||||
def libelle_with_section_index
|
|
||||||
if sections&.none?(&:libelle_with_section_index?)
|
|
||||||
"#{section_index}. #{libelle}"
|
|
||||||
else
|
|
||||||
libelle
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def libelle_with_section_index?
|
def libelle_with_section_index?
|
||||||
libelle =~ /^\d/
|
libelle =~ /^\d/
|
||||||
end
|
end
|
||||||
|
|
35
app/models/concerns/dossier_sections_concern.rb
Normal file
35
app/models/concerns/dossier_sections_concern.rb
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
module DossierSectionsConcern
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
def sections_for(champ)
|
||||||
|
@sections = Hash.new do |hash, parent|
|
||||||
|
case parent
|
||||||
|
when :public
|
||||||
|
hash[parent] = champs_public.filter(&:header_section?)
|
||||||
|
when :private
|
||||||
|
hash[parent] = champs_private.filter(&:header_section?)
|
||||||
|
else
|
||||||
|
hash[parent] = parent.champs.filter(&:header_section?)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
@sections[champ.parent || (champ.public? ? :public : :private)]
|
||||||
|
end
|
||||||
|
|
||||||
|
def auto_numbering_section_headers_for?(champ)
|
||||||
|
sections_for(champ)&.none?(&:libelle_with_section_index?)
|
||||||
|
end
|
||||||
|
|
||||||
|
def index_for_section_header(champ)
|
||||||
|
champs = champ.private? ? champs_private : champs_public
|
||||||
|
|
||||||
|
index = 1
|
||||||
|
champs.each do |c|
|
||||||
|
return index if c.stable_id == champ.stable_id
|
||||||
|
next unless c.visible?
|
||||||
|
|
||||||
|
index += 1 if c.type_de_champ.header_section?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -48,8 +48,9 @@
|
||||||
class Dossier < ApplicationRecord
|
class Dossier < ApplicationRecord
|
||||||
self.ignored_columns = [:en_construction_conservation_extension]
|
self.ignored_columns = [:en_construction_conservation_extension]
|
||||||
include DossierFilteringConcern
|
include DossierFilteringConcern
|
||||||
include DossierRebaseConcern
|
|
||||||
include DossierPrefillableConcern
|
include DossierPrefillableConcern
|
||||||
|
include DossierRebaseConcern
|
||||||
|
include DossierSectionsConcern
|
||||||
|
|
||||||
enum state: {
|
enum state: {
|
||||||
brouillon: 'brouillon',
|
brouillon: 'brouillon',
|
||||||
|
@ -1237,20 +1238,6 @@ class Dossier < ApplicationRecord
|
||||||
termine_expired_to_delete.find_each(&:purge_discarded)
|
termine_expired_to_delete.find_each(&:purge_discarded)
|
||||||
end
|
end
|
||||||
|
|
||||||
def sections_for(champ)
|
|
||||||
@sections = Hash.new do |hash, parent|
|
|
||||||
case parent
|
|
||||||
when :public
|
|
||||||
hash[parent] = champs_public.filter(&:header_section?)
|
|
||||||
when :private
|
|
||||||
hash[parent] = champs_private.filter(&:header_section?)
|
|
||||||
else
|
|
||||||
hash[parent] = parent.champs.filter(&:header_section?)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@sections[champ.parent || (champ.public? ? :public : :private)]
|
|
||||||
end
|
|
||||||
|
|
||||||
def clone
|
def clone
|
||||||
dossier_attributes = [:autorisation_donnees, :user_id, :revision_id, :groupe_instructeur_id]
|
dossier_attributes = [:autorisation_donnees, :user_id, :revision_id, :groupe_instructeur_id]
|
||||||
relationships = [:individual, :etablissement]
|
relationships = [:individual, :etablissement]
|
||||||
|
|
|
@ -192,6 +192,7 @@ class TypeDeChamp < ApplicationRecord
|
||||||
validates :type_champ, presence: true, allow_blank: false, allow_nil: false
|
validates :type_champ, presence: true, allow_blank: false, allow_nil: false
|
||||||
|
|
||||||
before_validation :check_mandatory
|
before_validation :check_mandatory
|
||||||
|
before_validation :normalize_libelle
|
||||||
before_save :remove_piece_justificative_template, if: -> { type_champ_changed? }
|
before_save :remove_piece_justificative_template, if: -> { type_champ_changed? }
|
||||||
before_validation :remove_drop_down_list, if: -> { type_champ_changed? }
|
before_validation :remove_drop_down_list, if: -> { type_champ_changed? }
|
||||||
before_save :remove_block, if: -> { type_champ_changed? }
|
before_save :remove_block, if: -> { type_champ_changed? }
|
||||||
|
@ -554,4 +555,8 @@ class TypeDeChamp < ApplicationRecord
|
||||||
.remove_children_of(self)
|
.remove_children_of(self)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def normalize_libelle
|
||||||
|
self.libelle&.strip!
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -141,7 +141,13 @@ def add_single_champ(pdf, champ)
|
||||||
when 'Champs::PieceJustificativeChamp', 'Champs::TitreIdentiteChamp'
|
when 'Champs::PieceJustificativeChamp', 'Champs::TitreIdentiteChamp'
|
||||||
return
|
return
|
||||||
when 'Champs::HeaderSectionChamp'
|
when 'Champs::HeaderSectionChamp'
|
||||||
add_section_title(pdf, tdc.libelle)
|
libelle = if @dossier.auto_numbering_section_headers_for?(champ)
|
||||||
|
"#{@dossier.index_for_section_header(champ)}. #{champ.libelle}"
|
||||||
|
else
|
||||||
|
champ.libelle
|
||||||
|
end
|
||||||
|
|
||||||
|
add_section_title(pdf, libelle)
|
||||||
when 'Champs::ExplicationChamp'
|
when 'Champs::ExplicationChamp'
|
||||||
format_in_2_lines(pdf, tdc.libelle, strip_tags(tdc.description))
|
format_in_2_lines(pdf, tdc.libelle, strip_tags(tdc.description))
|
||||||
when 'Champs::CarteChamp'
|
when 'Champs::CarteChamp'
|
||||||
|
|
|
@ -10,8 +10,7 @@
|
||||||
- else
|
- else
|
||||||
%tr
|
%tr
|
||||||
- if c.type_champ == TypeDeChamp.type_champs.fetch(:header_section)
|
- if c.type_champ == TypeDeChamp.type_champs.fetch(:header_section)
|
||||||
%th.header-section{ colspan: 3 }
|
%th.header-section{ colspan: 3, class: c.dossier.auto_numbering_section_headers_for?(c) ? "header-section-counter" : nil }= c.libelle
|
||||||
= c.libelle
|
|
||||||
- else
|
- else
|
||||||
%td.libelle{ class: repetition ? 'padded' : '' }
|
%td.libelle{ class: repetition ? 'padded' : '' }
|
||||||
= "#{c.libelle} :"
|
= "#{c.libelle} :"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
%table.table.vertical.dossier-champs{ role: :presentation }
|
%table.table.vertical.dossier-champs.counter-start-header-section{ role: :presentation }
|
||||||
%tbody
|
%tbody
|
||||||
- if dossier.show_groupe_instructeur_details?
|
- if dossier.show_groupe_instructeur_details?
|
||||||
%td.libelle= dossier.procedure.routing_criteria_name
|
%td.libelle= dossier.procedure.routing_criteria_name
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
- content_for(:notice_info) do
|
- content_for(:notice_info) do
|
||||||
= render partial: "shared/dossiers/france_connect_informations_notice", locals: { user_information: dossier.france_connect_information }
|
= render partial: "shared/dossiers/france_connect_informations_notice", locals: { user_information: dossier.france_connect_information }
|
||||||
|
|
||||||
.dossier-edit.container
|
.dossier-edit.container.counter-start-header-section
|
||||||
= render partial: "shared/dossiers/submit_is_over", locals: { dossier: dossier }
|
= render partial: "shared/dossiers/submit_is_over", locals: { dossier: dossier }
|
||||||
|
|
||||||
- if dossier.brouillon?
|
- if dossier.brouillon?
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
namespace :after_party do
|
||||||
|
desc 'Deployment task: strip_type_de_champ_libelle'
|
||||||
|
task strip_type_de_champ_libelle: :environment do
|
||||||
|
puts "Running deploy task 'strip_type_de_champ_libelle'"
|
||||||
|
|
||||||
|
# ~ 152K records matched
|
||||||
|
tdcs = TypeDeChamp.where("libelle LIKE ?", ' %').or(TypeDeChamp.where("libelle LIKE ?", '% '))
|
||||||
|
progress = ProgressReport.new(tdcs.count)
|
||||||
|
|
||||||
|
tdcs.find_each do |tdc|
|
||||||
|
tdc.save!
|
||||||
|
progress.inc
|
||||||
|
end
|
||||||
|
|
||||||
|
progress.finish
|
||||||
|
|
||||||
|
# Update task as completed. If you remove the line below, the task will
|
||||||
|
# run with every deploy (or every time you call after_party:run).
|
||||||
|
AfterParty::TaskRecord
|
||||||
|
.create version: AfterParty::TaskRecorder.new(__FILE__).timestamp
|
||||||
|
end
|
||||||
|
end
|
69
spec/models/concern/dossier_sections_concern_spec.rb
Normal file
69
spec/models/concern/dossier_sections_concern_spec.rb
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
describe DossierSectionsConcern do
|
||||||
|
describe '#auto_numbering_section_headers_for?' do
|
||||||
|
let(:public_libelle) { "Infos" }
|
||||||
|
let(:private_libelle) { "Infos Private" }
|
||||||
|
let(:types_de_champ_public) { [{ type: :header_section, libelle: public_libelle }, { type: :header_section, libelle: "Details" }] }
|
||||||
|
let(:types_de_champ_private) { [{ type: :header_section, libelle: private_libelle }, { type: :header_section, libelle: "Details Private" }] }
|
||||||
|
|
||||||
|
let(:procedure) { create(:procedure, :for_individual, types_de_champ_public:, types_de_champ_private:) }
|
||||||
|
let(:dossier) { create(:dossier, procedure: procedure) }
|
||||||
|
|
||||||
|
context "with no section having number" do
|
||||||
|
it { expect(dossier.auto_numbering_section_headers_for?(dossier.champs_public[1])).to eq(true) }
|
||||||
|
it { expect(dossier.auto_numbering_section_headers_for?(dossier.champs_private[1])).to eq(true) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with public section having number" do
|
||||||
|
let(:public_libelle) { "1 - infos" }
|
||||||
|
it { expect(dossier.auto_numbering_section_headers_for?(dossier.champs_public[1])).to eq(false) }
|
||||||
|
it { expect(dossier.auto_numbering_section_headers_for?(dossier.champs_private[1])).to eq(true) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with private section having number" do
|
||||||
|
let(:private_libelle) { "1 - infos private" }
|
||||||
|
it { expect(dossier.auto_numbering_section_headers_for?(dossier.champs_public[1])).to eq(true) }
|
||||||
|
it { expect(dossier.auto_numbering_section_headers_for?(dossier.champs_private[1])).to eq(false) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#index_for_section_header' do
|
||||||
|
include Logic
|
||||||
|
let(:number_stable_id) { 99 }
|
||||||
|
let(:types_de_champ) {
|
||||||
|
[
|
||||||
|
{ type: :header_section, libelle: "Infos" }, { type: :integer_number, stable_id: number_stable_id },
|
||||||
|
{ type: :header_section, libelle: "Details", condition: ds_eq(champ_value(99), constant(5)) }, { type: :header_section, libelle: "Conclusion" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
let(:procedure) { create(:procedure, :for_individual, types_de_champ_public: types_de_champ) }
|
||||||
|
let(:dossier) { create(:dossier, procedure: procedure) }
|
||||||
|
|
||||||
|
let(:headers) { dossier.champs_public.filter(&:header_section?) }
|
||||||
|
|
||||||
|
let(:number_value) { nil }
|
||||||
|
|
||||||
|
before do
|
||||||
|
dossier.champs_public.find { _1.stable_id == number_stable_id }.update(value: number_value)
|
||||||
|
dossier.reload
|
||||||
|
end
|
||||||
|
|
||||||
|
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(headers[1]).not_to be_visible
|
||||||
|
expect(dossier.index_for_section_header(headers[2])).to eq(2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when all headers are visible" do
|
||||||
|
let(:number_value) { 5 }
|
||||||
|
it "index accordingly header sections" do
|
||||||
|
expect(dossier.index_for_section_header(headers[0])).to eq(1)
|
||||||
|
expect(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
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -273,4 +273,9 @@ describe TypeDeChamp do
|
||||||
it_behaves_like "a non-prefillable type de champ", :type_de_champ_address
|
it_behaves_like "a non-prefillable type de champ", :type_de_champ_address
|
||||||
it_behaves_like "a non-prefillable type de champ", :type_de_champ_annuaire_education
|
it_behaves_like "a non-prefillable type de champ", :type_de_champ_annuaire_education
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#normalize_libelle' do
|
||||||
|
it { expect(create(:type_de_champ, :header_section, libelle: " 2.3 Test").libelle).to eq("2.3 Test") }
|
||||||
|
it { expect(create(:type_de_champ, libelle: " fix me ").libelle).to eq("fix me") }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue