=pod

=for vim
   vim: tw=72 ts=3 sts=3 sw=3 et ai :

=encoding utf8

=head1 NAME

Validate::CodiceFiscale - Validate an Italian "Codice Fiscale"


=head1 VERSION

This document describes Validate::CodiceFiscale version 0.003002.

=begin html

<a href="https://travis-ci.org/polettix/Validate-CodiceFiscale">
<img alt="Build Status" src="https://travis-ci.org/polettix/Validate-CodiceFiscale.svg?branch=master">
</a>
<a href="https://www.perl.org/">
<img alt="Perl Version" src="https://img.shields.io/badge/perl-5.24+-brightgreen.svg">
</a>
<a href="https://badge.fury.io/pl/Validate-CodiceFiscale">
<img alt="Current CPAN version" src="https://badge.fury.io/pl/Validate-CodiceFiscale.svg">
</a>
<a href="http://cpants.cpanauthors.org/dist/Validate-CodiceFiscale">
<img alt="Kwalitee" src="http://cpants.cpanauthors.org/dist/Validate-CodiceFiscale.png">
</a>
<a href="http://www.cpantesters.org/distro/O/Validate-CodiceFiscale.html?distmat=1">
<img alt="CPAN Testers" src="https://img.shields.io/badge/cpan-testers-blue.svg">
</a>
<a href="http://matrix.cpantesters.org/?dist=Validate-CodiceFiscale">
<img alt="CPAN Testers Matrix" src="https://img.shields.io/badge/matrix-@testers-blue.svg">
</a>

=end html

=head1 SYNOPSIS

One-liner:

   $ perl -MValidate::CodiceFiscale=r -er RSSMRA98S03B833G

Module usage:

   use Validate::CodiceFiscale qw< assert_valid_cf is_valid_cf validate_cf >;

   my $valid   = 'RSSMRA98S03B833G';
   my $invalid = 'RSICRL99C51C967X';

   # the first does not warn, the second does warn
   eval { assert_valid_cf($valid);   1 } or warn "died: $@";
   eval { assert_valid_cf($invalid); 1 } or warn "died: $@";

   # plain boolean test, any error short-circuits
   if (is_valid_cf($cf)) { ... }

   # get everything that's wrong
   if (my $errors = validate_cf($invalid)) {
      say for $errors->@*; # array with error report, one line per error
   }

   # it's possible to pass additional validation options, like specific
   # data. All are optional, if present they're validate, otherwise
   # ignored.
   assert_valid_cf($cf,
      {
         data => {
            name => 'Foo',
            surname => 'Bar',
            sex => 'f',
            date => '1998-03-11',
            place => 'B833',
         }
      }
   );

   # the assertion short-circuits by default, failing at the first
   # error. It's possible to check everyting and get a longer error
   # message, in case.
   assert_valid_cf($cf, { all_errors => 1 });

   # it's also possible to wrap the error generation, by returning the
   # exception to throw
   assert_valid_cf($cf,
      {
         all_errors => 1,
         on_error => sub {
            my @errors = @_;
            return "number of errors: $n_errors\n";
         }
      }
   );

   # of course, it's possible to throw the exception directly
   use Ouch;
   assert_valid_cf($cf, { on_error => sub { ouch 400, $_[0] } });

=head1 DESCRIPTION

This module performs partial validation of Italian Codice Fiscale,
mainly at the syntactic level. For proper and legally acknowledged
validation please refer to the official service by Agenzia delle Entrate
at L<https://telematici.agenziaentrate.gov.it/VerificaCF/Scegli.do?parameter=verificaCf>.

At the basic level, it allows spotting common errors that might come
from copying/typing the Codice Fiscale, e.g. typing a 0 (zero) instead
of an uppercase o letter, or similar errors with other digits/letters.

The validation can optionally take additional data regarding the name,
surname, birth date, birth place, and sex to perform a more thorough
validation based on the official rules. This part is subject to false
validations (negative or positive) for the following reasons:

=over

=item *

Places are generally validated against available data collected from
L<ANPR|https://www.anagrafenazionale.interno.it/> and
L<ISTAT|https://www.istat.it/>, but they might be lacking. Additionally,
there is no attempt at matching I<similar> names, or names that are
valid but e.g. expressed in their original language.

=item *

The algorithm to generate the CodiceFiscale might lead to the same
string for different people. This is accounted for by using a
substitution of digits with letters, but only Agenzia delle Entrate can
tell whether the specific person holds the specific variant of the code.

=back

In the end, it provides a reaasonable way of spotting negatives, but it
might validate codes that would otherwise be considered invalid by the
reference authority because there is nobody tied to the otherwise
synctactically valid code.

=head1 INTERFACE

There are four main functions for doing checks, each targeting a
different I<style> of use, plus an additional function that can be
helpful for one-liners.

=head2 B<< assert_valid_cf >>

   assert_valid_cf($cf, %options);

Check validity of the provided C<$cf> and throw an exception if the
check fails.

By default, the exception is thrown:

=over

=item *

using L<Carp>'s C<croak> with a message containing a string joining all
errors

=item *

as soon as one of the validation checks fails.

=back

Supported options:

=over

=item C<all_errors>

collect all errors to be fed into the exception, not only the first one.

=item C<data>

pass additional data for validation, as a hash reference optionally
containing keys C<name>, C<surname>, C<date>, C<sex>, and C<place>.

=item C<on_error>

wrap the exception generation with a sub reference used as a callback
with the following signature:

   sub (@error_strings) { ... }

The sub can throw the exception itself; otherwise, its return value will
be used as the argument for C<die>.

=back

=head2 B<< decode_cf >>

   my $decoded = decode_cf($cf, %options);

Perform a full validation and provides back a data structure with the
parsed data and the outcome of all available tests. This is the most
complete form of validation that can be performed by this module.

Supported options:

=over

=item C<data>

pass additional data for validation, as a hash reference optionally
containing keys C<name>, C<surname>, C<date>, C<sex>, and C<place>.

=item C<years_baseline>

the baseline year for disambiguating two-digits. As an example, people
born in 1920 and people born in 2020 both have C<20> as the year part in
their respective codes, so this allows setting the right expectation. In
lack of this parameter, whatever came last is selected (in the example
above, C<20> would be interpreted as C<2020>).

=back

The output data structure contains the following keys:

=over

=item C<errors>

pointing to an array reference with a list of validation errors (if
any);

=item C<portions>

pointing to a hash reference with the disassemble of the input data, if
possible (it is always possible as long as the input length is 16
characters).

=item C<date>

as C<YYYY-MM-DD>)

=item C<place>

as string of characters

=item C<sex>

as a single-letter character, either C<F> or C<M>

=item C<year>

=item C<month>

=item C<day>

as integers.

=back

The decoded data can be useful to perform further validations, e.g. for
validating the place name in cases that are not covered by this module
(e.g. when expressed in a language different from Italian, etc.).

=head2 B<< is_valid_cf >>

   my $boolean = is_valid_cf($cf, %options);

Check the validity; it short-circuits at the first error. Returns a
boolean value.

It's possible to pass an additional key/value pair with key C<data> and
a hash reference optionally containing keys C<name>, C<surname>,
C<date>, C<sex>, and C<place>.


=head2 B<< r >>

   $ perl -MValidate::CodiceFiscale=r -er RSSMRA98S03B833G

The C<r> function can be useful in one-liners to check one or more codes
from the command line. By default, the C<r> function will read input
codes from C<@ARGV>.

=head2 B<< validate_cf >>

   my $errors = validate_cf($cf, %options);

Check the validity and return C<undef> if no errors were encountered, or
a reference to an array containing the list of errors otherwise.

Supported options:

=over

=item C<all_errors>

collect all errors to be fed into the exception, not only the first one.
This option defaults to a true value, to collect all errors; it's
possible to pass a I<false> value to short-circuit and exit at the first
error.

=item C<data>

pass additional data for validation, as a hash reference optionally
containing keys C<name>, C<surname>, C<date>, C<sex>, and C<place>.

=item C<years_baseline>

the baseline year for disambiguating two-digits. As an example, people
born in 1920 and people born in 2020 both have C<20> as the year part in
their respective codes, so this allows setting the right expectation. In
lack of this parameter, whatever came last is selected (in the example
above, C<20> would be interpreted as C<2020>).

=back

=head1 SEE ALSO

L<String::CodiceFiscale> is an alternative and precedent module to deal
with Codice Fiscale. I'm a bit scared about using a class method
C<error> to collect validation errors, hence this module.

=head1 BUGS AND LIMITATIONS

Minimum perl version 5.24.

Report bugs through GitHub (patches welcome) at
L<https://github.com/polettix/Validate-CodiceFiscale>.

=head1 AUTHOR

Flavio Poletti <flavio@polettix.it>

=head1 COPYRIGHT AND LICENSE

This distribution is mainly composed of code, but it also includes
I<data> (in the C<__DATA__> section of the module) that is derived from
publicly available data sources.

=head2 Code

Copyright 2023 by Flavio Poletti <flavio@polettix.it>

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.


=head2 Data

The data contained in the C<__DATA__> section of the main module is
assembled from the following sources:

=over

=item *

Archive of Italian codes for places, including periods of validity, from file
L<ANPR_archivio_comuni.csv|https://www.anagrafenazionale.interno.it/wp-content/uploads/ANPR_archivio_comuni.csv>, available from Italian Ministero
dell'Interno with license L<CC-BY 4.0|https://creativecommons.org/licenses/by/4.0/legalcode.it> (according to page L<Note legali|https://www.anagrafenazionale.interno.it/note-legali/> as of 2023-08-06).
The file is accessible from page L<Archivio storico dei comuni|https://www.anagrafenazionale.interno.it/area-tecnica/archivio-storico-dei-comuni/>.

=item *

Archive of codes for foreign places, from file L<Elenco codici e denominazioni delle unità territoriali estere|https://www.istat.it/it/files//2011/01/Elenco-codici-e-denominazioni-unita-territoriali-estere.zip>, available from
L<ISTAT|https://www.istat.it/> with license L<CC-BY 3.0|http://creativecommons.org/licenses/by/3.0/it/> (according to page L<Note legali: responsabilità e licenza|https://www.istat.it/it/note-legali> as of 2023-08-06).
The file is accessible from page L<Codici delle unità territoriali estere|https://www.istat.it/it/archivio/6747>.

=back

=cut
