Module: Einvoicing::Validators::FR
- Defined in:
- lib/einvoicing/validators/fr.rb
Overview
French invoice validator. Checks SIREN, SIRET, TVA (VAT) number format and Luhn checksum, plus mandatory invoice fields for French B2B compliance.
Constant Summary collapse
- SIREN_RE =
/\A\d{9}\z/- SIRET_RE =
/\A\d{14}\z/- VAT_RE =
FR VAT: "FR" + 2 alphanumeric chars + 9-digit SIREN
/\AFR[A-Z0-9]{2}\d{9}\z/- INV_NUM_RE =
/\A[\w\-\/]{1,35}\z/- IBAN_RE =
IBAN: country code (2 alpha) + 2 check digits + BBAN (11-30 alphanumeric) = 15-34 total
/\A[A-Z]{2}[0-9]{2}[A-Z0-9]{11,30}\z/- BIC_RE =
BIC: 4 alpha (institution) + 2 alpha (country) + 2 alphanumeric (location) + optional 3 alphanumeric (branch)
/\A[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?\z/
Class Method Summary collapse
-
.valid_bic?(bic) ⇒ Boolean
Validate a BIC (ISO 9362) — 8 or 11 chars.
-
.valid_iban?(iban) ⇒ Boolean
Validate an IBAN (ISO 13616 format + mod-97 checksum).
-
.valid_invoice_number?(number) ⇒ Boolean
Validate an invoice number format (alphanumeric, dashes, slashes, 1-35 chars).
-
.valid_siren?(siren) ⇒ Boolean
Validate a single SIREN number.
-
.valid_siret?(siret) ⇒ Boolean
Validate a single SIRET number.
-
.valid_vat_number?(vat) ⇒ Boolean
Validate a French VAT number format.
-
.validate(invoice) ⇒ Array<Hash>
List of error hashes ({ field:, error:, message: }); empty if valid.
- .validate!(invoice) ⇒ Object
Class Method Details
.valid_bic?(bic) ⇒ Boolean
Validate a BIC (ISO 9362) — 8 or 11 chars.
92 93 94 |
# File 'lib/einvoicing/validators/fr.rb', line 92 def self.valid_bic?(bic) bic.to_s.match?(BIC_RE) end |
.valid_iban?(iban) ⇒ Boolean
Validate an IBAN (ISO 13616 format + mod-97 checksum).
79 80 81 82 83 84 85 86 87 |
# File 'lib/einvoicing/validators/fr.rb', line 79 def self.valid_iban?(iban) str = iban.to_s.gsub(/\s/, "").upcase return false unless str.match?(IBAN_RE) # Move first 4 chars to end, replace each letter with its numeric value (A=10..Z=35) rearranged = str[4..] + str[0..3] numeric = rearranged.chars.map { |c| c =~ /[A-Z]/ ? (c.ord - 55).to_s : c }.join numeric.to_i % 97 == 1 end |
.valid_invoice_number?(number) ⇒ Boolean
Validate an invoice number format (alphanumeric, dashes, slashes, 1-35 chars).
72 73 74 |
# File 'lib/einvoicing/validators/fr.rb', line 72 def self.valid_invoice_number?(number) number.to_s.match?(INV_NUM_RE) end |
.valid_siren?(siren) ⇒ Boolean
Validate a single SIREN number.
47 48 49 50 51 |
# File 'lib/einvoicing/validators/fr.rb', line 47 def self.valid_siren?(siren) return false unless siren.to_s.match?(SIREN_RE) Base.luhn_valid?(siren.to_s) end |
.valid_siret?(siret) ⇒ Boolean
Validate a single SIRET number.
56 57 58 59 60 |
# File 'lib/einvoicing/validators/fr.rb', line 56 def self.valid_siret?(siret) return false unless siret.to_s.match?(SIRET_RE) Base.luhn_valid?(siret.to_s) end |
.valid_vat_number?(vat) ⇒ Boolean
Validate a French VAT number format.
65 66 67 |
# File 'lib/einvoicing/validators/fr.rb', line 65 def self.valid_vat_number?(vat) vat.to_s.match?(VAT_RE) end |
.validate(invoice) ⇒ Array<Hash>
Returns list of error hashes ({ field:, error:, message: }); empty if valid.
27 28 29 30 31 32 33 34 |
# File 'lib/einvoicing/validators/fr.rb', line 27 def self.validate(invoice) [ *validate_invoice_fields(invoice), *validate_party(invoice.seller, :seller), *validate_party(invoice.buyer, :buyer), *validate_lines(invoice.lines) ] end |
.validate!(invoice) ⇒ Object
37 38 39 40 41 42 |
# File 'lib/einvoicing/validators/fr.rb', line 37 def self.validate!(invoice) errors = validate(invoice) raise ValidationError, errors.map { |e| e[:message] }.join("; ") unless errors.empty? true end |