Skip to content

Commit

Permalink
fix: Quote validation
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolasleger committed Dec 24, 2024
1 parent 58f5573 commit 3699313
Show file tree
Hide file tree
Showing 6 changed files with 26 additions and 21 deletions.
2 changes: 1 addition & 1 deletion app/views/quote_checks/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<% if defined?(@quote_valid) %>
<hr />

<h1>Résultat vérification du Devis : <%= @quote_valid ? 'Valide' : 'Invalide' %></h1>
<h1>Résultat vérification du Devis : <%= @quote_valid ? 'Valide' : 'Invalide' %></h1>
<pre><%= @quote_error_details&.join("\n") %></pre>
<% end %>

Expand Down
2 changes: 1 addition & 1 deletion lib/quote_reader/anonymiser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class NotImplementedError < ::NotImplementedError; end

FIELDS_TO_ANONYMISE = [
:adresses, :emails, :ibans, :numeros_tva, :rcss, :sirets, :telephones, :uris,
{ client: %i[adresse_chantier nom prenom] },
{ client: %i[adresse adresse_chantier nom prenom] },
{ pro: %i[adresse capital forme_juridique labels numero_tva raison_sociale rge_number siret] }
].freeze

Expand Down
35 changes: 18 additions & 17 deletions lib/quote_reader/naive_text.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ def read
devis: self.class.find_mention_devis(text),
numero_devis: self.class.find_numero_devis(text),
client: {
adresse: self.class.find_adresses(text).first,
adresse_chantier: self.class.find_adresse_chantier(text),
nom: self.class.find_nom(text),
prenom: nil # Necessary for Anonymisation ?
prenom: self.class.find_prenom(text) # Necessary for Anonymisation ?
},
pro: {
adresse: self.class.find_adresse_pro(text),
Expand Down Expand Up @@ -56,7 +57,7 @@ def version

NUMBER_REFERENCE_REGEX = /n?[.°]/i

BETWEEN_LABEL_VALUE_REGEX = /\s+(?:#{NUMBER_REFERENCE_REGEX})?\s*(?::\s*)?/i
BETWEEN_LABEL_VALUE_REGEX = /\s*(?:#{NUMBER_REFERENCE_REGEX})?\s*(?::\s*)?/i
EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
FORME_JURIDIQUE_REGEX = /
(?: # Begin group for legal forms
Expand All @@ -80,34 +81,34 @@ def version
URI_REGEX = %r{(?:https?|ftp)://(?:www\.)?[^\s/$.?#].[^\s]*|www\.[^\s/$.?#].[^\s]*}i

def self.find_adresses(text)
(text.scan(/Adresse\s*:\s*(#{FRENCH_CHARACTER_REGEX}+)/i).flatten +
(text.scan(/Adresse#{BETWEEN_LABEL_VALUE_REGEX}(#{FRENCH_CHARACTER_REGEX}+)/i).flatten +
text.scan(/(#{FRENCH_ADDRESS_REGEX})/i).flatten).filter_map { |e| e&.strip }.uniq
end

def self.find_adresse_chantier(text)
text[/Adresse chantier\s*:\s*(#{FRENCH_CHARACTER_REGEX}+)/i, 1] ||
text[/Adresse chantier#{BETWEEN_LABEL_VALUE_REGEX}(#{FRENCH_CHARACTER_REGEX}+)/i, 1].presence ||
find_adresses(text).first
end

def self.find_adresse_pro(text)
text[/Adresse Pro\s*:\s*(#{FRENCH_CHARACTER_REGEX}+)/i, 1] ||
text[/Adresse Pro#{BETWEEN_LABEL_VALUE_REGEX}(#{FRENCH_CHARACTER_REGEX}+)/i, 1].presence ||
find_adresses(text).first
end

def self.find_assurance(text)
text[/Assurance(?:\s+décennale)?#{BETWEEN_LABEL_VALUE_REGEX}((?:[#{FRENCH_CHARACTER_REGEX}:]+\s+)+(?:#{NUMBER_REFERENCE_REGEX}\s*)?(?:contrat\s+#{FRENCH_CHARACTER_REGEX}*\s*\d+)?)/i, 1] # rubocop:disable Layout/LineLength
text[/Assurance(?:\s+décennale)?#{BETWEEN_LABEL_VALUE_REGEX}((?:[#{FRENCH_CHARACTER_REGEX}:]+\s+)+(?:#{NUMBER_REFERENCE_REGEX}\s*)?(?:contrat\s+#{FRENCH_CHARACTER_REGEX}*\s*\d+)?)/i, 1].presence # rubocop:disable Layout/LineLength
end

def self.find_capital(text)
text[/(?:Capitale?|capilâide)(?:\s+de)?#{BETWEEN_LABEL_VALUE_REGEX}(\d+(?: \d{3})*)\s*€/i, 1]
text[/(?:Capitale?|capilâide)(?:\s+de)?#{BETWEEN_LABEL_VALUE_REGEX}(\d+(?: \d{3})*)\s*€/i, 1].presence
end

def self.find_emails(text)
text.scan(/\b(#{EMAIL_REGEX})\b/i).flatten.filter_map { |e| e&.strip }.uniq
end

def self.find_forme_juridique(text)
text[/\b(#{FORME_JURIDIQUE_REGEX})\b/, 1]
text[/\b(#{FORME_JURIDIQUE_REGEX})\b/, 1].presence
end

def self.find_ibans(text)
Expand All @@ -119,7 +120,7 @@ def self.find_ibans(text)
def self.find_label_numbers(text)
# Warning : insure caracter before not match the IBAN
text.scan(
%r{(?:\A|.*#{BETWEEN_LABEL_VALUE_REGEX})((?:(?:CPLUS|QB|QPAC|QPV|QS|VPLUS)\s*/\s*|(?:R|E-)?E)\s*\d{5,6})}i
%r{(?:\A|.*?#{BETWEEN_LABEL_VALUE_REGEX})((?:(?:CPLUS|QB|QPAC|QPV|QS|VPLUS)\s*/\s*|(?:R|E-)?E)\s*\d{5,6})}i
).flatten.filter_map { |e| e&.strip }.uniq
end

Expand All @@ -128,30 +129,30 @@ def self.find_mention_devis(text)
end

def self.find_nom(text)
text[/Nom\s*:\s*(#{FRENCH_CHARACTER_REGEX}+)/i, 1]
text[/Nom#{BETWEEN_LABEL_VALUE_REGEX}(#{FRENCH_CHARACTER_REGEX}+)/i, 1].presence
end

def self.find_numero_devis(text)
text[/DEVIS\s+N.?\s*(#{FRENCH_CHARACTER_REGEX}*\d{4,})/i, 1]
text[/DEVIS\s+N.?\s*(#{FRENCH_CHARACTER_REGEX}*\d{4,})/i, 1].presence
end

def self.find_numeros_tva(text)
text.scan(/\bFR[A-Z0-9]{2}\d{9}\b/i).flatten.filter_map { |e| e&.strip }.uniq
end

def self.find_prenom(text)
french_first_names = %w[Jean Marie Jacques Claire Pierre Sophie Amélie Luc Léa Élodie Chloé Théo]
french_first_names = %w[Jean Marie Jacques Claire Pierre Sophie Amélie Luc Léa Élodie Chloé Théo Martin]

french_first_names.detect { |first_name| text[/(#{first_name})/i, 1] } ||
text[/Prénom\s*:\s*(#{FRENCH_CHARACTER_REGEX}+)/i, 1]
french_first_names.detect { |first_name| text[/(#{first_name})/i, 1].presence } ||
text[/Prénom#{BETWEEN_LABEL_VALUE_REGEX}(#{FRENCH_CHARACTER_REGEX}+)/i, 1].presence
end

def self.find_raison_sociale(text)
forme_jurique_raison_sociale_regex = /#{FORME_JURIDIQUE_REGEX}\s+.+|.+\s+#{FORME_JURIDIQUE_REGEX}/i

text[/(#{forme_jurique_raison_sociale_regex})(?:\s+.*)?\Z/, 1] ||
text[/\A(?:.*\s)?+(#{forme_jurique_raison_sociale_regex}\s+.+)\s+/, 1] ||
text[/Raison sociale\s*:\s*(#{FRENCH_CHARACTER_REGEX}+)/i, 1]
text[/(#{forme_jurique_raison_sociale_regex})(?:\s+.*)?\Z/, 1].presence ||
text[/\A(?:.*\s)?+(#{forme_jurique_raison_sociale_regex}\s+.+)\s+/, 1].presence ||
text[/Raison sociale#{BETWEEN_LABEL_VALUE_REGEX}(#{FRENCH_CHARACTER_REGEX}+)/i, 1].presence
end

def self.find_rge_numbers(text)
Expand Down
5 changes: 4 additions & 1 deletion lib/quote_validator/global.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,22 @@ def validate_admin
# date d'emission, date de pré-visite (CEE uniquement ?),
# validité (par défaut 3 mois -> Juste un warning),
# Date de début de chantier (CEE uniquement)
# rubocop:disable Metrics/AbcSize
def validate_dates
# date_devis
add_error("date_devis_manquant", category: "admin", type: "missing") if quote[:date_devis].blank?

# date_debut_chantier
add_error("date_chantier_manquant", category: "admin", type: "missing") if quote[:date_chantier].blank?
date_chantier = quote[:date_chantier] || quote[:date_debut_chantier]
add_error("date_chantier_manquant", category: "admin", type: "missing") if date_chantier.blank?

# date_pre_visite
add_error("date_pre_visite_manquant", category: "admin", type: "missing") if quote[:date_pre_visite].blank?

# validite
add_error("date_validite_manquant", category: "admin", type: "missing") unless quote[:validite]
end
# rubocop:enable Metrics/AbcSize

# V0 on check la présence - attention devrait dépendre du geste, à terme,
# on pourra utiliser une API pour vérifier la validité
Expand Down
1 change: 1 addition & 0 deletions spec/lib/quote_reader/naive_text_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
client: {
nom: "Doe",
prenom: nil,
adresse: "42",
adresse_chantier: "43"
},
pro: {
Expand Down
2 changes: 1 addition & 1 deletion spec/services/quote_check_service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
expect(quote_check.validation_errors).to include(*%w[
devis_manquant pro_raison_sociale_manquant
pro_forme_juridique_manquant capital_manquant
client_prenom_manquant client_nom_manquant
client_nom_manquant
])

expect(quote_check.read_attributes.dig(
Expand Down

0 comments on commit 3699313

Please sign in to comment.