{"id":2107,"date":"2026-09-17T12:59:49","date_gmt":"2026-09-17T12:59:49","guid":{"rendered":"https:\/\/www.gestioneindustriale.it\/?page_id=2107"},"modified":"2026-09-17T12:59:50","modified_gmt":"2026-09-17T12:59:50","slug":"pagina-didattica","status":"publish","type":"page","link":"https:\/\/www.gestioneindustriale.it\/it_it\/pagina-didattica\/","title":{"rendered":"Pagina didattica"},"content":{"rendered":"    <div id=\"gf-app\" class=\"gf-app\">\n\n        <nav class=\"gf-tabs\" role=\"tablist\">\n            <button class=\"gf-tab gf-tab-attiva\" data-tab=\"genera\" type=\"button\">Genera Fattura<\/button>\n            <button class=\"gf-tab\" data-tab=\"importa-xml\" type=\"button\">Importa XML<\/button>\n            <button class=\"gf-tab\" data-tab=\"importa-pdf\" type=\"button\">Importa PDF<\/button>\n            <button class=\"gf-tab\" data-tab=\"anagrafiche\" type=\"button\">Anagrafiche<\/button>\n            <button class=\"gf-tab\" data-tab=\"genera-massiva\" type=\"button\">Genera Massiva<\/button>\n            <button class=\"gf-tab\" data-tab=\"sostituisci-piva\" type=\"button\">Sostituisci PIVA (Massivo)<\/button>\n        <\/nav>\n\n        <div class=\"gf-riquadro gf-risorse\">\n            <strong>Verifica ufficiale del file XML<\/strong> - questo strumento non sostituisce il controllo formale del Sistema di Interscambio.\n            Il validatore ufficiale (\"Controlla fattura\") si trova dentro il portale autenticato Fatture e Corrispettivi\n            dell'Agenzia delle Entrate (serve SPID, CNS o CIE) - non e' un programma scaricabile a parte:\n            <a href=\"https:\/\/www.fatturapa.gov.it\/it\/sistemainterscambio\/controlli-ed-errori\/\" target=\"_blank\" rel=\"noopener noreferrer\">pagina informativa \"Controlli ed errori\"<\/a>\n            \/\n            <a href=\"https:\/\/ivaservizi.agenziaentrate.gov.it\/portale\/\" target=\"_blank\" rel=\"noopener noreferrer\">accedi al portale Fatture e Corrispettivi<\/a>.\n        <\/div>\n\n        <!-- ================= SCHEDA: GENERA FATTURA ================= -->\n        <section class=\"gf-pannello gf-pannello-attivo\" data-pannello=\"genera\">\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>Cedente \/ Prestatore (i tuoi dati)<\/legend>\n                <div class=\"gf-griglia\">\n                    <label>Denominazione\n                        <input type=\"text\" id=\"gf-ced-denom\" placeholder=\"Es. Studio Didattico Rossi\">\n                    <\/label>\n                    <label>Partita IVA\n                        <input type=\"text\" id=\"gf-ced-piva\" maxlength=\"11\" placeholder=\"11 cifre\">\n                    <\/label>\n                    <label>Indirizzo\n                        <input type=\"text\" id=\"gf-ced-indirizzo\" placeholder=\"Via Roma 1\">\n                    <\/label>\n                    <label>CAP\n                        <input type=\"text\" id=\"gf-ced-cap\" maxlength=\"5\">\n                    <\/label>\n                    <label>Comune\n                        <input type=\"text\" id=\"gf-ced-comune\">\n                    <\/label>\n                    <label>Provincia\n                        <input type=\"text\" id=\"gf-ced-provincia\" maxlength=\"2\" placeholder=\"Es. TO\">\n                    <\/label>\n                    <label>Regime fiscale\n                        <select id=\"gf-ced-regime\">\n                            <option value=\"RF01\">RF01 - Ordinario<\/option>\n                            <option value=\"RF19\">RF19 - Forfetario<\/option>\n                        <\/select>\n                    <\/label>\n                <\/div>\n                <p class=\"gf-nota\">Questi dati vengono salvati solo nel tuo browser (localStorage), per non doverli reinserire ogni volta.<\/p>\n            <\/fieldset>\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>Cessionario \/ Committente (controparte fittizia)<\/legend>\n                <div class=\"gf-azioni\">\n                    <button type=\"button\" id=\"gf-btn-genera-controparte\" class=\"gf-btn gf-btn-primario\">Genera controparte fittizia<\/button>\n                    <label style=\"display:inline-flex; align-items:center; gap:6px; margin-left:10px;\">oppure usa dall'elenco:\n                        <select id=\"gf-ces-da-elenco\"><option value=\"\">-- nessuna --<\/option><\/select>\n                    <\/label>\n                <\/div>\n                <div class=\"gf-griglia\">\n                    <label>Denominazione\n                        <input type=\"text\" id=\"gf-ces-denom\">\n                    <\/label>\n                    <label>Partita IVA\n                        <input type=\"text\" id=\"gf-ces-piva\" maxlength=\"11\">\n                    <\/label>\n                    <label>Indirizzo\n                        <input type=\"text\" id=\"gf-ces-indirizzo\">\n                    <\/label>\n                    <label>CAP\n                        <input type=\"text\" id=\"gf-ces-cap\" maxlength=\"5\">\n                    <\/label>\n                    <label>Comune\n                        <input type=\"text\" id=\"gf-ces-comune\">\n                    <\/label>\n                    <label>Provincia\n                        <input type=\"text\" id=\"gf-ces-provincia\" maxlength=\"2\">\n                    <\/label>\n                <\/div>\n            <\/fieldset>\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>Dati generali documento<\/legend>\n                <div class=\"gf-griglia\">\n                    <label>Numero fattura\n                        <input type=\"text\" id=\"gf-doc-numero\" value=\"1\">\n                    <\/label>\n                    <label>Data\n                        <input type=\"date\" id=\"gf-doc-data\">\n                    <\/label>\n                    <label>Tipo documento\n                        <select id=\"gf-doc-tipo\"><\/select>\n                    <\/label>\n                    <label>Modalita' di pagamento\n                        <select id=\"gf-doc-pagamento\">\n                            <option value=\"MP05\">MP05 - Bonifico<\/option>\n                            <option value=\"MP01\">MP01 - Contanti<\/option>\n                            <option value=\"MP08\">MP08 - Carta di pagamento<\/option>\n                        <\/select>\n                    <\/label>\n                <\/div>\n            <\/fieldset>\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>Righe fattura<\/legend>\n                <div class=\"gf-tabella-scroll\">\n                <table class=\"gf-tabella-righe\" id=\"gf-tabella-righe\">\n                    <thead>\n                        <tr>\n                            <th>Descrizione<\/th>\n                            <th>Quantita'<\/th>\n                            <th>Prezzo unitario (EUR)<\/th>\n                            <th>Aliquota IVA<\/th>\n                            <th><\/th>\n                        <\/tr>\n                    <\/thead>\n                    <tbody id=\"gf-righe-body\"><\/tbody>\n                <\/table>\n                <\/div>\n                <div class=\"gf-azioni\">\n                    <button type=\"button\" id=\"gf-btn-aggiungi-riga\" class=\"gf-btn\">Aggiungi riga<\/button>\n                <\/div>\n            <\/fieldset>\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>Totali<\/legend>\n                <div id=\"gf-totali\" class=\"gf-totali\">Imponibile: 0,00 EUR - IVA: 0,00 EUR - Totale: 0,00 EUR<\/div>\n            <\/fieldset>\n\n            <div class=\"gf-azioni gf-azioni-finali\">\n                <button type=\"button\" id=\"gf-btn-genera-xml\" class=\"gf-btn gf-btn-primario gf-btn-grande\">Genera XML e scarica<\/button>\n            <\/div>\n            <div id=\"gf-messaggio\" class=\"gf-messaggio\"><\/div>\n\n        <\/section>\n\n        <!-- ================= SCHEDE PLACEHOLDER ================= -->\n        <section class=\"gf-pannello\" data-pannello=\"importa-xml\">\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>1. Carica il file XML reale<\/legend>\n                <input type=\"file\" id=\"gfx-file\" accept=\".xml\">\n                <p class=\"gf-nota\">Il file viene letto solo nel tuo browser (FileReader). Non viene inviato a nessun server.<\/p>\n                <div id=\"gfx-messaggio\" class=\"gf-messaggio\"><\/div>\n            <\/fieldset>\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>2. Quale soggetto vuoi anonimizzare?<\/legend>\n                <label><input type=\"radio\" name=\"gfx-ruolo\" value=\"cessionario\" checked> Cessionario\/Committente (il cliente - caso tipico: fattura di vendita)<\/label><br>\n                <label><input type=\"radio\" name=\"gfx-ruolo\" value=\"cedente\"> Cedente\/Prestatore (il fornitore - caso tipico: fattura di acquisto)<\/label>\n                <div class=\"gf-azioni\">\n                    <button type=\"button\" id=\"gfx-btn-genera-controparte\" class=\"gf-btn gf-btn-primario\">Genera anagrafica fittizia per il soggetto selezionato<\/button>\n                <\/div>\n            <\/fieldset>\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>Cedente \/ Prestatore<\/legend>\n                <div class=\"gf-griglia\">\n                    <label>Denominazione <input type=\"text\" id=\"gfx-ced-denom\"><\/label>\n                    <label>Partita IVA <input type=\"text\" id=\"gfx-ced-piva\" maxlength=\"11\"><\/label>\n                    <label>Indirizzo <input type=\"text\" id=\"gfx-ced-indirizzo\"><\/label>\n                    <label>CAP <input type=\"text\" id=\"gfx-ced-cap\" maxlength=\"5\"><\/label>\n                    <label>Comune <input type=\"text\" id=\"gfx-ced-comune\"><\/label>\n                    <label>Provincia <input type=\"text\" id=\"gfx-ced-provincia\" maxlength=\"2\"><\/label>\n                    <label>Regime fiscale\n                        <select id=\"gfx-ced-regime\">\n                            <option value=\"RF01\">RF01 - Ordinario<\/option>\n                            <option value=\"RF19\">RF19 - Forfetario<\/option>\n                        <\/select>\n                    <\/label>\n                <\/div>\n            <\/fieldset>\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>Cessionario \/ Committente<\/legend>\n                <div class=\"gf-griglia\">\n                    <label>Denominazione <input type=\"text\" id=\"gfx-ces-denom\"><\/label>\n                    <label>Partita IVA <input type=\"text\" id=\"gfx-ces-piva\" maxlength=\"11\"><\/label>\n                    <label>Indirizzo <input type=\"text\" id=\"gfx-ces-indirizzo\"><\/label>\n                    <label>CAP <input type=\"text\" id=\"gfx-ces-cap\" maxlength=\"5\"><\/label>\n                    <label>Comune <input type=\"text\" id=\"gfx-ces-comune\"><\/label>\n                    <label>Provincia <input type=\"text\" id=\"gfx-ces-provincia\" maxlength=\"2\"><\/label>\n                <\/div>\n            <\/fieldset>\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>Dati generali documento<\/legend>\n                <div class=\"gf-griglia\">\n                    <label>Numero fattura <input type=\"text\" id=\"gfx-doc-numero\"><\/label>\n                    <label>Data <input type=\"date\" id=\"gfx-doc-data\"><\/label>\n                    <label>Tipo documento <select id=\"gfx-doc-tipo\"><\/select><\/label>\n                    <label>Modalita' di pagamento\n                        <select id=\"gfx-doc-pagamento\">\n                            <option value=\"MP05\">MP05 - Bonifico<\/option>\n                            <option value=\"MP01\">MP01 - Contanti<\/option>\n                            <option value=\"MP08\">MP08 - Carta di pagamento<\/option>\n                        <\/select>\n                    <\/label>\n                <\/div>\n            <\/fieldset>\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>Righe fattura (lette dal file, modificabili)<\/legend>\n                <div class=\"gf-tabella-scroll\">\n                <table class=\"gf-tabella-righe\" id=\"gfx-tabella-righe\">\n                    <thead>\n                        <tr><th>Descrizione<\/th><th>Quantita'<\/th><th>Prezzo unitario (EUR)<\/th><th>Aliquota IVA<\/th><th><\/th><\/tr>\n                    <\/thead>\n                    <tbody id=\"gfx-righe-body\"><\/tbody>\n                <\/table>\n                <\/div>\n                <div class=\"gf-azioni\">\n                    <button type=\"button\" id=\"gfx-btn-aggiungi-riga\" class=\"gf-btn\">Aggiungi riga<\/button>\n                <\/div>\n            <\/fieldset>\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>Totali<\/legend>\n                <div id=\"gfx-totali\" class=\"gf-totali\">Imponibile: 0,00 EUR - IVA: 0,00 EUR - Totale: 0,00 EUR<\/div>\n            <\/fieldset>\n\n            <div class=\"gf-azioni gf-azioni-finali\">\n                <button type=\"button\" id=\"gfx-btn-genera-xml\" class=\"gf-btn gf-btn-primario gf-btn-grande\">Genera XML anonimizzato e scarica<\/button>\n            <\/div>\n\n        <\/section>\n        <section class=\"gf-pannello\" data-pannello=\"importa-pdf\">\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>1. Carica il PDF reale (schema Aruba)<\/legend>\n                <input type=\"file\" id=\"gfp-file\" accept=\".pdf\">\n                <p class=\"gf-nota\">Il PDF viene letto solo nel tuo browser tramite la libreria pdf.js (caricata da CDN pubblico, ma il contenuto del file non lascia mai il browser). Nessun upload su server.<\/p>\n                <div id=\"gfp-messaggio\" class=\"gf-messaggio\"><\/div>\n                <details class=\"gf-diagnostica\">\n                    <summary>Diagnostica testo estratto (apri se qualcosa non torna)<\/summary>\n                    <p class=\"gf-nota\">Se l'anagrafica di Fornitore\/Cliente risulta vuota o sbagliata, apri questo pannello, copia il contenuto e incollalo in chat: serve per correggere l'estrazione senza indovinare la struttura del PDF.<\/p>\n                    <textarea id=\"gfp-diagnostica\" rows=\"10\" readonly style=\"width:100%; font-family:monospace; font-size:11px;\"><\/textarea>\n                <\/details>\n            <\/fieldset>\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>2. Quale soggetto vuoi anonimizzare?<\/legend>\n                <label><input type=\"radio\" name=\"gfp-ruolo\" value=\"cessionario\" checked> Cessionario\/Committente (cliente)<\/label><br>\n                <label><input type=\"radio\" name=\"gfp-ruolo\" value=\"cedente\"> Cedente\/Prestatore (fornitore)<\/label>\n                <div class=\"gf-azioni\">\n                    <button type=\"button\" id=\"gfp-btn-genera-controparte\" class=\"gf-btn gf-btn-primario\">Genera anagrafica fittizia per il soggetto selezionato<\/button>\n                <\/div>\n            <\/fieldset>\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>Cedente \/ Prestatore (Fornitore)<\/legend>\n                <div class=\"gf-griglia\">\n                    <label>Denominazione <input type=\"text\" id=\"gfp-ced-denom\"><\/label>\n                    <label>Partita IVA <input type=\"text\" id=\"gfp-ced-piva\" maxlength=\"11\"><\/label>\n                    <label>Indirizzo <input type=\"text\" id=\"gfp-ced-indirizzo\"><\/label>\n                    <label>CAP <input type=\"text\" id=\"gfp-ced-cap\" maxlength=\"5\"><\/label>\n                    <label>Comune <input type=\"text\" id=\"gfp-ced-comune\"><\/label>\n                    <label>Provincia <input type=\"text\" id=\"gfp-ced-provincia\" maxlength=\"2\"><\/label>\n                    <label>Regime fiscale\n                        <select id=\"gfp-ced-regime\">\n                            <option value=\"RF01\">RF01 - Ordinario<\/option>\n                            <option value=\"RF19\">RF19 - Forfetario<\/option>\n                        <\/select>\n                    <\/label>\n                <\/div>\n            <\/fieldset>\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>Cessionario \/ Committente (Cliente)<\/legend>\n                <div class=\"gf-griglia\">\n                    <label>Denominazione <input type=\"text\" id=\"gfp-ces-denom\"><\/label>\n                    <label>Partita IVA <input type=\"text\" id=\"gfp-ces-piva\" maxlength=\"11\"><\/label>\n                    <label>Indirizzo <input type=\"text\" id=\"gfp-ces-indirizzo\"><\/label>\n                    <label>CAP <input type=\"text\" id=\"gfp-ces-cap\" maxlength=\"5\"><\/label>\n                    <label>Comune <input type=\"text\" id=\"gfp-ces-comune\"><\/label>\n                    <label>Provincia <input type=\"text\" id=\"gfp-ces-provincia\" maxlength=\"2\"><\/label>\n                <\/div>\n            <\/fieldset>\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>Dati generali documento<\/legend>\n                <div class=\"gf-griglia\">\n                    <label>Numero fattura <input type=\"text\" id=\"gfp-doc-numero\"><\/label>\n                    <label>Data <input type=\"date\" id=\"gfp-doc-data\"><\/label>\n                    <label>Tipo documento <select id=\"gfp-doc-tipo\"><\/select><\/label>\n                    <label>Modalita' di pagamento\n                        <select id=\"gfp-doc-pagamento\">\n                            <option value=\"MP05\">MP05 - Bonifico<\/option>\n                            <option value=\"MP01\">MP01 - Contanti<\/option>\n                            <option value=\"MP08\">MP08 - Carta di pagamento<\/option>\n                        <\/select>\n                    <\/label>\n                <\/div>\n            <\/fieldset>\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>Importi (il PDF non elenca le righe di dettaglio: viene creata una riga sintetica \"Importo fattura\")<\/legend>\n                <div class=\"gf-griglia\">\n                    <label>Imponibile (EUR) <input type=\"number\" step=\"0.01\" id=\"gfp-imponibile\"><\/label>\n                    <label>Totale IVA (EUR) <input type=\"number\" step=\"0.01\" id=\"gfp-iva\"><\/label>\n                    <label>Aliquota IVA calcolata (%) <input type=\"number\" step=\"0.01\" id=\"gfp-aliquota\"><\/label>\n                    <label>Totale documento (EUR) <input type=\"number\" step=\"0.01\" id=\"gfp-totale\"><\/label>\n                <\/div>\n                <p class=\"gf-nota\">Se l'aliquota calcolata non torna (es. IVA mista su piu' aliquote), correggila manualmente: qui si genera una sola riga XML con l'aliquota indicata.<\/p>\n            <\/fieldset>\n\n            <div class=\"gf-azioni gf-azioni-finali\">\n                <button type=\"button\" id=\"gfp-btn-genera-xml\" class=\"gf-btn gf-btn-primario gf-btn-grande\">Genera XML anonimizzato e scarica<\/button>\n            <\/div>\n\n        <\/section>\n        <section class=\"gf-pannello\" data-pannello=\"anagrafiche\">\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>Genera nuova anagrafica fittizia (arricchita dal web)<\/legend>\n                <p class=\"gf-nota\">Recupera un comune reale casuale da un elenco pubblico dei comuni italiani e un cognome casuale da un servizio pubblico, per avere piu' varieta' rispetto ai pochi nomi fissi. Se il servizio non risponde (rete assente\/bloccata), si usa in automatico l'elenco locale ridotto come riserva.<\/p>\n                <div class=\"gf-griglia\">\n                    <label>Tipo\n                        <select id=\"ga-nuovo-tipo\">\n                            <option value=\"Cliente\">Cliente<\/option>\n                            <option value=\"Fornitore\">Fornitore<\/option>\n                        <\/select>\n                    <\/label>\n                <\/div>\n                <div class=\"gf-azioni\">\n                    <button type=\"button\" id=\"ga-btn-genera-web\" class=\"gf-btn gf-btn-primario\">Genera con arricchimento web<\/button>\n                    <span id=\"ga-stato-generazione\" class=\"gf-nota\"><\/span>\n                <\/div>\n            <\/fieldset>\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>Aggiungi o modifica prima di salvare<\/legend>\n                <div class=\"gf-griglia\">\n                    <label>Tipo\n                        <select id=\"ga-tipo\">\n                            <option value=\"Cliente\">Cliente<\/option>\n                            <option value=\"Fornitore\">Fornitore<\/option>\n                        <\/select>\n                    <\/label>\n                    <label>Denominazione <input type=\"text\" id=\"ga-denom\"><\/label>\n                    <label>Partita IVA <input type=\"text\" id=\"ga-piva\" maxlength=\"11\"><\/label>\n                    <label>Indirizzo <input type=\"text\" id=\"ga-indirizzo\"><\/label>\n                    <label>CAP <input type=\"text\" id=\"ga-cap\" maxlength=\"5\"><\/label>\n                    <label>Comune <input type=\"text\" id=\"ga-comune\"><\/label>\n                    <label>Provincia <input type=\"text\" id=\"ga-provincia\" maxlength=\"2\"><\/label>\n                <\/div>\n                <div class=\"gf-azioni\">\n                    <button type=\"button\" id=\"ga-btn-aggiungi\" class=\"gf-btn gf-btn-primario\">Aggiungi all'elenco<\/button>\n                <\/div>\n                <div id=\"ga-messaggio\" class=\"gf-messaggio\"><\/div>\n            <\/fieldset>\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>Elenco anagrafiche salvate<\/legend>\n                <div class=\"gf-tabella-scroll\">\n                <table class=\"gf-tabella-righe\" id=\"ga-tabella\">\n                    <thead>\n                        <tr><th>Tipo<\/th><th>Denominazione<\/th><th>P.IVA<\/th><th>Comune<\/th><th>Prov.<\/th><th><\/th><\/tr>\n                    <\/thead>\n                    <tbody id=\"ga-tabella-body\"><\/tbody>\n                <\/table>\n                <\/div>\n                <p class=\"gf-nota\" id=\"ga-contatore\">0 anagrafiche salvate.<\/p>\n                <div class=\"gf-azioni\">\n                    <button type=\"button\" id=\"ga-btn-esporta\" class=\"gf-btn\">Esporta backup (JSON)<\/button>\n                    <button type=\"button\" id=\"ga-btn-importa-apri\" class=\"gf-btn\">Importa backup (JSON)<\/button>\n                    <input type=\"file\" id=\"ga-file-importa\" accept=\".json\" style=\"display:none;\">\n                <\/div>\n                <p class=\"gf-nota\">I dati restano solo in questo browser: usa Esporta\/Importa per portarli su un altro dispositivo o come copia di sicurezza.<\/p>\n            <\/fieldset>\n\n        <\/section>\n\n        <section class=\"gf-pannello\" data-pannello=\"genera-massiva\">\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>Dati aziendali propri<\/legend>\n                <div class=\"gf-griglia\">\n                    <label>Denominazione <input type=\"text\" id=\"gmx-propria-denom\"><\/label>\n                    <label>Partita IVA <input type=\"text\" id=\"gmx-propria-piva\" maxlength=\"11\"><\/label>\n                <\/div>\n                <p class=\"gf-nota\">Precompilati dai dati del Cedente inseriti nella scheda \"Genera Fattura\", modificabili qui senza toccarli laggiu'.<\/p>\n                <label><input type=\"radio\" name=\"gmx-tipo-anagrafica\" value=\"C\" checked> Vendita (i tuoi dati sono il Cedente, le controparti sono Clienti)<\/label><br>\n                <label><input type=\"radio\" name=\"gmx-tipo-anagrafica\" value=\"F\"> Acquisto (i tuoi dati sono il Cessionario, le controparti sono Fornitori)<\/label>\n            <\/fieldset>\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>Periodo e imponibile<\/legend>\n                <div class=\"gf-griglia\">\n                    <label>Mese\n                        <select id=\"gmx-mese\">\n                            <option value=\"1\">Gennaio<\/option><option value=\"2\">Febbraio<\/option><option value=\"3\">Marzo<\/option>\n                            <option value=\"4\">Aprile<\/option><option value=\"5\">Maggio<\/option><option value=\"6\">Giugno<\/option>\n                            <option value=\"7\">Luglio<\/option><option value=\"8\">Agosto<\/option><option value=\"9\">Settembre<\/option>\n                            <option value=\"10\">Ottobre<\/option><option value=\"11\">Novembre<\/option><option value=\"12\">Dicembre<\/option>\n                        <\/select>\n                    <\/label>\n                    <label>Anno <input type=\"number\" id=\"gmx-anno\" min=\"2000\" max=\"2100\"><\/label>\n                    <label>Imponibile netto (EUR) <input type=\"number\" step=\"0.01\" id=\"gmx-imponibile\" placeholder=\"Fatture meno note di credito\"><\/label>\n                    <label>Aliquota IVA (%) <input type=\"number\" step=\"0.01\" id=\"gmx-aliquota\" value=\"22\"><\/label>\n                <\/div>\n                <label><input type=\"checkbox\" id=\"gmx-escludi-weekend\" checked> Escludi sabato e domenica dalle date di emissione<\/label><br>\n                <label><input type=\"checkbox\" id=\"gmx-escludi-festivi\" checked> Escludi le festivita' italiane (fisse + Pasqua\/Pasquetta)<\/label>\n            <\/fieldset>\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>Mix tipologie documento (es. 15 TD01, 3 TD04, 2 TD21)<\/legend>\n                <div id=\"gmx-mix-lista\"><\/div>\n                <p class=\"gf-nota\" id=\"gmx-mix-vuoto\">Nessuna riga aggiunta.<\/p>\n                <button type=\"button\" id=\"gmx-btn-aggiungi-mix\" class=\"gf-btn\">+ Aggiungi<\/button>\n            <\/fieldset>\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>Distribuzione scadenze pagamento (es. 2 a 30gg, 5 a fine mese, 10 a 90gg)<\/legend>\n                <div id=\"gmx-scad-lista\"><\/div>\n                <datalist id=\"gmx-giorni-comuni\">\n                    <option value=\"0\"><option value=\"30\"><option value=\"45\"><option value=\"60\">\n                    <option value=\"90\"><option value=\"120\"><option value=\"150\"><option value=\"180\">\n                <\/datalist>\n                <p class=\"gf-nota\" id=\"gmx-scad-vuoto\">Nessuna riga aggiunta.<\/p>\n                <button type=\"button\" id=\"gmx-btn-aggiungi-scad\" class=\"gf-btn\">+ Aggiungi<\/button>\n            <\/fieldset>\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>Controparti da usare (a rotazione)<\/legend>\n                <div id=\"gmx-controparti-lista\" class=\"gf-nota\">Nessuna anagrafica salvata: vai nella scheda Anagrafiche per aggiungerne.<\/div>\n                <div class=\"gf-azioni\">\n                    <button type=\"button\" id=\"gmx-btn-aggiorna-controparti\" class=\"gf-btn\">Aggiorna elenco dalla scheda Anagrafiche<\/button>\n                <\/div>\n            <\/fieldset>\n\n            <div class=\"gf-azioni gf-azioni-finali\">\n                <button type=\"button\" id=\"gmx-btn-genera-anteprima\" class=\"gf-btn gf-btn-primario gf-btn-grande\">Genera set fatture (anteprima)<\/button>\n            <\/div>\n            <div id=\"gmx-messaggio\" class=\"gf-messaggio\"><\/div>\n\n            <fieldset class=\"gf-riquadro\" id=\"gmx-riquadro-anteprima\" style=\"display:none;\">\n                <legend>Anteprima set generato<\/legend>\n                <div id=\"gmx-verifica-totale\" class=\"gf-totali\"><\/div>\n                <div class=\"gf-tabella-scroll\">\n                <table class=\"gf-tabella-righe\">\n                    <thead><tr><th>#<\/th><th>Tipo<\/th><th>Emissione<\/th><th>Scadenza<\/th><th>Importo<\/th><th>Controparte<\/th><\/tr><\/thead>\n                    <tbody id=\"gmx-anteprima-body\"><\/tbody>\n                <\/table>\n                <\/div>\n                <div class=\"gf-azioni gf-azioni-finali\">\n                    <button type=\"button\" id=\"gmx-btn-scarica-zip\" class=\"gf-btn gf-btn-primario gf-btn-grande\">Scarica tutte in ZIP<\/button>\n                <\/div>\n            <\/fieldset>\n\n        <\/section>\n\n        <section class=\"gf-pannello\" data-pannello=\"sostituisci-piva\">\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>1. Partita IVA da sostituire<\/legend>\n                <div class=\"gf-griglia\">\n                    <label>Vecchia Partita IVA (di provenienza, presente nei file)\n                        <input type=\"text\" id=\"gsp-piva-da\" placeholder=\"Es. 06786300019\">\n                    <\/label>\n                    <label>Nuova Partita IVA (di destinazione)\n                        <input type=\"text\" id=\"gsp-piva-a\" placeholder=\"Es. 13402320017\">\n                    <\/label>\n                <\/div>\n                <label><input type=\"checkbox\" id=\"gsp-ignora-maiuscole\" checked> Ignora maiuscole\/minuscole nella ricerca<\/label>\n                <p class=\"gf-nota\">Sostituzione testuale diretta sul file XML: tutto il resto (importi, date, righe, altre anagrafiche) resta identico byte per byte. Ogni occorrenza esatta della \"Vecchia Partita IVA\" viene sostituita con la \"Nuova Partita IVA\", ovunque compaia nel file (intestazione, Cedente, Cessionario, ecc.).<\/p>\n            <\/fieldset>\n\n            <fieldset class=\"gf-riquadro\">\n                <legend>2. Carica i file XML (selezione multipla)<\/legend>\n                <input type=\"file\" id=\"gsp-file\" accept=\".xml\" multiple>\n                <p class=\"gf-nota\">I file vengono letti ed elaborati solo in questo browser. Nessun upload su server.<\/p>\n                <label><input type=\"checkbox\" id=\"gsp-escludi-metadati\" checked> Escludi dall'elaborazione i file il cui nome contiene:\n                    <input type=\"text\" id=\"gsp-testo-esclusione\" value=\"METADATO\" style=\"width:160px; display:inline-block; margin-left:6px;\">\n                <\/label>\n                <p class=\"gf-nota\">Ricerca senza distinzione maiuscole\/minuscole nel nome del file (non nel contenuto). Se i tuoi file di metadati SDI si chiamano in modo diverso (es. \"MT\", \"MetaDato\", \"_MT_\"), cambia il testo qui sopra prima di elaborare.<\/p>\n                <div class=\"gf-azioni\">\n                    <button type=\"button\" id=\"gsp-btn-elabora\" class=\"gf-btn gf-btn-primario gf-btn-grande\">Elabora file caricati<\/button>\n                <\/div>\n                <div id=\"gsp-messaggio\" class=\"gf-messaggio\"><\/div>\n            <\/fieldset>\n\n            <fieldset class=\"gf-riquadro\" id=\"gsp-riquadro-risultati\" style=\"display:none;\">\n                <legend>Risultato elaborazione<\/legend>\n                <div id=\"gsp-riepilogo\" class=\"gf-totali\"><\/div>\n                <div class=\"gf-tabella-scroll\">\n                <table class=\"gf-tabella-righe\">\n                    <thead><tr><th>File<\/th><th>Occorrenze sostituite<\/th><th>Trovato in<\/th><th>Esito<\/th><\/tr><\/thead>\n                    <tbody id=\"gsp-risultati-body\"><\/tbody>\n                <\/table>\n                <\/div>\n                <div class=\"gf-azioni gf-azioni-finali\">\n                    <button type=\"button\" id=\"gsp-btn-scarica-zip\" class=\"gf-btn gf-btn-primario gf-btn-grande\">Scarica tutti in ZIP<\/button>\n                <\/div>\n            <\/fieldset>\n\n        <\/section>\n    <\/div>\n\n    <style>\n        \/* Palette coerente con il tema grigio\/antracite del progetto originale *\/\n        .gf-app { --gf-sfondo:#f3f4f6; --gf-superficie:#ffffff; --gf-accento:#374151; --gf-bordo:#d1d5db; --gf-testo:#111827;\n            background:var(--gf-sfondo); color:var(--gf-testo); padding:20px; border-radius:8px; font-family:sans-serif; max-width:960px; }\n        .gf-tabs { display:flex; flex-wrap:wrap; gap:6px; margin-bottom:16px; }\n        .gf-tab { padding:8px 14px; border:1px solid var(--gf-bordo); background:var(--gf-superficie); color:var(--gf-testo);\n            border-radius:6px; cursor:pointer; font-size:14px; }\n        .gf-tab-attiva { background:var(--gf-accento); color:#ffffff; border-color:var(--gf-accento); }\n        .gf-pannello { display:none; }\n        .gf-pannello-attivo { display:block; }\n        .gf-riquadro { border:1px solid var(--gf-bordo); border-radius:8px; padding:14px; margin-bottom:16px; background:var(--gf-superficie); }\n        .gf-riquadro legend { font-weight:bold; padding:0 6px; }\n        .gf-griglia { display:grid; grid-template-columns:repeat(auto-fit, minmax(200px, 1fr)); gap:10px; }\n        .gf-griglia label { display:flex; flex-direction:column; font-size:13px; gap:4px; }\n        .gf-griglia input, .gf-griglia select { padding:6px 8px; border:1px solid var(--gf-bordo); border-radius:4px; font-size:14px; }\n        .gf-azioni { margin:10px 0; }\n        .gf-btn { padding:8px 14px; border:1px solid var(--gf-accento); background:var(--gf-superficie); color:var(--gf-accento);\n            border-radius:6px; cursor:pointer; font-size:14px; }\n        .gf-btn-primario { background:var(--gf-accento); color:#ffffff; }\n        .gf-btn-grande { padding:12px 22px; font-size:16px; }\n        .gf-tabella-righe { width:100%; border-collapse:collapse; font-size:13px; }\n        .gf-tabella-righe th, .gf-tabella-righe td { border:1px solid var(--gf-bordo); padding:6px; text-align:left; }\n        .gf-tabella-righe input, .gf-tabella-righe select { width:100%; box-sizing:border-box; padding:4px; border:1px solid var(--gf-bordo); border-radius:4px; }\n        .gf-totali { font-size:15px; font-weight:bold; }\n        .gf-nota { font-size:12px; color:#4b5563; }\n        .gf-diagnostica { margin-top:8px; font-size:12px; }\n        .gf-diagnostica summary { cursor:pointer; color:var(--gf-accento); }\n        .gf-risorse { font-size:13px; }\n        .gf-risorse a { color:var(--gf-accento); }\n        .gf-messaggio { margin-top:10px; font-size:13px; }\n        .gf-messaggio.errore { color:#b91c1c; }\n        .gf-messaggio.successo { color:#15803d; }\n\n        \/* ============ Ottimizzazioni per smartphone\/tablet ============ *\/\n        \/* Tutto qui dentro si attiva SOLO sotto 640px: il desktop resta identico a prima *\/\n        .gf-tabella-scroll { overflow-x:visible; }\n        @media (max-width: 640px) {\n            .gf-app { padding:10px; }\n            .gf-tabs { gap:4px; }\n            .gf-tab { flex:1 1 40%; text-align:center; padding:10px 4px; font-size:13px; }\n            .gf-riquadro { padding:10px; }\n            .gf-griglia { grid-template-columns:1fr; gap:8px; }\n            .gf-griglia input, .gf-griglia select {\n                font-size:16px; \/* evita lo zoom automatico di iOS quando si tocca un campo *\/\n                padding:8px;\n            }\n            .gf-azioni { display:flex; flex-direction:column; gap:6px; }\n            .gf-azioni .gf-btn { width:100%; box-sizing:border-box; }\n            .gf-azioni label { display:block !important; margin-left:0 !important; margin-top:6px; }\n            .gf-btn-grande { padding:14px; font-size:16px; }\n            .gf-tabella-scroll { overflow-x:auto; -webkit-overflow-scrolling:touch; margin:0 -4px; }\n            .gf-tabella-righe { min-width:560px; }\n            .gf-totali { font-size:14px; }\n        }\n    <\/style>\n\n    <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/pdf.js\/3.11.174\/pdf.min.js\"><\/script>\n    <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jszip\/3.10.1\/jszip.min.js\"><\/script>\n    <script>\n    (function () {\n        'use strict';\n        if (window.pdfjsLib) {\n            pdfjsLib.GlobalWorkerOptions.workerSrc = 'https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/pdf.js\/3.11.174\/pdf.worker.min.js';\n        }\n\n\n        \/\/ ---------- Gestione schede ----------\n        var scheda = document.querySelectorAll('.gf-tab');\n        for (var i = 0; i < scheda.length; i++) {\n            scheda[i].addEventListener('click', function (ev) {\n                var nomeScheda = ev.target.getAttribute('data-tab');\n                document.querySelectorAll('.gf-tab').forEach(function (b) { b.classList.remove('gf-tab-attiva'); });\n                document.querySelectorAll('.gf-pannello').forEach(function (p) { p.classList.remove('gf-pannello-attivo'); });\n                ev.target.classList.add('gf-tab-attiva');\n                document.querySelector('.gf-pannello[data-pannello=\"' + nomeScheda + '\"]').classList.add('gf-pannello-attivo');\n            });\n        }\n\n        \/\/ ---------- Persistenza dati Cedente (localStorage) ----------\n        var CHIAVE_CEDENTE = 'gf_cedente_v1';\n\n        function salvaCedente() {\n            var dati = {\n                denom: document.getElementById('gf-ced-denom').value,\n                piva: document.getElementById('gf-ced-piva').value,\n                indirizzo: document.getElementById('gf-ced-indirizzo').value,\n                cap: document.getElementById('gf-ced-cap').value,\n                comune: document.getElementById('gf-ced-comune').value,\n                provincia: document.getElementById('gf-ced-provincia').value,\n                regime: document.getElementById('gf-ced-regime').value\n            };\n            try { localStorage.setItem(CHIAVE_CEDENTE, JSON.stringify(dati)); } catch (e) { \/* localStorage non disponibile, si ignora *\/ }\n        }\n\n        function caricaCedente() {\n            try {\n                var grezzo = localStorage.getItem(CHIAVE_CEDENTE);\n                if (!grezzo) return;\n                var dati = JSON.parse(grezzo);\n                document.getElementById('gf-ced-denom').value = dati.denom || '';\n                document.getElementById('gf-ced-piva').value = dati.piva || '';\n                document.getElementById('gf-ced-indirizzo').value = dati.indirizzo || '';\n                document.getElementById('gf-ced-cap').value = dati.cap || '';\n                document.getElementById('gf-ced-comune').value = dati.comune || '';\n                document.getElementById('gf-ced-provincia').value = dati.provincia || '';\n                document.getElementById('gf-ced-regime').value = dati.regime || 'RF01';\n            } catch (e) { \/* dati salvati non validi, si riparte da vuoto *\/ }\n        }\n\n        document.querySelectorAll('#gf-app fieldset:first-of-type input, #gf-app fieldset:first-of-type select')\n            .forEach(function (campo) { campo.addEventListener('change', salvaCedente); });\n\n        \/\/ ---------- Generatore Partita IVA fittizia (con cifra di controllo valida) ----------\n        function calcolaCifraControllo(cifre) {\n            var somma = 0;\n            for (var i = 0; i < 10; i++) {\n                var c = cifre[i];\n                if (i % 2 === 0) {\n                    somma += c;\n                } else {\n                    var doppio = c * 2;\n                    if (doppio > 9) doppio -= 9;\n                    somma += doppio;\n                }\n            }\n            return (10 - (somma % 10)) % 10;\n        }\n\n        function generaPartitaIvaFittizia() {\n            var cifre = [];\n            for (var i = 0; i < 7; i++) cifre.push(Math.floor(Math.random() * 10));\n            \/\/ codice ufficio provinciale: valore generico fittizio\n            cifre.push(0, 0, 1);\n            var cifraControllo = calcolaCifraControllo(cifre);\n            cifre.push(cifraControllo);\n            return cifre.join('');\n        }\n\n        \/\/ ---------- Dati fittizi di comodo ----------\n        var DENOMINAZIONI_FITTIZIE = [\n            'Alfa Servizi Srl', 'Beta Commerciale Snc', 'Gamma Forniture Srl',\n            'Delta Logistica Spa', 'Epsilon Consulenze Srl', 'Zeta Materiali Srl',\n            'Eta Distribuzione Srl', 'Theta Impianti Snc'\n        ];\n        var CITTA_FITTIZIE = [\n            { comune: 'Casalpusterlengo', provincia: 'LO', cap: '26841' },\n            { comune: 'Fossano', provincia: 'CN', cap: '12045' },\n            { comune: 'Chiavari', provincia: 'GE', cap: '16043' },\n            { comune: 'Fidenza', provincia: 'PR', cap: '43036' },\n            { comune: 'Chieri', provincia: 'TO', cap: '10023' }\n        ];\n\n        function scegliCasuale(elenco) {\n            return elenco[Math.floor(Math.random() * elenco.length)];\n        }\n\n        \/\/ ---------- Via\/piazza\/largo reale del comune, da elenco pubblico (OpenStreetMap via Overpass) ----------\n        var VIE_GENERICHE_RISERVA = [\n            'Via Roma', 'Via Garibaldi', 'Via Dante Alighieri', 'Corso Vittorio Emanuele',\n            'Via Cavour', 'Piazza della Repubblica', 'Via Mazzini', 'Largo Europa',\n            'Piazza Duomo', 'Via Giuseppe Verdi'\n        ];\n        var cacheViePerComune = {}; \/\/ nomeComune -> elenco nomi via gia' recuperati in questa sessione\n\n        async function recuperaViaCasualeDalWeb(nomeComune) {\n            if (!nomeComune) return null;\n            if (cacheViePerComune[nomeComune] && cacheViePerComune[nomeComune].length) {\n                return scegliCasuale(cacheViePerComune[nomeComune]);\n            }\n            try {\n                var query = '[out:json][timeout:15];area[\"name\"=\"' + nomeComune.replace(\/\"\/g, '') +\n                    '\"][\"boundary\"=\"administrative\"]->.a;(way(area.a)[highway][name];);out tags 60;';\n                var url = 'https:\/\/overpass-api.de\/api\/interpreter?data=' + encodeURIComponent(query);\n                var controller = new AbortController();\n                var timeoutId = setTimeout(function () { controller.abort(); }, 9000);\n                var risposta = await fetch(url, { signal: controller.signal });\n                clearTimeout(timeoutId);\n                if (!risposta.ok) throw new Error('Overpass non disponibile');\n                var dati = await risposta.json();\n                var nomi = [];\n                (dati.elements || []).forEach(function (el) {\n                    if (el.tags && el.tags.name && nomi.indexOf(el.tags.name) === -1) nomi.push(el.tags.name);\n                });\n                if (nomi.length === 0) return null;\n                cacheViePerComune[nomeComune] = nomi;\n                return scegliCasuale(nomi);\n            } catch (e) {\n                return null; \/\/ il chiamante ricadra' sull'elenco generico locale\n            }\n        }\n\n        \/\/ Restituisce sempre un indirizzo completo (via + civico casuale): via reale se disponibile, altrimenti di riserva\n        async function generaIndirizzoCasuale(nomeComune) {\n            var via = await recuperaViaCasualeDalWeb(nomeComune);\n            if (!via) via = scegliCasuale(VIE_GENERICHE_RISERVA);\n            return via + ' ' + (1 + Math.floor(Math.random() * 120));\n        }\n        document.getElementById('gf-btn-genera-controparte').addEventListener('click', async function () {\n            var citta = scegliCasuale(CITTA_FITTIZIE);\n            document.getElementById('gf-ces-denom').value = scegliCasuale(DENOMINAZIONI_FITTIZIE);\n            document.getElementById('gf-ces-piva').value = generaPartitaIvaFittizia();\n            document.getElementById('gf-ces-cap').value = citta.cap;\n            document.getElementById('gf-ces-comune').value = citta.comune;\n            document.getElementById('gf-ces-provincia').value = citta.provincia;\n            document.getElementById('gf-ces-indirizzo').value = 'Recupero indirizzo...';\n            document.getElementById('gf-ces-indirizzo').value = await generaIndirizzoCasuale(citta.comune);\n        });\n\n        \/\/ ---------- Righe fattura ----------\n        var corpoRighe = document.getElementById('gf-righe-body');\n        var contatoreRighe = 0;\n\n        function aggiungiRiga(descrizione, quantita, prezzo, aliquota) {\n            contatoreRighe++;\n            var tr = document.createElement('tr');\n            tr.innerHTML =\n                '<td><input type=\"text\" class=\"gf-riga-descrizione\" value=\"' + (descrizione || '') + '\"><\/td>' +\n                '<td><input type=\"number\" class=\"gf-riga-quantita\" min=\"0\" step=\"0.01\" value=\"' + (quantita || 1) + '\"><\/td>' +\n                '<td><input type=\"number\" class=\"gf-riga-prezzo\" min=\"0\" step=\"0.01\" value=\"' + (prezzo || 0) + '\"><\/td>' +\n                '<td><select class=\"gf-riga-aliquota\">' +\n                    '<option value=\"22.00\"' + (aliquota === '22.00' ? ' selected' : '') + '>22%<\/option>' +\n                    '<option value=\"10.00\"' + (aliquota === '10.00' ? ' selected' : '') + '>10%<\/option>' +\n                    '<option value=\"4.00\"' + (aliquota === '4.00' ? ' selected' : '') + '>4%<\/option>' +\n                    '<option value=\"0.00\"' + (aliquota === '0.00' ? ' selected' : '') + '>0%<\/option>' +\n                '<\/select><\/td>' +\n                '<td><button type=\"button\" class=\"gf-btn gf-btn-rimuovi-riga\">Rimuovi<\/button><\/td>';\n            corpoRighe.appendChild(tr);\n            tr.querySelector('.gf-btn-rimuovi-riga').addEventListener('click', function () {\n                tr.remove();\n                aggiornaTotali();\n            });\n            tr.querySelectorAll('input, select').forEach(function (campo) {\n                campo.addEventListener('input', aggiornaTotali);\n            });\n            aggiornaTotali();\n        }\n\n        document.getElementById('gf-btn-aggiungi-riga').addEventListener('click', function () {\n            aggiungiRiga('', 1, 0, '22.00');\n        });\n\n        function leggiRighe() {\n            var righe = [];\n            corpoRighe.querySelectorAll('tr').forEach(function (tr) {\n                var descrizione = tr.querySelector('.gf-riga-descrizione').value.trim();\n                var quantita = parseFloat(tr.querySelector('.gf-riga-quantita').value) || 0;\n                var prezzo = parseFloat(tr.querySelector('.gf-riga-prezzo').value) || 0;\n                var aliquota = tr.querySelector('.gf-riga-aliquota').value;\n                righe.push({ descrizione: descrizione, quantita: quantita, prezzo: prezzo, aliquota: aliquota });\n            });\n            return righe;\n        }\n\n        function formattaImporto(numero) {\n            return numero.toFixed(2);\n        }\n\n        function calcolaRiepilogo(righe) {\n            \/\/ Raggruppa per aliquota IVA, come richiesto dallo schema FatturaPA (DatiRiepilogo)\n            var perAliquota = {};\n            righe.forEach(function (r) {\n                var totaleRiga = r.quantita * r.prezzo;\n                if (!perAliquota[r.aliquota]) perAliquota[r.aliquota] = 0;\n                perAliquota[r.aliquota] += totaleRiga;\n            });\n            var riepilogo = [];\n            var imponibileTotale = 0;\n            var ivaTotale = 0;\n            Object.keys(perAliquota).forEach(function (aliquota) {\n                var imponibile = perAliquota[aliquota];\n                var imposta = imponibile * (parseFloat(aliquota) \/ 100);\n                riepilogo.push({ aliquota: aliquota, imponibile: imponibile, imposta: imposta });\n                imponibileTotale += imponibile;\n                ivaTotale += imposta;\n            });\n            return { righeRiepilogo: riepilogo, imponibileTotale: imponibileTotale, ivaTotale: ivaTotale, totaleDocumento: imponibileTotale + ivaTotale };\n        }\n\n        function aggiornaTotali() {\n            var righe = leggiRighe();\n            var r = calcolaRiepilogo(righe);\n            document.getElementById('gf-totali').textContent =\n                'Imponibile: ' + formattaImporto(r.imponibileTotale) + ' EUR - IVA: ' + formattaImporto(r.ivaTotale) +\n                ' EUR - Totale: ' + formattaImporto(r.totaleDocumento) + ' EUR';\n        }\n\n        \/\/ ---------- Costruzione XML FatturaPA ----------\n        function escapeXml(testo) {\n            return String(testo || '')\n                .replace(\/&\/g, '&amp;')\n                .replace(\/<\/g, '&lt;')\n                .replace(\/>\/g, '&gt;')\n                .replace(\/\"\/g, '&quot;');\n        }\n\n        \/\/ ---------- Tabella ufficiale codici Tipo Documento FatturaPA ----------\n        \/\/ Fonte: Specifiche tecniche Agenzia delle Entrate v1.9.1 (in vigore dal 15\/05\/2026)\n        \/\/ https:\/\/www.fatturapa.gov.it\/it\/norme-e-regole\/documentazione-fattura-elettronica\/formato-fatturapa\/\n        \/\/ NOTA: il sito dell'Agenzia delle Entrate non espone intestazioni CORS per l'accesso\n        \/\/ da script di terze parti (a differenza dei servizi pubblici usati altrove in\n        \/\/ quest'app, come l'elenco comuni o le vie OpenStreetMap): un fetch live da questo\n        \/\/ browser fallirebbe sempre per policy del browser, non per un problema di rete.\n        \/\/ La tabella e' quindi integrata qui, aggiornata a mano dalla fonte ufficiale sopra.\n        var TABELLA_TIPO_DOCUMENTO = [\n            ['TD01', 'Fattura', 'Fattura'],\n            ['TD02', 'Acconto\/anticipo su fattura', 'Fattura'],\n            ['TD03', 'Acconto\/anticipo su parcella', 'Fattura'],\n            ['TD04', 'Nota di credito', 'Nota di variazione'],\n            ['TD05', 'Nota di debito', 'Nota di variazione'],\n            ['TD06', 'Parcella', 'Fattura'],\n            ['TD07', 'Fattura semplificata', 'Fattura'],\n            ['TD08', 'Nota di credito semplificata', 'Nota di variazione'],\n            ['TD09', 'Nota di debito semplificata', 'Nota di variazione'],\n            ['TD10', 'Fattura per acquisto intracomunitario beni', 'Fattura'],\n            ['TD11', 'Fattura per acquisto intracomunitario servizi', 'Fattura'],\n            ['TD12', 'Documento riepilogativo', 'Fattura'],\n            ['TD16', 'Integrazione fattura reverse charge interno', 'Autofattura'],\n            ['TD17', 'Integrazione\/autofattura acquisto servizi estero', 'Autofattura'],\n            ['TD18', 'Integrazione acquisto beni intracomunitari', 'Autofattura'],\n            ['TD19', 'Integrazione\/autofattura beni ex art.17 c.2', 'Autofattura'],\n            ['TD20', 'Autofattura per regolarizzazione', 'Autofattura'],\n            ['TD21', 'Autofattura per splafonamento', 'Autofattura'],\n            ['TD22', 'Estrazione beni da Deposito IVA', 'Autofattura'],\n            ['TD23', 'Estrazione beni da Deposito IVA con versamento', 'Autofattura'],\n            ['TD24', 'Fattura differita (art.21 c.4 lett. a)', 'Fattura'],\n            ['TD25', 'Fattura differita (art.21 c.4 lett. b)', 'Fattura'],\n            ['TD26', 'Cessione beni ammortizzabili\/passaggi interni', 'Fattura'],\n            ['TD27', 'Fattura per autoconsumo\/cessioni gratuite', 'Autofattura'],\n            ['TD28', 'Acquisti da San Marino con IVA', 'Fattura'],\n            ['TD29', 'Comunicazione omessa\/irregolare fatturazione', 'Autofattura']\n        ];\n\n        \/\/ Popola una <select> con tutti i codici Tipo Documento ufficiali, con tooltip\n        \/\/ (title) contenente la descrizione. valoreSelezionato viene preselezionato se presente.\n        function popolaSelectTipoDocumento(select, valoreSelezionato) {\n            select.innerHTML = '';\n            TABELLA_TIPO_DOCUMENTO.forEach(function (voce) {\n                var codice = voce[0], descrizione = voce[1], categoria = voce[2];\n                var opzione = document.createElement('option');\n                opzione.value = codice;\n                opzione.textContent = codice + ' - ' + descrizione;\n                opzione.title = categoria;\n                if (codice === valoreSelezionato) opzione.selected = true;\n                select.appendChild(opzione);\n            });\n        }\n\n        function costruisciXmlFatturaPA(input) {\n            var righeXml = '';\n            input.righe.forEach(function (r, indice) {\n                var totaleRiga = r.quantita * r.prezzo;\n                righeXml +=\n                    '  <DettaglioLinee>\\n' +\n                    '   <NumeroLinea>' + (indice + 1) + '<\/NumeroLinea>\\n' +\n                    '   <Descrizione>' + escapeXml(r.descrizione) + '<\/Descrizione>\\n' +\n                    '   <Quantita>' + formattaImporto(r.quantita) + '<\/Quantita>\\n' +\n                    '   <PrezzoUnitario>' + formattaImporto(r.prezzo) + '<\/PrezzoUnitario>\\n' +\n                    '   <PrezzoTotale>' + formattaImporto(totaleRiga) + '<\/PrezzoTotale>\\n' +\n                    '   <AliquotaIVA>' + r.aliquota + '<\/AliquotaIVA>\\n' +\n                    '  <\/DettaglioLinee>\\n';\n            });\n\n            var riepilogoXml = '';\n            input.riepilogo.righeRiepilogo.forEach(function (rr) {\n                riepilogoXml +=\n                    '  <DatiRiepilogo>\\n' +\n                    '   <AliquotaIVA>' + rr.aliquota + '<\/AliquotaIVA>\\n' +\n                    '   <ImponibileImporto>' + formattaImporto(rr.imponibile) + '<\/ImponibileImporto>\\n' +\n                    '   <Imposta>' + formattaImporto(rr.imposta) + '<\/Imposta>\\n' +\n                    '   <EsigibilitaIVA>I<\/EsigibilitaIVA>\\n' +\n                    '  <\/DatiRiepilogo>\\n';\n            });\n\n            return '<' + '?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n' +\n                '<p:FatturaElettronica xmlns:p=\"http:\/\/ivaservizi.agenziaentrate.gov.it\/docs\/xsd\/fatture\/v1.2\" versione=\"FPR12\" xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\">\\n' +\n                ' <FatturaElettronicaHeader>\\n' +\n                '  <DatiTrasmissione>\\n' +\n                '   <IdTrasmittente>\\n' +\n                '    <IdPaese>IT<\/IdPaese>\\n' +\n                '    <IdCodice>' + escapeXml(input.cedente.piva) + '<\/IdCodice>\\n' +\n                '   <\/IdTrasmittente>\\n' +\n                '   <ProgressivoInvio>' + escapeXml(input.doc.numero) + '<\/ProgressivoInvio>\\n' +\n                '   <FormatoTrasmissione>FPR12<\/FormatoTrasmissione>\\n' +\n                '   <CodiceDestinatario>0000000<\/CodiceDestinatario>\\n' +\n                '  <\/DatiTrasmissione>\\n' +\n                '  <CedentePrestatore>\\n' +\n                '   <DatiAnagrafici>\\n' +\n                '    <IdFiscaleIVA>\\n' +\n                '     <IdPaese>IT<\/IdPaese>\\n' +\n                '     <IdCodice>' + escapeXml(input.cedente.piva) + '<\/IdCodice>\\n' +\n                '    <\/IdFiscaleIVA>\\n' +\n                '    <Anagrafica>\\n' +\n                '     <Denominazione>' + escapeXml(input.cedente.denom) + '<\/Denominazione>\\n' +\n                '    <\/Anagrafica>\\n' +\n                '    <RegimeFiscale>' + escapeXml(input.cedente.regime) + '<\/RegimeFiscale>\\n' +\n                '   <\/DatiAnagrafici>\\n' +\n                '   <Sede>\\n' +\n                '    <Indirizzo>' + escapeXml(input.cedente.indirizzo) + '<\/Indirizzo>\\n' +\n                '    <CAP>' + escapeXml(input.cedente.cap) + '<\/CAP>\\n' +\n                '    <Comune>' + escapeXml(input.cedente.comune) + '<\/Comune>\\n' +\n                '    <Provincia>' + escapeXml(input.cedente.provincia) + '<\/Provincia>\\n' +\n                '    <Nazione>IT<\/Nazione>\\n' +\n                '   <\/Sede>\\n' +\n                '  <\/CedentePrestatore>\\n' +\n                '  <CessionarioCommittente>\\n' +\n                '   <DatiAnagrafici>\\n' +\n                '    <IdFiscaleIVA>\\n' +\n                '     <IdPaese>IT<\/IdPaese>\\n' +\n                '     <IdCodice>' + escapeXml(input.cessionario.piva) + '<\/IdCodice>\\n' +\n                '    <\/IdFiscaleIVA>\\n' +\n                '    <Anagrafica>\\n' +\n                '     <Denominazione>' + escapeXml(input.cessionario.denom) + '<\/Denominazione>\\n' +\n                '    <\/Anagrafica>\\n' +\n                '   <\/DatiAnagrafici>\\n' +\n                '   <Sede>\\n' +\n                '    <Indirizzo>' + escapeXml(input.cessionario.indirizzo) + '<\/Indirizzo>\\n' +\n                '    <CAP>' + escapeXml(input.cessionario.cap) + '<\/CAP>\\n' +\n                '    <Comune>' + escapeXml(input.cessionario.comune) + '<\/Comune>\\n' +\n                '    <Provincia>' + escapeXml(input.cessionario.provincia) + '<\/Provincia>\\n' +\n                '    <Nazione>IT<\/Nazione>\\n' +\n                '   <\/Sede>\\n' +\n                '  <\/CessionarioCommittente>\\n' +\n                ' <\/FatturaElettronicaHeader>\\n' +\n                ' <FatturaElettronicaBody>\\n' +\n                '  <DatiGenerali>\\n' +\n                '   <DatiGeneraliDocumento>\\n' +\n                '    <TipoDocumento>' + escapeXml(input.doc.tipoDocumento || 'TD01') + '<\/TipoDocumento>\\n' +\n                '    <Divisa>EUR<\/Divisa>\\n' +\n                '    <Data>' + escapeXml(input.doc.data) + '<\/Data>\\n' +\n                '    <Numero>' + escapeXml(input.doc.numero) + '<\/Numero>\\n' +\n                '    <ImportoTotaleDocumento>' + formattaImporto(input.riepilogo.totaleDocumento) + '<\/ImportoTotaleDocumento>\\n' +\n                '   <\/DatiGeneraliDocumento>\\n' +\n                '  <\/DatiGenerali>\\n' +\n                '  <DatiBeniServizi>\\n' +\n                righeXml +\n                riepilogoXml +\n                '  <\/DatiBeniServizi>\\n' +\n                '  <DatiPagamento>\\n' +\n                '   <CondizioniPagamento>TP02<\/CondizioniPagamento>\\n' +\n                '   <DettaglioPagamento>\\n' +\n                '    <ModalitaPagamento>' + escapeXml(input.doc.pagamento) + '<\/ModalitaPagamento>\\n' +\n                '    <ImportoPagamento>' + formattaImporto(input.riepilogo.totaleDocumento) + '<\/ImportoPagamento>\\n' +\n                '   <\/DettaglioPagamento>\\n' +\n                '  <\/DatiPagamento>\\n' +\n                ' <\/FatturaElettronicaBody>\\n' +\n                '<\/p:FatturaElettronica>\\n';\n        }\n\n        function mostraMessaggio(testo, tipo) {\n            var el = document.getElementById('gf-messaggio');\n            el.textContent = testo;\n            el.className = 'gf-messaggio ' + (tipo || '');\n        }\n\n        function scaricaTesto(nomeFile, contenuto) {\n            var blob = new Blob([contenuto], { type: 'application\/xml' });\n            var url = URL.createObjectURL(blob);\n            var a = document.createElement('a');\n            a.href = url;\n            a.download = nomeFile;\n            document.body.appendChild(a);\n            a.click();\n            document.body.removeChild(a);\n            URL.revokeObjectURL(url);\n        }\n\n        document.getElementById('gf-btn-genera-xml').addEventListener('click', function () {\n            var cedente = {\n                denom: document.getElementById('gf-ced-denom').value.trim(),\n                piva: document.getElementById('gf-ced-piva').value.trim(),\n                indirizzo: document.getElementById('gf-ced-indirizzo').value.trim(),\n                cap: document.getElementById('gf-ced-cap').value.trim(),\n                comune: document.getElementById('gf-ced-comune').value.trim(),\n                provincia: document.getElementById('gf-ced-provincia').value.trim(),\n                regime: document.getElementById('gf-ced-regime').value\n            };\n            var cessionario = {\n                denom: document.getElementById('gf-ces-denom').value.trim(),\n                piva: document.getElementById('gf-ces-piva').value.trim(),\n                indirizzo: document.getElementById('gf-ces-indirizzo').value.trim(),\n                cap: document.getElementById('gf-ces-cap').value.trim(),\n                comune: document.getElementById('gf-ces-comune').value.trim(),\n                provincia: document.getElementById('gf-ces-provincia').value.trim()\n            };\n            var doc = {\n                numero: document.getElementById('gf-doc-numero').value.trim(),\n                data: document.getElementById('gf-doc-data').value,\n                tipoDocumento: document.getElementById('gf-doc-tipo').value,\n                pagamento: document.getElementById('gf-doc-pagamento').value\n            };\n            var righe = leggiRighe();\n\n            \/\/ Controlli minimi prima di generare\n            if (!cedente.denom || !cedente.piva) {\n                mostraMessaggio('Inserisci almeno Denominazione e Partita IVA del Cedente\/Prestatore.', 'errore');\n                return;\n            }\n            if (!cessionario.denom || !cessionario.piva) {\n                mostraMessaggio('Inserisci o genera i dati del Cessionario\/Committente.', 'errore');\n                return;\n            }\n            if (righe.length === 0) {\n                mostraMessaggio('Aggiungi almeno una riga fattura.', 'errore');\n                return;\n            }\n            if (!doc.data) {\n                mostraMessaggio('Seleziona la data del documento.', 'errore');\n                return;\n            }\n\n            var riepilogo = calcolaRiepilogo(righe);\n            var xml = costruisciXmlFatturaPA({ cedente: cedente, cessionario: cessionario, doc: doc, righe: righe, riepilogo: riepilogo });\n\n            var nomeFile = 'IT' + cedente.piva + '_' + doc.numero + '.xml';\n            scaricaTesto(nomeFile, xml);\n            mostraMessaggio('XML generato e scaricato: ' + nomeFile, 'successo');\n        });\n\n        \/\/ ================= MODULO: IMPORTA XML =================\n        \/\/ Riusa le funzioni condivise definite sopra: escapeXml, formattaImporto,\n        \/\/ calcolaRiepilogo, costruisciXmlFatturaPA, generaPartitaIvaFittizia,\n        \/\/ DENOMINAZIONI_FITTIZIE, CITTA_FITTIZIE, scegliCasuale, scaricaTesto, mostraMessaggio\n\n        \/\/ Cerca elementi per NOME LOCALE del tag, ignorando il prefisso di namespace\n        \/\/ (i file FatturaPA reali possono usare prefissi diversi da quello del nostro generatore)\n        function trovaElementiPerNome(radice, nomeLocale) {\n            var tutti = radice.getElementsByTagName('*');\n            var risultato = [];\n            for (var i = 0; i < tutti.length; i++) {\n                if (tutti[i].localName === nomeLocale) risultato.push(tutti[i]);\n            }\n            return risultato;\n        }\n        function trovaElementoUnico(radice, nomeLocale) {\n            if (!radice) return null;\n            var elenco = trovaElementiPerNome(radice, nomeLocale);\n            return elenco.length ? elenco[0] : null;\n        }\n        function trovaTesto(radice, nomeLocale) {\n            var el = trovaElementoUnico(radice, nomeLocale);\n            return el ? el.textContent.trim() : '';\n        }\n\n        function mostraMessaggioX(testo, tipo) {\n            var el = document.getElementById('gfx-messaggio');\n            el.textContent = testo;\n            el.className = 'gf-messaggio ' + (tipo || '');\n        }\n\n        \/\/ ---------- Righe fattura (tabella indipendente da quella della scheda Genera) ----------\n        var corpoRigheX = document.getElementById('gfx-righe-body');\n\n        function aggiornaTotaliX() {\n            var righe = leggiRigheX();\n            var r = calcolaRiepilogo(righe);\n            document.getElementById('gfx-totali').textContent =\n                'Imponibile: ' + formattaImporto(r.imponibileTotale) + ' EUR - IVA: ' + formattaImporto(r.ivaTotale) +\n                ' EUR - Totale: ' + formattaImporto(r.totaleDocumento) + ' EUR';\n        }\n\n        function aggiungiRigaX(descrizione, quantita, prezzo, aliquota) {\n            var tr = document.createElement('tr');\n            tr.innerHTML =\n                '<td><input type=\"text\" class=\"gf-riga-descrizione\" value=\"' + (descrizione || '') + '\"><\/td>' +\n                '<td><input type=\"number\" class=\"gf-riga-quantita\" min=\"0\" step=\"0.01\" value=\"' + (quantita || 1) + '\"><\/td>' +\n                '<td><input type=\"number\" class=\"gf-riga-prezzo\" min=\"0\" step=\"0.01\" value=\"' + (prezzo || 0) + '\"><\/td>' +\n                '<td><select class=\"gf-riga-aliquota\">' +\n                    '<option value=\"22.00\"' + (aliquota === '22.00' ? ' selected' : '') + '>22%<\/option>' +\n                    '<option value=\"10.00\"' + (aliquota === '10.00' ? ' selected' : '') + '>10%<\/option>' +\n                    '<option value=\"4.00\"' + (aliquota === '4.00' ? ' selected' : '') + '>4%<\/option>' +\n                    '<option value=\"0.00\"' + (aliquota === '0.00' ? ' selected' : '') + '>0%<\/option>' +\n                '<\/select><\/td>' +\n                '<td><button type=\"button\" class=\"gf-btn gf-btn-rimuovi-riga\">Rimuovi<\/button><\/td>';\n            corpoRigheX.appendChild(tr);\n            tr.querySelector('.gf-btn-rimuovi-riga').addEventListener('click', function () {\n                tr.remove();\n                aggiornaTotaliX();\n            });\n            tr.querySelectorAll('input, select').forEach(function (campo) {\n                campo.addEventListener('input', aggiornaTotaliX);\n            });\n            aggiornaTotaliX();\n        }\n\n        function leggiRigheX() {\n            var righe = [];\n            corpoRigheX.querySelectorAll('tr').forEach(function (tr) {\n                righe.push({\n                    descrizione: tr.querySelector('.gf-riga-descrizione').value.trim(),\n                    quantita: parseFloat(tr.querySelector('.gf-riga-quantita').value) || 0,\n                    prezzo: parseFloat(tr.querySelector('.gf-riga-prezzo').value) || 0,\n                    aliquota: tr.querySelector('.gf-riga-aliquota').value\n                });\n            });\n            return righe;\n        }\n\n        document.getElementById('gfx-btn-aggiungi-riga').addEventListener('click', function () {\n            aggiungiRigaX('', 1, 0, '22.00');\n        });\n\n        \/\/ ---------- Lettura e parsing del file XML reale ----------\n        function svuotaTabellaX() {\n            corpoRigheX.innerHTML = '';\n        }\n\n        function popolaCampiSoggetto(prefisso, elementoXml) {\n            document.getElementById(prefisso + '-denom').value = trovaTesto(elementoXml, 'Denominazione');\n            document.getElementById(prefisso + '-piva').value = trovaTesto(trovaElementoUnico(elementoXml, 'IdFiscaleIVA'), 'IdCodice');\n            document.getElementById(prefisso + '-indirizzo').value = trovaTesto(elementoXml, 'Indirizzo');\n            document.getElementById(prefisso + '-cap').value = trovaTesto(elementoXml, 'CAP');\n            document.getElementById(prefisso + '-comune').value = trovaTesto(elementoXml, 'Comune');\n            document.getElementById(prefisso + '-provincia').value = trovaTesto(elementoXml, 'Provincia');\n        }\n\n        document.getElementById('gfx-file').addEventListener('change', function (ev) {\n            var file = ev.target.files[0];\n            if (!file) return;\n            var lettore = new FileReader();\n            lettore.onload = function (e) {\n                try {\n                    var testoXml = e.target.result;\n                    var parser = new DOMParser();\n                    var xmlDoc = parser.parseFromString(testoXml, 'application\/xml');\n\n                    var erroreParsing = xmlDoc.getElementsByTagName('parsererror');\n                    if (erroreParsing.length > 0) {\n                        mostraMessaggioX('Il file non sembra un XML valido: impossibile analizzarlo.', 'errore');\n                        return;\n                    }\n\n                    var cedenteEl = trovaElementoUnico(xmlDoc, 'CedentePrestatore');\n                    var cessionarioEl = trovaElementoUnico(xmlDoc, 'CessionarioCommittente');\n                    if (!cedenteEl || !cessionarioEl) {\n                        mostraMessaggioX('Non trovo CedentePrestatore\/CessionarioCommittente: verifica che sia un file FatturaPA.', 'errore');\n                        return;\n                    }\n\n                    popolaCampiSoggetto('gfx-ced', cedenteEl);\n                    document.getElementById('gfx-ced-regime').value = trovaTesto(cedenteEl, 'RegimeFiscale') || 'RF01';\n                    popolaCampiSoggetto('gfx-ces', cessionarioEl);\n\n                    var datiGenDocEl = trovaElementoUnico(xmlDoc, 'DatiGeneraliDocumento');\n                    document.getElementById('gfx-doc-numero').value = trovaTesto(datiGenDocEl, 'Numero');\n                    var dataGrezza = trovaTesto(datiGenDocEl, 'Data');\n                    document.getElementById('gfx-doc-data').value = dataGrezza || '';\n                    var tipoDocumentoLetto = trovaTesto(datiGenDocEl, 'TipoDocumento');\n                    if (tipoDocumentoLetto && TABELLA_TIPO_DOCUMENTO.some(function (v) { return v[0] === tipoDocumentoLetto; })) {\n                        document.getElementById('gfx-doc-tipo').value = tipoDocumentoLetto;\n                    }\n\n                    var pagamentoEl = trovaElementoUnico(xmlDoc, 'DettaglioPagamento');\n                    var modalitaPagamento = trovaTesto(pagamentoEl, 'ModalitaPagamento');\n                    if (modalitaPagamento) document.getElementById('gfx-doc-pagamento').value = modalitaPagamento;\n\n                    svuotaTabellaX();\n                    var righeXml = trovaElementiPerNome(xmlDoc, 'DettaglioLinee');\n                    if (righeXml.length === 0) {\n                        aggiungiRigaX('', 1, 0, '22.00');\n                    } else {\n                        righeXml.forEach(function (rigaEl) {\n                            var descrizione = trovaTesto(rigaEl, 'Descrizione');\n                            var quantita = parseFloat(trovaTesto(rigaEl, 'Quantita')) || 1;\n                            var prezzo = parseFloat(trovaTesto(rigaEl, 'PrezzoUnitario')) || 0;\n                            var aliquotaGrezza = trovaTesto(rigaEl, 'AliquotaIVA');\n                            var aliquota = aliquotaGrezza ? parseFloat(aliquotaGrezza).toFixed(2) : '22.00';\n                            aggiungiRigaX(descrizione, quantita, prezzo, aliquota);\n                        });\n                    }\n\n                    mostraMessaggioX('File letto correttamente. Ora scegli quale soggetto anonimizzare e genera l\\'XML.', 'successo');\n                } catch (erroreGenerico) {\n                    mostraMessaggioX('Errore durante la lettura del file: ' + erroreGenerico.message, 'errore');\n                }\n            };\n            lettore.readAsText(file, 'UTF-8');\n        });\n\n        \/\/ ---------- Anonimizzazione del soggetto selezionato ----------\n        document.getElementById('gfx-btn-genera-controparte').addEventListener('click', async function () {\n            var ruolo = document.querySelector('input[name=\"gfx-ruolo\"]:checked').value;\n            var prefisso = (ruolo === 'cedente') ? 'gfx-ced' : 'gfx-ces';\n            var citta = scegliCasuale(CITTA_FITTIZIE);\n            document.getElementById(prefisso + '-denom').value = scegliCasuale(DENOMINAZIONI_FITTIZIE);\n            document.getElementById(prefisso + '-piva').value = generaPartitaIvaFittizia();\n            document.getElementById(prefisso + '-cap').value = citta.cap;\n            document.getElementById(prefisso + '-comune').value = citta.comune;\n            document.getElementById(prefisso + '-provincia').value = citta.provincia;\n            document.getElementById(prefisso + '-indirizzo').value = 'Recupero indirizzo...';\n            document.getElementById(prefisso + '-indirizzo').value = await generaIndirizzoCasuale(citta.comune);\n        });\n\n        \/\/ ---------- Generazione e download dell'XML anonimizzato ----------\n        document.getElementById('gfx-btn-genera-xml').addEventListener('click', function () {\n            var cedente = {\n                denom: document.getElementById('gfx-ced-denom').value.trim(),\n                piva: document.getElementById('gfx-ced-piva').value.trim(),\n                indirizzo: document.getElementById('gfx-ced-indirizzo').value.trim(),\n                cap: document.getElementById('gfx-ced-cap').value.trim(),\n                comune: document.getElementById('gfx-ced-comune').value.trim(),\n                provincia: document.getElementById('gfx-ced-provincia').value.trim(),\n                regime: document.getElementById('gfx-ced-regime').value\n            };\n            var cessionario = {\n                denom: document.getElementById('gfx-ces-denom').value.trim(),\n                piva: document.getElementById('gfx-ces-piva').value.trim(),\n                indirizzo: document.getElementById('gfx-ces-indirizzo').value.trim(),\n                cap: document.getElementById('gfx-ces-cap').value.trim(),\n                comune: document.getElementById('gfx-ces-comune').value.trim(),\n                provincia: document.getElementById('gfx-ces-provincia').value.trim()\n            };\n            var doc = {\n                numero: document.getElementById('gfx-doc-numero').value.trim(),\n                data: document.getElementById('gfx-doc-data').value,\n                tipoDocumento: document.getElementById('gfx-doc-tipo').value,\n                pagamento: document.getElementById('gfx-doc-pagamento').value\n            };\n            var righe = leggiRigheX();\n\n            if (!cedente.denom || !cedente.piva || !cessionario.denom || !cessionario.piva) {\n                mostraMessaggioX('Mancano dati di Cedente o Cessionario: carica un file oppure completa i campi.', 'errore');\n                return;\n            }\n            if (righe.length === 0) {\n                mostraMessaggioX('Nessuna riga fattura presente.', 'errore');\n                return;\n            }\n            if (!doc.data) {\n                mostraMessaggioX('Manca la data del documento.', 'errore');\n                return;\n            }\n\n            var riepilogo = calcolaRiepilogo(righe);\n            var xml = costruisciXmlFatturaPA({ cedente: cedente, cessionario: cessionario, doc: doc, righe: righe, riepilogo: riepilogo });\n            var nomeFile = 'ANONIMIZZATA_' + (doc.numero || 'documento') + '.xml';\n            scaricaTesto(nomeFile, xml);\n            mostraMessaggioX('XML anonimizzato generato e scaricato: ' + nomeFile, 'successo');\n        });\n\n        \/\/ ================= MODULO: IMPORTA PDF =================\n        \/\/ Riusa le funzioni condivise: calcolaRiepilogo, costruisciXmlFatturaPA, formattaImporto,\n        \/\/ generaPartitaIvaFittizia, DENOMINAZIONI_FITTIZIE, CITTA_FITTIZIE, scegliCasuale, scaricaTesto\n\n        function mostraMessaggioP(testo, tipo) {\n            var el = document.getElementById('gfp-messaggio');\n            el.textContent = testo;\n            el.className = 'gf-messaggio ' + (tipo || '');\n        }\n\n        \/\/ Estrae tutti gli elementi di testo di tutte le pagine con le loro coordinate,\n        \/\/ poi li raggruppa in \"righe\" per coordinata Y (con una tolleranza), ordinando\n        \/\/ per X all'interno di ogni riga. Questo ricostruisce un testo con un layout\n        \/\/ simile a quello che produceva \"pdftotext -layout\" nel progetto VB6 originale,\n        \/\/ ma qui partendo dalle coordinate REALI restituite da pdf.js invece che da una\n        \/\/ griglia a larghezza fissa.\n        async function estraiElementiConCoordinate(arrayBuffer) {\n            var pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;\n            var elementi = [];\n            for (var numPagina = 1; numPagina <= pdf.numPages; numPagina++) {\n                var pagina = await pdf.getPage(numPagina);\n                var contenuto = await pagina.getTextContent();\n                contenuto.items.forEach(function (voce) {\n                    if (!voce.str || !voce.str.trim()) return;\n                    elementi.push({\n                        testo: voce.str,\n                        x: voce.transform[4],\n                        y: voce.transform[5],\n                        pagina: numPagina\n                    });\n                });\n            }\n            return elementi;\n        }\n\n        function raggruppaInRighe(elementi) {\n            var TOLLERANZA_Y = 3;\n            var ordinati = elementi.slice().sort(function (a, b) {\n                if (a.pagina !== b.pagina) return a.pagina - b.pagina;\n                if (Math.abs(a.y - b.y) > TOLLERANZA_Y) return b.y - a.y; \/\/ dall'alto verso il basso\n                return a.x - b.x;\n            });\n            var righe = [];\n            var rigaCorrente = null;\n            ordinati.forEach(function (el) {\n                if (!rigaCorrente || Math.abs(rigaCorrente.y - el.y) > TOLLERANZA_Y || rigaCorrente.pagina !== el.pagina) {\n                    rigaCorrente = { y: el.y, pagina: el.pagina, elementi: [] };\n                    righe.push(rigaCorrente);\n                }\n                rigaCorrente.elementi.push(el);\n            });\n            return righe;\n        }\n\n        function testoDellaRiga(riga) {\n            return riga.elementi.map(function (e) { return e.testo; }).join(' ').replace(\/\\s+\/g, ' ').trim();\n        }\n\n        function testoCompletoLineare(righe) {\n            return righe.map(testoDellaRiga).join('\\n');\n        }\n\n        \/\/ Cerca un'etichetta e restituisce il valore associato: se subito dopo l'etichetta\n        \/\/ c'e' un a-capo immediato (etichetta e valore su righe separate, come scoperto nel\n        \/\/ progetto Dart con Syncfusion), salta quell'a-capo e prende la riga successiva.\n        function estraiValoreDopoEtichetta(testoCompleto, etichetta) {\n            var indice = testoCompleto.toLowerCase().indexOf(etichetta.toLowerCase());\n            if (indice === -1) return '';\n            var resto = testoCompleto.substring(indice + etichetta.length);\n            resto = resto.replace(\/^[ \\t]*\\n\/, ''); \/\/ salta un eventuale a-capo immediato\n            var finePrimaRiga = resto.indexOf('\\n');\n            var primaRiga = finePrimaRiga === -1 ? resto : resto.substring(0, finePrimaRiga);\n            return primaRiga.trim();\n        }\n\n        function estraiNumero(testoValore) {\n            if (!testoValore) return 0;\n            var pulito = testoValore.replace(\/[^\\d,.-]\/g, '').replace(\/\\.\/g, '').replace(',', '.');\n            var numero = parseFloat(pulito);\n            return isNaN(numero) ? 0 : numero;\n        }\n\n        function estraiData(testoValore) {\n            var match = testoValore.match(\/(\\d{2})[\\\/\\-.](\\d{2})[\\\/\\-.](\\d{4})\/);\n            if (!match) return '';\n            return match[3] + '-' + match[2] + '-' + match[1]; \/\/ formato input[type=date]: AAAA-MM-GG\n        }\n\n        \/\/ Tentativo di estrazione colonne FORNITORE\/CLIENTE per POSIZIONE X reale.\n        \/\/ Provvisorio: verificare sempre con la diagnostica su un PDF vero prima di fidarsi\n        \/\/ ciecamente del risultato, la struttura esatta puo' variare.\n        function estraiAnagraficheDaColonne(righe) {\n            var risultato = { fornitore: { denom: '', piva: '' }, cliente: { denom: '', piva: '' } };\n            var rigaIntestazione = null;\n            var xFornitore = null, xCliente = null;\n            righe.forEach(function (riga) {\n                riga.elementi.forEach(function (el) {\n                    var testoMaiuscolo = el.testo.toUpperCase();\n                    if (testoMaiuscolo.indexOf('FORNITORE') !== -1 && xFornitore === null) {\n                        xFornitore = el.x;\n                        rigaIntestazione = riga;\n                    }\n                    if (testoMaiuscolo.indexOf('CLIENTE') !== -1 && xCliente === null) {\n                        xCliente = el.x;\n                    }\n                });\n            });\n            if (xFornitore === null) return risultato; \/\/ struttura non trovata, si lascia vuoto\n\n            var indiceRigaIntestazione = righe.indexOf(rigaIntestazione);\n            var sogliaX = (xCliente !== null) ? (xFornitore + xCliente) \/ 2 : (xFornitore + 300);\n            var testoFornitore = [];\n            var testoCliente = [];\n\n            \/\/ Analizza un ragionevole numero di righe successive all'intestazione (blocco anagrafica)\n            for (var i = indiceRigaIntestazione + 1; i < Math.min(righe.length, indiceRigaIntestazione + 15); i++) {\n                righe[i].elementi.forEach(function (el) {\n                    if (xCliente !== null && el.x >= sogliaX) {\n                        testoCliente.push(el.testo);\n                    } else {\n                        testoFornitore.push(el.testo);\n                    }\n                });\n            }\n\n            var bloccoFornitore = testoFornitore.join(' ');\n            var bloccoCliente = testoCliente.join(' ');\n\n            risultato.fornitore.denom = testoFornitore.length ? testoFornitore[0] : '';\n            risultato.cliente.denom = testoCliente.length ? testoCliente[0] : '';\n\n            var matchPivaFornitore = bloccoFornitore.match(\/P\\.?\\s?IVA[:\\s]*([0-9]{11})\/i);\n            var matchPivaCliente = bloccoCliente.match(\/P\\.?\\s?IVA[:\\s]*([0-9]{11})\/i);\n            risultato.fornitore.piva = matchPivaFornitore ? matchPivaFornitore[1] : '';\n            risultato.cliente.piva = matchPivaCliente ? matchPivaCliente[1] : '';\n\n            return risultato;\n        }\n\n        document.getElementById('gfp-file').addEventListener('change', function (ev) {\n            var file = ev.target.files[0];\n            if (!file) return;\n            if (!window.pdfjsLib) {\n                mostraMessaggioP('La libreria pdf.js non si e\\' caricata (verifica la connessione o eventuali blocchi del sito a script esterni).', 'errore');\n                return;\n            }\n            var lettore = new FileReader();\n            lettore.onload = async function (e) {\n                try {\n                    mostraMessaggioP('Lettura del PDF in corso...', '');\n                    var arrayBuffer = e.target.result;\n                    var elementi = await estraiElementiConCoordinate(arrayBuffer);\n                    var righe = raggruppaInRighe(elementi);\n                    var testoCompleto = testoCompletoLineare(righe);\n\n                    \/\/ Diagnostica: 350 caratteri dopo \"FORNITORE\", con gli a-capo resi visibili\n                    var indiceFornitore = testoCompleto.toUpperCase().indexOf('FORNITORE');\n                    var diagnostica;\n                    if (indiceFornitore === -1) {\n                        diagnostica = 'Etichetta \"FORNITORE\" non trovata nel testo estratto. Primi 500 caratteri del documento:\\n' +\n                            testoCompleto.substring(0, 500).replace(\/\\n\/g, '[NL]\\n');\n                    } else {\n                        diagnostica = testoCompleto.substring(indiceFornitore, indiceFornitore + 350).replace(\/\\n\/g, '[NL]\\n');\n                    }\n                    document.getElementById('gfp-diagnostica').value = diagnostica;\n\n                    \/\/ Totali e date (stessa logica \"salta a-capo immediato\" gia' corretta nel progetto Dart)\n                    var totaleDocumento = estraiNumero(estraiValoreDopoEtichetta(testoCompleto, 'Totale documento'));\n                    var totaleIva = estraiNumero(estraiValoreDopoEtichetta(testoCompleto, 'Totale IVA'));\n                    var nettoAPagare = estraiNumero(estraiValoreDopoEtichetta(testoCompleto, 'Netto a pagare'));\n                    var dataGrezza = estraiValoreDopoEtichetta(testoCompleto, 'Data');\n                    var numeroGrezzo = estraiValoreDopoEtichetta(testoCompleto, 'Numero');\n\n                    var totaleFinale = totaleDocumento || nettoAPagare;\n                    var imponibile = totaleFinale - totaleIva;\n                    var aliquotaCalcolata = imponibile > 0 ? (totaleIva \/ imponibile * 100) : 22;\n\n                    document.getElementById('gfp-imponibile').value = imponibile.toFixed(2);\n                    document.getElementById('gfp-iva').value = totaleIva.toFixed(2);\n                    document.getElementById('gfp-aliquota').value = aliquotaCalcolata.toFixed(2);\n                    document.getElementById('gfp-totale').value = totaleFinale.toFixed(2);\n                    if (dataGrezza) document.getElementById('gfp-doc-data').value = estraiData(dataGrezza);\n                    if (numeroGrezzo) document.getElementById('gfp-doc-numero').value = numeroGrezzo;\n\n                    \/\/ Tentativo anagrafica Fornitore\/Cliente per posizione X reale (provvisorio)\n                    var anagrafiche = estraiAnagraficheDaColonne(righe);\n                    document.getElementById('gfp-ced-denom').value = anagrafiche.fornitore.denom;\n                    document.getElementById('gfp-ced-piva').value = anagrafiche.fornitore.piva;\n                    document.getElementById('gfp-ces-denom').value = anagrafiche.cliente.denom;\n                    document.getElementById('gfp-ces-piva').value = anagrafiche.cliente.piva;\n\n                    if (!anagrafiche.fornitore.denom && !anagrafiche.cliente.denom) {\n                        mostraMessaggioP('Totali\/data letti. Anagrafica Fornitore\/Cliente NON riconosciuta automaticamente: apri la diagnostica qui sopra, incollala in chat cosi\\' correggiamo l\\'estrazione, oppure completa i campi a mano.', 'errore');\n                    } else {\n                        mostraMessaggioP('PDF letto. Controlla i campi (l\\'anagrafica Fornitore\/Cliente e\\' un\\'estrazione automatica provvisoria: verificala prima di generare l\\'XML).', 'successo');\n                    }\n                } catch (erroreGenerico) {\n                    mostraMessaggioP('Errore durante la lettura del PDF: ' + erroreGenerico.message, 'errore');\n                }\n            };\n            lettore.readAsArrayBuffer(file);\n        });\n\n        document.getElementById('gfp-btn-genera-controparte').addEventListener('click', async function () {\n            var ruolo = document.querySelector('input[name=\"gfp-ruolo\"]:checked').value;\n            var prefisso = (ruolo === 'cedente') ? 'gfp-ced' : 'gfp-ces';\n            var citta = scegliCasuale(CITTA_FITTIZIE);\n            document.getElementById(prefisso + '-denom').value = scegliCasuale(DENOMINAZIONI_FITTIZIE);\n            document.getElementById(prefisso + '-piva').value = generaPartitaIvaFittizia();\n            document.getElementById(prefisso + '-cap').value = citta.cap;\n            document.getElementById(prefisso + '-comune').value = citta.comune;\n            document.getElementById(prefisso + '-provincia').value = citta.provincia;\n            document.getElementById(prefisso + '-indirizzo').value = 'Recupero indirizzo...';\n            document.getElementById(prefisso + '-indirizzo').value = await generaIndirizzoCasuale(citta.comune);\n        });\n\n        document.getElementById('gfp-btn-genera-xml').addEventListener('click', function () {\n            var cedente = {\n                denom: document.getElementById('gfp-ced-denom').value.trim(),\n                piva: document.getElementById('gfp-ced-piva').value.trim(),\n                indirizzo: document.getElementById('gfp-ced-indirizzo').value.trim(),\n                cap: document.getElementById('gfp-ced-cap').value.trim(),\n                comune: document.getElementById('gfp-ced-comune').value.trim(),\n                provincia: document.getElementById('gfp-ced-provincia').value.trim(),\n                regime: document.getElementById('gfp-ced-regime').value\n            };\n            var cessionario = {\n                denom: document.getElementById('gfp-ces-denom').value.trim(),\n                piva: document.getElementById('gfp-ces-piva').value.trim(),\n                indirizzo: document.getElementById('gfp-ces-indirizzo').value.trim(),\n                cap: document.getElementById('gfp-ces-cap').value.trim(),\n                comune: document.getElementById('gfp-ces-comune').value.trim(),\n                provincia: document.getElementById('gfp-ces-provincia').value.trim()\n            };\n            var doc = {\n                numero: document.getElementById('gfp-doc-numero').value.trim(),\n                data: document.getElementById('gfp-doc-data').value,\n                tipoDocumento: document.getElementById('gfp-doc-tipo').value,\n                pagamento: document.getElementById('gfp-doc-pagamento').value\n            };\n            var imponibile = parseFloat(document.getElementById('gfp-imponibile').value) || 0;\n            var aliquota = parseFloat(document.getElementById('gfp-aliquota').value);\n            if (isNaN(aliquota)) aliquota = 22;\n            var righe = [{ descrizione: 'Importo fattura', quantita: 1, prezzo: imponibile, aliquota: aliquota.toFixed(2) }];\n\n            if (!cedente.denom || !cedente.piva || !cessionario.denom || !cessionario.piva) {\n                mostraMessaggioP('Mancano dati di Fornitore o Cliente: completa i campi (l\\'estrazione automatica e\\' provvisoria).', 'errore');\n                return;\n            }\n            if (!doc.data) {\n                mostraMessaggioP('Manca la data del documento.', 'errore');\n                return;\n            }\n\n            var riepilogo = calcolaRiepilogo(righe);\n            var xml = costruisciXmlFatturaPA({ cedente: cedente, cessionario: cessionario, doc: doc, righe: righe, riepilogo: riepilogo });\n            var nomeFile = 'ANONIMIZZATA_PDF_' + (doc.numero || 'documento') + '.xml';\n            scaricaTesto(nomeFile, xml);\n            mostraMessaggioP('XML generato e scaricato: ' + nomeFile, 'successo');\n        });\n\n        \/\/ ================= MODULO: ANAGRAFICHE FITTIZIE =================\n        var CHIAVE_ANAGRAFICHE = 'gf_anagrafiche_v1';\n        var URL_COMUNI_ITALIANI = 'https:\/\/raw.githubusercontent.com\/matteocontrini\/comuni-json\/master\/comuni.json';\n        var URL_COGNOME_CASUALE = 'https:\/\/randomuser.me\/api\/?nat=it&results=1&inc=name';\n        var cacheComuniItaliani = null; \/\/ caricata al primo utilizzo, valida per la sessione corrente\n\n        var SUFFISSI_AZIENDALI = ['Srl', 'Snc', 'Spa', 'e Figli Srl', '& C. Snc', 'Industriale Srl'];\n\n        function mostraMessaggioA(testo, tipo) {\n            var el = document.getElementById('ga-messaggio');\n            el.textContent = testo;\n            el.className = 'gf-messaggio ' + (tipo || '');\n        }\n\n        function caricaAnagrafiche() {\n            try {\n                var grezzo = localStorage.getItem(CHIAVE_ANAGRAFICHE);\n                return grezzo ? JSON.parse(grezzo) : [];\n            } catch (e) { return []; }\n        }\n\n        function salvaAnagrafiche(elenco) {\n            try {\n                localStorage.setItem(CHIAVE_ANAGRAFICHE, JSON.stringify(elenco));\n                return true;\n            } catch (e) {\n                mostraMessaggioA('Impossibile salvare: memoria del browser piena o non disponibile.', 'errore');\n                return false;\n            }\n        }\n\n        function generaIdUnivoco() {\n            return 'a' + Date.now().toString(36) + Math.floor(Math.random() * 10000).toString(36);\n        }\n\n        function renderizzaTabellaAnagrafiche() {\n            var elenco = caricaAnagrafiche();\n            var corpo = document.getElementById('ga-tabella-body');\n            corpo.innerHTML = '';\n            elenco.forEach(function (voce) {\n                var tr = document.createElement('tr');\n                tr.innerHTML =\n                    '<td>' + escapeXml(voce.tipo) + '<\/td>' +\n                    '<td>' + escapeXml(voce.denom) + '<\/td>' +\n                    '<td>' + escapeXml(voce.piva) + '<\/td>' +\n                    '<td>' + escapeXml(voce.comune) + '<\/td>' +\n                    '<td>' + escapeXml(voce.provincia) + '<\/td>' +\n                    '<td><button type=\"button\" class=\"gf-btn ga-btn-rimuovi\" data-id=\"' + voce.id + '\">Rimuovi<\/button><\/td>';\n                corpo.appendChild(tr);\n            });\n            corpo.querySelectorAll('.ga-btn-rimuovi').forEach(function (btn) {\n                btn.addEventListener('click', function () {\n                    var id = btn.getAttribute('data-id');\n                    var nuovoElenco = caricaAnagrafiche().filter(function (v) { return v.id !== id; });\n                    salvaAnagrafiche(nuovoElenco);\n                    renderizzaTabellaAnagrafiche();\n                    aggiornaSelectAnagraficheInGenera();\n                });\n            });\n            document.getElementById('ga-contatore').textContent = elenco.length + ' anagrafiche salvate.';\n        }\n\n        \/\/ Aggiorna la select \"oppure usa dall'elenco\" nella scheda Genera Fatture\n        function aggiornaSelectAnagraficheInGenera() {\n            var select = document.getElementById('gf-ces-da-elenco');\n            var elenco = caricaAnagrafiche();\n            var valoreAttuale = select.value;\n            select.innerHTML = '<option value=\"\">-- nessuna --<\/option>';\n            elenco.forEach(function (voce) {\n                var opzione = document.createElement('option');\n                opzione.value = voce.id;\n                opzione.textContent = voce.tipo + ' - ' + voce.denom + ' (' + voce.comune + ')';\n                select.appendChild(opzione);\n            });\n            select.value = valoreAttuale; \/\/ ripristina la selezione se ancora presente\n        }\n\n        document.getElementById('gf-ces-da-elenco').addEventListener('change', function () {\n            var id = this.value;\n            if (!id) return;\n            var voce = caricaAnagrafiche().filter(function (v) { return v.id === id; })[0];\n            if (!voce) return;\n            document.getElementById('gf-ces-denom').value = voce.denom;\n            document.getElementById('gf-ces-piva').value = voce.piva;\n            document.getElementById('gf-ces-indirizzo').value = voce.indirizzo;\n            document.getElementById('gf-ces-cap').value = voce.cap;\n            document.getElementById('gf-ces-comune').value = voce.comune;\n            document.getElementById('gf-ces-provincia').value = voce.provincia;\n        });\n\n        document.getElementById('ga-btn-aggiungi').addEventListener('click', function () {\n            var voce = {\n                id: generaIdUnivoco(),\n                tipo: document.getElementById('ga-tipo').value,\n                denom: document.getElementById('ga-denom').value.trim(),\n                piva: document.getElementById('ga-piva').value.trim(),\n                indirizzo: document.getElementById('ga-indirizzo').value.trim(),\n                cap: document.getElementById('ga-cap').value.trim(),\n                comune: document.getElementById('ga-comune').value.trim(),\n                provincia: document.getElementById('ga-provincia').value.trim()\n            };\n            if (!voce.denom || !voce.piva) {\n                mostraMessaggioA('Denominazione e Partita IVA sono obbligatorie.', 'errore');\n                return;\n            }\n            var elenco = caricaAnagrafiche();\n            elenco.push(voce);\n            if (salvaAnagrafiche(elenco)) {\n                mostraMessaggioA('Anagrafica aggiunta: ' + voce.denom, 'successo');\n                renderizzaTabellaAnagrafiche();\n                aggiornaSelectAnagraficheInGenera();\n            }\n        });\n\n        \/\/ ---------- Generazione arricchita dal web ----------\n        async function recuperaComuneCasualeDalWeb() {\n            try {\n                if (!cacheComuniItaliani) {\n                    var risposta = await fetch(URL_COMUNI_ITALIANI);\n                    if (!risposta.ok) throw new Error('Risposta non valida dal servizio comuni');\n                    cacheComuniItaliani = await risposta.json();\n                }\n                var comune = cacheComuniItaliani[Math.floor(Math.random() * cacheComuniItaliani.length)];\n                var cap = (comune.cap && comune.cap.length) ? comune.cap[Math.floor(Math.random() * comune.cap.length)] : '00000';\n                return {\n                    comune: comune.nome,\n                    provincia: comune.sigla || (comune.provincia && comune.provincia.nome ? comune.provincia.nome.substring(0, 2).toUpperCase() : 'XX'),\n                    cap: cap\n                };\n            } catch (e) {\n                return null; \/\/ il chiamante ricadra' sull'elenco locale\n            }\n        }\n\n        async function recuperaCognomeCasualeDalWeb() {\n            try {\n                var risposta = await fetch(URL_COGNOME_CASUALE);\n                if (!risposta.ok) throw new Error('Risposta non valida dal servizio cognomi');\n                var dati = await risposta.json();\n                return dati.results[0].name.last;\n            } catch (e) {\n                return null;\n            }\n        }\n\n        document.getElementById('ga-btn-genera-web').addEventListener('click', async function () {\n            var statoEl = document.getElementById('ga-stato-generazione');\n            statoEl.textContent = 'Recupero dati dal web...';\n            var tipo = document.getElementById('ga-nuovo-tipo').value;\n\n            var comune = await recuperaComuneCasualeDalWeb();\n            var cognome = await recuperaCognomeCasualeDalWeb();\n\n            var usatoWeb = true;\n            if (!comune) {\n                comune = scegliCasuale(CITTA_FITTIZIE);\n                usatoWeb = false;\n            }\n            var denominazione;\n            if (cognome) {\n                denominazione = cognome + ' ' + scegliCasuale(SUFFISSI_AZIENDALI);\n            } else {\n                denominazione = scegliCasuale(DENOMINAZIONI_FITTIZIE);\n                usatoWeb = false;\n            }\n\n            document.getElementById('ga-tipo').value = tipo;\n            document.getElementById('ga-denom').value = denominazione;\n            document.getElementById('ga-piva').value = generaPartitaIvaFittizia();\n            document.getElementById('ga-cap').value = comune.cap;\n            document.getElementById('ga-comune').value = comune.comune;\n            document.getElementById('ga-provincia').value = comune.provincia;\n            document.getElementById('ga-indirizzo').value = 'Recupero indirizzo...';\n            document.getElementById('ga-indirizzo').value = await generaIndirizzoCasuale(comune.comune);\n\n            statoEl.textContent = usatoWeb\n                ? 'Dati recuperati dal web. Controlla i campi qui sotto e premi \"Aggiungi all\\'elenco\".'\n                : 'Il servizio web non ha risposto: usati i dati locali di riserva. Controlla i campi e premi \"Aggiungi all\\'elenco\".';\n        });\n\n        \/\/ ---------- Backup: esporta\/importa JSON ----------\n        document.getElementById('ga-btn-esporta').addEventListener('click', function () {\n            var elenco = caricaAnagrafiche();\n            var blob = new Blob([JSON.stringify(elenco, null, 2)], { type: 'application\/json' });\n            var url = URL.createObjectURL(blob);\n            var a = document.createElement('a');\n            a.href = url;\n            a.download = 'anagrafiche-fittizie-backup.json';\n            document.body.appendChild(a);\n            a.click();\n            document.body.removeChild(a);\n            URL.revokeObjectURL(url);\n        });\n\n        document.getElementById('ga-btn-importa-apri').addEventListener('click', function () {\n            document.getElementById('ga-file-importa').click();\n        });\n\n        document.getElementById('ga-file-importa').addEventListener('change', function (ev) {\n            var file = ev.target.files[0];\n            if (!file) return;\n            var lettore = new FileReader();\n            lettore.onload = function (e) {\n                try {\n                    var importati = JSON.parse(e.target.result);\n                    if (!Array.isArray(importati)) throw new Error('Il file non contiene un elenco valido.');\n                    var esistenti = caricaAnagrafiche();\n                    var idEsistenti = esistenti.map(function (v) { return v.id; });\n                    var aggiunti = 0;\n                    importati.forEach(function (voce) {\n                        if (!voce.id || idEsistenti.indexOf(voce.id) === -1) {\n                            if (!voce.id) voce.id = generaIdUnivoco();\n                            esistenti.push(voce);\n                            aggiunti++;\n                        }\n                    });\n                    salvaAnagrafiche(esistenti);\n                    renderizzaTabellaAnagrafiche();\n                    aggiornaSelectAnagraficheInGenera();\n                    mostraMessaggioA('Importazione completata: ' + aggiunti + ' nuove anagrafiche aggiunte (duplicati ignorati).', 'successo');\n                } catch (erroreGenerico) {\n                    mostraMessaggioA('File non valido: ' + erroreGenerico.message, 'errore');\n                }\n            };\n            lettore.readAsText(file, 'UTF-8');\n        });\n\n        \/\/ ================= MODULO: GENERA MASSIVA =================\n        \/\/ Porting fedele di generatore_fatture.dart e genera_xml.dart (progetto Dart).\n        \/\/ Riusa: formattaImporto, scaricaTesto (per il pattern, qui serve una variante blob)\n\n        \/\/ ---------- Calendario festivita' italiane (fisse + Pasqua\/Pasquetta) ----------\n        function calcolaPasqua(anno) {\n            \/\/ Algoritmo di Gauss (Meeus\/Jones\/Butcher) per la Pasqua gregoriana\n            var a = anno % 19, b = Math.floor(anno \/ 100), c = anno % 100;\n            var d = Math.floor(b \/ 4), e = b % 4, f = Math.floor((b + 8) \/ 25);\n            var g = Math.floor((b - f + 1) \/ 3);\n            var h = (19 * a + b - d - g + 15) % 30;\n            var i = Math.floor(c \/ 4), k = c % 4;\n            var l = (32 + 2 * e + 2 * i - h - k) % 7;\n            var m = Math.floor((a + 11 * h + 22 * l) \/ 451);\n            var mese = Math.floor((h + l - 7 * m + 114) \/ 31);\n            var giorno = ((h + l - 7 * m + 114) % 31) + 1;\n            return new Date(anno, mese - 1, giorno);\n        }\n\n        function festivitaItaliane(anno) {\n            var pasqua = calcolaPasqua(anno);\n            var pasquetta = new Date(pasqua.getFullYear(), pasqua.getMonth(), pasqua.getDate() + 1);\n            var fisse = [[1,1], [1,6], [4,25], [5,1], [6,2], [8,15], [11,1], [12,8], [12,25], [12,26]]\n                .map(function (mg) { return new Date(anno, mg[0] - 1, mg[1]); });\n            return fisse.concat([pasqua, pasquetta]);\n        }\n\n        function stessaData(a, b) {\n            return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();\n        }\n\n        \/\/ mese: 1-12. Restituisce un Date casuale nel mese che rispetta i vincoli richiesti\n        function giornoLavorativoCasualeInMese(anno, mese, escludiWeekend, escludiFestivi) {\n            var ultimoGiorno = new Date(anno, mese, 0).getDate();\n            var festivita = escludiFestivi ? festivitaItaliane(anno) : [];\n            var candidati = [];\n            for (var g = 1; g <= ultimoGiorno; g++) {\n                var data = new Date(anno, mese - 1, g);\n                var weekday = data.getDay();\n                if (escludiWeekend && (weekday === 0 || weekday === 6)) continue;\n                if (escludiFestivi && festivita.some(function (f) { return stessaData(f, data); })) continue;\n                candidati.push(data);\n            }\n            if (candidati.length === 0) return new Date(anno, mese - 1, 1); \/\/ fallback estremo\n            return candidati[Math.floor(Math.random() * candidati.length)];\n        }\n\n        function ultimoGiornoDelMeseData(anno, mese) {\n            return new Date(anno, mese, 0);\n        }\n\n        function calcolaScadenzaData(dataEmissione, giorni) {\n            var d = new Date(dataEmissione.getFullYear(), dataEmissione.getMonth(), dataEmissione.getDate());\n            d.setDate(d.getDate() + giorni);\n            return d;\n        }\n\n        \/\/ Aggiunge mesi interi a una data secondo la regola del calendario commerciale:\n        \/\/ se la data di partenza e' l'ultimo giorno del suo mese, il risultato e' l'ultimo\n        \/\/ giorno del mese di arrivo (es. 30 settembre + 4 mesi = 31 gennaio, non 30 gennaio);\n        \/\/ altrimenti mantiene lo stesso numero di giorno, troncato se il mese di arrivo e' piu' corto.\n        function aggiungiMesiConEOM(data, mesi) {\n            var ultimoGiornoMesePartenza = new Date(data.getFullYear(), data.getMonth() + 1, 0).getDate();\n            var eraFineMese = data.getDate() === ultimoGiornoMesePartenza;\n            var meseTotale = data.getMonth() + mesi;\n            var nuovoAnno = data.getFullYear() + Math.floor(meseTotale \/ 12);\n            var nuovoMese = ((meseTotale % 12) + 12) % 12;\n            var ultimoGiornoMeseArrivo = new Date(nuovoAnno, nuovoMese + 1, 0).getDate();\n            var giorno = eraFineMese ? ultimoGiornoMeseArrivo : Math.min(data.getDate(), ultimoGiornoMeseArrivo);\n            return new Date(nuovoAnno, nuovoMese, giorno);\n        }\n\n        \/\/ Sposta una data al primo giorno non festivo\/non festivo utile (sabato, domenica\n        \/\/ e festivita' italiane escluse), avanzando di un giorno alla volta\n        function rollaAGiornoLavorativoUtile(data) {\n            var d = new Date(data.getFullYear(), data.getMonth(), data.getDate());\n            var tentativi = 0;\n            while (tentativi < 30) {\n                var giornoSettimana = d.getDay();\n                var festivitaAnnoCorrente = festivitaItaliane(d.getFullYear());\n                var isFestivo = festivitaAnnoCorrente.some(function (f) { return stessaData(f, d); });\n                if (giornoSettimana !== 0 && giornoSettimana !== 6 && !isFestivo) break;\n                d.setDate(d.getDate() + 1);\n                tentativi++;\n            }\n            return d;\n        }\n\n        \/\/ Calcola la data di scadenza secondo la modalita' scelta per la riga\n        \/\/ (vista fattura \/ fine mese \/ a tot giorni con base data-fattura o fine-mese),\n        \/\/ poi applica sempre il rinvio al primo giorno lavorativo utile\n        function calcolaDataScadenzaCompleta(scadenzaParam, dataEmissione, anno, mese) {\n            var dataGrezza;\n            if (scadenzaParam.modalita === 'vista') {\n                dataGrezza = new Date(dataEmissione.getFullYear(), dataEmissione.getMonth(), dataEmissione.getDate());\n            } else if (scadenzaParam.modalita === 'finemese') {\n                dataGrezza = ultimoGiornoDelMeseData(anno, mese);\n            } else { \/\/ 'giorni'\n                var giorni = scadenzaParam.giorni || 0;\n                if (scadenzaParam.base === 'finemese') {\n                    var baseFineMese = ultimoGiornoDelMeseData(anno, mese);\n                    var mesiInteri = Math.floor(giorni \/ 30);\n                    var giorniResidui = giorni % 30;\n                    dataGrezza = aggiungiMesiConEOM(baseFineMese, mesiInteri);\n                    if (giorniResidui > 0) dataGrezza.setDate(dataGrezza.getDate() + giorniResidui);\n                } else {\n                    dataGrezza = calcolaScadenzaData(dataEmissione, giorni);\n                }\n            }\n            return rollaAGiornoLavorativoUtile(dataGrezza);\n        }\n\n        function formattaDataISO(data) {\n            var anno = data.getFullYear().toString().padStart(4, '0');\n            var mese = (data.getMonth() + 1).toString().padStart(2, '0');\n            var giorno = data.getDate().toString().padStart(2, '0');\n            return anno + '-' + mese + '-' + giorno;\n        }\n\n\n        \/\/ ---------- Motore di generazione (porting di GeneratoreFatture.generaSetFatture) ----------\n        var TIPO_DOC_NOTA_CREDITO = 'TD04';\n\n        function arrotondaACentesimi(valore) { return Math.round(valore * 100) \/ 100; }\n\n        function distribuisciImportoMassivo(importoTotale, numeroVoci) {\n            var pesi = [];\n            for (var i = 0; i < numeroVoci; i++) pesi.push(Math.random() + 0.1);\n            var sommaPesi = pesi.reduce(function (s, p) { return s + p; }, 0);\n            var importi = new Array(numeroVoci).fill(0);\n            var sommaParziale = 0;\n            for (var i = 0; i < numeroVoci - 1; i++) {\n                var imp = arrotondaACentesimi(importoTotale * pesi[i] \/ sommaPesi);\n                importi[i] = imp;\n                sommaParziale += imp;\n            }\n            importi[numeroVoci - 1] = arrotondaACentesimi(importoTotale - sommaParziale);\n            return importi;\n        }\n\n        function espandiMixTipologie(mix, numeroRichiesto) {\n            var totaleMix = mix.reduce(function (s, m) { return s + m.numeroDocumenti; }, 0);\n            if (totaleMix === 0) return new Array(numeroRichiesto).fill('TD01');\n            var elenco = [];\n            mix.forEach(function (m) { for (var i = 0; i < m.numeroDocumenti; i++) elenco.push(m.tipoDocumento); });\n            var risultato = [];\n            for (var i = 0; i < numeroRichiesto; i++) risultato.push(elenco[i % totaleMix]);\n            return risultato;\n        }\n\n        function espandiScadenze(scadenze, numeroRichiesto) {\n            var totale = scadenze.reduce(function (s, sc) { return s + sc.numeroDocumenti; }, 0);\n            if (totale === 0) return new Array(numeroRichiesto).fill({ modalita: 'giorni', giorni: 30, base: 'finemese' });\n            var elenco = [];\n            scadenze.forEach(function (sc) { for (var i = 0; i < sc.numeroDocumenti; i++) elenco.push(sc); });\n            var risultato = [];\n            for (var i = 0; i < numeroRichiesto; i++) risultato.push(elenco[i % totale]);\n            return risultato;\n        }\n\n        function mescolaIndici(numeroElementi) {\n            var indici = [];\n            for (var i = 0; i < numeroElementi; i++) indici.push(i);\n            for (var i = numeroElementi - 1; i > 0; i--) {\n                var j = Math.floor(Math.random() * (i + 1));\n                var tmp = indici[i]; indici[i] = indici[j]; indici[j] = tmp;\n            }\n            return indici;\n        }\n\n        function generaSetFatture(parametri) {\n            var controparti = parametri.controparti;\n            if (!controparti || controparti.length === 0) {\n                throw new Error('Nessuna controparte (cliente\/fornitore) selezionata per la generazione.');\n            }\n\n            var numeroFatture = parametri.mixTipologie.reduce(function (s, m) { return s + m.numeroDocumenti; }, 0);\n            if (numeroFatture === 0) return [];\n\n            var tipiEspansi = espandiMixTipologie(parametri.mixTipologie, numeroFatture);\n            var scadenzeEspanse = espandiScadenze(parametri.distribuzioneScadenze, numeroFatture);\n            var indiciScadenzeMescolati = mescolaIndici(numeroFatture);\n\n            var numNoteCredito = tipiEspansi.filter(function (t) { return t === TIPO_DOC_NOTA_CREDITO; }).length;\n            var numPositivi = numeroFatture - numNoteCredito;\n\n            var importoNoteCredito, importoPositiviTotale;\n            if (numPositivi === 0) {\n                importoNoteCredito = Math.abs(parametri.imponibileTarget);\n                importoPositiviTotale = 0;\n            } else {\n                if (numNoteCredito > 0) {\n                    var quota = 0.05 + Math.random() * 0.15;\n                    importoNoteCredito = arrotondaACentesimi(parametri.imponibileTarget * quota);\n                } else {\n                    importoNoteCredito = 0;\n                }\n                importoPositiviTotale = parametri.imponibileTarget + importoNoteCredito;\n            }\n\n            var importiPositivi = numPositivi > 0 ? distribuisciImportoMassivo(importoPositiviTotale, numPositivi) : [];\n            var importiNoteCredito = numNoteCredito > 0 ? distribuisciImportoMassivo(importoNoteCredito, numNoteCredito) : [];\n\n            var risultato = [];\n            var cursorePositivi = 0, cursoreNoteCredito = 0;\n\n            for (var i = 0; i < numeroFatture; i++) {\n                var tipo = tipiEspansi[i];\n                var importo;\n                if (tipo === TIPO_DOC_NOTA_CREDITO) {\n                    importo = importiNoteCredito[cursoreNoteCredito]; cursoreNoteCredito++;\n                } else {\n                    importo = importiPositivi[cursorePositivi]; cursorePositivi++;\n                }\n\n                var dataEmissione = giornoLavorativoCasualeInMese(\n                    parametri.anno, parametri.mese, parametri.escludiWeekend, parametri.escludiFestivi\n                );\n\n                var scadenzaParam = scadenzeEspanse[indiciScadenzeMescolati[i]];\n                var dataScadenza = calcolaDataScadenzaCompleta(\n                    scadenzaParam, dataEmissione, parametri.anno, parametri.mese\n                );\n\n                var controparte = controparti[i % controparti.length];\n\n                risultato.push({\n                    tipoDocumento: tipo,\n                    dataEmissione: dataEmissione,\n                    dataScadenza: dataScadenza,\n                    importo: importo,\n                    partitaIvaControparte: controparte.piva,\n                    nomeControparte: controparte.denom,\n                    indirizzoControparte: controparte.indirizzo,\n                    capControparte: controparte.cap,\n                    comuneControparte: controparte.comune,\n                    provinciaControparte: controparte.provincia\n                });\n            }\n            return risultato;\n        }\n\n        function verificaImponibileNetto(righe) {\n            var totale = 0;\n            righe.forEach(function (r) {\n                totale += (r.tipoDocumento === TIPO_DOC_NOTA_CREDITO) ? -r.importo : r.importo;\n            });\n            return totale;\n        }\n\n        \/\/ ---------- Costruzione XML per riga generata (porting di GeneraXml) ----------\n        function separaPrefissoECodice(piva) {\n            var t = (piva || '').trim();\n            if (t.length >= 2 && \/^[A-Za-z]{2}$\/.test(t.substring(0, 2))) {\n                return [t.substring(0, 2).toUpperCase(), t.substring(2)];\n            }\n            return ['IT', t];\n        }\n\n        function descrizioneRigaGenerica(tipoDocumento) {\n            if (tipoDocumento === 'TD04') return 'Nota di credito relativa a fattura precedente';\n            if (tipoDocumento === 'TD21') return 'Autofattura per splafonamento';\n            return 'Prestazione di servizi\/cessione di beni';\n        }\n\n        function pulisciPerNomeFile(testo) {\n            return (testo || '').replace(\/[\\\\\/:*?\"<>|]\/g, '_');\n        }\n\n        function nomeFileFatturaPAMassivo(partitaIvaPropria, numeroProgressivo, numeroDocumentoOriginale) {\n            var progressivo = numeroProgressivo.toString().padStart(5, '0');\n            var pivaPulita = pulisciPerNomeFile(partitaIvaPropria);\n            if (numeroDocumentoOriginale && numeroDocumentoOriginale.trim()) {\n                return pivaPulita + '_' + pulisciPerNomeFile(numeroDocumentoOriginale.trim()) + '_' + progressivo + '.xml';\n            }\n            return pivaPulita + '_' + progressivo + '.xml';\n        }\n\n        \/\/ Indirizzo di riserva usato quando la controparte non ha un indirizzo salvato\n        \/\/ (stesso placeholder del progetto Dart originale)\n        var SEDE_PLACEHOLDER = { indirizzo: 'Via Roma 1', cap: '00100', comune: 'Roma', provincia: 'RM' };\n\n        function costruisciXmlMassivo(riga, numeroDocumento, partitaIvaPropria, denominazionePropria, tipoAnagrafica, aliquotaIvaPercento) {\n            var aliquotaIva = aliquotaIvaPercento \/ 100;\n            var imposta = arrotondaACentesimi(riga.importo * aliquotaIva);\n            var importoTotale = riga.importo + imposta;\n            var esente = aliquotaIva === 0;\n            var aliquotaFormattata = (aliquotaIva * 100).toFixed(2);\n\n            var prefissoPropria_codicePropria = separaPrefissoECodice(partitaIvaPropria);\n            var prefissoContro_codiceContro = separaPrefissoECodice(riga.partitaIvaControparte);\n            var prefissoPropria = prefissoPropria_codicePropria[0], codicePropria = prefissoPropria_codicePropria[1];\n            var prefissoContro = prefissoContro_codiceContro[0], codiceContro = prefissoContro_codiceContro[1];\n\n            var sedeContro = (riga.indirizzoControparte)\n                ? { indirizzo: riga.indirizzoControparte, cap: riga.capControparte, comune: riga.comuneControparte, provincia: riga.provinciaControparte }\n                : SEDE_PLACEHOLDER;\n\n            var prefissoCedente, codiceCedente, denomCedente, sedeCedente;\n            var prefissoCessionario, codiceCessionario, denomCessionario, sedeCessionario;\n\n            if (tipoAnagrafica === 'C') {\n                prefissoCedente = prefissoPropria; codiceCedente = codicePropria; denomCedente = denominazionePropria; sedeCedente = SEDE_PLACEHOLDER;\n                prefissoCessionario = prefissoContro; codiceCessionario = codiceContro; denomCessionario = riga.nomeControparte; sedeCessionario = sedeContro;\n            } else {\n                prefissoCedente = prefissoContro; codiceCedente = codiceContro; denomCedente = riga.nomeControparte; sedeCedente = sedeContro;\n                prefissoCessionario = prefissoPropria; codiceCessionario = codicePropria; denomCessionario = denominazionePropria; sedeCessionario = SEDE_PLACEHOLDER;\n            }\n\n            var numeroFattura = numeroDocumento.toString();\n\n            var xml = '<' + '?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n' +\n                '<p:FatturaElettronica xmlns:p=\"http:\/\/ivaservizi.agenziaentrate.gov.it\/docs\/xsd\/fatture\/v1.2\" versione=\"FPR12\" xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\">\\n' +\n                ' <FatturaElettronicaHeader>\\n' +\n                '  <DatiTrasmissione>\\n' +\n                '   <IdTrasmittente>\\n' +\n                '    <IdPaese>' + escapeXml(prefissoPropria) + '<\/IdPaese>\\n' +\n                '    <IdCodice>' + escapeXml(codicePropria) + '<\/IdCodice>\\n' +\n                '   <\/IdTrasmittente>\\n' +\n                '   <ProgressivoInvio>' + escapeXml(numeroFattura) + '<\/ProgressivoInvio>\\n' +\n                '   <FormatoTrasmissione>FPR12<\/FormatoTrasmissione>\\n' +\n                '   <CodiceDestinatario>0000000<\/CodiceDestinatario>\\n' +\n                '  <\/DatiTrasmissione>\\n' +\n                '  <CedentePrestatore>\\n' +\n                '   <DatiAnagrafici>\\n' +\n                '    <IdFiscaleIVA>\\n' +\n                '     <IdPaese>' + escapeXml(prefissoCedente) + '<\/IdPaese>\\n' +\n                '     <IdCodice>' + escapeXml(codiceCedente) + '<\/IdCodice>\\n' +\n                '    <\/IdFiscaleIVA>\\n' +\n                '    <Anagrafica>\\n' +\n                '     <Denominazione>' + escapeXml(denomCedente) + '<\/Denominazione>\\n' +\n                '    <\/Anagrafica>\\n' +\n                '    <RegimeFiscale>RF01<\/RegimeFiscale>\\n' +\n                '   <\/DatiAnagrafici>\\n' +\n                '   <Sede>\\n' +\n                '    <Indirizzo>' + escapeXml(sedeCedente.indirizzo) + '<\/Indirizzo>\\n' +\n                '    <CAP>' + escapeXml(sedeCedente.cap) + '<\/CAP>\\n' +\n                '    <Comune>' + escapeXml(sedeCedente.comune) + '<\/Comune>\\n' +\n                '    <Provincia>' + escapeXml(sedeCedente.provincia) + '<\/Provincia>\\n' +\n                '    <Nazione>' + escapeXml(prefissoCedente) + '<\/Nazione>\\n' +\n                '   <\/Sede>\\n' +\n                '  <\/CedentePrestatore>\\n' +\n                '  <CessionarioCommittente>\\n' +\n                '   <DatiAnagrafici>\\n' +\n                '    <IdFiscaleIVA>\\n' +\n                '     <IdPaese>' + escapeXml(prefissoCessionario) + '<\/IdPaese>\\n' +\n                '     <IdCodice>' + escapeXml(codiceCessionario) + '<\/IdCodice>\\n' +\n                '    <\/IdFiscaleIVA>\\n' +\n                '    <Anagrafica>\\n' +\n                '     <Denominazione>' + escapeXml(denomCessionario) + '<\/Denominazione>\\n' +\n                '    <\/Anagrafica>\\n' +\n                '   <\/DatiAnagrafici>\\n' +\n                '   <Sede>\\n' +\n                '    <Indirizzo>' + escapeXml(sedeCessionario.indirizzo) + '<\/Indirizzo>\\n' +\n                '    <CAP>' + escapeXml(sedeCessionario.cap) + '<\/CAP>\\n' +\n                '    <Comune>' + escapeXml(sedeCessionario.comune) + '<\/Comune>\\n' +\n                '    <Provincia>' + escapeXml(sedeCessionario.provincia) + '<\/Provincia>\\n' +\n                '    <Nazione>' + escapeXml(prefissoCessionario) + '<\/Nazione>\\n' +\n                '   <\/Sede>\\n' +\n                '  <\/CessionarioCommittente>\\n' +\n                ' <\/FatturaElettronicaHeader>\\n' +\n                ' <FatturaElettronicaBody>\\n' +\n                '  <DatiGenerali>\\n' +\n                '   <DatiGeneraliDocumento>\\n' +\n                '    <TipoDocumento>' + escapeXml(riga.tipoDocumento) + '<\/TipoDocumento>\\n' +\n                '    <Divisa>EUR<\/Divisa>\\n' +\n                '    <Data>' + formattaDataISO(riga.dataEmissione) + '<\/Data>\\n' +\n                '    <Numero>' + escapeXml(numeroFattura) + '<\/Numero>\\n' +\n                '    <ImportoTotaleDocumento>' + formattaImporto(importoTotale) + '<\/ImportoTotaleDocumento>\\n' +\n                '   <\/DatiGeneraliDocumento>\\n' +\n                '  <\/DatiGenerali>\\n' +\n                '  <DatiBeniServizi>\\n' +\n                '   <DettaglioLinee>\\n' +\n                '    <NumeroLinea>1<\/NumeroLinea>\\n' +\n                '    <Descrizione>' + escapeXml(descrizioneRigaGenerica(riga.tipoDocumento)) + '<\/Descrizione>\\n' +\n                '    <Quantita>1.00<\/Quantita>\\n' +\n                '    <PrezzoUnitario>' + formattaImporto(riga.importo) + '<\/PrezzoUnitario>\\n' +\n                '    <PrezzoTotale>' + formattaImporto(riga.importo) + '<\/PrezzoTotale>\\n' +\n                '    <AliquotaIVA>' + aliquotaFormattata + '<\/AliquotaIVA>\\n' +\n                (esente ? '    <Natura>N1<\/Natura>\\n' : '') +\n                '   <\/DettaglioLinee>\\n' +\n                '   <DatiRiepilogo>\\n' +\n                '    <AliquotaIVA>' + aliquotaFormattata + '<\/AliquotaIVA>\\n' +\n                (esente ? '    <Natura>N1<\/Natura>\\n' : '') +\n                '    <ImponibileImporto>' + formattaImporto(riga.importo) + '<\/ImponibileImporto>\\n' +\n                '    <Imposta>' + formattaImporto(imposta) + '<\/Imposta>\\n' +\n                '    <EsigibilitaIVA>I<\/EsigibilitaIVA>\\n' +\n                (esente ? '    <RiferimentoNormativo>Operazione non imponibile - documento didattico<\/RiferimentoNormativo>\\n' : '') +\n                '   <\/DatiRiepilogo>\\n' +\n                '  <\/DatiBeniServizi>\\n' +\n                '  <DatiPagamento>\\n' +\n                '   <CondizioniPagamento>TP02<\/CondizioniPagamento>\\n' +\n                '   <DettaglioPagamento>\\n' +\n                '    <ModalitaPagamento>MP05<\/ModalitaPagamento>\\n' +\n                '    <DataScadenzaPagamento>' + formattaDataISO(riga.dataScadenza) + '<\/DataScadenzaPagamento>\\n' +\n                '    <ImportoPagamento>' + formattaImporto(importoTotale) + '<\/ImportoPagamento>\\n' +\n                '   <\/DettaglioPagamento>\\n' +\n                '  <\/DatiPagamento>\\n' +\n                ' <\/FatturaElettronicaBody>\\n' +\n                '<\/p:FatturaElettronica>\\n';\n\n            return { xml: xml, nomeFile: nomeFileFatturaPAMassivo(partitaIvaPropria, numeroDocumento, numeroFattura) };\n        }\n\n        \/\/ ---------- Gestione righe dinamiche: Mix tipologie ----------\n        var righeMix = []; \/\/ { id, tipoDocumento, numeroDocumenti }\n        var righeScadenze = []; \/\/ { id, modalita: 'vista'|'finemese'|'giorni', giorni, base: 'finemese'|'fattura', numeroDocumenti }\n\n        function rendMix() {\n            var contenitore = document.getElementById('gmx-mix-lista');\n            contenitore.innerHTML = '';\n            righeMix.forEach(function (riga) {\n                var div = document.createElement('div');\n                div.className = 'gf-griglia';\n                div.style.marginBottom = '6px';\n                div.innerHTML =\n                    '<label>Tipo documento<select data-campo=\"tipoDocumento\"><\/select><\/label>' +\n                    '<label>Numero documenti<input type=\"number\" min=\"0\" data-campo=\"numeroDocumenti\" value=\"' + riga.numeroDocumenti + '\"><\/label>' +\n                    '<label>&nbsp;<button type=\"button\" class=\"gf-btn gmx-rimuovi-mix\">Rimuovi<\/button><\/label>';\n                var selectTipo = div.querySelector('[data-campo=\"tipoDocumento\"]');\n                popolaSelectTipoDocumento(selectTipo, riga.tipoDocumento);\n                selectTipo.addEventListener('change', function () { riga.tipoDocumento = this.value; });\n                div.querySelector('[data-campo=\"numeroDocumenti\"]').addEventListener('input', function () { riga.numeroDocumenti = parseInt(this.value, 10) || 0; });\n                div.querySelector('.gmx-rimuovi-mix').addEventListener('click', function () {\n                    righeMix = righeMix.filter(function (r) { return r.id !== riga.id; });\n                    rendMix();\n                });\n                contenitore.appendChild(div);\n            });\n            document.getElementById('gmx-mix-vuoto').style.display = righeMix.length ? 'none' : 'block';\n        }\n\n        function rendScadenze() {\n            var contenitore = document.getElementById('gmx-scad-lista');\n            contenitore.innerHTML = '';\n            righeScadenze.forEach(function (riga) {\n                var div = document.createElement('div');\n                div.className = 'gf-griglia';\n                div.style.marginBottom = '6px';\n                var mostraGiorni = riga.modalita === 'giorni';\n                div.innerHTML =\n                    '<label>Tipologia<select data-campo=\"modalita\">' +\n                        '<option value=\"vista\"' + (riga.modalita === 'vista' ? ' selected' : '') + '>Vista fattura (scadenza = emissione)<\/option>' +\n                        '<option value=\"finemese\"' + (riga.modalita === 'finemese' ? ' selected' : '') + '>Fine mese<\/option>' +\n                        '<option value=\"giorni\"' + (riga.modalita === 'giorni' ? ' selected' : '') + '>A tot giorni<\/option>' +\n                    '<\/select><\/label>' +\n                    (mostraGiorni ?\n                        '<label>Giorni<input type=\"number\" min=\"0\" list=\"gmx-giorni-comuni\" data-campo=\"giorni\" value=\"' + riga.giorni + '\"><\/label>' +\n                        '<label>Calcolo<select data-campo=\"base\">' +\n                            '<option value=\"finemese\"' + (riga.base === 'finemese' ? ' selected' : '') + '>Calendario commerciale (fine mese)<\/option>' +\n                            '<option value=\"fattura\"' + (riga.base === 'fattura' ? ' selected' : '') + '>Data fattura (giorni esatti)<\/option>' +\n                        '<\/select><\/label>'\n                        : '') +\n                    '<label>Numero documenti<input type=\"number\" min=\"0\" data-campo=\"numeroDocumenti\" value=\"' + riga.numeroDocumenti + '\"><\/label>' +\n                    '<label>&nbsp;<button type=\"button\" class=\"gf-btn gmx-rimuovi-scad\">Rimuovi<\/button><\/label>';\n\n                div.querySelector('[data-campo=\"modalita\"]').addEventListener('change', function () {\n                    riga.modalita = this.value;\n                    rendScadenze(); \/\/ ri-renderizza per mostrare\/nascondere i campi giorni\/calcolo\n                });\n                if (mostraGiorni) {\n                    div.querySelector('[data-campo=\"giorni\"]').addEventListener('input', function () { riga.giorni = parseInt(this.value, 10) || 0; });\n                    div.querySelector('[data-campo=\"base\"]').addEventListener('change', function () { riga.base = this.value; });\n                }\n                div.querySelector('[data-campo=\"numeroDocumenti\"]').addEventListener('input', function () { riga.numeroDocumenti = parseInt(this.value, 10) || 0; });\n                div.querySelector('.gmx-rimuovi-scad').addEventListener('click', function () {\n                    righeScadenze = righeScadenze.filter(function (r) { return r.id !== riga.id; });\n                    rendScadenze();\n                });\n                contenitore.appendChild(div);\n            });\n            document.getElementById('gmx-scad-vuoto').style.display = righeScadenze.length ? 'none' : 'block';\n        }\n\n        document.getElementById('gmx-btn-aggiungi-mix').addEventListener('click', function () {\n            righeMix.push({ id: generaIdUnivoco(), tipoDocumento: 'TD01', numeroDocumenti: 1 });\n            rendMix();\n        });\n        document.getElementById('gmx-btn-aggiungi-scad').addEventListener('click', function () {\n            righeScadenze.push({ id: generaIdUnivoco(), modalita: 'giorni', giorni: 30, base: 'finemese', numeroDocumenti: 1 });\n            rendScadenze();\n        });\n\n        \/\/ ---------- Selezione controparti dall'elenco Anagrafiche ----------\n        function aggiornaListaContropartiMassiva() {\n            var elenco = caricaAnagrafiche();\n            var contenitore = document.getElementById('gmx-controparti-lista');\n            if (elenco.length === 0) {\n                contenitore.innerHTML = 'Nessuna anagrafica salvata: vai nella scheda Anagrafiche per aggiungerne.';\n                return;\n            }\n            contenitore.innerHTML = '';\n            elenco.forEach(function (voce) {\n                var label = document.createElement('label');\n                label.style.display = 'block';\n                label.innerHTML = '<input type=\"checkbox\" class=\"gmx-controparte-check\" value=\"' + voce.id + '\"> ' +\n                    escapeXml(voce.tipo) + ' - ' + escapeXml(voce.denom) + ' (' + escapeXml(voce.comune) + ')';\n                contenitore.appendChild(label);\n            });\n        }\n        document.getElementById('gmx-btn-aggiorna-controparti').addEventListener('click', aggiornaListaContropartiMassiva);\n\n        \/\/ ---------- Genera anteprima ----------\n        var ultimoSetGenerato = null;\n\n        document.getElementById('gmx-btn-genera-anteprima').addEventListener('click', function () {\n            try {\n                var idSelezionati = Array.from(document.querySelectorAll('.gmx-controparte-check:checked')).map(function (c) { return c.value; });\n                var controparti = caricaAnagrafiche().filter(function (v) { return idSelezionati.indexOf(v.id) !== -1; });\n\n                var parametri = {\n                    anno: parseInt(document.getElementById('gmx-anno').value, 10),\n                    mese: parseInt(document.getElementById('gmx-mese').value, 10),\n                    imponibileTarget: parseFloat(document.getElementById('gmx-imponibile').value) || 0,\n                    mixTipologie: righeMix.filter(function (r) { return r.numeroDocumenti > 0; }),\n                    distribuzioneScadenze: righeScadenze.filter(function (r) { return r.numeroDocumenti > 0; }),\n                    controparti: controparti,\n                    escludiWeekend: document.getElementById('gmx-escludi-weekend').checked,\n                    escludiFestivi: document.getElementById('gmx-escludi-festivi').checked\n                };\n\n                if (controparti.length === 0) {\n                    mostraMessaggioGmx('Seleziona almeno una controparte dall\\'elenco Anagrafiche.', 'errore');\n                    return;\n                }\n                if (parametri.mixTipologie.length === 0) {\n                    mostraMessaggioGmx('Aggiungi almeno una riga al Mix tipologie documento.', 'errore');\n                    return;\n                }\n                if (parametri.distribuzioneScadenze.length === 0) {\n                    mostraMessaggioGmx('Aggiungi almeno una riga alla Distribuzione scadenze pagamento.', 'errore');\n                    return;\n                }\n\n                var righe = generaSetFatture(parametri);\n                ultimoSetGenerato = {\n                    righe: righe,\n                    partitaIvaPropria: document.getElementById('gmx-propria-piva').value.trim(),\n                    denominazionePropria: document.getElementById('gmx-propria-denom').value.trim(),\n                    tipoAnagrafica: document.querySelector('input[name=\"gmx-tipo-anagrafica\"]:checked').value,\n                    aliquotaIva: parseFloat(document.getElementById('gmx-aliquota').value) || 22\n                };\n\n                var corpo = document.getElementById('gmx-anteprima-body');\n                corpo.innerHTML = '';\n                righe.forEach(function (r, indice) {\n                    var tr = document.createElement('tr');\n                    tr.innerHTML =\n                        '<td>' + (indice + 1) + '<\/td>' +\n                        '<td>' + escapeXml(r.tipoDocumento) + '<\/td>' +\n                        '<td>' + formattaDataISO(r.dataEmissione) + '<\/td>' +\n                        '<td>' + formattaDataISO(r.dataScadenza) + '<\/td>' +\n                        '<td>' + (r.tipoDocumento === TIPO_DOC_NOTA_CREDITO ? '-' : '') + formattaImporto(r.importo) + ' EUR<\/td>' +\n                        '<td>' + escapeXml(r.nomeControparte) + '<\/td>';\n                    corpo.appendChild(tr);\n                });\n\n                var nettoGenerato = verificaImponibileNetto(righe);\n                document.getElementById('gmx-verifica-totale').textContent =\n                    'Documenti generati: ' + righe.length + ' - Imponibile netto ottenuto: ' + formattaImporto(nettoGenerato) +\n                    ' EUR (target: ' + formattaImporto(parametri.imponibileTarget) + ' EUR)';\n\n                document.getElementById('gmx-riquadro-anteprima').style.display = 'block';\n                mostraMessaggioGmx('Set generato. Controlla l\\'anteprima prima di scaricare lo ZIP.', 'successo');\n            } catch (erroreGenerico) {\n                mostraMessaggioGmx(erroreGenerico.message, 'errore');\n            }\n        });\n\n        function mostraMessaggioGmx(testo, tipo) {\n            var el = document.getElementById('gmx-messaggio');\n            el.textContent = testo;\n            el.className = 'gf-messaggio ' + (tipo || '');\n        }\n\n        \/\/ ---------- Download ZIP ----------\n        function scaricaBlob(nomeFile, blob) {\n            var url = URL.createObjectURL(blob);\n            var a = document.createElement('a');\n            a.href = url;\n            a.download = nomeFile;\n            document.body.appendChild(a);\n            a.click();\n            document.body.removeChild(a);\n            URL.revokeObjectURL(url);\n        }\n\n        document.getElementById('gmx-btn-scarica-zip').addEventListener('click', async function () {\n            if (!ultimoSetGenerato || !ultimoSetGenerato.righe.length) {\n                mostraMessaggioGmx('Genera prima l\\'anteprima del set.', 'errore');\n                return;\n            }\n            if (!window.JSZip) {\n                mostraMessaggioGmx('La libreria ZIP non si e\\' caricata (verifica la connessione).', 'errore');\n                return;\n            }\n            if (!ultimoSetGenerato.partitaIvaPropria || !ultimoSetGenerato.denominazionePropria) {\n                mostraMessaggioGmx('Inserisci Denominazione e Partita IVA dei dati aziendali propri.', 'errore');\n                return;\n            }\n\n            var zip = new JSZip();\n            ultimoSetGenerato.righe.forEach(function (riga, indice) {\n                var risultato = costruisciXmlMassivo(\n                    riga, indice + 1,\n                    ultimoSetGenerato.partitaIvaPropria,\n                    ultimoSetGenerato.denominazionePropria,\n                    ultimoSetGenerato.tipoAnagrafica,\n                    ultimoSetGenerato.aliquotaIva\n                );\n                zip.file(risultato.nomeFile, risultato.xml);\n            });\n\n            mostraMessaggioGmx('Preparazione ZIP in corso...', '');\n            var blob = await zip.generateAsync({ type: 'blob' });\n            scaricaBlob('fatture_generate_' + ultimoSetGenerato.righe.length + '.zip', blob);\n            mostraMessaggioGmx('ZIP scaricato con ' + ultimoSetGenerato.righe.length + ' fatture.', 'successo');\n        });\n\n        \/\/ ================= MODULO: SOSTITUISCI PIVA (MASSIVO) =================\n        \/\/ Sostituzione testuale diretta sul contenuto XML grezzo: nessun\n        \/\/ parsing\/ricostruzione, cosi' tutto il resto del file (formattazione,\n        \/\/ ordine tag, spazi) resta ESATTAMENTE identico - cambia solo il\n        \/\/ testo della Partita IVA indicata, ovunque compaia nel file.\n\n        function escapeRegExp(testo) {\n            return testo.replace(\/[.*+?^${}()|[\\]\\\\]\/g, '\\\\$&');\n        }\n\n        \/\/ Rileva IN QUALE TAG compare il valore cercato: <IdCodice> (la\n        \/\/ Partita IVA vera e propria) e\/o <CodiceFiscale> (fatture scansionate\n        \/\/ senza PIVA compilata, dove l'identificativo e' lo stesso valore ma\n        \/\/ nel campo Codice Fiscale). La sostituzione avviene comunque ovunque\n        \/\/ nel file: questa funzione serve solo a dare visibilita' sul dove,\n        \/\/ non cambia cosa viene sostituito.\n        function analizzaContestoOccorrenze(testoOriginale, valoreCercato, ignoraMaiuscole) {\n            var flag = ignoraMaiuscole ? 'i' : '';\n            var valoreEscapato = escapeRegExp(valoreCercato);\n            var regexPiva = new RegExp('<IdCodice>\\\\s*' + valoreEscapato + '\\\\s*<\/IdCodice>', flag);\n            var regexCodiceFiscale = new RegExp('<CodiceFiscale>\\\\s*' + valoreEscapato + '\\\\s*<\/CodiceFiscale>', flag);\n            return {\n                inPiva: regexPiva.test(testoOriginale),\n                inCodiceFiscale: regexCodiceFiscale.test(testoOriginale)\n            };\n        }\n\n        function etichettaContesto(contesto, occorrenze) {\n            if (occorrenze === 0) return '-';\n            var etichette = [];\n            if (contesto.inPiva) etichette.push('Partita IVA');\n            if (contesto.inCodiceFiscale) etichette.push('Codice Fiscale');\n            if (etichette.length === 0) return 'Altro punto del file';\n            return etichette.join(' + ');\n        }\n\n        function mostraMessaggioGsp(testo, tipo) {\n            var el = document.getElementById('gsp-messaggio');\n            el.textContent = testo;\n            el.className = 'gf-messaggio ' + (tipo || '');\n        }\n\n        var risultatiSostituzionePiva = []; \/\/ { nomeFile, contenuto, occorrenze }\n\n        function leggiFileComeTesto(file) {\n            return new Promise(function (resolve, reject) {\n                var lettore = new FileReader();\n                lettore.onload = function (e) { resolve(e.target.result); };\n                lettore.onerror = function () { reject(new Error('Errore di lettura file')); };\n                lettore.readAsText(file, 'UTF-8');\n            });\n        }\n\n        document.getElementById('gsp-btn-elabora').addEventListener('click', async function () {\n            var pivaDa = document.getElementById('gsp-piva-da').value.trim();\n            var pivaA = document.getElementById('gsp-piva-a').value.trim();\n            var ignoraMaiuscole = document.getElementById('gsp-ignora-maiuscole').checked;\n            var escludiMetadati = document.getElementById('gsp-escludi-metadati').checked;\n            var testoEsclusione = document.getElementById('gsp-testo-esclusione').value.trim().toUpperCase();\n            var fileGrezzi = document.getElementById('gsp-file').files;\n\n            if (!pivaDa || !pivaA) {\n                mostraMessaggioGsp('Inserisci sia la Partita IVA da sostituire sia quella nuova.', 'errore');\n                return;\n            }\n            if (!fileGrezzi || fileGrezzi.length === 0) {\n                mostraMessaggioGsp('Carica almeno un file XML.', 'errore');\n                return;\n            }\n\n            var file = [];\n            var esclusiMetadati = [];\n            for (var i = 0; i < fileGrezzi.length; i++) {\n                var nomeMaiuscolo = fileGrezzi[i].name.toUpperCase();\n                if (escludiMetadati && testoEsclusione && nomeMaiuscolo.indexOf(testoEsclusione) !== -1) {\n                    esclusiMetadati.push(fileGrezzi[i].name);\n                } else {\n                    file.push(fileGrezzi[i]);\n                }\n            }\n\n            if (file.length === 0) {\n                mostraMessaggioGsp('Tutti i file caricati sono stati esclusi come META-DATI: nulla da elaborare.', 'errore');\n                return;\n            }\n\n            mostraMessaggioGsp('Elaborazione di ' + file.length + ' file in corso' +\n                (esclusiMetadati.length > 0 ? ' (' + esclusiMetadati.length + ' META-DATI esclusi)' : '') + '...', '');\n\n            var espressione = new RegExp(escapeRegExp(pivaDa), ignoraMaiuscole ? 'gi' : 'g');\n            risultatiSostituzionePiva = [];\n            var totaleOccorrenze = 0;\n            var fileSenzaOccorrenze = 0;\n\n            for (var i = 0; i < file.length; i++) {\n                var f = file[i];\n                try {\n                    var testoOriginale = await leggiFileComeTesto(f);\n                    var occorrenze = (testoOriginale.match(espressione) || []).length;\n                    var testoModificato = testoOriginale.replace(espressione, pivaA);\n                    var contesto = analizzaContestoOccorrenze(testoOriginale, pivaDa, ignoraMaiuscole);\n\n                    risultatiSostituzionePiva.push({\n                        nomeFile: f.name,\n                        contenuto: testoModificato,\n                        occorrenze: occorrenze,\n                        contesto: contesto\n                    });\n\n                    totaleOccorrenze += occorrenze;\n                    if (occorrenze === 0) fileSenzaOccorrenze++;\n                } catch (erroreGenerico) {\n                    risultatiSostituzionePiva.push({\n                        nomeFile: f.name,\n                        contenuto: null,\n                        occorrenze: 0,\n                        contesto: { inPiva: false, inCodiceFiscale: false },\n                        errore: erroreGenerico.message\n                    });\n                    fileSenzaOccorrenze++;\n                }\n            }\n\n            var corpo = document.getElementById('gsp-risultati-body');\n            corpo.innerHTML = '';\n            risultatiSostituzionePiva.forEach(function (r) {\n                var tr = document.createElement('tr');\n                var esito = r.errore\n                    ? 'Errore: ' + escapeXml(r.errore)\n                    : (r.occorrenze > 0 ? 'OK' : 'PIVA non trovata nel file');\n                tr.innerHTML =\n                    '<td>' + escapeXml(r.nomeFile) + '<\/td>' +\n                    '<td>' + r.occorrenze + '<\/td>' +\n                    '<td>' + escapeXml(etichettaContesto(r.contesto, r.occorrenze)) + '<\/td>' +\n                    '<td>' + esito + '<\/td>';\n                corpo.appendChild(tr);\n            });\n\n            document.getElementById('gsp-riepilogo').textContent =\n                'File elaborati: ' + risultatiSostituzionePiva.length +\n                ' - Occorrenze sostituite in totale: ' + totaleOccorrenze +\n                (fileSenzaOccorrenze > 0 ? ' - File senza occorrenze\/con errori: ' + fileSenzaOccorrenze : '') +\n                (esclusiMetadati.length > 0 ? ' - META-DATI esclusi: ' + esclusiMetadati.length : '');\n\n            document.getElementById('gsp-riquadro-risultati').style.display = 'block';\n\n            mostraMessaggioGsp(\n                totaleOccorrenze > 0\n                    ? 'Elaborazione completata.'\n                    : 'Elaborazione completata, ma nessuna occorrenza della Partita IVA indicata e\\' stata trovata: verifica di averla scritta esattamente come compare nei file.',\n                totaleOccorrenze > 0 ? 'successo' : 'errore'\n            );\n        });\n\n        document.getElementById('gsp-btn-scarica-zip').addEventListener('click', async function () {\n            var validi = risultatiSostituzionePiva.filter(function (r) { return r.contenuto !== null; });\n            if (validi.length === 0) {\n                mostraMessaggioGsp('Nessun file valido da scaricare.', 'errore');\n                return;\n            }\n            if (!window.JSZip) {\n                mostraMessaggioGsp('La libreria ZIP non si e\\' caricata (verifica la connessione).', 'errore');\n                return;\n            }\n\n            var zip = new JSZip();\n            validi.forEach(function (r) {\n                zip.file(r.nomeFile, r.contenuto);\n            });\n\n            mostraMessaggioGsp('Preparazione ZIP in corso...', '');\n            var blob = await zip.generateAsync({ type: 'blob' });\n            scaricaBlob('fatture_piva_sostituita_' + validi.length + '.zip', blob);\n            mostraMessaggioGsp('ZIP scaricato con ' + validi.length + ' file.', 'successo');\n        });\n\n        \/\/ ---------- Inizializzazione ----------\n        caricaCedente();\n        document.getElementById('gf-doc-data').value = new Date().toISOString().slice(0, 10);\n        popolaSelectTipoDocumento(document.getElementById('gf-doc-tipo'), 'TD01');\n        popolaSelectTipoDocumento(document.getElementById('gfx-doc-tipo'), 'TD01');\n        popolaSelectTipoDocumento(document.getElementById('gfp-doc-tipo'), 'TD01');\n        aggiungiRiga('', 1, 0, '22.00');\n        aggiungiRigaX('', 1, 0, '22.00');\n        renderizzaTabellaAnagrafiche();\n        aggiornaSelectAnagraficheInGenera();\n\n        \/\/ Genera Massiva: precompila dai dati Cedente gia' salvati, imposta mese\/anno correnti\n        try {\n            var datiCedenteSalvati = localStorage.getItem(CHIAVE_CEDENTE);\n            if (datiCedenteSalvati) {\n                var cedSalvato = JSON.parse(datiCedenteSalvati);\n                document.getElementById('gmx-propria-denom').value = cedSalvato.denom || '';\n                document.getElementById('gmx-propria-piva').value = cedSalvato.piva || '';\n            }\n        } catch (e) { \/* nessun dato salvato, si parte da vuoto *\/ }\n        var oggi = new Date();\n        document.getElementById('gmx-anno').value = oggi.getFullYear();\n        document.getElementById('gmx-mese').value = (oggi.getMonth() + 1).toString();\n        righeMix.push({ id: generaIdUnivoco(), tipoDocumento: 'TD01', numeroDocumenti: 10 });\n        rendMix();\n        righeScadenze.push({ id: generaIdUnivoco(), modalita: 'giorni', giorni: 30, base: 'finemese', numeroDocumenti: 10 });\n        rendScadenze();\n        aggiornaListaContropartiMassiva();\n\n    })();\n    <\/script>\n    \n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-2107","page","type-page","status-publish"],"_links":{"self":[{"href":"https:\/\/www.gestioneindustriale.it\/it_it\/wp-json\/wp\/v2\/pages\/2107","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.gestioneindustriale.it\/it_it\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.gestioneindustriale.it\/it_it\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.gestioneindustriale.it\/it_it\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.gestioneindustriale.it\/it_it\/wp-json\/wp\/v2\/comments?post=2107"}],"version-history":[{"count":1,"href":"https:\/\/www.gestioneindustriale.it\/it_it\/wp-json\/wp\/v2\/pages\/2107\/revisions"}],"predecessor-version":[{"id":2108,"href":"https:\/\/www.gestioneindustriale.it\/it_it\/wp-json\/wp\/v2\/pages\/2107\/revisions\/2108"}],"wp:attachment":[{"href":"https:\/\/www.gestioneindustriale.it\/it_it\/wp-json\/wp\/v2\/media?parent=2107"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}