Skip to content

kamaladafrica/codice-fiscale

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

99 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Codice Fiscale

Maven Central pmd CodeQL

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

Features

Usage

In order to see it in action, check the tests

Calculation

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

Cities

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');

Reverse calculation

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.

Validation

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

Comparison and compatibility

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

Support for omocode levels

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
Implementation note

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).

Getting started

All you need to do is to declare the dependency

<dependency>
  <groupId>it.kamaladafrica</groupId>
  <artifactId>codice-fiscale</artifactId>
  <version>...</version>
</dependency>

Contributing

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.

Update italian cities names, provinces and codes

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