Data una retribuzione annua lorda, questo prototipo proietta il netto annuale e mensile ed espone ogni singola voce che sta in mezzo — con, accanto a ciascuna, la spiegazione di cosa sia e la fonte normativa da cui il numero proviene.
Anno d'imposta 2026. Impiegato privato a tempo indeterminato, tempo pieno, in uno qualunque dei 7.896 comuni italiani: regione e comune si scelgono, e con loro cambiano le due addizionali, la fonte linkata e il punto in cui la curva fa il suo salto. Anche il periodo di lavoro è un input: per un rapporto iniziato o finito a metà anno i numeri non scendono in proporzione, e la pagina dice quali voci le date accendono e quali spengono. Il perimetro completo, e cosa resta fuori, sta in docs/ASSUNZIONI.md.
Per una RAL di 30.000 € su 13 mensilità, a Milano:
| Voce | Importo |
|---|---|
| Retribuzione annua lorda (RAL) | 30.000,00 € |
| Contributi a carico del dipendente | − 2.757,01 € |
| Imponibile fiscale | 27.242,99 € |
| IRPEF lorda | 6.265,89 € |
| Detrazione per lavoro dipendente | − 2.044,26 € |
| Ulteriore detrazione | − 1.000,00 € |
| IRPEF netta | 3.221,63 € |
| Addizionale regionale (Lombardia) | − 377,94 € |
| Addizionale comunale (Milano) | − 217,94 € |
| Trattamento integrativo | 0,00 € |
| Somma integrativa | 0,00 € |
| Netto annuo | 23.425,48 € |
| Netto mensile (media) | 1.801,96 € |
Ogni riga porta con sé un testo in lingua comune. Ad esempio, sull'addizionale comunale:
Imposta del Comune di Milano sullo stesso imponibile: 0,8%, con esenzione fino a 23.000 € di imponibile.
⚠️ Attenzione: sopra 23.000 € l’aliquota si applica all’intero imponibile, non solo alla parte eccedente. È una soglia, non una franchigia.
La pagina vive su https://teodea.github.io/jethr-task/: si apre e funziona, senza installare niente.
Per servirla in locale serve un server statico — gli ES modules non si caricano da
file://, il browser li blocca per la same-origin policy:
python -m http.server 8000Poi http://localhost:8000. Va bene qualunque altro server statico (npx serve .,
php -S localhost:8000): non c'è nessun build step da eseguire prima.
La suite di test:
npm testIl motore è usabile anche da solo, senza passare dalla pagina:
import { calcolaCascata } from './src/cascata.js'
import { presentaCascata } from './src/presentazione.js'
import { testiCascata, avvisoScalino, ORDINE_VOCI } from './src/testi.js'
// Il contesto del calcolo è la coppia { anno, comune }. Senza `comune` vale il luogo
// predefinito (Milano), curato in src/costanti/ con la fonte accanto al valore: il motore
// resta usabile senza caricare i dati dell'Italia intera.
const cascata = calcolaCascata({ ral: 30000, mensilita: 13, anno: 2026 })
const voci = presentaCascata(cascata) // importi arrotondati e quadrati
const testi = testiCascata(cascata) // etichetta, spiegazione e nota di ogni voce
const avviso = avvisoScalino(cascata) // null fuori dalle zone critiche
// Il rapporto può coprire solo parte dell'anno: due date ISO, e giorni, quota e mensilità
// maturate li deriva il motore (src/periodo.js). Rapportare non basta: alcune voci
// cambiano proprio di stato, e cambiDiStatoDelPeriodo dice quali si accendono e spengono.
const semestre = calcolaCascata({
ral: 30000,
anno: 2026,
dataInizio: '2026-07-01',
dataFine: '2026-12-31',
})
// Il massimale contributivo vale per i soli iscritti dopo il 1995: chi ha iniziato prima
// paga contributi su tutta la RAL. È un flag opzionale, non un input — sotto il massimale
// non cambia nessuna cifra, e per questo la pagina lo chiede solo sopra il tetto.
const senzaTetto = calcolaCascata({ ral: 150000, iscrittoAnte1996: true })Il netto non si ottiene con una percentuale. Si ottiene facendo passare la RAL attraverso una sequenza ordinata, in cui ogni passo parte da dove è arrivato il precedente:
RAL
− contributi previdenziali → resta l'IMPONIBILE FISCALE
× aliquote a scaglioni → dà l'IRPEF LORDA
− detrazioni → dà l'IRPEF NETTA
− addizionali regionale e comunale
+ erogazioni (i bonus)
= NETTO
L'ordine non è un dettaglio di implementazione: è dominio. Le addizionali, per esempio, si calcolano sull'imponibile fiscale ma sono dovute solo se l'IRPEF netta è positiva — quindi non possono essere calcolate prima di aver applicato le detrazioni.
Tre meccanismi diversi, che intervengono in tre punti diversi della cascata. Confonderli è l'errore che produce numeri plausibili e sbagliati.
| Leva | Agisce | Su cosa | Esempio nel motore |
|---|---|---|---|
| Deduzione | prima dell'imposta | abbassa il reddito | i contributi INPS |
| Detrazione | dopo l'imposta | abbassa l'imposta | detrazione per lavoro dipendente |
| Erogazione | fuori dall'imposta | aggiunge soldi | somma integrativa |
Perché la distinzione conta davvero: 1.000 € valgono cifre diverse a seconda della leva.
| Reddito 9.000 € | Reddito 25.000 € | Reddito 60.000 € | |
|---|---|---|---|
| 1.000 € di deduzione | 0 € | 230 € | 430 € |
| 1.000 € di detrazione | 0 € | 1.000 € | 1.000 € |
| 1.000 € di erogazione | 1.000 € | 1.000 € | 1.000 € |
La deduzione premia i redditi alti. La detrazione è piatta, ma non arriva a chi non paga imposta. Solo l'erogazione raggiunge tutti — ed è il motivo per cui esiste la somma integrativa introdotta dalla legge di bilancio 2025.
Sono la ragione per cui un calcolatore serve più di una percentuale a mente.
Alcune agevolazioni si perdono per intero superando una soglia, invece di ridursi gradualmente. Sul luogo predefinito succede in quattro punti, tutti censiti in src/discontinuita.js e derivati dalle costanti, mai scritti a mano. Tre stanno su soglie di reddito nazionali; il quarto è l'esenzione comunale, raccontata più sotto perché si sposta con il comune scelto:
| Il reddito passa da | a | e il netto |
|---|---|---|
| 8.500 € | 8.501 € | −257 € (cala la percentuale della somma integrativa e, finita la no tax area, si accende l'addizionale regionale) |
| 15.000 € | 15.001 € | −130 € (finisce il trattamento integrativo) |
| 35.000 € | 35.001 € | −65 € (si perde la maggiorazione dell'art. 13) |
Quando la RAL cade in una di queste zone, il prototipo lo dice con gli estremi espliciti:
⚠️ Sei appena sopra uno scalino. A 38.542,01 € di RAL il netto annuo era più alto di quello attuale, fino a 65,00 € in più. Non è un errore del calcolo: la legge prevede soglie oltre le quali un’agevolazione si perde per intero invece di ridursi. Torni sopra quel livello da 38.707,63 € di RAL.
Dentro l'avviso, e solo lì, un mini-grafico del netto a scala ristretta attorno a quella zona, con il punto sulla RAL inserita. La scala è il punto: un gradino da 65 € su una RAL di 38.000 € è invisibile su un grafico intero, e visibile a colpo d'occhio su una finestra di poche centinaia di euro.
Le detrazioni valgono solo fino a concorrenza dell'imposta: l'eccedenza non diventa un credito. È la cosiddetta incapienza, e colpisce proprio i redditi più bassi. A RAL 9.000 €:
Ti spettavano 1.955,00 € di sconti, ma la tua imposta è più bassa: 75,23 € non vengono utilizzati e non sono rimborsabili.
L'addizionale comunale di Milano è esente fino a 23.000 € di imponibile. A 23.000,01 € si paga lo 0,8% sull'intero imponibile, non sull'eccedenza: circa 184 € persi per un centesimo guadagnato.
E questa soglia si sposta con il comune. Non è scritta da nessuna parte nel codice: il censimento dei salti la deriva dalle costanti del luogo scelto, quindi a Trento lo scalino sta a 30.000 € (dove l'esenzione è della Provincia autonoma, non del Comune), a Milano a 23.000 €, e in un comune senza esenzione non c'è affatto. Mini-grafico compreso — è il motivo per cui il selettore rende personale l'intuizione migliore del prototipo invece di lasciarla milanese.
Netto annuo diviso le mensilità. Nessuna busta paga reale coincide con quel numero: le trattenute mensili sono provvisorie fino al conguaglio di dicembre. Il prototipo calcola il risultato annuale a consuntivo e non simula le dodici buste.
| Percorso | Cosa contiene |
|---|---|
| index.html | la pagina pubblicata: importa il motore, zero build e zero dipendenze |
| web/ | lo strato di pagina — legge gli input, chiama i seam, renderizza; nessuna decisione di dominio |
| src/cascata.js | il motore: ogni voce dichiara cosa toglie e su quale base |
| src/costanti/ | un file per anno d'imposta (2025, 2026); ogni valore con la sua fonte |
| src/luoghi.js | dal codice catastale alle costanti del luogo: registro dei comuni e caricamento per regione |
| src/periodo.js | il periodo di lavoro nell'anno: giorni del rapporto, quota d'anno, mensilità maturate |
| dati/addizionali/ | le addizionali di tutti i comuni, importate dal MEF: un JSON per ente impositore, con la data di scarico |
| strumenti/ | lo script rieseguibile che rigenera quei JSON dalle fonti MEF e ISTAT |
| src/presentazione.js | arrotondamento ai centesimi e quadratura delle voci esposte |
| src/testi.js | i testi dell'interfaccia, con i numeri interpolati dalle costanti |
| src/discontinuita.js | censimento dei salti legittimi e delle zone non monotone |
| src/validazione.js | input fuori dominio (errori) e implausibili (avvisi) |
| src/formato.js | come si legge e si riscrive un importo in italiano; rifiuta le forme ambigue |
| test/ | casi ancorati alle fonti, suite di proprietà, test dei testi |
| CONTEXT.md | il glossario del dominio: fonte di verità dei termini |
| docs/ASSUNZIONI.md | cosa il prototipo non copre, e cosa cambierebbe se lo coprisse |
| docs/ricerca/ | la ricerca sulle fonti primarie, una per domanda, con citazioni |
Nessuna costante fiscale senza la sua fonte primaria, come commento sulla riga:
primaFasciaAnnua: 56224, // EUR - fonte: Circolare INPS n. 6 del 30/01/2026, par. 5, <url>, anno 2026Solo fonti primarie: Agenzia delle Entrate, INPS, Gazzetta Ufficiale, Normattiva, e le delibere di regione e comune per le addizionali. Mai il riassunto di terze parti — se una fonte cita una norma, si risale alla norma.
Vale anche per i valori attesi dei test: quelli certificati da una fonte portano la
citazione, quelli calcolati dalle formule di legge sono marcati [derivazione].
Anche i testi rispettano la regola: nessuna cifra è scritta a mano nelle stringhe, tutte sono interpolate dalle costanti dell'anno. Con due set attivi, un "33%" hardcodato sarebbe falso metà delle volte — e un test lo verifica confrontando gli stessi testi sul 2025 e sul 2026.
E la fonte non resta nel codice: la mappa FONTI, esportata dallo stesso file delle
costanti di ogni anno, la porta in pagina. Apri una voce della cascata e in fondo trovi
la circolare, la delibera o l'articolo da cui quel numero proviene, come link che apre il
documento in una nuova scheda. Un test al seam dei testi fallisce se una voce resta senza
fonte, se l'URL non è utilizzabile o se punta fuori dai domini delle fonti primarie: la
promessa è verificata, non affidata alla memoria.
Perché JSON non ha commenti, e la fonte deve stare accanto al valore: un file di fonti
separato divergerebbe al primo aggiornamento. In più il formato usa Infinity per lo
scaglione più alto, che in JSON non esiste. Tabelle importate da una macchina — i comuni
dal portale MEF — stanno invece in JSON: sono dati, non costanti curate.
Sono 7.896 comuni e 21 enti impositori (19 regioni più Trento e Bolzano, che hanno leggi proprie). Nessuno li trascrive a mano: li importa uno script rieseguibile che unisce gli elenchi MEF all'anagrafica ISTAT sul codice catastale, e li scrive un file per ente, con la data di scarico dentro:
node strumenti/importa-addizionali.mjsUn file per ente perché la pagina scarica solo la regione scelta: il primo dei tre menù a cascata è anche la strategia di caricamento. Il secondo — la provincia — non tocca il calcolo: serve a spezzare gli elenchi, perché i comuni della sola Lombardia sono 1.502 e col filtro il più lungo scende a 312. Lo script si ferma invece di indovinare — se cambia il tracciato di un CSV, se il join perde comuni, se compare un ente nuovo, o se cambia il testo di un'agevolazione regionale curata a mano. E quello che non sa leggere lo dichiara: i 108 comuni con esenzioni descrittive e i 2 con fasce non progressive nell'elenco MEF finiscono in pagina come avvertenza accanto al numero, non in silenzio.
Milano e la Lombardia restano scritte a mano in src/costanti/, con la fonte accanto al
valore, e servono da oracolo: un test confronta il dato importato con quello curato, e
fallisce se le due strade divergono. È così che un errore del parser si fa vedere.
Il 2026 introduce una modifica sostanziale: la seconda aliquota IRPEF scende dal 35% al 33% (L. 199/2025, art. 1, c. 3). Una tabella non aggiornata sbaglia fino a 440 € a persona. Il set di costanti 2025 è mantenuto in parallelo proprio per rendere l'errore rilevabile: gli stessi test girano sui due anni e devono divergere nei punti attesi.
Tre valori dipendono da documenti non ancora pubblicati alla data dell'ultima verifica (11/08/2026). Lo stato di ogni ciclo di controllo è in docs/ricerca/rivalidazioni-fonti.md; il più rilevante: l'addizionale comunale di Milano vale per proroga della delibera del 2020, e una nuova delibera pubblicata entro il 20/12/2026 avrebbe effetto retroattivo al 1° gennaio 2026.