infoalloggi-monorepo/apps/infoalloggi/src/lib/form_utils.ts

297 lines
7.1 KiB
TypeScript
Raw Normal View History

import type { z } from "zod/v4";
2025-08-28 18:27:07 +02:00
import type { ListOption } from "~/components/custom_ui/multiselect";
2025-08-04 17:45:44 +02:00
import { getComuneFromIndex } from "~/i18n/comuni";
import { searchNazioneByIndex } from "~/i18n/nazioni";
export const getComuneFieldValue = (
2025-08-28 18:27:07 +02:00
fieldValue: string,
comuni_options: string[],
2025-08-04 17:45:44 +02:00
): ListOption | undefined => {
2025-08-28 18:27:07 +02:00
if (fieldValue !== "") {
const num = parseInt(fieldValue);
if (num <= comuni_options.length) {
return {
value: String(num),
label: String(comuni_options[num as keyof typeof comuni_options]),
};
}
}
return undefined;
2025-08-04 17:45:44 +02:00
};
const getInizialiCF = (stringa: string) => {
2025-08-28 18:27:07 +02:00
const vocali = ["A", "E", "I", "O", "U"];
let iniziali = "";
for (const c of stringa) {
if (iniziali.length < 3) {
if (!vocali.includes(c)) {
iniziali += c;
}
}
}
if (iniziali.length < 3) {
for (const c of stringa) {
if (iniziali.length < 3) {
if (vocali.includes(c)) {
iniziali += c;
}
}
}
}
if (iniziali.length === 2) {
iniziali += "X";
}
return iniziali;
2025-08-04 17:45:44 +02:00
};
const lettere_mesi = "ABCDEHLMPRST";
const getMonthLetterCF = (month: number) => {
2025-08-28 18:27:07 +02:00
return lettere_mesi[month];
2025-08-04 17:45:44 +02:00
};
const getDayCF = (day: number, sesso: string) => {
2025-08-28 18:27:07 +02:00
if (sesso === "M") {
return day.toString().padStart(2, "0");
}
return (day + 40).toString().padStart(2, "0");
2025-08-04 17:45:44 +02:00
};
const AltMapping = "LMNPQRSTUV";
const getAlternativeCF = (input: string): string[] => {
2025-08-28 18:27:07 +02:00
const substring = input.slice(0, Math.min(input.length, 15));
const result = [];
let wrk_string = substring;
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
for (let i = substring.length - 1; i >= 0; i--) {
const char = substring[i];
if (char && !Number.isNaN(parseInt(char))) {
const str_arr = wrk_string.split("");
if (parseInt(char) > 9) {
throw new Error("Invalid CF");
}
// biome-ignore lint/style/noNonNullAssertion: <known length>
str_arr[i] = AltMapping[parseInt(char)]!;
wrk_string = str_arr.join("");
result.push(wrk_string);
}
}
return result;
2025-08-04 17:45:44 +02:00
};
const genCatasto = ({
2025-08-28 18:27:07 +02:00
luogoNascita,
nazioneNascita,
2025-08-04 17:45:44 +02:00
}: {
2025-08-28 18:27:07 +02:00
luogoNascita: string;
nazioneNascita: string;
2025-08-04 17:45:44 +02:00
}) => {
2025-08-28 18:27:07 +02:00
let catasto = "";
if (nazioneNascita === "0") {
if (luogoNascita !== "") {
const comune = getComuneFromIndex(parseInt(luogoNascita));
if (comune) {
catasto = comune.CATASTO;
}
}
} else {
if (nazioneNascita !== "") {
const nazione = searchNazioneByIndex(parseInt(nazioneNascita));
if (nazione) {
catasto = nazione.CATASTO;
}
}
}
return catasto;
2025-08-04 17:45:44 +02:00
};
const gen15CF = ({
2025-08-28 18:27:07 +02:00
cognome,
nome,
dataNascita,
sesso,
luogoNascita,
nazioneNascita,
2025-08-04 17:45:44 +02:00
}: {
2025-08-28 18:27:07 +02:00
cognome: string;
nome: string;
dataNascita: Date;
sesso: string;
luogoNascita: string;
nazioneNascita: string;
2025-08-04 17:45:44 +02:00
}): string => {
2025-08-28 18:27:07 +02:00
const data = new Date(dataNascita);
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
return `${getInizialiCF(cognome)}${getInizialiCF(nome)}${data.getFullYear().toString().slice(-2)}${getMonthLetterCF(data.getMonth())}${getDayCF(data.getDate(), sesso)}${genCatasto({ luogoNascita, nazioneNascita })}`;
2025-08-04 17:45:44 +02:00
};
const set1 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const set2 = "ABCDEFGHIJABCDEFGHIJKLMNOPQRSTUVWXYZ";
const setpari = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const setdisp = "BAKPLCQDREVOSFTGUHMINJWZYX";
const setcontrollo = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const genCarattereControllo = (str: string) => {
2025-08-28 18:27:07 +02:00
let s = 0;
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
for (let i = 1; i <= 13; i += 2)
s += setpari.indexOf(set2.charAt(set1.indexOf(str.charAt(i))));
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
for (let i = 0; i <= 14; i += 2)
s += setdisp.indexOf(set2.charAt(set1.indexOf(str.charAt(i))));
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
return setcontrollo[s % 26];
2025-08-04 17:45:44 +02:00
};
export const checkFiscalCodeValidity = ({
2025-08-28 18:27:07 +02:00
path,
codiceFiscale,
cognome,
nome,
dataNascita,
sesso,
refinementContext,
luogoNascita,
nazioneNascita,
2025-08-04 17:45:44 +02:00
}: {
2025-08-28 18:27:07 +02:00
path: string;
codiceFiscale: string;
cognome: string;
nome: string;
dataNascita: Date;
sesso: string;
refinementContext: z.RefinementCtx;
luogoNascita: string;
nazioneNascita: string;
2025-08-04 17:45:44 +02:00
}) => {
2025-08-28 18:27:07 +02:00
const cognomeUpper = cognome.toUpperCase();
const nomeUpper = nome.toUpperCase();
const gen = gen15CF({
cognome: cognomeUpper,
nome: nomeUpper,
dataNascita,
sesso,
luogoNascita,
nazioneNascita,
});
const control = genCarattereControllo(gen);
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
if (
gen !== codiceFiscale.slice(0, 15) &&
!(codiceFiscale in getAlternativeCF(gen))
) {
console.log("gen", gen);
console.log("codiceFiscale", codiceFiscale);
console.log("alternative", getAlternativeCF(gen));
refinementContext.issues.push({
code: "custom",
message: "Codice fiscale non valido",
path: [path],
input: "",
});
}
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
if (control !== codiceFiscale.slice(-1)) {
refinementContext.issues.push({
code: "custom",
message: "Codice di controllo non valido",
path: [path],
input: "",
});
}
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
if (codiceFiscale.length !== 16) {
refinementContext.issues.push({
code: "custom",
message: "Inserisci un codice fiscale valido",
path: [path],
input: "",
});
}
if (
!/^(?:[A-Z][AEIOU][AEIOUX]|[AEIOU]X{2}|[B-DF-HJ-NP-TV-Z]{2}[A-Z]){2}(?:[\dLMNP-V]{2}(?:[A-EHLMPR-T](?:[04LQ][1-9MNP-V]|[15MR][\dLMNP-V]|[26NS][0-8LMNP-U])|[DHPS][37PT][0L]|[ACELMRT][37PT][01LM]|[AC-EHLMPR-T][26NS][9V])|(?:[02468LNQSU][048LQU]|[13579MPRTV][26NS])B[26NS][9V])(?:[A-MZ][1-9MNP-V][\dLMNP-V]{2}|[A-M][0L](?:[1-9MNP-V][\dLMNP-V]|[0L][1-9MNP-V]))[A-Z]$/i.exec(
codiceFiscale,
)
) {
refinementContext.issues.push({
code: "custom",
message: "Inserisci un codice fiscale valido",
path: [path],
input: "",
});
}
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
if (codiceFiscale.slice(0, 3) !== getInizialiCF(cognomeUpper)) {
refinementContext.issues.push({
code: "custom",
message: "Codice fiscale incompatibile con cognome inserito",
path: [path],
input: "",
});
}
if (codiceFiscale.slice(3, 6) !== getInizialiCF(nomeUpper)) {
refinementContext.issues.push({
code: "custom",
message: "Codice fiscale incompatibile con nome inserito",
path: [path],
input: "",
});
}
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
if (
codiceFiscale.slice(6, 8) !== dataNascita.getFullYear().toString().slice(-2)
) {
refinementContext.issues.push({
code: "custom",
message: "Codice fiscale incompatibile con anno di nascita",
path: [path],
input: "",
});
}
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
if (codiceFiscale.slice(8, 9) !== getMonthLetterCF(dataNascita.getMonth())) {
refinementContext.issues.push({
code: "custom",
message: "Codice fiscale incompatibile con mese di nascita",
path: [path],
input: "",
});
}
if (codiceFiscale.slice(9, 11) !== getDayCF(dataNascita.getDate(), sesso)) {
refinementContext.issues.push({
code: "custom",
message: "Codice fiscale incompatibile con giorno di nascita",
path: [path],
input: "",
});
}
if (nazioneNascita === "0") {
if (luogoNascita !== "") {
const comune = getComuneFromIndex(parseInt(luogoNascita));
if (comune && codiceFiscale.slice(11, 15) !== comune.CATASTO) {
refinementContext.issues.push({
code: "custom",
message: "Codice fiscale incompatibile con luogo di nascita",
path: [path],
input: "",
});
}
}
} else {
if (nazioneNascita !== "") {
const nazione = searchNazioneByIndex(parseInt(nazioneNascita));
if (nazione && codiceFiscale.slice(11, 15) !== nazione.CATASTO) {
refinementContext.issues.push({
code: "custom",
message: "Codice fiscale incompatibile con luogo di nascita",
path: [path],
input: "",
});
}
}
}
2025-08-04 17:45:44 +02:00
};