Skip to main content

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 levell'inquadramento contrattuale — Quadro, Impiegato L2
ruolocosa fa la persona — ricercatore, project manager
macro-livellola 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_level valorizzato: 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 di data-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 chiama name, 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:

codicenomeordine
dirigenteDirigente1
quadroQuadro2
livello_11° livello3
livello_22° livello4
livello_33° livello5

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_roles e macro_levels sono 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 102 le dà city e sort_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_location scritta come testo su una colonna che si chiama work_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.