diff --git a/apps/platforms/src/lib/longevity/anagrafica.ts b/apps/platforms/src/lib/longevity/anagrafica.ts index cfea267..ee33827 100644 --- a/apps/platforms/src/lib/longevity/anagrafica.ts +++ b/apps/platforms/src/lib/longevity/anagrafica.ts @@ -4,7 +4,7 @@ // risalga alla persona (richiesta del cliente, 21/08): un secondo punto di giunzione // la annullerebbe in silenzio. Un test in tests/longevity/anagrafica.test.ts lo verifica. import type Database from 'better-sqlite3'; -import { createIdentityDb } from './db'; +import { createIdentityDb, getLongevityDb } from './db'; // Connessione singola a identity, privata di questo modulo: non è esportata, quindi non // è raggiungibile da nessun'altra parte del codice, nemmeno con un re-export su più file @@ -94,3 +94,88 @@ export function etaAllaData(dataNascita: string, alla: string): number { if (m < 0 || (m === 0 && d.getDate() < n.getDate())) eta--; return eta; } + +// +// Gestionale del trainer (#2, unità 2a): tre letture sull'anagrafica e l'apertura di un +// fascicolo, tutte sotto lo stesso vincolo di codicePerUtente/codicePerUtenteLoggato qui +// sopra — una versione PARAMETRIZZATA (identity esplicita, esportata solo per i test e per +// comporre da qui dentro) e una versione OPERATIVA (nessun argomento, apre da sola +// getIdentityDb) che è quella che pagine ed endpoint del gestionale importano davvero. La +// versione operativa non si può testare su un DB :memory: — il singleton è uno solo per +// processo — quindi il test vero gira sulla parametrizzata, esattamente come +// anagrafica.test.ts fa già con codicePerUtente invece di codicePerUtenteLoggato. +// + +export type ClienteElenco = { + client_code: string; + nome: string; + cognome: string; + data_nascita: string | null; + /** True se il cliente ha già un account per vedere il proprio referto (user_id valorizzato + * in identity.clienti). Creare quell'account è #8, fuori da questa unità: qui si legge + * soltanto se esiste già. */ + ha_accesso: boolean; +}; + +/** L'elenco dei clienti per la tabella del gestionale, ordinato per cognome poi nome — + * l'ordine in cui un trainer cerca una persona, non l'ordine di creazione del fascicolo. */ +export function elencoClientiParam(identity: Database.Database): ClienteElenco[] { + const righe = identity.prepare( + `SELECT client_code, nome, cognome, data_nascita, user_id FROM clienti ORDER BY cognome, nome` + ).all() as { client_code: string; nome: string; cognome: string; data_nascita: string | null; user_id: number | null }[]; + return righe.map(({ user_id, ...resto }) => ({ ...resto, ha_accesso: user_id !== null })); +} + +export function elencoClienti(): ClienteElenco[] { + return elencoClientiParam(getIdentityDb()); +} + +export type ClienteDettaglio = ClienteElenco & { + email: string | null; + telefono: string | null; +}; + +/** L'anagrafica di un cliente per la sua scheda nel gestionale, `null` se il codice non + * esiste — la pagina lo traduce in un 404, non in uno schermo vuoto. */ +export function clienteDaCodiceParam(identity: Database.Database, code: string): ClienteDettaglio | null { + const r = identity.prepare( + `SELECT client_code, nome, cognome, data_nascita, email, telefono, user_id + FROM clienti WHERE client_code = ?` + ).get(code) as + { client_code: string; nome: string; cognome: string; data_nascita: string | null; + email: string | null; telefono: string | null; user_id: number | null } | undefined; + if (!r) return null; + const { user_id, ...resto } = r; + return { ...resto, ha_accesso: user_id !== null }; +} + +export function clienteDaCodice(code: string): ClienteDettaglio | null { + return clienteDaCodiceParam(getIdentityDb(), code); +} + +/** L'età del cliente alla data indicata, `null` se non ha una data di nascita in anagrafica + * (compilazione facoltativa: il fascicolo può nascere senza). Non è un ripiego a zero — + * calcolaSessione tratta già `eta_alla_data: null` come "niente Fitness Age", stessa + * barriera del motore (vedi l'intestazione di motore/index.ts). */ +export function etaDelClienteParam(identity: Database.Database, code: string, alla: string): number | null { + const r = identity.prepare(`SELECT data_nascita FROM clienti WHERE client_code = ?`).get(code) as + { data_nascita: string | null } | undefined; + if (!r?.data_nascita) return null; + return etaAllaData(r.data_nascita, alla); +} + +export function etaDelCliente(code: string, alla: string): number | null { + return etaDelClienteParam(getIdentityDb(), code, alla); +} + +/** Apre un fascicolo nuovo dal gestionale: SEMPRE senza user_id — creare l'account che + * darebbe accesso al cliente è #8, non questa unità, e il tipo di `dati` qui sotto non + * prevede quel campo (stessa garanzia strutturale di CorpoQuestionario nell'endpoint del + * questionario: chi volesse passare uno user_id da qui non supererebbe `tsc --noEmit`). + * Delega tutto a `creaCliente`, che è già la versione testabile con DB propri. */ +export function apriFascicolo(dati: { + nome: string; cognome: string; sesso: 'M' | 'F'; + data_nascita?: string; email?: string; telefono?: string; +}): string { + return creaCliente(getIdentityDb(), getLongevityDb(), dati); +}