class Cfita::CodiceFiscale

Controllo codice fiscale italiano

Constants

CIFRE
CONSONANTS
DISPARI
MESI
OMOCODICI
VOWELS

Attributes

birth_date[R]
birth_place[R]
errors[R]
fiscal_code[R]
sex[R]

Public Class Methods

ccat() click to toggle source
# File lib/cfita/codice_fiscale.rb, line 15
def self.ccat
  @ccat ||= JSON.parse(open('ccat.json'))
end
new( fiscal_code, birth_place: nil, birth_date: nil, name: nil, surname: nil, sex: nil ) click to toggle source
# File lib/cfita/codice_fiscale.rb, line 19
def initialize(
  fiscal_code,
  birth_place: nil,
  birth_date: nil,
  name: nil,
  surname: nil,
  sex: nil
)
  @fiscal_code = fiscal_code.upcase.strip
  @birth_place = birth_place&.upcase
  @birth_date = birth_date
  @birth_date = Date.parse(birth_date) if birth_date.is_a?(String)
  @name = name&.parameterize&.upcase
  @surname = surname&.parameterize&.upcase
  @sex = sex&.upcase
  @errors = []
  parse
end

Public Instance Methods

cf_birth_date() click to toggle source
# File lib/cfita/codice_fiscale.rb, line 64
def cf_birth_date
  return @cf_birth_date if @cf_birth_date
  yy = cifre(6..7)
  day = cifre(9..10)
  @errors << 'Cifra decina giorno di nascita errata' if day && day > 71
 
  month = MESI.index(@fiscal_code[8])
  @errors << 'Mese errato' unless month
  return unless yy && month && day

  @cf_birth_date = Date.new(yy2yyyy(yy), month + 1, day % 40) rescue nil
end
cf_birth_places() click to toggle source
# File lib/cfita/codice_fiscale.rb, line 58
def cf_birth_places
  return @cf_birth_places if @cf_birth_places

  @cf_birth_places = CODICI_CATASTALI[codice_catastale]
end
cf_sex() click to toggle source
# File lib/cfita/codice_fiscale.rb, line 46
def cf_sex
  return @cf_sex if @cf_sex
  case @fiscal_code[9]
  when /[0-3LMNP]/
    @cf_sex = 'M'
  when /[4-7QRST]/
    @cf_sex = 'F'
  else
    @errors << 'Cifra decina giorno di nascita errata'
  end
end
to_s() click to toggle source
# File lib/cfita/codice_fiscale.rb, line 38
def to_s
  fiscal_code
end
valid?() click to toggle source
# File lib/cfita/codice_fiscale.rb, line 42
def valid?
  errors.empty?
end

Private Instance Methods

check_birth_date() click to toggle source
# File lib/cfita/codice_fiscale.rb, line 182
def check_birth_date
  @errors << 'Data di nascita errata' unless cf_birth_date
  return if @errors.any?

  if @birth_date
    @errors << 'Data di nascita errata' if @birth_date != cf_birth_date
  else
    @birth_date = cf_birth_date
  end
end
check_birth_place() click to toggle source
# File lib/cfita/codice_fiscale.rb, line 166
def check_birth_place
  @errors << "Codice istat #{codice_catastale} non trovato" unless cf_birth_places
  if @birth_place
    unless cf_birth_places&.include?(@birth_place)
      @errors << "Luogo di nascita #{@birth_place} non coerente, al codice catastale #{codice_catastale} corrisponde a #{cf_birth_places.join(' o a ')}"
    end
  end
end
check_chars() click to toggle source
# File lib/cfita/codice_fiscale.rb, line 149
def check_chars
  test = @fiscal_code == @fiscal_code[/^[A-Z0-9]*$/]
  errors << 'Caratteri non ammessi' unless test
end
check_checksum() click to toggle source
# File lib/cfita/codice_fiscale.rb, line 154
def check_checksum
  errors << 'Checksum errato' if checksum != @fiscal_code.last
end
check_name() click to toggle source
# File lib/cfita/codice_fiscale.rb, line 108
def check_name
  return unless @name

  a, b, c, d = consonants(@name)
  name_code = (
    (d ? [a, c, d] : [a, b, c]).compact.join +
    vowels(@name).join +
    'XXX'
  )[0..2]

  errors << "Il nome non corrisponde al codice '#{name_code}'" unless name_code == @fiscal_code[3..5]
end
check_sex() click to toggle source
# File lib/cfita/codice_fiscale.rb, line 158
def check_sex
  if @sex
    errors << 'Sesso errato' if @sex != cf_sex
  else
    @sex = cf_sex
  end
end
check_size() click to toggle source
# File lib/cfita/codice_fiscale.rb, line 144
def check_size
  size = @fiscal_code.size
  errors << "Lunghezza errata (#{size})" unless size == 16
end
check_surname() click to toggle source
# File lib/cfita/codice_fiscale.rb, line 121
def check_surname
  return unless @surname

  surname_code = (
    consonants(@surname).join +
    vowels(@surname).join +
    'XXX'
  )[0..2]

  errors << "Il cognome non corrisponde al codice '#{surname_code}'" unless surname_code == @fiscal_code[0..2]
end
checksum() click to toggle source
# File lib/cfita/codice_fiscale.rb, line 218
def checksum
  tot = 0
  @fiscal_code[0..14].bytes.first(15).each.with_index do |byte, i|
    next unless byte

    byte -= byte < 65 ? 48 : 65
    sign = (i % 2).zero?
    tot += sign ? DISPARI[byte] : byte
  end
  (tot % 26 + 65).chr
end
cifre(range) click to toggle source
# File lib/cfita/codice_fiscale.rb, line 193
def cifre(range)
  result = 0
  range.each do |position|
    char = @fiscal_code[position]
    value = CIFRE.index(char)
    @errors << "Carattere '#{char}' errato in posizione #{position}" unless value
    return nil unless value

    result *= 10
    result += value % 10
  end
  result
end
codice_catastale() click to toggle source
# File lib/cfita/codice_fiscale.rb, line 79
def codice_catastale 
  return @codice_catastale if @codice_catastale
  letter = @fiscal_code[11]
  numbers =
    @fiscal_code[12..14]
    .split(//)
    .map do |c|
      i = OMOCODICI.index(c)
      i ? i.to_s : c
    end
    .join
  letter + numbers
end
consonants(word) click to toggle source
# File lib/cfita/codice_fiscale.rb, line 140
def consonants(word)
  word.chars.select { |char| char.in? CONSONANTS }
end
parse() click to toggle source
# File lib/cfita/codice_fiscale.rb, line 93
def parse
  check_size
  check_chars
  return if errors.any?

  check_checksum
  return if errors.any?

  check_name
  check_surname
  check_birth_date
  check_birth_place
  check_sex
end
vowels(word) click to toggle source
# File lib/cfita/codice_fiscale.rb, line 136
def vowels(word)
  word.chars.select { |char| char.in? VOWELS }
end
yy2yyyy(year_as_yy) click to toggle source
# File lib/cfita/codice_fiscale.rb, line 177
def yy2yyyy(year_as_yy)
  Date.today.year -
    (Date.today.year % 100 + 100 - year_as_yy) % 100
end