lib/cfita/codice_fiscale.rb in cfita-0.0.1 vs lib/cfita/codice_fiscale.rb in cfita-0.0.2

- old
+ new

@@ -1,23 +1,29 @@ # frozen_string_literal: true require 'active_support/all' +# require './belfiore.rb' module Cfita # Controllo codice fiscale italiano class CodiceFiscale - attr_reader :codice_fiscale, :data, :errors, :sesso + attr_reader :fiscal_code, + :sex, + :birth_place, + :birth_date, + :data, + :errors - def initialize(codice_fiscale) - @codice_fiscale = codice_fiscale.upcase.strip + def initialize(fiscal_code) + @fiscal_code = fiscal_code.upcase.strip @data = {} @errors = [] parse end def to_s - codice_fiscale + fiscal_code end def valid? errors.empty? end @@ -30,50 +36,114 @@ return if errors.any? check_checksum return if errors.any? - check_sex + check_birth_date + # check_birth_place end def check_size - size = @codice_fiscale.size + size = @fiscal_code.size errors << "Lunghezza errata (#{size})" unless size == 16 end def check_chars - test = @codice_fiscale == @codice_fiscale.parameterize.upcase[/^[A-Z0-9]*$/] + test = @fiscal_code == @fiscal_code[/^[A-Z0-9]*$/] errors << 'Caratteri non ammessi' unless test end def check_checksum - errors << 'Checksum errato' if checksum != @codice_fiscale.last + errors << 'Checksum errato' if checksum != @fiscal_code.last end def check_sex - case @codice_fiscale[9] + case @fiscal_code[9] when /[0-3LMNP]/ - @data[:sesso] = 'M' + @data[:sex] = 'M' when /[4-7QRST]/ - @data[:sesso] = 'F' + @data[:sex] = 'F' else @errors << 'Cifra decina giorno di nascita errata' end end + def check_birth_place + letter = @fiscal_code[11] + numbers = + @fiscal_code[12..14] + .split(//) + .map do |c| + i = OMOCODICI.index(c) + i ? i.to_s : c + end + .join + belfiore = letter + numbers + + @birth_place = BELFIORE[belfiore] + @errors << "Codice istat #{belfiore} non trovato" unless @birth_place + end + + MESI = 'ABCDEHLMPRST'.freeze + + def check_birth_date + yy = cifre(6..7) + return if @errors.any? + + day = cifre(9..10) + return if @errors.any? + + @errors << 'Cifra decina giorno di nascita errata' if day > 71 + return if @errors.any? + + @data[:sex] = day > 40 ? 'F' : 'M' + + month = MESI.index(@fiscal_code[8]) + @errors << 'Mese errato' unless month + return if @errors.any? + + @birth_date = + Date.new(yy2yyyy(yy), month + 1, day % 40) rescue nil + + @errors << 'Data di nascita errata' unless @birth_date + return if @errors.any? + + @data[:birth_date] = @birth_date + end + + def yy2yyyy(yy) + Date.today.year - + (Date.today.year % 100 + 100 - yy ) % 100 + end + + 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 + DISPARI = [ 1, 0, 5, 7, 9, 13, 15, 17, 19, 21, 2, 4, 18, 20, 11, 3, 6, 8, 12, 14, 16, 10, 22, 25, 24, 23 ].freeze OMOCODICI = 'LMNPQRSTUV' + CIFRE = ('0123456789' + OMOCODICI).freeze def checksum tot = 0 - @codice_fiscale[0..14].bytes.first(15).each.with_index do |byte, i| + @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