mirror of
https://github.com/danog/CodiceFiscaleJS.git
synced 2024-11-26 20:14:55 +01:00
Bug fix and example completion
This commit is contained in:
parent
cf50ad76c1
commit
50524230e9
4
bower.json
Normal file → Executable file
4
bower.json
Normal file → Executable file
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "codice-fiscale-js",
|
||||
"description": "Calcolo del codice fiscale",
|
||||
"main": "dist/codice.fiscale.var.js",
|
||||
"main": "dist/codice.fiscale.js",
|
||||
"authors": [
|
||||
"Luca Vandro",
|
||||
"Walter Barbagallo"
|
||||
@ -25,4 +25,4 @@
|
||||
"test",
|
||||
"tests"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
8453
build/js/comuni.js
8453
build/js/comuni.js
File diff suppressed because it is too large
Load Diff
@ -1,103 +0,0 @@
|
||||
export const MONTH_CODES = [
|
||||
'A',
|
||||
'B',
|
||||
'C',
|
||||
'D',
|
||||
'E',
|
||||
'H',
|
||||
'L',
|
||||
'M',
|
||||
'P',
|
||||
'R',
|
||||
'S',
|
||||
'T',
|
||||
];
|
||||
export const CHECK_CODE_ODD = {
|
||||
0: 1,
|
||||
1: 0,
|
||||
2: 5,
|
||||
3: 7,
|
||||
4: 9,
|
||||
5: 13,
|
||||
6: 15,
|
||||
7: 17,
|
||||
8: 19,
|
||||
9: 21,
|
||||
A: 1,
|
||||
B: 0,
|
||||
C: 5,
|
||||
D: 7,
|
||||
E: 9,
|
||||
F: 13,
|
||||
G: 15,
|
||||
H: 17,
|
||||
I: 19,
|
||||
J: 21,
|
||||
K: 2,
|
||||
L: 4,
|
||||
M: 18,
|
||||
N: 20,
|
||||
O: 11,
|
||||
P: 3,
|
||||
Q: 6,
|
||||
R: 8,
|
||||
S: 12,
|
||||
T: 14,
|
||||
U: 16,
|
||||
V: 10,
|
||||
W: 22,
|
||||
X: 25,
|
||||
Y: 24,
|
||||
Z: 23,
|
||||
};
|
||||
export const CHECK_CODE_EVEN = {
|
||||
0: 0,
|
||||
1: 1,
|
||||
2: 2,
|
||||
3: 3,
|
||||
4: 4,
|
||||
5: 5,
|
||||
6: 6,
|
||||
7: 7,
|
||||
8: 8,
|
||||
9: 9,
|
||||
A: 0,
|
||||
B: 1,
|
||||
C: 2,
|
||||
D: 3,
|
||||
E: 4,
|
||||
F: 5,
|
||||
G: 6,
|
||||
H: 7,
|
||||
I: 8,
|
||||
J: 9,
|
||||
K: 10,
|
||||
L: 11,
|
||||
M: 12,
|
||||
N: 13,
|
||||
O: 14,
|
||||
P: 15,
|
||||
Q: 16,
|
||||
R: 17,
|
||||
S: 18,
|
||||
T: 19,
|
||||
U: 20,
|
||||
V: 21,
|
||||
W: 22,
|
||||
X: 23,
|
||||
Y: 24,
|
||||
Z: 25,
|
||||
};
|
||||
export const OMOCODIA_TABLE = {
|
||||
0: 'L',
|
||||
1: 'M',
|
||||
2: 'N',
|
||||
3: 'P',
|
||||
4: 'Q',
|
||||
5: 'R',
|
||||
6: 'S',
|
||||
7: 'T',
|
||||
8: 'U',
|
||||
9: 'V',
|
||||
};
|
||||
export const CHECK_CODE_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
@ -1,47 +0,0 @@
|
||||
export function normalizeString(str) {
|
||||
return str.trim()
|
||||
.replace(new RegExp(/[àá]/g), 'a\'')
|
||||
.replace(new RegExp(/[èé]/g), 'e\'')
|
||||
.replace(new RegExp(/[ìí]/g), 'i\'')
|
||||
.replace(new RegExp(/[òó]/g), 'o\'')
|
||||
.replace(new RegExp(/[ùú]/g), 'u\'')
|
||||
.toUpperCase();
|
||||
}
|
||||
export function daysInMonth(m, y) {
|
||||
switch (m) {
|
||||
case 1:
|
||||
return (y % 4 === 0 && y % 100 !== 0) || (y % 400 === 0) ? 29 : 28;
|
||||
case 8:
|
||||
case 3:
|
||||
case 5:
|
||||
case 10:
|
||||
return 30;
|
||||
default:
|
||||
return 31;
|
||||
}
|
||||
}
|
||||
export function isValidDate(d, m, y) {
|
||||
const month = m - 1;
|
||||
return month >= 0 && month < 12 && d > 0 && d <= daysInMonth(month, y);
|
||||
}
|
||||
export function getValidDate(d, m, y) {
|
||||
if (isValidDate(d, m, y)) {
|
||||
return new Date(y, m - 1, d, 0, 0, 0, 0);
|
||||
}
|
||||
else {
|
||||
throw new Error(`The date ${y}/${m}/${d} is not a valid date`);
|
||||
}
|
||||
}
|
||||
export function extractVowels(str) {
|
||||
return str.replace(/[^AEIOU]/gi, '');
|
||||
}
|
||||
export function extractConsonants(str) {
|
||||
return str.replace(/[^BCDFGHJKLMNPQRSTVWXYZ]/gi, '');
|
||||
}
|
||||
export function pad(n, size = 2) {
|
||||
let s = String(n);
|
||||
while (s.length < size) {
|
||||
s = `0${s}`;
|
||||
}
|
||||
return s;
|
||||
}
|
2
dist/codice.fiscale.amd.js
vendored
Normal file → Executable file
2
dist/codice.fiscale.amd.js
vendored
Normal file → Executable file
File diff suppressed because one or more lines are too long
2
dist/codice.fiscale.commonjs2.js
vendored
Normal file → Executable file
2
dist/codice.fiscale.commonjs2.js
vendored
Normal file → Executable file
File diff suppressed because one or more lines are too long
2
dist/codice.fiscale.umd.js
vendored
Normal file → Executable file
2
dist/codice.fiscale.umd.js
vendored
Normal file → Executable file
File diff suppressed because one or more lines are too long
2
dist/codice.fiscale.var.js
vendored
Normal file → Executable file
2
dist/codice.fiscale.var.js
vendored
Normal file → Executable file
File diff suppressed because one or more lines are too long
7
example/bootstrap.bundle.min.js
vendored
Normal file
7
example/bootstrap.bundle.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
12
example/bootstrap.min.css
vendored
Normal file
12
example/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
example/codice.fiscale.var.js
Executable file
1
example/codice.fiscale.var.js
Executable file
File diff suppressed because one or more lines are too long
@ -1,29 +1,273 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script
|
||||
src="https://code.jquery.com/jquery-3.3.1.min.js"
|
||||
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="../dist/codice.fiscale.var.js"></script>
|
||||
<link rel="stylesheet" href="bootstrap.min.css">
|
||||
<title>CodiceFiscaleJS</title>
|
||||
<script src="jquery-3.3.1.min.js"></script>
|
||||
<script src="bootstrap.bundle.min.js"></script>
|
||||
<script src="codice.fiscale.var.js"></script>
|
||||
<!-- Place this tag in your head or just before your close body tag. -->
|
||||
<script async defer src="https://buttons.github.io/buttons.js"></script>
|
||||
<style>
|
||||
body{
|
||||
background-color: #f7f7f9;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<form action="">
|
||||
<input type="text" name="cf" id="cf" placeholder="name"><br>
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<a class="navbar-brand" href="#">Codice Fiscale JS</a>
|
||||
</nav>
|
||||
<div class="jumbotron jumbotron-fluid text-center">
|
||||
<div class="container">
|
||||
<h1 class="display-4 text-success">Codice Fiscale JS</h1>
|
||||
<p class="lead">The Italian Tax Code Library for Javascript and Typescript</p>
|
||||
<hr class="my-4">
|
||||
<!-- Place this tag where you want the button to render. -->
|
||||
<a class="github-button" href="https://github.com/lucavandro/CodiceFiscaleJs" data-size="large" data-show-count="true" aria-label="Star lucavandro/CodiceFiscaleJs on GitHub">Star</a>
|
||||
<!-- Place this tag where you want the button to render. -->
|
||||
<a class="github-button" href="https://github.com/lucavandro/CodiceFiscaleJs/issues" data-size="large" data-show-count="true" aria-label="Issue lucavandro/CodiceFiscaleJs on GitHub">Issue</a>
|
||||
<!-- Place this tag where you want the button to render. -->
|
||||
<a class="github-button" href="https://github.com/lucavandro/CodiceFiscaleJs/archive/master.zip" data-size="large" aria-label="Download lucavandro/CodiceFiscaleJs on GitHub">Download</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="jumbotron jumbotron-fluid">
|
||||
<div class="container">
|
||||
<h1>Codice Fiscale </h1>
|
||||
<form id="cf-direct">
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label for="name">Name</label>
|
||||
<input type="text" class="form-control" id="name" placeholder="Name" name="name" value="John" required>
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label for="surname">Surname</label>
|
||||
<input type="text" class="form-control" id="surname" placeholder="Surname" name="surname" value="Doe" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label for="birthday">Gender</label>
|
||||
<select class="custom-select" name="gender" id="gender" required>
|
||||
<option selected value="M">Male</option>
|
||||
<option value="F">Female</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label for="birthday">Birthday</label>
|
||||
<input type="date" class="form-control" id="birthday" name="birthday" value="1987-04-07" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label for="birthday">Birthplace province</label>
|
||||
<select class="custom-select" name="birthplace_province" id="birthplace_province" required>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label for="birthday">Birthplace</label>
|
||||
<select class="custom-select" name="birthplace" id="birthplace" required>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
<button type="reset" class="btn btn-danger">Reset</button>
|
||||
</form>
|
||||
<pre></pre>
|
||||
<hr>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-12">
|
||||
<label for="cf">Codice Fiscale</label>
|
||||
<div class="input-group mb-3">
|
||||
<input type="text" class="form-control" id="cf-1" readonly>
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-outline-primary" type="button" id="copy">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert alert-success alert-dismissible fade" id="alert">
|
||||
Codice fiscale copied to your clipboard!!!
|
||||
<button type="button" class="close" id="copy-alert-btn" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<hr>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="jumbotron jumbotron-fluid" id="reverse">
|
||||
<div class="container">
|
||||
<h1>Reverse Codice Fiscale</h1>
|
||||
<form id="cf-reverse">
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-12">
|
||||
<label for="cf">Codice Fiscale</label>
|
||||
<div class="input-group mb-3">
|
||||
<input type="text" class="form-control" id="cf-2">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-outline-primary" type="submit">Reverse</button>
|
||||
</div>
|
||||
<div class="invalid-feedback">
|
||||
Invalid Codice Fiscale
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label for="name">Name</label>
|
||||
<input type="text" class="form-control" id="name-reverse" readonly>
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label for="surname">Surname</label>
|
||||
<input type="text" class="form-control" id="surname-reverse" readonly>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label for="birthday">Gender</label>
|
||||
<input type="text" class="form-control" id="gender-reverse" readonly>
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label for="birthday">Birthday</label>
|
||||
<input type="text" class="form-control" id="birthday-reverse" readonly>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label for="birthday">Birthplace province</label>
|
||||
<input type="text" class="form-control" id="birthplace_province-reverse" readonly>
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label for="birthday">Birthplace</label>
|
||||
<input type="text" class="form-control" id="birthplace-reverse" readonly>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="jumbotron jumbotron-fluid" id="validate">
|
||||
<div class="container">
|
||||
<h1>Validate Codice Fiscale</h1>
|
||||
<form id="cf-validate">
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-12">
|
||||
<label for="cf">Codice Fiscale</label>
|
||||
<div class="input-group mb-3">
|
||||
<input type="text" class="form-control" id="cf-3">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-outline-primary" type="submit">Validate</button>
|
||||
</div>
|
||||
<div class="invalid-feedback">
|
||||
Invalid Codice Fiscale
|
||||
</div>
|
||||
<div class="valid-feedback">
|
||||
Valid Codice Fiscale
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<hr>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(function(){
|
||||
$("#cf").change(function(){
|
||||
var cf = new CodiceFiscale($(this).val());
|
||||
$('pre').text(cf.toJSON());
|
||||
/*
|
||||
* COMPUTE CODICE FISCALE
|
||||
*/
|
||||
|
||||
CodiceFiscale.utils.birthplaceFields("#birthplace_province", "#birthplace");
|
||||
$("#birthplace_province").val("EE").change();
|
||||
setTimeout(()=>$("#birthplace").val("Z404"), 1000);
|
||||
$('form#cf-direct').submit(function(ev){
|
||||
ev.preventDefault();
|
||||
let cf = new CodiceFiscale({
|
||||
name : $("#name").val(),
|
||||
surname : $("#surname").val(),
|
||||
gender : $("#gender").val(),
|
||||
birthday : $("#birthday").val(),
|
||||
birthplace : $("#birthplace").val()
|
||||
});
|
||||
|
||||
$("#cf-1").val(cf.toString());
|
||||
|
||||
});
|
||||
|
||||
$("#copy").click(function(){
|
||||
copyToClipboard($("#cf-1").val());
|
||||
$('#alert').alert().addClass("show");
|
||||
setTimeout(()=>$('#alert').removeClass("show"), 3000);
|
||||
});
|
||||
|
||||
$("#copy-alert-btn").click(function(){
|
||||
$('#alert').removeClass("show");
|
||||
})
|
||||
|
||||
/*
|
||||
* REVERSE CODICE FISCALE
|
||||
*/
|
||||
|
||||
$("#cf-reverse").submit(function(e){
|
||||
e.preventDefault();
|
||||
|
||||
let cf = $("#cf-reverse #cf-2").val();
|
||||
if(CodiceFiscale.check(cf)){
|
||||
$("#cf-reverse #cf-2").removeClass('is-invalid');
|
||||
let cfData = CodiceFiscale.computeInverse(cf);
|
||||
$("#name-reverse").val(cfData.name);
|
||||
$("#surname-reverse").val(cfData.surname);
|
||||
$("#gender-reverse").val(cfData.gender);
|
||||
$("#birthplace-reverse").val(cfData.birthplace);
|
||||
$("#birthplace_province-reverse").val(cfData.birthplaceProvincia);
|
||||
$("#birthday-reverse").val(cfData.birthday);
|
||||
} else {
|
||||
$("#cf-reverse #cf-2").addClass('is-invalid');
|
||||
$("#cf-reverse").reset();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
* Validate CODICE FISCALE
|
||||
*/
|
||||
|
||||
$("#cf-validate").submit(function(e){
|
||||
e.preventDefault();
|
||||
$("#cf-validate #cf-3").removeClass('is-valid is-invalid');
|
||||
let cf = $("#cf-validate #cf-3").val();
|
||||
if(CodiceFiscale.check(cf)){
|
||||
$("#cf-validate #cf-3").addClass('is-valid');
|
||||
} else {
|
||||
$("#cf-validate #cf-3").addClass('is-invalid');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
function copyToClipboard (str) {
|
||||
const el = document.createElement('textarea');
|
||||
el.value = str;
|
||||
document.body.appendChild(el);
|
||||
el.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(el);
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
2
example/jquery-3.3.1.min.js
vendored
Normal file
2
example/jquery-3.3.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
11
jasmine.json
11
jasmine.json
@ -1,11 +0,0 @@
|
||||
{
|
||||
"spec_dir": "test",
|
||||
"spec_files": [
|
||||
"**/*[sS]pec.ts"
|
||||
],
|
||||
"helpers": [
|
||||
"../node_modules/ts-node/register/type-check.js"
|
||||
],
|
||||
"stopSpecOnExpectationFailure": false,
|
||||
"random": true
|
||||
}
|
7988
package-lock.json
generated
7988
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
47
package.json
Normal file → Executable file
47
package.json
Normal file → Executable file
@ -1,43 +1,34 @@
|
||||
{
|
||||
"name": "codice-fiscale-js",
|
||||
"version": "2.0.1",
|
||||
"description": "CodiceFiscale.js is a utility library to compute and validate Italian Tax code (codice fiscale).",
|
||||
"version": "2.1.0",
|
||||
"description": "The Italian Tax Code Library for Javascript and Typescript",
|
||||
"main": "dist/codice.fiscale.umd.js",
|
||||
"types": "types/main.d.ts",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/lucavandro/CodiceFiscaleJS.git"
|
||||
},
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.9.0"
|
||||
"test": "tests"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"@types/jasmine": "^2.8.8",
|
||||
"@types/node": "^8.10.29",
|
||||
"babel-loader": "^8.0.2",
|
||||
"babel-preset-minify": "^0.4.3",
|
||||
"jasmine": "^3.2.0",
|
||||
"parallel-webpack": "^2.3.0",
|
||||
"rimraf": "~2.6.2",
|
||||
"ts-loader": "^4.5.0",
|
||||
"ts-node": "^7.0.1",
|
||||
"tslint": "~5.11.0",
|
||||
"tslint-microsoft-contrib": "~5.1.0",
|
||||
"tsutils": "~3.0.0",
|
||||
"typescript": "^3.0.3",
|
||||
"webpack": "^4.17.1",
|
||||
"webpack-cli": "^3.1.0"
|
||||
"babel-core": "^6.26.3",
|
||||
"babel-jest": "^22.0.1",
|
||||
"babel-loader": "^7.1.2",
|
||||
"babel-preset-env": "^1.7.0",
|
||||
"eslint": "^3.0.0",
|
||||
"jest": "^22.0.1",
|
||||
"parallel-webpack": "^2.2.0",
|
||||
"regenerator-runtime": "^0.11.1",
|
||||
"request": "^2.88.0",
|
||||
"standard": "^10.0.3",
|
||||
"webpack": "^4.20.2"
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rimraf build types dist",
|
||||
"build": "npm run clean && tsc -p tsconfig.json && webpack",
|
||||
"lint": "tslint -t stylish --project \"tsconfig.json\"",
|
||||
"test": "jasmine JASMINE_CONFIG_PATH=jasmine.json"
|
||||
"build": "webpack && cp ./dist/codice.fiscale.var.js ./example/",
|
||||
"clean": "rm ./example/codice.fiscale.var.js",
|
||||
"pretest": "npm run build",
|
||||
"test": "standard && jest",
|
||||
"fix": "standard --fix"
|
||||
},
|
||||
"author": "Luca Vandro <lucavandro@gmail.com>",
|
||||
"contributors": [
|
||||
|
23
build/js/main.js → src/codice-fiscale.js
Normal file → Executable file
23
build/js/main.js → src/codice-fiscale.js
Normal file → Executable file
@ -1,7 +1,8 @@
|
||||
import { Comune } from './comune';
|
||||
import { CHECK_CODE_CHARS, CHECK_CODE_EVEN, CHECK_CODE_ODD, MONTH_CODES, OMOCODIA_TABLE } from './constants';
|
||||
import { extractConsonants, extractVowels, getValidDate } from './utils';
|
||||
export class CodiceFiscale {
|
||||
import { extractConsonants, extractVowels, getValidDate, birthplaceFields } from './utils';
|
||||
|
||||
class CodiceFiscale {
|
||||
get day() {
|
||||
return this.birthday.getDate();
|
||||
}
|
||||
@ -47,7 +48,7 @@ export class CodiceFiscale {
|
||||
this.name = cfData.name;
|
||||
this.surname = cfData.surname;
|
||||
this.gender = this.checkGender(cfData.gender);
|
||||
this.birthday = getValidDate(cfData.day, cfData.month, cfData.year);
|
||||
this.birthday = cfData.birthday ? getValidDate(cfData.birthday) : getValidDate(cfData.day, cfData.month, cfData.year);
|
||||
this.birthplace = new Comune(cfData.birthplace, cfData.birthplaceProvincia);
|
||||
this.compute();
|
||||
}
|
||||
@ -96,9 +97,7 @@ export class CodiceFiscale {
|
||||
name: this.name,
|
||||
surname: this.surname,
|
||||
gender: this.gender,
|
||||
day: this.day,
|
||||
month: this.month,
|
||||
year: this.year,
|
||||
birthday: this.birthday.toISOString().slice(0,10),
|
||||
birthplace: this.birthplace.nome,
|
||||
birthplaceProvincia: this.birthplace.prov,
|
||||
cf: this.code,
|
||||
@ -137,12 +136,10 @@ export class CodiceFiscale {
|
||||
this.code = code;
|
||||
}
|
||||
reverse() {
|
||||
if (this.name !== undefined) {
|
||||
|
||||
this.name = this.code.substr(3, 3);
|
||||
}
|
||||
if (this.surname !== undefined) {
|
||||
this.surname = this.code.substr(0, 3);
|
||||
}
|
||||
|
||||
const yearCode = this.code.substr(6, 2);
|
||||
const year19XX = parseInt(`19${yearCode}`, 10);
|
||||
const year20XX = parseInt(`20${yearCode}`, 10);
|
||||
@ -199,3 +196,9 @@ export class CodiceFiscale {
|
||||
return String(year + month + dayStr);
|
||||
}
|
||||
}
|
||||
|
||||
CodiceFiscale.utils = {
|
||||
birthplaceFields : birthplaceFields
|
||||
}
|
||||
|
||||
module.exports = CodiceFiscale
|
49
build/js/comune.js → src/comune.js
Normal file → Executable file
49
build/js/comune.js → src/comune.js
Normal file → Executable file
@ -1,26 +1,33 @@
|
||||
import { COMUNI } from './comuni';
|
||||
import { COMUNI } from './geo-data';
|
||||
import { normalizeString } from './utils';
|
||||
export class Comune {
|
||||
get nomeNorm() {
|
||||
return normalizeString(this.nome);
|
||||
}
|
||||
constructor(nome, prov, cc, check = true) {
|
||||
this.nome = nome;
|
||||
this.prov = prov;
|
||||
this.cc = cc;
|
||||
if (check || this.cc === undefined || this.prov === undefined) {
|
||||
|
||||
if (check || cc === undefined || prov === undefined) {
|
||||
let comune;
|
||||
comune = this.prov !== undefined ? this.searchByNameAndProvince(this.nome, this.prov) : this.searchByName(this.nome);
|
||||
if (comune === undefined) {
|
||||
throw new Error(`Comune with name ${this.nome} doesn't exist`);
|
||||
comune = prov !== undefined ? this.searchByNameAndProvince(nome, prov) : this.searchByName(nome);
|
||||
if (comune === undefined && nome.length === 4) {
|
||||
comune = this.searchByCC(nome);
|
||||
}
|
||||
else if (this.cc !== undefined && comune.cc !== this.cc) {
|
||||
throw new Error(`Comune with cc ${this.cc} doesn't exist`);
|
||||
|
||||
if (comune === undefined) {
|
||||
throw new Error(`Comune with name ${nome} doesn't exist`);
|
||||
}
|
||||
else if (cc !== undefined && comune.cc !== cc) {
|
||||
throw new Error(`Comune with cc ${cc} doesn't exist`);
|
||||
}
|
||||
else {
|
||||
this.nome = comune.nome;
|
||||
this.prov = comune.prov;
|
||||
this.cc = comune.cc;
|
||||
}
|
||||
} else {
|
||||
this.nome = nome;
|
||||
this.prov = prov;
|
||||
this.cc = cc;
|
||||
}
|
||||
}
|
||||
static GetByName(name, prov) {
|
||||
@ -39,6 +46,16 @@ export class Comune {
|
||||
}
|
||||
throw new Error(`Comune with cc ${cc} doesn't exist`);
|
||||
}
|
||||
|
||||
searchByCC(cc) {
|
||||
let result;
|
||||
try{
|
||||
result = Comune.GetByCC(cc);
|
||||
}catch(e){}
|
||||
if (result !== undefined) {
|
||||
return result.toJSON();
|
||||
}
|
||||
}
|
||||
searchByName(nome) {
|
||||
const query = normalizeString(nome);
|
||||
let left = 0;
|
||||
@ -70,9 +87,6 @@ export class Comune {
|
||||
else if (result.length > 1) {
|
||||
throw new Error(`Comune with name of ${nome} is found in more than one province. Please specify the province code`);
|
||||
}
|
||||
else {
|
||||
throw new Error(`Comune with name of ${nome} doesn't exists`);
|
||||
}
|
||||
}
|
||||
searchByNameAndProvince(nome, prov) {
|
||||
const query = normalizeString(nome);
|
||||
@ -108,4 +122,13 @@ export class Comune {
|
||||
throw new Error(`Comune with name of ${nome} and prov ${prov} doesn't exists`);
|
||||
}
|
||||
}
|
||||
|
||||
toJSON(){
|
||||
return {
|
||||
cc : this.cc,
|
||||
nome : this.nome,
|
||||
prov : this.prov
|
||||
}
|
||||
}
|
||||
}
|
||||
|
125
src/comune.ts
125
src/comune.ts
@ -1,125 +0,0 @@
|
||||
import { COMUNI } from './comuni';
|
||||
import { normalizeString } from './utils';
|
||||
|
||||
export interface IComuneObject {
|
||||
nome: string;
|
||||
prov?: string;
|
||||
cc?: string;
|
||||
}
|
||||
/**
|
||||
* Comune class
|
||||
*/
|
||||
export class Comune {
|
||||
public nome: string;
|
||||
public prov: string;
|
||||
public cc: string;
|
||||
|
||||
public get nomeNorm() : string {
|
||||
return normalizeString(this.nome);
|
||||
}
|
||||
|
||||
constructor (nome: string, prov?: string, cc?: string, check: boolean = true) {
|
||||
this.nome = nome;
|
||||
this.prov = prov;
|
||||
this.cc = cc;
|
||||
if (check || this.cc === undefined || this.prov === undefined) {
|
||||
let comune : IComuneObject;
|
||||
comune = this.prov !== undefined ? this.searchByNameAndProvince(this.nome, this.prov) : this.searchByName(this.nome);
|
||||
if (comune === undefined) {
|
||||
throw new Error(`Comune with name ${this.nome} doesn't exist`);
|
||||
} else if (this.cc !== undefined && comune.cc !== this.cc) {
|
||||
throw new Error(`Comune with cc ${this.cc} doesn't exist`);
|
||||
} else {
|
||||
this.prov = comune.prov;
|
||||
this.cc = comune.cc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tslint:disable-next-line:function-name member-access
|
||||
static GetByName(name: string, prov? : string): Comune {
|
||||
return new Comune(name, prov);
|
||||
}
|
||||
|
||||
// tslint:disable-next-line:function-name member-access
|
||||
static GetByCC(cc: string): Comune {
|
||||
let result;
|
||||
for (const item of COMUNI) {
|
||||
if (item[0] === cc) {
|
||||
result = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result !== undefined) {
|
||||
return new Comune(result[2], result[1], result[0], false);
|
||||
}
|
||||
throw new Error(`Comune with cc ${cc} doesn't exist`);
|
||||
}
|
||||
|
||||
private searchByName(nome: string): IComuneObject {
|
||||
const query = normalizeString(nome);
|
||||
let left : number = 0;
|
||||
let right : number = COMUNI.length - 1;
|
||||
const result = [];
|
||||
while (left <= right) {
|
||||
const middle = Math.floor((left + right) / 2);
|
||||
const currentItem = COMUNI[middle];
|
||||
if (query === currentItem[2]) {
|
||||
result.push(currentItem);
|
||||
// Search for comuni with the same name in the neighbourhood +/- 1
|
||||
if (middle > 0 && COMUNI[middle - 1][2] === query) {
|
||||
result.push(COMUNI[middle - 1]);
|
||||
} else if (middle < COMUNI.length - 1 && COMUNI[middle + 1][2] === query) {
|
||||
result.push(COMUNI[middle + 1]);
|
||||
}
|
||||
break;
|
||||
} else if (query < currentItem[2]) {
|
||||
right = middle - 1;
|
||||
} else {
|
||||
left = middle + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (result.length === 1) {
|
||||
return { cc : result[0][0], prov : result[0][1], nome: result[0][2] };
|
||||
} else if (result.length > 1) {
|
||||
throw new Error(`Comune with name of ${nome} is found in more than one province. Please specify the province code`);
|
||||
} else {
|
||||
throw new Error(`Comune with name of ${nome} doesn't exists`);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private searchByNameAndProvince(nome: string, prov: string): IComuneObject {
|
||||
const query = normalizeString(nome);
|
||||
let left : number = 0;
|
||||
let right : number = COMUNI.length - 1;
|
||||
let result;
|
||||
while (left <= right) {
|
||||
const middle = Math.floor((left + right) / 2);
|
||||
const currentItem = COMUNI[middle];
|
||||
if (query === currentItem[2]) {
|
||||
if (prov === currentItem[1]) {
|
||||
result = currentItem;
|
||||
// Search for comuni with the same name in the neighbourhood +/- 1
|
||||
} else if (middle > 0 && COMUNI[middle - 1][2] === query && prov === COMUNI[middle - 1][1]) {
|
||||
result = COMUNI[middle - 1];
|
||||
} else if (middle < COMUNI.length - 1 && COMUNI[middle + 1][2] === query && prov === COMUNI[middle + 1][1]) {
|
||||
result = COMUNI[middle + 1];
|
||||
}
|
||||
break;
|
||||
} else if (query < currentItem[2]) {
|
||||
right = middle - 1;
|
||||
} else {
|
||||
left = middle + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (result !== undefined) {
|
||||
return { cc : result[0], prov : result[1], nome: result[2] };
|
||||
} else {
|
||||
throw new Error(`Comune with name of ${nome} and prov ${prov} doesn't exists`);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
8453
src/comuni.ts
8453
src/comuni.ts
File diff suppressed because it is too large
Load Diff
18
src/constants.ts → src/constants.js
Normal file → Executable file
18
src/constants.ts → src/constants.js
Normal file → Executable file
@ -10,8 +10,8 @@ export const MONTH_CODES = [
|
||||
'P',
|
||||
'R',
|
||||
'S',
|
||||
'T',
|
||||
];
|
||||
'T'
|
||||
]
|
||||
|
||||
export const CHECK_CODE_ODD = {
|
||||
0: 1,
|
||||
@ -49,8 +49,8 @@ export const CHECK_CODE_ODD = {
|
||||
W: 22,
|
||||
X: 25,
|
||||
Y: 24,
|
||||
Z: 23,
|
||||
};
|
||||
Z: 23
|
||||
}
|
||||
|
||||
export const CHECK_CODE_EVEN = {
|
||||
0: 0,
|
||||
@ -88,8 +88,8 @@ export const CHECK_CODE_EVEN = {
|
||||
W: 22,
|
||||
X: 23,
|
||||
Y: 24,
|
||||
Z: 25,
|
||||
};
|
||||
Z: 25
|
||||
}
|
||||
|
||||
export const OMOCODIA_TABLE = {
|
||||
0: 'L',
|
||||
@ -101,7 +101,7 @@ export const OMOCODIA_TABLE = {
|
||||
6: 'S',
|
||||
7: 'T',
|
||||
8: 'U',
|
||||
9: 'V',
|
||||
};
|
||||
9: 'V'
|
||||
}
|
||||
|
||||
export const CHECK_CODE_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
export const CHECK_CODE_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
8567
src/geo-data.js
Executable file
8567
src/geo-data.js
Executable file
File diff suppressed because it is too large
Load Diff
264
src/main.ts
264
src/main.ts
@ -1,264 +0,0 @@
|
||||
import { Comune } from './comune';
|
||||
import { CHECK_CODE_CHARS, CHECK_CODE_EVEN, CHECK_CODE_ODD, MONTH_CODES, OMOCODIA_TABLE} from './constants';
|
||||
import { extractConsonants, extractVowels, getValidDate } from './utils';
|
||||
|
||||
export interface ICodiceFiscaleObject {
|
||||
name: string;
|
||||
surname: string;
|
||||
gender: string;
|
||||
day: number;
|
||||
month: number;
|
||||
year: number;
|
||||
birthplace: string;
|
||||
birthplaceProvincia: string;
|
||||
cf?: string;
|
||||
}
|
||||
/**
|
||||
* Compute and validate Italian Italian Tax code (codice fiscale).
|
||||
*/
|
||||
export class CodiceFiscale {
|
||||
public birthday: Date;
|
||||
public birthplace : Comune;
|
||||
public name: string;
|
||||
public surname: string;
|
||||
public gender: string;
|
||||
private code: string;
|
||||
|
||||
get day(): number {
|
||||
return this.birthday.getDate();
|
||||
}
|
||||
|
||||
set day(d: number) {
|
||||
this.birthday.setDate(d);
|
||||
}
|
||||
|
||||
get month(): number {
|
||||
return this.birthday.getMonth() + 1;
|
||||
}
|
||||
|
||||
set month(m: number) {
|
||||
this.birthday.setMonth(m - 1);
|
||||
}
|
||||
|
||||
get year(): number {
|
||||
return this.birthday.getFullYear();
|
||||
}
|
||||
|
||||
set year(y: number) {
|
||||
this.birthday.setFullYear(y);
|
||||
}
|
||||
|
||||
get cf() : string {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
get nameCode(): string {
|
||||
return this.code.substr(3, 3);
|
||||
}
|
||||
|
||||
get surnameCode(): string {
|
||||
return this.code.substr(0, 3);
|
||||
}
|
||||
|
||||
get checkCode() : string {
|
||||
return this.code.substr(15, 1);
|
||||
}
|
||||
|
||||
// tslint:disable-next-line:no-any
|
||||
constructor(data: string | ICodiceFiscaleObject | object) {
|
||||
if (typeof data === 'string') {
|
||||
if (CodiceFiscale.check(data)) {
|
||||
this.code = data;
|
||||
this.reverse();
|
||||
} else {
|
||||
throw new Error('Provided input is not a valid Codice Fiscale');
|
||||
}
|
||||
} else if (typeof data === 'object') {
|
||||
const cfData = <ICodiceFiscaleObject>data;
|
||||
this.name = cfData.name;
|
||||
this.surname = cfData.surname;
|
||||
this.gender = this.checkGender(cfData.gender);
|
||||
this.birthday = getValidDate(cfData.day, cfData.month, cfData.year);
|
||||
this.birthplace = new Comune(cfData.birthplace, cfData.birthplaceProvincia);
|
||||
this.compute();
|
||||
} else {
|
||||
throw new Error('Comune constructor accept either a string or a plain object. Check the documentation');
|
||||
}
|
||||
}
|
||||
|
||||
// tslint:disable-next-line:function-name member-access
|
||||
static getCheckCode (codiceFiscale: string) : string {
|
||||
let val = 0 ;
|
||||
for (let i = 0; i < 15; i = i + 1) {
|
||||
const c = codiceFiscale[i];
|
||||
val += i % 2 !== 0 ? <number>CHECK_CODE_EVEN[c] : <number>CHECK_CODE_ODD[c];
|
||||
}
|
||||
val = val % 26;
|
||||
|
||||
return CHECK_CODE_CHARS.charAt(val);
|
||||
}
|
||||
// tslint:disable-next-line:function-name member-access
|
||||
static findLocationCode (name : string, prov?: string) {
|
||||
return new Comune(name, prov).cc;
|
||||
}
|
||||
|
||||
// tslint:disable-next-line:function-name member-access
|
||||
static computeInverse (codiceFiscale: string) : ICodiceFiscaleObject {
|
||||
return new CodiceFiscale(codiceFiscale).toJSON();
|
||||
}
|
||||
// tslint:disable-next-line:function-name member-access
|
||||
static compute (obj : object) {
|
||||
return new CodiceFiscale(obj).toString();
|
||||
}
|
||||
// tslint:disable-next-line:function-name member-access
|
||||
static check (codiceFiscale: string) {
|
||||
if (typeof codiceFiscale !== 'string') {
|
||||
return false;
|
||||
}
|
||||
let cf = codiceFiscale.toUpperCase();
|
||||
if (cf.length !== 16) {
|
||||
return false;
|
||||
}
|
||||
const expectedCheckCode = codiceFiscale.charAt(15);
|
||||
cf = codiceFiscale.slice(0, 15);
|
||||
|
||||
return CodiceFiscale.getCheckCode(cf) === expectedCheckCode;
|
||||
}
|
||||
// tslint:disable-next-line:function-name member-access
|
||||
static getOmocodie(cf : string) {
|
||||
return new CodiceFiscale(cf).omocodie();
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
public toJSON(): ICodiceFiscaleObject {
|
||||
return {
|
||||
name: this.name,
|
||||
surname: this.surname,
|
||||
gender: this.gender,
|
||||
day: this.day,
|
||||
month: this.month,
|
||||
year: this.year,
|
||||
birthplace: this.birthplace.nome,
|
||||
birthplaceProvincia: this.birthplace.prov,
|
||||
cf: this.code,
|
||||
};
|
||||
}
|
||||
|
||||
public isValid () : boolean {
|
||||
if (typeof this.code !== 'string') {
|
||||
return false;
|
||||
}
|
||||
this.code = this.code.toUpperCase();
|
||||
if (this.code.length !== 16) {
|
||||
return false;
|
||||
}
|
||||
const expectedCheckCode = this.code.charAt(15);
|
||||
const cf = this.code.slice(0, 15);
|
||||
|
||||
return CodiceFiscale.getCheckCode(cf) === expectedCheckCode;
|
||||
}
|
||||
|
||||
public omocodie () : string[] {
|
||||
const results = [];
|
||||
let lastOmocode = (this.code = this.code.slice(0, 15));
|
||||
for (let i = this.code.length - 1; i >= 0; i = i - 1) {
|
||||
const char = this.code[i];
|
||||
if (char.match(/\d/) !== null) {
|
||||
lastOmocode = `${lastOmocode.substr(0, i)}${OMOCODIA_TABLE[char]}${lastOmocode.substr(i + 1)}`;
|
||||
results.push(lastOmocode + CodiceFiscale.getCheckCode(lastOmocode));
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public compute() {
|
||||
let code = this.getSurnameCode();
|
||||
code += this.getNameCode();
|
||||
code += this.dateCode();
|
||||
code += this.birthplace.cc;
|
||||
code += CodiceFiscale.getCheckCode(code);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public reverse() {
|
||||
if (this.name !== undefined) {
|
||||
this.name = this.code.substr(3, 3);
|
||||
}
|
||||
|
||||
if (this.surname !== undefined) {
|
||||
this.surname = this.code.substr(0, 3);
|
||||
}
|
||||
|
||||
const yearCode = this.code.substr(6, 2);
|
||||
const year19XX = parseInt(`19${yearCode}`, 10);
|
||||
const year20XX = parseInt(`20${yearCode}`, 10);
|
||||
const currentYear20XX = new Date().getFullYear();
|
||||
const year = year20XX > currentYear20XX ? year19XX : year20XX;
|
||||
const monthChar = this.code.substr(8, 1);
|
||||
const month = MONTH_CODES.indexOf(monthChar);
|
||||
this.gender = 'M';
|
||||
let day = parseInt(this.code.substr(9, 2), 10);
|
||||
if (day > 31) {
|
||||
this.gender = 'F';
|
||||
day = day - 40;
|
||||
}
|
||||
this.birthday = new Date(year, month, day, 0, 0, 0, 0);
|
||||
const cc = this.code.substr(11, 4);
|
||||
this.birthplace = Comune.GetByCC(cc);
|
||||
|
||||
return this.toJSON();
|
||||
}
|
||||
|
||||
private checkGender(gender?: string) {
|
||||
this.gender = gender !== undefined ? gender.toUpperCase() : this.gender.toUpperCase();
|
||||
if (typeof this.gender !== 'string') {
|
||||
throw new Error('Gender must be a string');
|
||||
}
|
||||
if (this.gender !== 'M' && this.gender !== 'F') {
|
||||
throw new Error('Gender must be either \'M\' or \'F\'');
|
||||
}
|
||||
|
||||
return gender;
|
||||
}
|
||||
|
||||
private getSurnameCode () : string {
|
||||
const codeSurname = `${extractConsonants(this.surname)}${extractVowels(this.surname)}XXX`;
|
||||
|
||||
return codeSurname.substr(0, 3).toUpperCase();
|
||||
}
|
||||
|
||||
private getNameCode () : string {
|
||||
let codNome = extractConsonants(this.name);
|
||||
if (codNome.length >= 4) {
|
||||
codNome = codNome.charAt(0) + codNome.charAt(2) + codNome.charAt(3);
|
||||
} else {
|
||||
codNome += `${extractVowels(this.name)}XXX`;
|
||||
codNome = codNome.substr(0, 3);
|
||||
}
|
||||
|
||||
return codNome.toUpperCase();
|
||||
}
|
||||
|
||||
private dateCode () : string {
|
||||
// Padding year
|
||||
let year = `0${this.birthday.getFullYear()}`;
|
||||
year = year.substr(year.length - 2, 2);
|
||||
|
||||
const month = MONTH_CODES[this.birthday.getMonth()];
|
||||
let day = this.birthday.getDate();
|
||||
if (this.gender.toUpperCase() === 'F') {
|
||||
day += 40;
|
||||
}
|
||||
|
||||
// Padding day
|
||||
let dayStr = `0${day}`;
|
||||
dayStr = dayStr.substr(dayStr.length - 2, 2);
|
||||
|
||||
return String(year + month + dayStr);
|
||||
}
|
||||
|
||||
}
|
91
src/utils.js
Normal file
91
src/utils.js
Normal file
@ -0,0 +1,91 @@
|
||||
import { PROVINCE, COMUNI } from './geo-data';
|
||||
|
||||
export function normalizeString(str) {
|
||||
return str.trim()
|
||||
.replace(new RegExp(/[àá]/g), 'a\'')
|
||||
.replace(new RegExp(/[èé]/g), 'e\'')
|
||||
.replace(new RegExp(/[ìí]/g), 'i\'')
|
||||
.replace(new RegExp(/[òó]/g), 'o\'')
|
||||
.replace(new RegExp(/[ùú]/g), 'u\'')
|
||||
.toUpperCase();
|
||||
}
|
||||
export function daysInMonth(m, y) {
|
||||
switch (m) {
|
||||
case 1:
|
||||
return (y % 4 === 0 && y % 100 !== 0) || (y % 400 === 0) ? 29 : 28;
|
||||
case 8:
|
||||
case 3:
|
||||
case 5:
|
||||
case 10:
|
||||
return 30;
|
||||
default:
|
||||
return 31;
|
||||
}
|
||||
}
|
||||
export function isValidDate(d, m, y) {
|
||||
const month = m - 1;
|
||||
return month >= 0 && month < 12 && d > 0 && d <= daysInMonth(month, y);
|
||||
}
|
||||
export function getValidDate(d, m, y) {
|
||||
if(typeof d === 'string' && m === undefined && y === undefined){
|
||||
return new Date(d);
|
||||
}else if (isValidDate(d, m, y)) {
|
||||
return new Date(y, m - 1, d, 0, 0, 0, 0);
|
||||
}
|
||||
else {
|
||||
throw new Error(`The date ${y}/${m}/${d} is not a valid date`);
|
||||
}
|
||||
}
|
||||
export function extractVowels(str) {
|
||||
return str.replace(/[^AEIOU]/gi, '');
|
||||
}
|
||||
export function extractConsonants(str) {
|
||||
return str.replace(/[^BCDFGHJKLMNPQRSTVWXYZ]/gi, '');
|
||||
}
|
||||
export function pad(n, size = 2) {
|
||||
let s = String(n);
|
||||
while (s.length < size) {
|
||||
s = `0${s}`;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
export function birthplaceFields(provinceSelector, birthplaceSelector) {
|
||||
const provinceSelect = document.querySelector(provinceSelector);
|
||||
const birthplaceSelect = document.querySelector(birthplaceSelector);
|
||||
const optGroupProv = document.createElement('optgroup');
|
||||
const optGroupEE = document.createElement('optgroup');
|
||||
optGroupEE.label = "-----------";
|
||||
provinceSelect.appendChild(optGroupProv);
|
||||
provinceSelect.appendChild(optGroupEE);
|
||||
Object.keys(PROVINCE).forEach((code, i) => {
|
||||
const name = PROVINCE[code];
|
||||
const option = document.createElement('option');
|
||||
option.value = code;
|
||||
option.textContent = name;
|
||||
if(code === 'EE'){
|
||||
optGroupEE.appendChild(option);
|
||||
} else {
|
||||
optGroupProv.appendChild(option);
|
||||
}
|
||||
});
|
||||
|
||||
provinceSelect.onchange = (e)=>{
|
||||
let province = provinceSelect.value;
|
||||
while (birthplaceSelect.firstChild) {
|
||||
birthplaceSelect.removeChild(birthplaceSelect.firstChild);
|
||||
}
|
||||
COMUNI.forEach((comune)=>{
|
||||
let cc = comune[0], nome = comune[2], prov = comune[1];
|
||||
if(prov === province){
|
||||
let option = document.createElement('option');
|
||||
option.value = cc;
|
||||
option.textContent = nome.toLowerCase().replace(/\b\w/g, l => l.toUpperCase());
|
||||
birthplaceSelect.appendChild(option);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
provinceSelect.selectedIndex = "0";
|
||||
provinceSelect.onchange();
|
||||
}
|
74
src/utils.ts
74
src/utils.ts
@ -1,74 +0,0 @@
|
||||
export function normalizeString (str: string) {
|
||||
return str.trim()
|
||||
.replace(new RegExp(/[àá]/g), 'a\'')
|
||||
.replace(new RegExp(/[èé]/g), 'e\'')
|
||||
.replace(new RegExp(/[ìí]/g), 'i\'')
|
||||
.replace(new RegExp(/[òó]/g), 'o\'')
|
||||
.replace(new RegExp(/[ùú]/g), 'u\'')
|
||||
.toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of days in any particular month
|
||||
* @link https://stackoverflow.com/a/1433119/1293256
|
||||
* @param {integer} m The month (valid: 0-11)
|
||||
* @param {integer} y The year
|
||||
* @return {integer} The number of days in the month
|
||||
*/
|
||||
export function daysInMonth(m: number, y: number) {
|
||||
switch (m) {
|
||||
case 1 :
|
||||
return (y % 4 === 0 && y % 100 !== 0) || (y % 400 === 0) ? 29 : 28;
|
||||
case 8 : case 3 : case 5 : case 10 :
|
||||
return 30;
|
||||
default :
|
||||
return 31;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a date is valid
|
||||
* @link https://stackoverflow.com/a/1433119/1293256
|
||||
* @param {[type]} d The day
|
||||
* @param {[type]} m The month
|
||||
* @param {[type]} y The year
|
||||
* @return {Boolean} Returns true if valid
|
||||
*/
|
||||
export function isValidDate(d: number, m: number, y: number) {
|
||||
const month = m - 1;
|
||||
|
||||
return month >= 0 && month < 12 && d > 0 && d <= daysInMonth(month, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a date is valid
|
||||
* @link https://stackoverflow.com/a/1433119/1293256
|
||||
* @param {[type]} d The day
|
||||
* @param {[type]} m The month
|
||||
* @param {[type]} y The year
|
||||
* @return {Boolean} Returns true if valid
|
||||
*/
|
||||
export function getValidDate(d: number, m: number, y: number) {
|
||||
if (isValidDate(d, m, y)) {
|
||||
return new Date(y, m - 1, d, 0, 0, 0, 0);
|
||||
} else {
|
||||
throw new Error(`The date ${y}/${m}/${d} is not a valid date`);
|
||||
}
|
||||
}
|
||||
|
||||
export function extractVowels (str: string) {
|
||||
return str.replace(/[^AEIOU]/gi, '');
|
||||
}
|
||||
|
||||
export function extractConsonants (str : string) {
|
||||
return str.replace(/[^BCDFGHJKLMNPQRSTVWXYZ]/gi, '');
|
||||
}
|
||||
|
||||
export function pad(n: number, size : number = 2) {
|
||||
let s = String(n);
|
||||
while (s.length < size) {
|
||||
s = `0${s}`;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
@ -1,157 +0,0 @@
|
||||
import { Comune } from '../src/comune';
|
||||
import { CodiceFiscale } from '../src/main';
|
||||
|
||||
describe('Gli oggetti appartenenti alla classe Codice Fiscale', () => {
|
||||
const c1 : CodiceFiscale = new CodiceFiscale({
|
||||
name: 'Luca',
|
||||
surname: 'Moreno',
|
||||
gender: 'M',
|
||||
day: 1,
|
||||
month: 1,
|
||||
year: 2000,
|
||||
birthplace: 'Roma',
|
||||
birthplaceProvincia: 'RM',
|
||||
});
|
||||
|
||||
it('possono essere creati utilizzando una oggetto', () => {
|
||||
const creaOggetto = () => new CodiceFiscale({
|
||||
name: 'Luca',
|
||||
surname: 'Moreno',
|
||||
gender: 'M',
|
||||
day: 1,
|
||||
month: 1,
|
||||
year: 2000,
|
||||
birthplace: 'Roma',
|
||||
birthplaceProvincia: 'RM',
|
||||
});
|
||||
|
||||
expect(creaOggetto).not.toThrow();
|
||||
});
|
||||
|
||||
it('una volta creati, calcaloano il codice fiscale, che può essere letto attraversi la proprietà cf o il metodo toString', () => {
|
||||
expect(c1.cf).toBe('MRNLCU00A01H501J');
|
||||
expect(c1.toString()).toBe('MRNLCU00A01H501J');
|
||||
});
|
||||
|
||||
it('hanno la proprietà name', () => {
|
||||
expect(c1.name).toBe('Luca');
|
||||
});
|
||||
|
||||
it('hanno la proprietà surname', () => {
|
||||
expect(c1.surname).toBe('Moreno');
|
||||
});
|
||||
|
||||
it('hanno la proprietà gender', () => {
|
||||
expect(c1.gender).toBe('M');
|
||||
});
|
||||
|
||||
it('hanno la proprietà day', () => {
|
||||
expect(c1.day).toBe(1);
|
||||
});
|
||||
|
||||
it('hanno la proprietà month', () => {
|
||||
expect(c1.month).toBe(1);
|
||||
});
|
||||
|
||||
it('hanno la proprietà year', () => {
|
||||
expect(c1.year).toBe(2000);
|
||||
});
|
||||
|
||||
it('hanno la proprietà birtday', () => {
|
||||
expect(c1.birthday).toEqual(new Date(2000, 0, 1, 0, 0, 0, 0));
|
||||
});
|
||||
|
||||
it('hanno la proprietà comune', () => {
|
||||
const comune = new Comune('Roma');
|
||||
expect(c1.birthplace).toEqual(comune);
|
||||
});
|
||||
|
||||
it('tutte le proprietà possono essere esportate utilizando il metodo toJSON()', () => {
|
||||
expect(c1.toJSON()).toEqual({
|
||||
name: 'Luca',
|
||||
surname: 'Moreno',
|
||||
gender: 'M',
|
||||
day: 1,
|
||||
month: 1,
|
||||
year: 2000,
|
||||
birthplace: 'Roma',
|
||||
birthplaceProvincia: 'RM',
|
||||
cf: 'MRNLCU00A01H501J',
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('Gli oggetti appartenenti alla classe Codice Fiscale creati utilizzando una stringa con un codice fiscale', () => {
|
||||
const c1 = new CodiceFiscale('MRNLCU00A01H501J');
|
||||
|
||||
it('possono essere creati', () => {
|
||||
const creaOggetto1 = () => new CodiceFiscale('MRNLCU00A01H501J');
|
||||
expect(creaOggetto1).not.toThrow();
|
||||
});
|
||||
|
||||
it('lanciano un eccezione se la stringa passata al costruttore non è un codice fiscale valido', () => {
|
||||
const creaOggetto2 = () => new CodiceFiscale('MRNLCU00A01H501K');
|
||||
expect(creaOggetto2).toThrow();
|
||||
});
|
||||
|
||||
it('non hanno la proprietà name', () => {
|
||||
expect(c1.name).toBeUndefined();
|
||||
});
|
||||
|
||||
it('non hanno la proprietà surname', () => {
|
||||
expect(c1.surname).toBeUndefined();
|
||||
});
|
||||
|
||||
it('hanno la proprietà gender', () => {
|
||||
expect(c1.gender).toBe('M');
|
||||
});
|
||||
|
||||
it('hanno la proprietà day', () => {
|
||||
expect(c1.day).toBe(1);
|
||||
});
|
||||
|
||||
it('hanno la proprietà month', () => {
|
||||
expect(c1.month).toBe(1);
|
||||
});
|
||||
|
||||
it('hanno la proprietà year', () => {
|
||||
expect(c1.year).toBe(2000);
|
||||
});
|
||||
|
||||
it('hanno la proprietà birtday', () => {
|
||||
expect(c1.birthday).toEqual(new Date(2000, 0, 1));
|
||||
});
|
||||
|
||||
it('hanno la proprietà comune', () => {
|
||||
const comune = new Comune('ROMA');
|
||||
expect(c1.birthplace).toEqual(comune);
|
||||
});
|
||||
|
||||
it('hanno la proprietà year', () => {
|
||||
expect(c1.year).toBe(2000);
|
||||
});
|
||||
|
||||
it('calcola le omocodie dato un codice fiscale', () => {
|
||||
const c2 = new CodiceFiscale('BNZVCN32S10E573Z');
|
||||
const omocodie = c2.omocodie();
|
||||
expect(omocodie).toContain('BNZVCN32S10E57PV');
|
||||
expect(omocodie).toContain('BNZVCNPNSMLERTPX');
|
||||
});
|
||||
|
||||
it('hanno la proprietà surnameCode', () => {
|
||||
const c2 = new CodiceFiscale('BNZVCN32S10E573Z');
|
||||
expect(c2.surnameCode).toEqual('BNZ');
|
||||
});
|
||||
|
||||
it('hanno la proprietà nameCode', () => {
|
||||
const c2 = new CodiceFiscale('BNZVCN32S10E573Z');
|
||||
expect(c2.nameCode).toEqual('VCN');
|
||||
});
|
||||
|
||||
it('hanno la proprietà checkCode', () => {
|
||||
const c2 = new CodiceFiscale('BNZVCN32S10E573Z');
|
||||
expect(c2.checkCode).toEqual('Z');
|
||||
});
|
||||
|
||||
});
|
@ -1,149 +0,0 @@
|
||||
import { CodiceFiscale } from '../src/main';
|
||||
|
||||
describe('Codice Fiscale', () => {
|
||||
it('esiste un oggetto chiamato CodiceFiscale', () => {
|
||||
expect(CodiceFiscale).not.toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('CodiceFiscale.getCheckCode', () => {
|
||||
it('è definito', () => {
|
||||
expect(CodiceFiscale.getCheckCode).not.toBe(undefined);
|
||||
});
|
||||
|
||||
it('restituisce il corretto risultato', () => {
|
||||
expect(CodiceFiscale.getCheckCode('MRNLCU00A01H501')).toBe('J');
|
||||
});
|
||||
});
|
||||
|
||||
describe('CodiceFiscale.compute', () => {
|
||||
it('è definito', () => {
|
||||
expect(CodiceFiscale.compute).not.toBe(undefined);
|
||||
});
|
||||
|
||||
it('calcola il codice fiscale', () => {
|
||||
expect(CodiceFiscale.compute({
|
||||
name: 'Luca',
|
||||
surname: 'Moreno',
|
||||
gender: 'M',
|
||||
day: 1,
|
||||
month: 1,
|
||||
year: 2000,
|
||||
birthplace: 'Roma',
|
||||
birthplaceProvincia: 'RM',
|
||||
}))
|
||||
.toBe('MRNLCU00A01H501J');
|
||||
});
|
||||
|
||||
it('calcola il codice fiscale di persone nate all\'estero', () => {
|
||||
expect(CodiceFiscale.compute({
|
||||
name: 'Luca',
|
||||
surname: 'Moreno',
|
||||
gender: 'M',
|
||||
day: 1,
|
||||
month: 1,
|
||||
year: 2000,
|
||||
birthplace: 'Albania',
|
||||
birthplaceProvincia: 'EE',
|
||||
}))
|
||||
.toBe('MRNLCU00A01Z100P');
|
||||
});
|
||||
|
||||
it('se il comune non esiste lancia un eccezione', () => {
|
||||
const comuneInventato = () => {
|
||||
CodiceFiscale.compute({
|
||||
name: 'Luca',
|
||||
surname: 'Moreno',
|
||||
gender: 'M',
|
||||
day: 1,
|
||||
month: 1,
|
||||
year: 2000,
|
||||
birthplace: 'Foo',
|
||||
birthplaceProvincia: 'EE',
|
||||
});
|
||||
};
|
||||
expect(comuneInventato).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('CodiceFiscale.findLocationCode', () => {
|
||||
it('è definito', () => {
|
||||
expect(CodiceFiscale.findLocationCode).not.toBe(undefined);
|
||||
});
|
||||
|
||||
it('trova il codice del comune', () => {
|
||||
expect(CodiceFiscale.findLocationCode('Roma', 'RM')).toBe('H501');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('CodiceFiscale.check', () => {
|
||||
it('è definito', () => {
|
||||
expect(CodiceFiscale.check).not.toBe(undefined);
|
||||
});
|
||||
|
||||
it('controlla se il codice fiscale è valido', () => {
|
||||
expect(CodiceFiscale.check('MRNLCU00A01H501J')).toBe(true);
|
||||
});
|
||||
|
||||
it('controlla che sia composto dal non più 16 valori alfanumerici', () => {
|
||||
expect(CodiceFiscale.check('MRNLCU00A01H501JK')).toBe(false);
|
||||
});
|
||||
|
||||
it('controlla che sia composto dal almeno 16 valori alfanumerici', () => {
|
||||
expect(CodiceFiscale.check('MRNLCU00A01H501J3')).toBe(false);
|
||||
});
|
||||
|
||||
it('controlla che il carattere di controllo sia esatto', () => {
|
||||
expect(CodiceFiscale.check('VNDLDL87D07B963G')).toBe(false);
|
||||
});
|
||||
|
||||
it('funziona anche in caso di omocodie', () => {
|
||||
expect(CodiceFiscale.check('BNZVCN32S10E57PV')).toBe(true);
|
||||
expect(CodiceFiscale.check('BNZVCNPNSMLERTPX')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('CodiceFiscale.getOmocodie', () => {
|
||||
it('è definito', () => {
|
||||
expect(CodiceFiscale.getOmocodie).not.toBe(undefined);
|
||||
});
|
||||
|
||||
it('calcola le omocodie dato un codice fiscale', () => {
|
||||
const omocodie = CodiceFiscale.getOmocodie('BNZVCN32S10E573Z');
|
||||
expect(omocodie).toContain('BNZVCN32S10E57PV');
|
||||
expect(omocodie).toContain('BNZVCNPNSMLERTPX');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Calcolo codice fiscale inverso -> metodo .computeInverse', () => {
|
||||
it('è definito', () => {
|
||||
expect(CodiceFiscale.computeInverse).not.toBe(undefined);
|
||||
});
|
||||
|
||||
it('restituisce falso se l\'input è stringa formattata male', () => {
|
||||
const notValid = () => {
|
||||
CodiceFiscale.computeInverse('BNZVCN32SC0E573Z');
|
||||
};
|
||||
expect(notValid).toThrowError('Provided input is not a valid Codice Fiscale');
|
||||
});
|
||||
|
||||
it('restituisce il genere corretto', () => {
|
||||
expect(CodiceFiscale.computeInverse('MRNLCU00A01H501J').gender).toEqual('M');
|
||||
});
|
||||
|
||||
it('restituisce la città natale corretta', () => {
|
||||
expect(CodiceFiscale.computeInverse('MRNLCU00A01H501J').birthplace).toEqual('ROMA');
|
||||
});
|
||||
|
||||
it('restituisce la provincia della città natale corretta', () => {
|
||||
expect(CodiceFiscale.computeInverse('MRNLCU00A01H501J').birthplaceProvincia).toEqual('RM');
|
||||
});
|
||||
|
||||
it('restituisce il giorno di nascita come numero compreso tra 1 e 31', () => {
|
||||
expect(
|
||||
CodiceFiscale.computeInverse('MRNLCU00A01H501J').day >= 1 &&
|
||||
CodiceFiscale.computeInverse('MRNLCU00A01H501J').day <= 31,
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
@ -1,62 +0,0 @@
|
||||
import { Comune } from '../src/comune';
|
||||
|
||||
describe('Comune', () => {
|
||||
|
||||
it('crea nuove istanze utilizzando nome, provincia e codice catastale', () => {
|
||||
// tslint:disable-next-line:no-console
|
||||
const c1 = new Comune('Caserta', 'CE', 'B963');
|
||||
expect(c1.nome).toEqual('Caserta');
|
||||
expect(c1.prov).toEqual('CE');
|
||||
expect(c1.cc).toEqual('B963');
|
||||
});
|
||||
|
||||
it('esiste una classe chiamata comune', () => {
|
||||
expect(Comune).toBeDefined();
|
||||
expect(Comune.name).toEqual('Comune');
|
||||
});
|
||||
|
||||
it('se nome, provincia o codice catastale sono errati genera un errore', () => {
|
||||
expect(() => new Comune('Caserta', 'CE', 'A000')).toThrow();
|
||||
expect(() => new Comune('Caserta', 'CU')).toThrow();
|
||||
});
|
||||
|
||||
it('è possibile creare un\'istanza utilizzando solo il nome del comune', () => {
|
||||
const c1 = Comune.GetByName('Caserta');
|
||||
expect(c1.nome).toEqual('Caserta');
|
||||
expect(c1.prov).toEqual('CE');
|
||||
expect(c1.cc).toEqual('B963');
|
||||
});
|
||||
|
||||
it('se ci sono due comuni con lo stesso nome e la provincia non viene specificata, il metodo .getByName genera un errore', () => {
|
||||
expect(() => Comune.GetByName('Castro'))
|
||||
.toThrowError('Comune with name of Castro is found in more than one province. Please specify the province code');
|
||||
expect(() => Comune.GetByName('Castro', 'LE')).not.toThrowError();
|
||||
expect(() => Comune.GetByName('Castro', 'BG')).not.toThrowError();
|
||||
});
|
||||
|
||||
it('è possibile creare un\'istanza utilizzando solo il codice catastale', () => {
|
||||
const c1 = Comune.GetByCC('B963');
|
||||
expect(c1.nomeNorm).toEqual('CASERTA');
|
||||
expect(c1.nome).toEqual('CASERTA');
|
||||
expect(c1.prov).toEqual('CE');
|
||||
expect(c1.cc).toEqual('B963');
|
||||
});
|
||||
|
||||
it('trova il codice di un comune che contiene lettere accentate', () => {
|
||||
const comune = new Comune('Riccò del golfo di Spezia', 'SP');
|
||||
expect(comune.nome).toBe('Riccò del golfo di Spezia');
|
||||
expect(comune.nomeNorm).toBe('RICCO\' DEL GOLFO DI SPEZIA');
|
||||
expect(comune.prov).toBe('SP');
|
||||
expect(comune.cc).toBe('H275');
|
||||
});
|
||||
|
||||
it('trova il codice di un comune che contiene apostrofi', () => {
|
||||
const comune = new Comune('Sant\'Angelo Romano', 'RM');
|
||||
expect(comune.cc).toBe('I284');
|
||||
});
|
||||
|
||||
it('consente di inserire stati esteri', () => {
|
||||
const stato = new Comune('Albania', 'EE');
|
||||
expect(stato.cc).toBe('Z100');
|
||||
});
|
||||
});
|
@ -1,11 +0,0 @@
|
||||
import { normalizeString } from '../src/utils';
|
||||
|
||||
describe('normalizeString', () => {
|
||||
it('sostituisce gli accenti con gli apostrofi', () => {
|
||||
expect(normalizeString('papà')).toBe('PAPA\'');
|
||||
expect(normalizeString('purè')).toBe('PURE\'');
|
||||
expect(normalizeString('pipì')).toBe('PIPI\'');
|
||||
expect(normalizeString('popò')).toBe('POPO\'');
|
||||
expect(normalizeString('pupù')).toBe('PUPU\'');
|
||||
});
|
||||
});
|
212
tests/index.spec.js
Executable file
212
tests/index.spec.js
Executable file
@ -0,0 +1,212 @@
|
||||
import CodiceFiscale from '../src/index'
|
||||
let { describe, test, expect } = global
|
||||
|
||||
describe('Codice Fiscale', () => {
|
||||
test('esiste un oggetto chiamato CodiceFiscale', () => {
|
||||
expect(CodiceFiscale).not.toBe(undefined)
|
||||
})
|
||||
})
|
||||
|
||||
describe('CodiceFiscale.surnameCode', () => {
|
||||
test('è definito', () => {
|
||||
expect(CodiceFiscale.surnameCode).not.toBe(undefined)
|
||||
})
|
||||
|
||||
test('restituisce il corretto risultato in caso di sufficienti consonanti', () => {
|
||||
expect(CodiceFiscale.surnameCode('Moreno')).toBe('MRN')
|
||||
})
|
||||
|
||||
test('restituisce il corretto risultato in caso di insufficienti consonanti', () => {
|
||||
expect(CodiceFiscale.surnameCode('Julea')).toBe('JLU')
|
||||
})
|
||||
})
|
||||
|
||||
describe('CodiceFiscale.nameCode', () => {
|
||||
test('è definito', () => {
|
||||
expect(CodiceFiscale.nameCode).not.toBe(undefined)
|
||||
})
|
||||
|
||||
test('restituisce il corretto risultato in caso di sufficienti consonanti', () => {
|
||||
expect(CodiceFiscale.nameCode('Marco')).toBe('MRC')
|
||||
})
|
||||
|
||||
test('restituisce il corretto risultato in caso di insufficienti consonanti', () => {
|
||||
expect(CodiceFiscale.nameCode('Luca')).toBe('LCU')
|
||||
})
|
||||
})
|
||||
|
||||
describe('CodiceFiscale.dateCode', () => {
|
||||
test('è definito', () => {
|
||||
expect(CodiceFiscale.dateCode).not.toBe(undefined)
|
||||
})
|
||||
|
||||
test('restituisce il corretto risultato', () => {
|
||||
expect(CodiceFiscale.dateCode(1, 1, 2000, 'M')).toBe('00A01')
|
||||
})
|
||||
})
|
||||
|
||||
describe('CodiceFiscale.getCheckCode', () => {
|
||||
test('è definito', () => {
|
||||
expect(CodiceFiscale.getCheckCode).not.toBe(undefined)
|
||||
})
|
||||
|
||||
test('restituisce il corretto risultato', () => {
|
||||
expect(CodiceFiscale.getCheckCode('MRNLCU00A01H501')).toBe('J')
|
||||
})
|
||||
})
|
||||
|
||||
describe('CodiceFiscale.compute', () => {
|
||||
test('è definito', () => {
|
||||
expect(CodiceFiscale.compute).not.toBe(undefined)
|
||||
})
|
||||
|
||||
test('calcola il codice fiscale', () => {
|
||||
expect(CodiceFiscale.compute({
|
||||
name: 'Luca',
|
||||
surname: 'Moreno',
|
||||
gender: 'M',
|
||||
day: 1,
|
||||
month: 1,
|
||||
year: 2000,
|
||||
birthplace: 'Roma',
|
||||
birthplaceProvincia: 'RM'
|
||||
}))
|
||||
.toBe('MRNLCU00A01H501J')
|
||||
})
|
||||
|
||||
test("calcola il codice fiscale di persone nate all'estero", () => {
|
||||
expect(CodiceFiscale.compute({
|
||||
name: 'Luca',
|
||||
surname: 'Moreno',
|
||||
gender: 'M',
|
||||
day: 1,
|
||||
month: 1,
|
||||
year: 2000,
|
||||
birthplace: 'Albania',
|
||||
birthplaceProvincia: 'EE'
|
||||
}))
|
||||
.toBe('MRNLCU00A01Z100P')
|
||||
})
|
||||
|
||||
test('se il comune non esiste lancia un eccezione', () => {
|
||||
var comuneInventato = function () {
|
||||
CodiceFiscale.compute({
|
||||
name: 'Luca',
|
||||
surname: 'Moreno',
|
||||
gender: 'M',
|
||||
day: 1,
|
||||
month: 1,
|
||||
year: 2000,
|
||||
birthplace: 'Foo',
|
||||
birthplaceProvincia: 'EE'
|
||||
})
|
||||
}
|
||||
expect(comuneInventato).toThrowError('Location not found')
|
||||
})
|
||||
})
|
||||
|
||||
describe('CodiceFiscale.findLocationCode', () => {
|
||||
test('è definito', () => {
|
||||
expect(CodiceFiscale.findLocationCode).not.toBe(undefined)
|
||||
})
|
||||
|
||||
test('trova il codice del comune', () => {
|
||||
expect(CodiceFiscale.findLocationCode('Roma', 'RM')).toBe('H501')
|
||||
})
|
||||
|
||||
test('trova il codice di un comune che contiene apostrofi', () => {
|
||||
expect(CodiceFiscale.findLocationCode("Sant'Angelo Romano", 'RM')).toBe('I284')
|
||||
})
|
||||
|
||||
test('trova il codice di un comune che contiene lettere accentate', () => {
|
||||
expect(CodiceFiscale.findLocationCode('Riccò del Golfo di Spezia', 'SP')).toBe('H275')
|
||||
})
|
||||
|
||||
test('se la provincia non esiste lancia un eccezione', () => {
|
||||
var comuneInventato = function () {
|
||||
CodiceFiscale.findLocationCode('Foo', 'Bar')
|
||||
}
|
||||
expect(comuneInventato).toThrowError('Area code not found')
|
||||
})
|
||||
|
||||
test('se il comune non esiste lancia un eccezione', () => {
|
||||
var comuneInventato = function () {
|
||||
CodiceFiscale.findLocationCode('Foo', 'RM')
|
||||
}
|
||||
expect(comuneInventato).toThrowError('Location not found')
|
||||
})
|
||||
})
|
||||
|
||||
describe('CodiceFiscale.check', () => {
|
||||
test('è definito', () => {
|
||||
expect(CodiceFiscale.check).not.toBe(undefined)
|
||||
})
|
||||
|
||||
test('controlla se il codice fiscale è valido', () => {
|
||||
expect(CodiceFiscale.check('MRNLCU00A01H501J')).toBe(true)
|
||||
})
|
||||
|
||||
test('controlla che sia composto dal non più 16 valori alfanumerici', () => {
|
||||
expect(CodiceFiscale.check('MRNLCU00A01H501JK')).toBe(false)
|
||||
})
|
||||
|
||||
test('controlla che sia composto dal almeno 16 valori alfanumerici', () => {
|
||||
expect(CodiceFiscale.check('MRNLCU00A01H501J3')).toBe(false)
|
||||
})
|
||||
|
||||
test('controlla che il carattere di controllo sia esatto', () => {
|
||||
expect(CodiceFiscale.check('VNDLDL87D07B963G')).toBe(false)
|
||||
})
|
||||
|
||||
test('funziona anche in caso di omocodie', () => {
|
||||
expect(CodiceFiscale.check('BNZVCN32S10E57PV')).toBe(true)
|
||||
expect(CodiceFiscale.check('BNZVCNPNSMLERTPX')).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('CodiceFiscale.getOmocodie', () => {
|
||||
test('è definito', () => {
|
||||
expect(CodiceFiscale.getOmocodie).not.toBe(undefined)
|
||||
})
|
||||
|
||||
test('calcola le omocodie dato un codice fiscale', () => {
|
||||
expect(CodiceFiscale.getOmocodie('BNZVCN32S10E573Z'))
|
||||
.toEqual(expect.arrayContaining(['BNZVCN32S10E57PV', 'BNZVCNPNSMLERTPX']))
|
||||
})
|
||||
})
|
||||
|
||||
describe('Calcolo codice fiscale inverso -> metodo .computeInverse', () => {
|
||||
test('è definito', () => {
|
||||
expect(CodiceFiscale.computeInverse).not.toBe(undefined)
|
||||
})
|
||||
|
||||
test("se l'input non è una stringa lancia un eccezione", () => {
|
||||
var notAString = function () {
|
||||
CodiceFiscale.computeInverse(0)
|
||||
}
|
||||
expect(notAString).toThrowError('Provided input is not a valid Codice Fiscale')
|
||||
})
|
||||
|
||||
test("restituisce falso se l'input è stringa formattata male", () => {
|
||||
var notValid = function () {
|
||||
CodiceFiscale.computeInverse('BNZVCN32SC0E573Z')
|
||||
}
|
||||
expect(notValid).toThrowError('Provided input is not a valid Codice Fiscale')
|
||||
})
|
||||
|
||||
test('restituisce il genere corretto', () => {
|
||||
expect(CodiceFiscale.computeInverse('MRNLCU00A01H501J').gender).toEqual('M')
|
||||
})
|
||||
|
||||
test('restituisce la città natale corretta', () => {
|
||||
expect(CodiceFiscale.computeInverse('MRNLCU00A01H501J').birthplace).toEqual('ROMA')
|
||||
})
|
||||
|
||||
test('restituisce la provincia della città natale corretta', () => {
|
||||
expect(CodiceFiscale.computeInverse('MRNLCU00A01H501J').birthplaceProvincia).toEqual('RM')
|
||||
})
|
||||
|
||||
test('restituisce il giorno di nascita come numero compreso tra 1 e 31', () => {
|
||||
expect(CodiceFiscale.computeInverse('MRNLCU00A01H501J').day >= 1 && CodiceFiscale.computeInverse('MRNLCU00A01H501J').day <= 31).toBe(true)
|
||||
})
|
||||
})
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"includes": [
|
||||
"src/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"test/*.ts"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": false,
|
||||
"target": "es6",
|
||||
"declaration": true,
|
||||
"declarationDir": "types",
|
||||
"rootDir": "./src/",
|
||||
"outDir": "build/js",
|
||||
"removeComments": true
|
||||
}
|
||||
}
|
35
tslint.json
35
tslint.json
@ -1,35 +0,0 @@
|
||||
/**
|
||||
* This starter project recommended using Microsoft TSLint rules.
|
||||
* Please see https://github.com/Microsoft/tslint-microsoft-contrib for more details.
|
||||
*/
|
||||
|
||||
{
|
||||
"extends": [
|
||||
"tslint-microsoft-contrib"
|
||||
],
|
||||
"rules": {
|
||||
"no-unsafe-any": false,
|
||||
"mocha-no-side-effect-code": false,
|
||||
"missing-jsdoc": false,
|
||||
"no-relative-imports": false,
|
||||
"export-name": false,
|
||||
"promise-function-async": false,
|
||||
"no-void-expression": false,
|
||||
"no-redundant-jsdoc": false,
|
||||
"prefer-type-cast": false,
|
||||
"trailing-comma": [ // https://palantir.github.io/tslint/rules/trailing-comma/
|
||||
true,
|
||||
{
|
||||
"multiline": "always",
|
||||
"singleline": "never"
|
||||
}
|
||||
],
|
||||
"typedef": [
|
||||
true,
|
||||
"parameter",
|
||||
"arrow-parameter",
|
||||
"property-declaration",
|
||||
"member-variable-declaration"
|
||||
]
|
||||
}
|
||||
}
|
21
types/main.d.ts → types/codice-fiscale.d.ts
vendored
21
types/main.d.ts → types/codice-fiscale.d.ts
vendored
@ -1,5 +1,6 @@
|
||||
import { Comune } from './comune';
|
||||
export interface ICodiceFiscaleObject {
|
||||
import { birthplaceFields } from './utils';
|
||||
interface ICodiceFiscaleObject {
|
||||
name: string;
|
||||
surname: string;
|
||||
gender: string;
|
||||
@ -10,13 +11,7 @@ export interface ICodiceFiscaleObject {
|
||||
birthplaceProvincia: string;
|
||||
cf?: string;
|
||||
}
|
||||
export declare class CodiceFiscale {
|
||||
birthday: Date;
|
||||
birthplace: Comune;
|
||||
name: string;
|
||||
surname: string;
|
||||
gender: string;
|
||||
private code;
|
||||
declare class CodiceFiscale {
|
||||
day: number;
|
||||
month: number;
|
||||
year: number;
|
||||
@ -24,6 +19,15 @@ export declare class CodiceFiscale {
|
||||
readonly nameCode: string;
|
||||
readonly surnameCode: string;
|
||||
readonly checkCode: string;
|
||||
static utils: {
|
||||
birthplaceFields: typeof birthplaceFields;
|
||||
};
|
||||
birthday: Date;
|
||||
birthplace: Comune;
|
||||
name: string;
|
||||
surname: string;
|
||||
gender: string;
|
||||
private code;
|
||||
constructor(data: string | ICodiceFiscaleObject | object);
|
||||
static getCheckCode(codiceFiscale: string): string;
|
||||
static findLocationCode(name: string, prov?: string): string;
|
||||
@ -42,3 +46,4 @@ export declare class CodiceFiscale {
|
||||
private getNameCode;
|
||||
private dateCode;
|
||||
}
|
||||
export default CodiceFiscale;
|
1
types/comuni.d.ts
vendored
1
types/comuni.d.ts
vendored
@ -1 +0,0 @@
|
||||
export declare const COMUNI: string[][];
|
113
types/geo-data.d.ts
vendored
Normal file
113
types/geo-data.d.ts
vendored
Normal file
@ -0,0 +1,113 @@
|
||||
export declare const COMUNI: string[][];
|
||||
export declare const PROVINCE: {
|
||||
AG: string;
|
||||
AL: string;
|
||||
AN: string;
|
||||
AO: string;
|
||||
AP: string;
|
||||
AQ: string;
|
||||
AR: string;
|
||||
AT: string;
|
||||
AV: string;
|
||||
BA: string;
|
||||
BG: string;
|
||||
BI: string;
|
||||
BL: string;
|
||||
BN: string;
|
||||
BO: string;
|
||||
BR: string;
|
||||
BS: string;
|
||||
BT: string;
|
||||
BZ: string;
|
||||
CA: string;
|
||||
CB: string;
|
||||
CE: string;
|
||||
CH: string;
|
||||
CI: string;
|
||||
CL: string;
|
||||
CN: string;
|
||||
CO: string;
|
||||
CR: string;
|
||||
CS: string;
|
||||
CT: string;
|
||||
CZ: string;
|
||||
EN: string;
|
||||
FC: string;
|
||||
FE: string;
|
||||
FG: string;
|
||||
FI: string;
|
||||
FM: string;
|
||||
FR: string;
|
||||
GE: string;
|
||||
GO: string;
|
||||
GR: string;
|
||||
IM: string;
|
||||
IS: string;
|
||||
KR: string;
|
||||
LC: string;
|
||||
LE: string;
|
||||
LI: string;
|
||||
LO: string;
|
||||
LT: string;
|
||||
LU: string;
|
||||
MB: string;
|
||||
MC: string;
|
||||
ME: string;
|
||||
MI: string;
|
||||
MN: string;
|
||||
MO: string;
|
||||
MS: string;
|
||||
MT: string;
|
||||
NA: string;
|
||||
NO: string;
|
||||
NU: string;
|
||||
OG: string;
|
||||
OR: string;
|
||||
OT: string;
|
||||
PA: string;
|
||||
PC: string;
|
||||
PD: string;
|
||||
PE: string;
|
||||
PG: string;
|
||||
PI: string;
|
||||
PN: string;
|
||||
PO: string;
|
||||
PR: string;
|
||||
PT: string;
|
||||
PU: string;
|
||||
PV: string;
|
||||
PZ: string;
|
||||
RA: string;
|
||||
RC: string;
|
||||
RE: string;
|
||||
RG: string;
|
||||
RI: string;
|
||||
RM: string;
|
||||
RN: string;
|
||||
RO: string;
|
||||
SA: string;
|
||||
SI: string;
|
||||
SO: string;
|
||||
SP: string;
|
||||
SR: string;
|
||||
SS: string;
|
||||
SV: string;
|
||||
TA: string;
|
||||
TE: string;
|
||||
TN: string;
|
||||
TO: string;
|
||||
TP: string;
|
||||
TR: string;
|
||||
TS: string;
|
||||
TV: string;
|
||||
UD: string;
|
||||
VA: string;
|
||||
VB: string;
|
||||
VC: string;
|
||||
VE: string;
|
||||
VI: string;
|
||||
VR: string;
|
||||
VS: string;
|
||||
VT: string;
|
||||
VV: string;
|
||||
};
|
1
types/utils.d.ts
vendored
1
types/utils.d.ts
vendored
@ -5,3 +5,4 @@ export declare function getValidDate(d: number, m: number, y: number): Date;
|
||||
export declare function extractVowels(str: string): string;
|
||||
export declare function extractConsonants(str: string): string;
|
||||
export declare function pad(n: number, size?: number): string;
|
||||
export declare function birthplaceFields(selector: string): void;
|
||||
|
14
webpack.config.js
Normal file → Executable file
14
webpack.config.js
Normal file → Executable file
@ -5,7 +5,7 @@ const createVariants = require('parallel-webpack').createVariants
|
||||
function createConfig (options) {
|
||||
return {
|
||||
entry: {
|
||||
'codice.fiscale': './build/js/main.js'
|
||||
'codice.fiscale': './src/codice-fiscale.js'
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
@ -16,17 +16,25 @@ function createConfig (options) {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.json$/,
|
||||
loader: 'json-loader'
|
||||
}, {
|
||||
test: /\.js$/,
|
||||
exclude: /(node_modules|bower_components)/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: ["@babel/preset-env", "babel-preset-minify"]
|
||||
presets: ['babel-preset-env']
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
new webpack
|
||||
.optimize
|
||||
.UglifyJsPlugin()
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user