Skip to content

Instantly share code, notes, and snippets.

@henrik
Created June 27, 2019 10:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save henrik/bedf70f301e48512334263120c89e220 to your computer and use it in GitHub Desktop.
Save henrik/bedf70f301e48512334263120c89e220 to your computer and use it in GitHub Desktop.
Validate Spanish NIFs ("CIFs") in Ruby.
# By "organization number" we mean an organization NIF (Spanish tax identification number). We don't mean personal NIFs such as DNI/NIE (see `SpanishIdentityNumber` for that).
#
# This class also supports personal NIFs starting with the letters K, L or M, though it's inaccurate to call these "organization numbers". More about these: https://translate.google.co.uk/translate?sl=auto&tl=en&u=https%3A%2F%2Fes.wikipedia.org%2Fwiki%2FN%25C3%25BAmero_de_identificaci%25C3%25B3n_fiscal
#
# It's easier to support them than not to, and means that any such users can provide this number, even though it's represented a bit inaccurately in our system. They're likely to be very rare. Feel free to think up a way to represent it more accurately. (Renaming `spanish_organization_number` to something like `non_dni_or_nie_spanish_tax_identification_number`?)
class SpanishOrganizationNumber
# NIFs do not have the letter prefixes "I" and "O" since those can be confused for the digits "1" and "0".
# Don't know why "T" is not a prefix.
# "X", "Y" and "Z" are NIE prefixes, so they're not used for NIFs that are not NIEs.
RE = /\A[ABCDEFGHIJKLMNOPQRSUVW]\d{7}[\dA-Z]\z/
PREFIXES_THAT_HAVE_A_LETTER_AS_CHECK_SUM = %w[ N P Q R S W ]
def self.valid?(number)
number = number.to_s.upcase.strip
return false unless number.match?(RE)
central_digits = number[1, 7]
sum = central_digits.each_char.with_index.sum { |digit_string, zero_based_index|
digit = digit_string.to_i
index = zero_based_index + 1
if index.odd?
double = digit * 2
(double / 10) + (double % 10)
else
digit
end
}
digit = (10 - sum.to_s.last.to_i)
digit = 0 if digit == 10
check_sum =
if PREFIXES_THAT_HAVE_A_LETTER_AS_CHECK_SUM.include?(number[0])
"JABCDEFGHI"[digit]
else
digit.to_s
end
number[-1] == check_sum
end
end
require "spec_helper"
require "spanish_organization_number"
describe SpanishOrganizationNumber, ".valid?" do
it "is true for a valid organization NIF" do
expect(SpanishOrganizationNumber.valid?("B08670408")).to be true
expect(SpanishOrganizationNumber.valid?("Q5355054G")).to be true
end
it "is false for a NIF that doesn't match the checksum" do
expect(SpanishOrganizationNumber.valid?("B08670409")).to be false
end
it "is false for a NIF with an invalid prefix letter" do
expect(SpanishOrganizationNumber.valid?("T08670408")).to be false
end
it "is false for a number that doesn't match the format at all" do
expect(SpanishOrganizationNumber.valid?("ABC123")).to be false
end
it "is false for nil" do
expect(SpanishOrganizationNumber.valid?(nil)).to be false
end
it "is false for the empty string" do
expect(SpanishOrganizationNumber.valid?("")).to be false
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment