Tre cose distinte, e una che non esisteva
Il punto 4 della Fase 1 chiedeva di separare tre concetti che erano confusi in uno. Aprendolo è emerso che quell'uno, per giunta, non funzionava.
| job level | l'inquadramento contrattuale — Quadro, Impiegato L2 |
| ruolo | cosa fa la persona — ricercatore, project manager |
| macro-livello | la fascia con cui gli enti finanziatori tariffano |
Sono indipendenti: un ricercatore può essere quadro o impiegato, e il macro-livello con cui un ente lo tariffa è più grossolano di entrambi. Tenerli in un campo solo obbliga a scegliere quale dei tre perdere.
Cosa era rotto
employees.job_level non è mai esistita. Il codice la scriveva come testo
a ogni salvataggio, e datahub scarta in silenzio le colonne che non conosce
rispondendo 200. Il livello di inquadramento di un dipendente non è mai stato
salvato, e nessuno ha visto un errore.
Da lì discendeva il resto:
- Il costo standard ricadeva sempre su zero. Il forecast a tariffa standard
sceglie il prezzo guardando il livello del dipendente. Quel livello era
sempre vuoto, quindi la tariffa non si trovava mai. Nei golden test la cosa
non si vedeva, perché le fixture contenevano
job_levelvalorizzato: un campo che il database non può contenere. La fixture era più generosa dello schema, e rendeva verde un codice che in produzione non lo era. - Il catalogo veniva letto e scritto con un nome inesistente,
rendicontazione_job_levels. Zero occorrenze in ogni file di schema: la tabella vera èrendicontazione.job_levels. Erano i 404 nei log didata-service, e la pagina Impostazioni non ha mai funzionato. - La semina dei valori predefiniti era dentro un
.catch()che inghiottiva il fallimento. Un catalogo vuoto e un catalogo non scrivibile avevano lo stesso aspetto: è ciò che ha tenuto nascosto il 404. - Il frontend usava
label, che non esiste: la colonna si chiamaname, come in tutti i cataloghi vicini. L'altra metà dello stesso 404.
Non c'era nessun valore da migrare — e non è una fortuna, è la stessa cosa detta in altro modo. Non essendo mai stato scritto niente, non c'era niente da recuperare.
Com'è adesso
Tre cataloghi tenant-aware — job_levels, job_roles, macro_levels — e tre
riferimenti su employees. Le chiavi sono composite, come vuole ADR-001:
un dipendente non può puntare al catalogo di un'altra organizzazione, e non è
un controllo applicativo ma un vincolo. Verificato: l'aggiornamento
cross-tenant viene rifiutato dal database.
Il codice è unico dentro l'organizzazione, non oltre: due aziende possono
chiamare quadro due inquadramenti diversi.
Il database tiene gli id, il dominio ragiona in codici — la tariffa standard di
un progetto è una mappa da codice di livello a euro. modules/workforce/inquadramento.js
sta in mezzo e traduce, aggiungendo a ogni dipendente il codice sotto il nome
secco (job_level) e il nome leggibile sotto job_level_name.
Come si è verificato che serviva
Le fixture sono state riscritte per descrivere il database vero: job_level_id
più il catalogo, al posto del testo. Il codice risolto doveva quindi
riprodurre gli stessi codici di prima — e la differenza si è ridotta ai
campi nuovi, senza una riga tolta.
Fatto questo, un test è diventato rosso da solo:
[equivalenza] il ricalcolo globale coincide con quello di data-service
+ inRange: 4 - inRange: 5
+ skippedNoRate: 3 - skippedNoRate: 2
Tre righe in più saltate per tariffa mancante — il difetto in produzione, reso visibile. È tornato verde quando anche il forecast ha risolto il catalogo.
Perturbando conInquadramento per non risolvere mai il codice cadono sei test,
fra cui quell'equivalenza.
I livelli che ci sono
La 100 aveva seminato quattro codici inventati — dirigente, quadro,
impiegato_l1, impiegato_l2 — per non lasciare una tendina vuota. Erano una
supposizione, e in un catalogo di anagrafica una supposizione è peggio del
vuoto: un vuoto si nota, un valore plausibile no.
La 101 mette gli inquadramenti del CCNL Terziario, Distribuzione e
Servizi, limitatamente a ciò che serve:
| codice | nome | ordine |
|---|---|---|
dirigente | Dirigente | 1 |
quadro | Quadro | 2 |
livello_1 | 1° livello | 3 |
livello_2 | 2° livello | 4 |
livello_3 | 3° livello | 5 |
Dal 4° al 7° esistono nel contratto ma non qui: si aggiungono quando servono, dalla pagina Impostazioni — che da oggi funziona.
I due segnaposto vengono tolti solo se non li usa nessuno. La chiave esterna
è ON DELETE RESTRICT, quindi cancellare una riga referenziata porterebbe giù
tutta la migrazione: meglio lasciarli e dirlo con un NOTICE. Un livello di
troppo in tendina si toglie a mano; un dipendente che perde l'inquadramento no.
Cosa resta da fare
job_rolesemacro_levelssono vuoti. I cataloghi esistono e sono leggibili, ma non c'è ancora una maschera per riempirli né un campo nell'anagrafica: fa parte del punto 9 (UI del mini gestionale HR).- Le tariffe per macro-livello sono Fase 3. Il catalogo nasce ora perché è qui che si separano le tre cose, ma la tariffa per ente più macro-livello più intervallo di validità arriva dopo.
- La sede segue la stessa strada. La migrazione
102le dàcityesort_order, una chiave composita, e uno storico dell'assegnazione con la stessa forma dello storico costi: la sede di un dipendente si chiede a una data, perché in Fase 3 la compatibilità fra sede del progetto e sede della risorsa si valuta al momento del lavoro, non oggi. Il difetto di partenza era identico —work_locationscritta come testo su una colonna che si chiamawork_location_id. - L'import assegna il job level, il ruolo no. Un codice fuori catalogo non
ferma l'import — novantanove righe buone non devono cadere per una — ma
finisce nel conteggio come
unknownJobLevels.