Yet another Codice Fiscale (italian tax code) calculator. Only natural person tax code calculation is supported (16 chars).
Codice fiscale is composed by 5 parts:
- Lastname part
- Firstname part
- Birth date and sex part
- Belfiore part
- Control part
You can access every part:
CodiceFiscale cf = CodiceFiscale.of("RSSMRA75C22H501I");
Part firstnamePart = cf.getFirstname();
System.out.println(firstnamePart.getValue()); // MRA
DatePart datePart = cf.getDate();
System.out.println(datePart.isFemale()); // false
- Calculation (codice fiscale from person data)
- Reverse calculation (person data from codice fiscale)
- Validation
- Comparison and compatibility
- Support for omocode levels
In order to see it in action, check the tests
You can calulate the tax code by person data.
CityByName cities = CityProvider.ofDefault();
City rome = cities.findByName("Roma");
// City rome = City.builder().name("ROMA").prov("RM").belfiore("H501").build();
Person person = Person.builder()
.firstname("Mario")
.lastname("Rossi")
.birthDate(LocalDate.of(1975, 3, 22))
.isFemale(false)
.city(rome)
.build();
CodiceFiscale cf = CodiceFiscale.of(PERSON);
System.out.println(cf.getValue()); // RSSMRA75C22H501I
City objects can be built by hands:
City city = City.builder().name("ROMA").prov("RM").belfiore("H501").build();
or, as suggested, using a CityProvider
object.
A CityProvider implements the two interfaces CityByName
and CityByBelfiore
: the former is used during calculation, the latter during reverse calculation.
CityByName cities = CityProvider.ofDefault();
City rome = cities.findByName('Roma');
// City rome = cities.findByBelfiore('H501');
A default CityProvider
is provided but you can use your own CityProvider
if you want.
CityProvider cities = CityProvider.ofDefault();
// CityByName cities = CityProvider.ofDefault();
// CityByBelfiore cities = CityProvider.ofDefault();
Default CityProvider
reads cities from official Istat file, downloaded from https://www.istat.it/storage/codici-unita-amministrative/Elenco-codici-statistici-e-denominazioni-delle-unita-territoriali.zip
Of course, you can provide your own set of cities creating a CityProviderImpl
instance with a Supplier<Set<City>>
object that supplies you own City
set.
Set<City> myCities = ...;
CityProvider cities = CityProvider.of(() -> myCities);
City city = cities.findByName('Roma');
You can figure out some of the person data from a codice fiscale.
CodiceFiscale cf = CodiceFiscale.of("RSSMRA75C22H501I");
// or if you want your own implementation of CityProvider
//
// CityByBelfiore cities = new MyCityProvider();
// CodiceFiscale cf = CodiceFiscale.of("RSSMRA75C22H501I", cities);
Person person = cf.getPerson();
System.out.println(person.getCity().getName()); // ROMA
System.out.println(person.isFemale()); // false
System.out.println(person.getFirstname()); // MRA // you can't figure out real first name
Of course you can figure out only sex, birth date and city of birth, but no the real first name and last name.
You can validate a codice fiscale by building a CodiceFiscale
instance or using the static method CodiceFiscale.validate(String)
or CodiceFiscale.isFormatValid(String)
.
Creating an instance of CodiceFiscale
using of
performs a deep validation checking the validity of the Belfiore part.
On the other hand validate
(that throws exception if code is invalid) and isFormatValid
(return true if code is valid, false otherwise) only check that codice fiscale is "grammatically" correct and the control char matches.
CodiceFiscale cf = CodiceFiscale.of("RSSMRA75C22Z999I"); // throws exception due to both bad Belfiore part and bad control char
CodiceFiscale.isFormatValid("RSSMRA75C22H501I"); // returns true
CodiceFiscale.isFormatValid("RSSMRA75C22H501X"); // returns false due to bad control char (should be I)
CodiceFiscale.validate("RSSMRA75C22H501X"); // throws exception due to bad control char
You can compare two codice fiscale even thought they have different omocode levels.
CodiceFiscale cf1 = CodiceFiscale.of("RSSMRA75C22H501I"); // no omocode
CodiceFiscale cf2 = CodiceFiscale.of("RSSMRA75C22H5LML"); // omocode level 3
cf1.isEqual(cf2); // returns true (ignore omocode level)
cf1.isEqual(cf2, false); // return false (compares omocode level)
cf1.isEqual("RSSMRA75C22H5LML"); // returns true
Also you can check if a codice fiscale is compatible with a person data set.
Person person = Person.builder()
.firstname("Mario")
.lastname("Rossi")
.birthDate(LocalDate.of(1975, 3, 22))
.isFemale(false)
.city(rome)
.build();
CodiceFiscale cf = CodiceFiscale.isCompatible("RSSMRA75C22H5LML", person); // return true
A codice fiscale can be omocodic if two or more people share the same codice fiscale (learn more on Wikipedia).
Level indicates how many letters are changed. A letter is changed when another person has the same codice fiscale, so a omocode level 2 means that at least 3 people have the same codice fiscale.
Level is 0 <= level <= 127.
You can change omocode level using toOmocodeLevel
. The method normalized
is the same as toOmocodeLevel(0)
Person person = Person.builder()
.firstname("Mario")
.lastname("Rossi")
.birthDate(LocalDate.of(1975, 3, 22))
.isFemale(false)
.city(rome)
.build();
CodiceFiscale cf0 = CodiceFiscale.of(person).toOmocodeLevel(3);
System.out.println(cf0.getValue()); // RSSMRA75C22H501I
System.out.println(cf0.getOmocodeLevel()); // 0
CodiceFiscale cf2 = CodiceFiscale.of(person).toOmocodeLevel(3);
System.out.println(cf2.getValue()); // RSSMRA75C22H5LML
System.out.println(cf2.getOmocodeLevel()); // 3
System.out.println(cf0.isEqual(cf2)); // true
CodiceFiscale cf = cf2.normalized();
System.out.println(cf.getValue()); // RSSMRA75C22H501I
System.out.println(cf.getOmocodeLevel()); // 0
In omocodic codes only numbers can be replaced with a one-to-one mapping to a letter. Numbers are 7 in a standard codice fiscale so the level is computed as it was a bitmask where first 4 digit are for date part, last 3 for belfiore part. For example 0001-001 means, from left to right, that are changing the last digit of date part and the last of belfiore part (ones that have bit set to 1); the level associated is 9 (0001001).
For example, in RSSMRA75C22H5LML code the omocode level is 3 because it is computed as 0000011 (no date part is replaced, last 2 digit of belfiore part are replaced).
All you need to do is to declare the dependency
<dependency>
<groupId>it.kamaladafrica</groupId>
<artifactId>codice-fiscale</artifactId>
<version>...</version>
</dependency>
Here are some ways for you to contribute:
- Create GitHub tickets for bugs or new features and comment on the ones that you are interested in.
- GitHub is for social coding: if you want to write code, we encourage contributions through pull requests from forks of this repository. If you want to contribute code this way, please reference a GitHub ticket as well covering the specific issue you are addressing.
Cities names, provinces and codes are stored in a csv file located in src/main/resources/italia.csv
.
In order to update csv with latest changes from Anagrafe Nazione della Popolazione Residente easily, you can take advantage of the script scripts/get_belfiore_anpr.sh
.
It prints ready to use csv (filtered and formatted) to the standard output so you can redirect it to a file, for example.
Usage:
$ scripts/get_belfiore_anpr.sh > src/main/resources/italia.csv