aziendasanitaria-utils 1.2.71 → 1.2.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -3,7 +3,8 @@ import {Assistito, DATI} from "../classi/Assistito.js";
|
|
|
3
3
|
import moment from "moment";
|
|
4
4
|
import _ from "lodash";
|
|
5
5
|
import CryptHelper from "../CryptHelper.js";
|
|
6
|
-
import {
|
|
6
|
+
import {Ts} from "./Ts.js";
|
|
7
|
+
import {STATO_TS, snapshotDaSogei, classificaStatoTs, ASP_MESSINA, estraiCodiceAsl} from "./statoAssistitoTs.js";
|
|
7
8
|
|
|
8
9
|
export class Nar2 {
|
|
9
10
|
static LOGIN_URL = "https://nar2.regione.sicilia.it/services/index.php/api/login";
|
|
@@ -193,6 +194,10 @@ export class Nar2 {
|
|
|
193
194
|
this._cryptData = (crtyptData.hasOwnProperty("KEY") && crtyptData.hasOwnProperty("IV"))
|
|
194
195
|
? crtyptData
|
|
195
196
|
: null;
|
|
197
|
+
// Conservato per il fallback legacy verso il portale TS (scraping) quando
|
|
198
|
+
// l'endpoint Sogei è irraggiungibile: il Ts viene creato lazy da #tsScraper().
|
|
199
|
+
this._impostazioni = impostazioniServiziTerzi;
|
|
200
|
+
this._ts = null;
|
|
196
201
|
}
|
|
197
202
|
|
|
198
203
|
/**
|
|
@@ -695,10 +700,11 @@ export class Nar2 {
|
|
|
695
700
|
|
|
696
701
|
// 3) Scelta medico corrente (per revoca_scelta_precedente)
|
|
697
702
|
// La scelta attiva si trova in storico_medici[*] con pm_fstato="A" e pm_dt_disable=null.
|
|
698
|
-
//
|
|
699
|
-
//
|
|
700
|
-
// una scelta strettamente attiva (es. dopo una revoca
|
|
701
|
-
// recente in storico (pm_id più alto).
|
|
703
|
+
// Il server richiede SEMPRE il blocco revoca_scelta_precedente (errore 400/500 se
|
|
704
|
+
// assente), ANCHE in prima iscrizione: se non c'è un rapporto precedente revoca_id = ""
|
|
705
|
+
// (come il portale). Se non c'è una scelta strettamente attiva (es. dopo una revoca
|
|
706
|
+
// standalone) si usa il rapporto più recente in storico (pm_id più alto).
|
|
707
|
+
// forzaSenzaRevoca:true omette del tutto il blocco (sconsigliato: il server lo rifiuta).
|
|
702
708
|
let revocaPrecedente = null;
|
|
703
709
|
if (!forzaSenzaRevoca) {
|
|
704
710
|
const storico = Array.isArray(fullData.storico_medici) ? fullData.storico_medici : [];
|
|
@@ -707,15 +713,19 @@ export class Nar2 {
|
|
|
707
713
|
sceltaPrecedente = storico.slice().sort((a, b) => (parseInt(b?.pm_id, 10) || 0) - (parseInt(a?.pm_id, 10) || 0))[0];
|
|
708
714
|
}
|
|
709
715
|
const pmIdCorrente = sceltaPrecedente?.pm_id ?? null;
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
716
|
+
// Il server VUOLE SEMPRE il blocco revoca_scelta_precedente, anche in PRIMA
|
|
717
|
+
// ISCRIZIONE (nessun medico precedente): in quel caso revoca_id = "" (stringa
|
|
718
|
+
// vuota), esattamente come fa il portale. Ometterlo del tutto → 500 "Server Error"
|
|
719
|
+
// (verificato confrontando la POST reale del portale con la nostra).
|
|
720
|
+
revocaPrecedente = {
|
|
721
|
+
pm_dt_disable: dataRevoca,
|
|
722
|
+
dm_dt_ins_revoca: dataInsRevoca,
|
|
723
|
+
dm_motivo_revoca: motivoRevoca, // popolato sotto se null (default A04)
|
|
724
|
+
dm_tipoop_revoca: tipoOperazioneRevoca,
|
|
725
|
+
revoca_id: pmIdCorrente != null
|
|
726
|
+
? (typeof pmIdCorrente === "string" ? parseInt(pmIdCorrente, 10) : pmIdCorrente)
|
|
727
|
+
: ""
|
|
728
|
+
};
|
|
719
729
|
}
|
|
720
730
|
|
|
721
731
|
// 4) Situazione assistenziale + motivo + tipo operazione (dal catalogo)
|
|
@@ -759,11 +769,19 @@ export class Nar2 {
|
|
|
759
769
|
return {ok: false, dryRun, payload: null, response: null, error: "Impossibile determinare il motivo della scelta"};
|
|
760
770
|
}
|
|
761
771
|
|
|
762
|
-
//
|
|
772
|
+
// Motivo della revoca della scelta precedente: il portale usa A04 "Cambio medico"
|
|
773
|
+
// (anche in prima iscrizione, con revoca_id vuoto) — NON il motivo della nuova scelta.
|
|
774
|
+
// L'override esplicito (config.motivoRevoca, es. A17 in deroga) resta rispettato.
|
|
763
775
|
if (revocaPrecedente && !revocaPrecedente.dm_motivo_revoca) {
|
|
764
|
-
revocaPrecedente.dm_motivo_revoca =
|
|
776
|
+
revocaPrecedente.dm_motivo_revoca = Nar2.MOTIVO_CAMBIO_MEDICO;
|
|
765
777
|
}
|
|
766
778
|
|
|
779
|
+
// Pediatri: "data fine proroga" (assistenza fino a 16 anni). Il portale la imposta a
|
|
780
|
+
// data di nascita + 16 anni; se non fornita la deriviamo (per gli MMG resta null).
|
|
781
|
+
const fineProrogaPed = tipoMedico === Nar2.PEDIATRA
|
|
782
|
+
? (dataFineProrogaPed ?? moment(fullData.pz_dt_nas, "YYYY-MM-DD HH:mm:ss").add(16, "years").format("YYYY-MM-DD"))
|
|
783
|
+
: null;
|
|
784
|
+
|
|
767
785
|
// 5) Payload
|
|
768
786
|
const payload = {
|
|
769
787
|
data: {
|
|
@@ -781,7 +799,7 @@ export class Nar2 {
|
|
|
781
799
|
dm_ambito_scelta: ambitoScelta?.toString(),
|
|
782
800
|
dm_motivo_scelta: motivoFinale,
|
|
783
801
|
dm_tipoop_scelta: tipoOpFinale,
|
|
784
|
-
dm_dt_fine_proroga_ped:
|
|
802
|
+
dm_dt_fine_proroga_ped: fineProrogaPed,
|
|
785
803
|
dm_motivo_pror_scad_ped: tipoMedico === Nar2.PEDIATRA ? motivoProrogaPed : null
|
|
786
804
|
}
|
|
787
805
|
};
|
|
@@ -2839,6 +2857,174 @@ export class Nar2 {
|
|
|
2839
2857
|
return {ok, unico, documenti: risultati};
|
|
2840
2858
|
}
|
|
2841
2859
|
|
|
2860
|
+
// Crea lazy lo scraper del portale TS (legacy) dalla stessa ImpostazioniServiziTerzi.
|
|
2861
|
+
// Usato come fallback quando l'endpoint NAR2/Sogei è irraggiungibile.
|
|
2862
|
+
#tsScraper() {
|
|
2863
|
+
if (!this._ts) this._ts = new Ts(this._impostazioni);
|
|
2864
|
+
return this._ts;
|
|
2865
|
+
}
|
|
2866
|
+
|
|
2867
|
+
// MAPPER UNICO p801 → Assistito (estratto dal ramo di successo di
|
|
2868
|
+
// getDatiAssistitoFromCfSuSogeiNew, logica INVARIATA). Usato sia dalla risposta Sogei
|
|
2869
|
+
// sia dal fallback legacy, così la mappatura è identica per entrambe le fonti.
|
|
2870
|
+
#popolaAssistitoDaP801(assistito, data, cf, envelope) {
|
|
2871
|
+
const nullArray = (d) => (Array.isArray(d) && d.length === 0 ? "" : d);
|
|
2872
|
+
const deceduto = data?.p801descrizioneCodiceTipoAssistito?.toLowerCase().includes("deceduto") ?? false;
|
|
2873
|
+
const asp = nullArray(data.p801codiceRegioneResidenzaAsl) + " - " +
|
|
2874
|
+
nullArray(data.p801descrizioneRegioneResidenzaAsl) + " " +
|
|
2875
|
+
nullArray(data.p801codiceAslResidenzaAsl) + " - " +
|
|
2876
|
+
nullArray(data.p801descrizioneAslResidenzaAsl).trim();
|
|
2877
|
+
|
|
2878
|
+
assistito.setTs(DATI.CF, cf.toUpperCase().trim());
|
|
2879
|
+
assistito.setTs(DATI.CF_NORMALIZZATO, nullArray(data.p801codiceFiscale));
|
|
2880
|
+
assistito.setTs(DATI.COGNOME, nullArray(data.p801cognome));
|
|
2881
|
+
assistito.setTs(DATI.NOME, nullArray(data.p801nome));
|
|
2882
|
+
assistito.setTs(DATI.SESSO, nullArray(data.p801sesso));
|
|
2883
|
+
assistito.setTs(DATI.DATA_NASCITA, nullArray(data.p801dataNascita));
|
|
2884
|
+
assistito.setTs(DATI.COMUNE_NASCITA, nullArray(data.p801comuneNascita));
|
|
2885
|
+
assistito.setTs(DATI.COD_COMUNE_NASCITA, nullArray(data.p801codiceComuneNascita));
|
|
2886
|
+
assistito.setTs(DATI.COD_ISTAT_COMUNE_NASCITA, nullArray(data.p801codiceistatiComuneNascita));
|
|
2887
|
+
assistito.setTs(DATI.INDIRIZZO_RESIDENZA, nullArray(data.p801recapitoTessera));
|
|
2888
|
+
assistito.setTs(DATI.COD_COMUNE_RESIDENZA, nullArray(data.p801codiceComuneResidenza));
|
|
2889
|
+
assistito.setTs(DATI.COD_ISTAT_COMUNE_RESIDENZA, nullArray(data.p801codiceistatiComuneResidenza));
|
|
2890
|
+
assistito.setTs(DATI.ASP, asp !== " - - " ? asp : "");
|
|
2891
|
+
assistito.setTs(DATI.MMG_CF, nullArray(data.p801codiceFiscaleMedico));
|
|
2892
|
+
assistito.setTs(DATI.MMG_COGNOME, nullArray(data.p801cognomeMedico));
|
|
2893
|
+
assistito.setTs(DATI.MMG_NOME, nullArray(data.p801nomeMedico));
|
|
2894
|
+
assistito.setTs(DATI.MMG_DATA_SCELTA, nullArray(data.p801dataAssociazioneMedico));
|
|
2895
|
+
assistito.setTs(DATI.SSN_TIPO_ASSISTITO, nullArray(data.p801descrizioneCodiceTipoAssistito));
|
|
2896
|
+
assistito.setTs(DATI.SSN_INIZIO_ASSISTENZA, nullArray(data.p801dataInizioValidita));
|
|
2897
|
+
assistito.setTs(DATI.SSN_FINE_ASSISTENZA, data.p801dataFineValidita === "31/12/9999" ? "illimitata" : nullArray(data.p801dataFineValidita));
|
|
2898
|
+
assistito.setTs(DATI.SSN_MOTIVAZIONE_FINE_ASSISTENZA, data.p801dataFineValidita !== "31/12/9999" ? nullArray(data.p801motivazioneFineValidita) : null);
|
|
2899
|
+
assistito.setTs(DATI.SSN_NUMERO_TESSERA, nullArray(data.p801numeroTessera));
|
|
2900
|
+
assistito.setTs(DATI.DATA_DECESSO, deceduto ? nullArray(data.p801dataDecesso) : null);
|
|
2901
|
+
assistito.okTs = true;
|
|
2902
|
+
assistito.fullDataTs = envelope;
|
|
2903
|
+
}
|
|
2904
|
+
|
|
2905
|
+
// FALLBACK LEGACY: quando Sogei è irraggiungibile, ricostruisce i campi p801* dallo
|
|
2906
|
+
// scraping del portale TS (getDatiAnagraficiAssistito + getIndirizzoUltimaTessera) e
|
|
2907
|
+
// li passa al MAPPER esistente (#popolaAssistitoDaP801), restituendo la STESSA shape
|
|
2908
|
+
// { ok, fullData:{status,data:{p801*},_fonte:"legacy-ts"}, data:Assistito }.
|
|
2909
|
+
// Ritorna null se anche lo scraping è irraggiungibile (→ il chiamante torna ok:false).
|
|
2910
|
+
async #fallbackLegacyTsToP801(cf, assistito) {
|
|
2911
|
+
const cfUp = cf?.toUpperCase()?.trim();
|
|
2912
|
+
if (!cfUp || !this._impostazioni) return null;
|
|
2913
|
+
const ts = this.#tsScraper();
|
|
2914
|
+
|
|
2915
|
+
// 1) Anagrafica + dati assistenza (pagina "Visualizza Assistito")
|
|
2916
|
+
const anagRes = await ts.getDatiAnagraficiAssistito({codiceFiscale: cfUp});
|
|
2917
|
+
if (anagRes?.error) {
|
|
2918
|
+
const msg = (anagRes.message || "").toString();
|
|
2919
|
+
// "non trovato/censito" = esito VALIDO (non un guasto): ok:true + okTs:false,
|
|
2920
|
+
// così resta la distinzione esiste=false (verificaAssistitoTsNar2).
|
|
2921
|
+
if (/non\s+trovat|nessun|non\s+censit|non\s+esiste/i.test(msg)) {
|
|
2922
|
+
assistito.okTs = false;
|
|
2923
|
+
assistito.erroreTs = msg || "Assistito non presente sul Sistema TS (portale)";
|
|
2924
|
+
return {ok: true, fullData: {status: false, _fonte: "legacy-ts"}, data: assistito};
|
|
2925
|
+
}
|
|
2926
|
+
return null; // login/rete: scraping irraggiungibile → ok:false a monte
|
|
2927
|
+
}
|
|
2928
|
+
const a = anagRes.data || {};
|
|
2929
|
+
const s = (x) => (x == null ? "" : String(x).trim());
|
|
2930
|
+
|
|
2931
|
+
// 2) Recapito + numero tessera (best-effort: non blocca se assente)
|
|
2932
|
+
let indirizzo = "", numeroTessera = "";
|
|
2933
|
+
try {
|
|
2934
|
+
const tessRes = await ts.getIndirizzoUltimaTessera({codiceFiscale: cfUp});
|
|
2935
|
+
if (!tessRes?.error && tessRes.data) {
|
|
2936
|
+
indirizzo = s(tessRes.data.indirizzo);
|
|
2937
|
+
numeroTessera = s(tessRes.data.numero_tessera);
|
|
2938
|
+
}
|
|
2939
|
+
} catch { /* recapito non disponibile */ }
|
|
2940
|
+
|
|
2941
|
+
// 3) Parsing dei dati grezzi in campi p801
|
|
2942
|
+
// CF medico dal campo "medico" = "COGNOME NOME - CFMEDICO"
|
|
2943
|
+
const medicoStr = s(a.medico);
|
|
2944
|
+
const mCf = medicoStr.match(/\b([A-Z]{6}\d{2}[A-Z]\d{2}[A-Z]\d{3}[A-Z])\b/i);
|
|
2945
|
+
const cfMedico = mCf ? mCf[1].toUpperCase() : "";
|
|
2946
|
+
const cognomeMedico = medicoStr ? medicoStr.replace(/\s*-\s*[A-Z0-9]{16}\s*$/i, "").trim() : "";
|
|
2947
|
+
// codice tipo assistito da "1 - assistito in ASL di residenza" (NB: inaffidabile per neonati)
|
|
2948
|
+
const tipoDesc = s(a.tipo_assistito);
|
|
2949
|
+
const mTipo = tipoDesc.match(/^\s*([A-Z0-9]+)\s*-/i);
|
|
2950
|
+
const codTipo = mTipo ? mTipo[1] : "";
|
|
2951
|
+
// ASL/regione da "190 - Sicilia 205 - ASP MESSINA"
|
|
2952
|
+
const aslStr = s(a.asl) || s(a.campi?.["ASL"]);
|
|
2953
|
+
const fonteAtStr = s(a.campi?.["Fonte AT"]) || aslStr;
|
|
2954
|
+
const codAsl = estraiCodiceAsl(aslStr) || "";
|
|
2955
|
+
const codAslAT = estraiCodiceAsl(fonteAtStr) || "";
|
|
2956
|
+
const mReg = aslStr.match(/(\d{3})\s*-\s*([^0-9]+?)\s+(\d{3})\s*-\s*(.+)$/);
|
|
2957
|
+
const codReg = mReg ? mReg[1] : "";
|
|
2958
|
+
const descReg = mReg ? mReg[2].trim() : "";
|
|
2959
|
+
const descAsl = mReg ? mReg[4].trim() : "";
|
|
2960
|
+
// data fine validità: "Illimitata"/vuoto → sentinella 31/12/9999 (come Sogei)
|
|
2961
|
+
const fineRaw = s(a.data_fine_validita);
|
|
2962
|
+
const dataFineValidita = (fineRaw === "" || /illimitat/i.test(fineRaw)) ? "31/12/9999" : fineRaw;
|
|
2963
|
+
// comune di nascita: catastale (Belfiore) dal CF stesso (es. ...F158... → "F158")
|
|
2964
|
+
const belfiore = /^[A-Z0-9]{16}$/i.test(cfUp) ? cfUp.substring(11, 15).toUpperCase() : "";
|
|
2965
|
+
|
|
2966
|
+
// 4) Reverse-lookup del comune di RESIDENZA dal recapito (CAP, poi nome) → id NAR2 +
|
|
2967
|
+
// catastale. Best-effort: usa l'endpoint comuni NAR2, disponibile solo se NAR2 è
|
|
2968
|
+
// raggiungibile (serve all'inserimento nuovo assistito, che richiede comunque NAR2;
|
|
2969
|
+
// il cambio medico usa solo il recapito testuale, già disponibile).
|
|
2970
|
+
let comuneResidenzaId = "", catastaleResidenza = "";
|
|
2971
|
+
const mRecap = indirizzo.match(/(\d{5})\s+(.+?)\s+\(([A-Za-z]{2})\)\s*$/);
|
|
2972
|
+
const capRes = mRecap ? mRecap[1] : (indirizzo.match(/\b(\d{5})\b/)?.[1] ?? "");
|
|
2973
|
+
const nomeRes = mRecap ? mRecap[2].trim() : "";
|
|
2974
|
+
const prendiComune = (cm) => {
|
|
2975
|
+
if (cm?.ok && cm.comune?.id != null) {
|
|
2976
|
+
comuneResidenzaId = cm.comune.id.toString();
|
|
2977
|
+
catastaleResidenza = (cm.comune.catastale ?? "").toString();
|
|
2978
|
+
return true;
|
|
2979
|
+
}
|
|
2980
|
+
return false;
|
|
2981
|
+
};
|
|
2982
|
+
try {
|
|
2983
|
+
const cm = capRes ? await this.getComuneNar2({cap: capRes}) : null;
|
|
2984
|
+
if (!prendiComune(cm) && nomeRes) prendiComune(await this.getComuneNar2({nome: nomeRes}));
|
|
2985
|
+
} catch { /* lookup comune non disponibile (NAR2 irraggiungibile) */ }
|
|
2986
|
+
|
|
2987
|
+
// 5) Ricostruzione p801* (stessi nomi consumati dal mapper e dai chiamanti)
|
|
2988
|
+
const p801 = {
|
|
2989
|
+
p801codiceFiscale: s(a.codice_fiscale) || cfUp,
|
|
2990
|
+
p801cognome: s(a.cognome),
|
|
2991
|
+
p801nome: s(a.nome),
|
|
2992
|
+
p801sesso: s(a.sesso),
|
|
2993
|
+
p801dataNascita: s(a.data_nascita),
|
|
2994
|
+
p801comuneNascita: s(a.comune_nascita),
|
|
2995
|
+
p801codiceComuneNascita: belfiore,
|
|
2996
|
+
p801codiceistatiComuneNascita: "",
|
|
2997
|
+
p801codiceistatiComuneId: "",
|
|
2998
|
+
p801recapitoTessera: indirizzo,
|
|
2999
|
+
p801codiceComuneResidenza: catastaleResidenza,
|
|
3000
|
+
p801codiceistatiComuneResidenza: "",
|
|
3001
|
+
p801ComuneResidenzaId: comuneResidenzaId,
|
|
3002
|
+
p801codiceFiscaleMedico: cfMedico,
|
|
3003
|
+
p801cognomeMedico: cognomeMedico,
|
|
3004
|
+
p801nomeMedico: "",
|
|
3005
|
+
p801dataAssociazioneMedico: s(a.data_associazione_medico),
|
|
3006
|
+
p801descrizioneCodiceTipoAssistito: tipoDesc,
|
|
3007
|
+
p801codiceTipoAssistito: codTipo,
|
|
3008
|
+
p801dataInizioValidita: s(a.data_inizio_validita),
|
|
3009
|
+
p801dataFineValidita: dataFineValidita,
|
|
3010
|
+
p801motivazioneFineValidita: "",
|
|
3011
|
+
p801numeroTessera: numeroTessera,
|
|
3012
|
+
p801dataDecesso: "",
|
|
3013
|
+
p801codiceAslAssistenza: codAsl,
|
|
3014
|
+
p801codiceRegioneAssistenza: codReg,
|
|
3015
|
+
p801codiceAslResidenzaAsl: codAsl,
|
|
3016
|
+
p801codiceAslResidenzaAT: codAslAT,
|
|
3017
|
+
p801codiceRegioneResidenzaAsl: codReg,
|
|
3018
|
+
p801descrizioneRegioneResidenzaAsl: descReg,
|
|
3019
|
+
p801descrizioneAslResidenzaAsl: descAsl,
|
|
3020
|
+
p801listaMessaggi: [],
|
|
3021
|
+
};
|
|
3022
|
+
const envelope = {status: "true", data: p801, listaMessaggi: {}, _fonte: "legacy-ts"};
|
|
3023
|
+
this.#popolaAssistitoDaP801(assistito, p801, cfUp, envelope);
|
|
3024
|
+
assistito.erroreTs = null;
|
|
3025
|
+
return {ok: true, fullData: envelope, data: assistito};
|
|
3026
|
+
}
|
|
3027
|
+
|
|
2842
3028
|
async getDatiAssistitoFromCfSuSogeiNew(cf, assistito = null, fallback = false) {
|
|
2843
3029
|
let ok = false;
|
|
2844
3030
|
let risultato = null; // valore di ritorno del ramo di successo (fix: prima non veniva mai ritornato)
|
|
@@ -2891,38 +3077,9 @@ export class Nar2 {
|
|
|
2891
3077
|
};
|
|
2892
3078
|
} else {
|
|
2893
3079
|
ok = true;
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
nullArray(out.data.data.p801codiceAslResidenzaAsl) + " - " +
|
|
2898
|
-
nullArray(out.data.data.p801descrizioneAslResidenzaAsl).trim();
|
|
2899
|
-
|
|
2900
|
-
// Popoliamo i dati in fromTs usando i setter
|
|
2901
|
-
assistito.setTs(DATI.CF, cf.toUpperCase().trim());
|
|
2902
|
-
assistito.setTs(DATI.CF_NORMALIZZATO, nullArray(out.data.data.p801codiceFiscale));
|
|
2903
|
-
assistito.setTs(DATI.COGNOME, nullArray(out.data.data.p801cognome));
|
|
2904
|
-
assistito.setTs(DATI.NOME, nullArray(out.data.data.p801nome));
|
|
2905
|
-
assistito.setTs(DATI.SESSO, nullArray(out.data.data.p801sesso));
|
|
2906
|
-
assistito.setTs(DATI.DATA_NASCITA, nullArray(out.data.data.p801dataNascita));
|
|
2907
|
-
assistito.setTs(DATI.COMUNE_NASCITA, nullArray(out.data.data.p801comuneNascita));
|
|
2908
|
-
assistito.setTs(DATI.COD_COMUNE_NASCITA, nullArray(out.data.data.p801codiceComuneNascita));
|
|
2909
|
-
assistito.setTs(DATI.COD_ISTAT_COMUNE_NASCITA, nullArray(out.data.data.p801codiceistatiComuneNascita));
|
|
2910
|
-
assistito.setTs(DATI.INDIRIZZO_RESIDENZA, nullArray(out.data.data.p801recapitoTessera));
|
|
2911
|
-
assistito.setTs(DATI.COD_COMUNE_RESIDENZA, nullArray(out.data.data.p801codiceComuneResidenza));
|
|
2912
|
-
assistito.setTs(DATI.COD_ISTAT_COMUNE_RESIDENZA, nullArray(out.data.data.p801codiceistatiComuneResidenza));
|
|
2913
|
-
assistito.setTs(DATI.ASP, asp !== " - - " ? asp : "");
|
|
2914
|
-
assistito.setTs(DATI.MMG_CF, nullArray(out.data.data.p801codiceFiscaleMedico));
|
|
2915
|
-
assistito.setTs(DATI.MMG_COGNOME, nullArray(out.data.data.p801cognomeMedico));
|
|
2916
|
-
assistito.setTs(DATI.MMG_NOME, nullArray(out.data.data.p801nomeMedico));
|
|
2917
|
-
assistito.setTs(DATI.MMG_DATA_SCELTA, nullArray(out.data.data.p801dataAssociazioneMedico));
|
|
2918
|
-
assistito.setTs(DATI.SSN_TIPO_ASSISTITO, nullArray(out.data.data.p801descrizioneCodiceTipoAssistito));
|
|
2919
|
-
assistito.setTs(DATI.SSN_INIZIO_ASSISTENZA, nullArray(out.data.data.p801dataInizioValidita));
|
|
2920
|
-
assistito.setTs(DATI.SSN_FINE_ASSISTENZA, out.data.data.p801dataFineValidita === "31/12/9999" ? "illimitata" : nullArray(out.data.data.p801dataFineValidita));
|
|
2921
|
-
assistito.setTs(DATI.SSN_MOTIVAZIONE_FINE_ASSISTENZA, out.data.data.p801dataFineValidita !== "31/12/9999" ? nullArray(out.data.data.p801motivazioneFineValidita) : null);
|
|
2922
|
-
assistito.setTs(DATI.SSN_NUMERO_TESSERA, nullArray(out.data.data.p801numeroTessera));
|
|
2923
|
-
assistito.setTs(DATI.DATA_DECESSO, deceduto ? nullArray(out.data.data.p801dataDecesso) : null);
|
|
2924
|
-
assistito.okTs = true;
|
|
2925
|
-
assistito.fullDataTs = out.data;
|
|
3080
|
+
// Mapping p801 → Assistito centralizzato in #popolaAssistitoDaP801, riusato
|
|
3081
|
+
// IDENTICO dal fallback legacy TS (la mappatura resta UNA SOLA e invariata).
|
|
3082
|
+
this.#popolaAssistitoDaP801(assistito, out.data.data, cf, out.data);
|
|
2926
3083
|
risultato = {
|
|
2927
3084
|
ok: true,
|
|
2928
3085
|
fullData: out.data,
|
|
@@ -2935,7 +3092,16 @@ export class Nar2 {
|
|
|
2935
3092
|
}
|
|
2936
3093
|
}
|
|
2937
3094
|
if (!ok) {
|
|
2938
|
-
console.log(`[getDatiAssistitoFromCfSuSogeiNew] Sogei fallito dopo ${this._maxRetry} tentativi per CF: ${cf?.substring(0, 6)}
|
|
3095
|
+
console.log(`[getDatiAssistitoFromCfSuSogeiNew] Sogei fallito dopo ${this._maxRetry} tentativi per CF: ${cf?.substring(0, 6)}*** — provo fallback legacy TS`);
|
|
3096
|
+
try {
|
|
3097
|
+
const fb = await this.#fallbackLegacyTsToP801(cf, assistito);
|
|
3098
|
+
if (fb) {
|
|
3099
|
+
console.log(`[getDatiAssistitoFromCfSuSogeiNew] Fallback legacy TS riuscito (fonte: scraping portale)`);
|
|
3100
|
+
return fb;
|
|
3101
|
+
}
|
|
3102
|
+
} catch (e) {
|
|
3103
|
+
console.log(`[getDatiAssistitoFromCfSuSogeiNew] Fallback legacy TS fallito:`, e.message);
|
|
3104
|
+
}
|
|
2939
3105
|
return {
|
|
2940
3106
|
ok: false,
|
|
2941
3107
|
fullData: null,
|
|
@@ -2977,9 +3143,34 @@ export class Nar2 {
|
|
|
2977
3143
|
let p801 = sogeiData;
|
|
2978
3144
|
if (!p801) {
|
|
2979
3145
|
const sogei = await this.getDatiAssistitoFromCfSuSogeiNew(codiceFiscale);
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
3146
|
+
// Sogei irraggiungibile OPPURE dato ricostruito dal fallback legacy: la
|
|
3147
|
+
// classificazione dello stato passa dallo snapshot del PORTALE (Ts.getStatoAssistitoTs
|
|
3148
|
+
// → snapshotDaPortale), che NON dipende da p801codiceTipoAssistito — inaffidabile dal
|
|
3149
|
+
// portale (per i neonati mostra "3" → falso TRASFERITO). Così i neonati restano corretti.
|
|
3150
|
+
if (!sogei?.ok || sogei?.fullData?._fonte === "legacy-ts") {
|
|
3151
|
+
try {
|
|
3152
|
+
const st = await this.#tsScraper().getStatoAssistitoTs({codiceFiscale, codiceAslAsp});
|
|
3153
|
+
if (st?.ok) {
|
|
3154
|
+
const cval = (k) => { const x = st.coppie?.[k]; return x == null ? "" : String(x).trim(); };
|
|
3155
|
+
return {
|
|
3156
|
+
ok: true, cf: codiceFiscale,
|
|
3157
|
+
stato: st.stato, etichetta: st.etichetta, daFarRientrare: st.daFarRientrare,
|
|
3158
|
+
residenzaInAsp: st.residenzaInAsp, aslAssistenzaInAsp: st.aslAssistenzaInAsp,
|
|
3159
|
+
haMedico: st.haMedico, codiceTipoAssistito: st.codiceTipoAssistito,
|
|
3160
|
+
descrizioneTipoAssistito: st.descrizioneTipoAssistito,
|
|
3161
|
+
anagrafica: {cognome: cval("Cognome"), nome: cval("Nome"), sesso: cval("Sesso"), dataNascita: cval("Data Nascita")},
|
|
3162
|
+
fonte: "portale", evidenze: st.evidenze ?? [], raw: st.coppie ?? null
|
|
3163
|
+
};
|
|
3164
|
+
}
|
|
3165
|
+
} catch (e) {
|
|
3166
|
+
console.log(`[getStatoAssistitoTs] Fallback portale fallito:`, e.message);
|
|
3167
|
+
}
|
|
3168
|
+
// Portale non disponibile: se Sogei è davvero irraggiungibile → errore.
|
|
3169
|
+
// Se invece avevamo un dato legacy ricostruito, proseguo best-effort con esso.
|
|
3170
|
+
if (!sogei?.ok) {
|
|
3171
|
+
const esito = classificaStatoTs({fonte: "sogei", trovato: false}, {codiceAslAsp});
|
|
3172
|
+
return {ok: false, cf: codiceFiscale, ...esito, anagrafica: null, raw: null, error: "Sistema TS (Sogei) non raggiungibile"};
|
|
3173
|
+
}
|
|
2983
3174
|
}
|
|
2984
3175
|
if (sogei?.data?.okTs !== true) {
|
|
2985
3176
|
const esito = classificaStatoTs({fonte: "sogei", trovato: false}, {codiceAslAsp});
|
|
@@ -128,7 +128,7 @@ export function snapshotDaPortale({coppie = {}, menuVoci = [], trovato = true, d
|
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
// "190 - Sicilia 205 - ASP MESSINA" → "205" (ultimo codice numerico = ASL)
|
|
131
|
-
function estraiCodiceAsl(s) {
|
|
131
|
+
export function estraiCodiceAsl(s) {
|
|
132
132
|
const m = String(s || "").match(/(\d{3})\s*-\s*ASP|(\d{3})(?!.*\d{3})/i);
|
|
133
133
|
if (!m) return null;
|
|
134
134
|
return m[1] || m[2] || null;
|