I want an app that is capable of converting Italian fatture pdfs to xml.
To upload files, please first save the app
import streamlit as st
import PyPDF2
import xml.etree.ElementTree as ET
from xml.dom import minidom
import re
from datetime import datetime
import io
import pandas as pd
def extract_text_from_pdf(pdf_file):
"""Extract text from uploaded PDF file"""
try:
pdf_reader = PyPDF2.PdfReader(pdf_file)
text = ""
for page in pdf_reader.pages:
text += page.extract_text()
return text
except Exception as e:
st.error(f"Error reading PDF: {str(e)}")
return None
def parse_invoice_data(text):
"""Parse invoice data from extracted text using regex patterns"""
data = {}
# Common Italian invoice patterns
patterns = {
'numero_fattura': r'(?:FATTURA|Fattura)\s*(?:N\.?|n\.?|numero)\s*(\d+)',
'data_fattura': r'(?:Data|DATA)\s*(?:fattura|FATTURA)?\s*:?\s*(\d{1,2}[\/\-]\d{1,2}[\/\-]\d{4})',
'partita_iva_fornitore': r'(?:P\.?\s*IVA|Partita\s*IVA)\s*:?\s*([A-Z]{2}\d{11}|\d{11})',
'codice_fiscale_fornitore': r'(?:C\.?\s*F\.?|Codice\s*Fiscale)\s*:?\s*([A-Z]{6}\d{2}[A-Z]\d{2}[A-Z]\d{3}[A-Z])',
'denominazione_fornitore': r'(?:Ditta|DITTA|Ragione\s*sociale)\s*:?\s*([A-Za-z\s\.]+)',
'importo_totale': r'(?:Totale|TOTALE|Importo\s*totale)\s*€?\s*:?\s*(\d+[,\.]\d{2})',
'iva': r'(?:IVA|I\.V\.A\.)\s*(?:\d+%)?\s*€?\s*:?\s*(\d+[,\.]\d{2})',
'imponibile': r'(?:Imponibile|IMPONIBILE)\s*€?\s*:?\s*(\d+[,\.]\d{2})'
}
for key, pattern in patterns.items():
match = re.search(pattern, text, re.IGNORECASE)
if match:
data[key] = match.group(1).strip()
return data
def create_xml_invoice(data):
"""Create XML structure for Italian electronic invoice"""
# Root element
root = ET.Element("p:FatturaElettronica")
root.set("versione", "FPR12")
root.set("xmlns:ds", "http://www.w3.org/2000/09/xmldsig#")
root.set("xmlns:p", "http://ivaservizi.agenziaentrate.gov.it/docs/xsd/fatture/v1.2")
root.set("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
root.set("xsi:schemaLocation", "http://ivaservizi.agenziaentrate.gov.it/docs/xsd/fatture/v1.2 http://www.fatturapa.gov.it/export/fatturazione/sdi/fatturapa/v1.2/Schema_del_file_xml_FatturaPA_versione_1.2.xsd")
# Header
header = ET.SubElement(root, "FatturaElettronicaHeader")
# Dati trasmissione
dati_trasmissione = ET.SubElement(header, "DatiTrasmissione")
ET.SubElement(dati_trasmissione, "IdTrasmittente").text = data.get('partita_iva_fornitore', 'IT00000000000')
ET.SubElement(dati_trasmissione, "ProgressivoInvio").text = data.get('numero_fattura', '1')
ET.SubElement(dati_trasmissione, "FormatoTrasmissione").text = "FPR12"
ET.SubElement(dati_trasmissione, "CodiceDestinatario").text = "0000000"
# Cedente prestatore
cedente = ET.SubElement(header, "CedentePrestatore")
dati_anagrafici_cedente = ET.SubElement(cedente, "DatiAnagrafici")
id_fiscale_iva_cedente = ET.SubElement(dati_anagrafici_cedente, "IdFiscaleIVA")
ET.SubElement(id_fiscale_iva_cedente, "IdPaese").text = "IT"
ET.SubElement(id_fiscale_iva_cedente, "IdCodice").text = data.get('partita_iva_fornitore', '00000000000')
if 'codice_fiscale_fornitore' in data:
ET.SubElement(dati_anagrafici_cedente, "CodiceFiscale").text = data['codice_fiscale_fornitore']
anagrafica_cedente = ET.SubElement(dati_anagrafici_cedente, "Anagrafica")
ET.SubElement(anagrafica_cedente, "Denominazione").text = data.get('denominazione_fornitore', 'Nome Azienda')
# Cessionario committente
cessionario = ET.SubElement(header, "CessionarioCommittente")
dati_anagrafici_cess = ET.SubElement(cessionario, "DatiAnagrafici")
id_fiscale_iva_cess = ET.SubElement(dati_anagrafici_cess, "IdFiscaleIVA")
ET.SubElement(id_fiscale_iva_cess, "IdPaese").text = "IT"
ET.SubElement(id_fiscale_iva_cess, "IdCodice").text = "00000000000"
anagrafica_cess = ET.SubElement(dati_anagrafici_cess, "Anagrafica")
ET.SubElement(anagrafica_cess, "Denominazione").text = "Cliente"
# Body
body = ET.SubElement(root, "FatturaElettronicaBody")
# Dati generali
dati_generali = ET.SubElement(body, "DatiGenerali")
dati_generali_doc = ET.SubElement(dati_generali, "DatiGeneraliDocumento")
ET.SubElement(dati_generali_doc, "TipoDocumento").text = "TD01"
ET.SubElement(dati_generali_doc, "Divisa").text = "EUR"
ET.SubElement(dati_generali_doc, "Data").text = format_date(data.get('data_fattura', '01/01/2024'))
ET.SubElement(dati_generali_doc, "Numero").text = data.get('numero_fattura', '1')
# Importi
importo_totale = data.get('importo_totale', '0,00').replace(',', '.')
ET.SubElement(dati_generali_doc, "ImportoTotaleDocumento").text = importo_totale
# Dati beni servizi
dati_beni_servizi = ET.SubElement(body, "DatiBeniServizi")
dettaglio_linee = ET.SubElement(dati_beni_servizi, "DettaglioLinee")
ET.SubElement(dettaglio_linee, "NumeroLinea").text = "1"
ET.SubElement(dettaglio_linee, "Descrizione").text = "Servizio/Prodotto"
ET.SubElement(dettaglio_linee, "Quantita").text = "1.00"
ET.SubElement(dettaglio_linee, "PrezzoUnitario").text = data.get('imponibile', '0.00').replace(',', '.')
ET.SubElement(dettaglio_linee, "PrezzoTotale").text = data.get('imponibile', '0.00').replace(',', '.')
ET.SubElement(dettaglio_linee, "AliquotaIVA").text = "22.00"
# Dati riepilogo
dati_riepilogo = ET.SubElement(dati_beni_servizi, "DatiRiepilogo")
ET.SubElement(dati_riepilogo, "AliquotaIVA").text = "22.00"
ET.SubElement(dati_riepilogo, "ImponibileImporto").text = data.get('imponibile', '0.00').replace(',', '.')
ET.SubElement(dati_riepilogo, "Imposta").text = data.get('iva', '0.00').replace(',', '.')
return root
def format_date(date_str):
"""Convert Italian date format to XML format"""
try:
# Try to parse various date formats
for fmt in ['%d/%m/%Y', '%d-%m-%Y', '%d.%m.%Y']:
try:
date_obj = datetime.strptime(date_str, fmt)
return date_obj.strftime('%Y-%m-%d')
except ValueError:
continue
return '2024-01-01' # Default date
except:
return '2024-01-01'
def prettify_xml(elem):
"""Return a pretty-printed XML string for the Element."""
rough_string = ET.tostring(elem, 'unicode')
reparsed = minidom.parseString(rough_string)
return reparsed.toprettyxml(indent=" ")
st.title("🇮🇹 Convertitore Fatture PDF → XML")
st.write("Converte fatture italiane da PDF al formato XML per la Fatturazione Elettronica")
# File upload
uploaded_file = st.file_uploader("Carica la fattura PDF", type=['pdf'])
if uploaded_file is not None:
# Extract text from PDF
with st.spinner("Estrazione testo dal PDF..."):
text = extract_text_from_pdf(uploaded_file)
if text:
st.success("PDF caricato e testo estratto con successo!")
# Show extracted text in expandable section
with st.expander("Visualizza testo estratto"):
st.text_area("Testo dal PDF:", text, height=200)
# Parse invoice data
with st.spinner("Analisi dei dati della fattura..."):
invoice_data = parse_invoice_data(text)
st.subheader("📊 Dati estratti dalla fattura")
# Display extracted data in editable form
col1, col2 = st.columns(2)
with col1:
numero_fattura = st.text_input("Numero Fattura", value=invoice_data.get('numero_fattura', ''))
data_fattura = st.text_input("Data Fattura", value=invoice_data.get('data_fattura', ''))
partita_iva = st.text_input("Partita IVA Fornitore", value=invoice_data.get('partita_iva_fornitore', ''))
codice_fiscale = st.text_input("Codice Fiscale Fornitore", value=invoice_data.get('codice_fiscale_fornitore', ''))
with col2:
denominazione = st.text_input("Denominazione Fornitore", value=invoice_data.get('denominazione_fornitore', ''))
importo_totale = st.text_input("Importo Totale", value=invoice_data.get('importo_totale', ''))
iva = st.text_input("IVA", value=invoice_data.get('iva', ''))
imponibile = st.text_input("Imponibile", value=invoice_data.get('imponibile', ''))
# Update invoice data with user inputs
updated_data = {
'numero_fattura': numero_fattura,
'data_fattura': data_fattura,
'partita_iva_fornitore': partita_iva,
'codice_fiscale_fornitore': codice_fiscale,
'denominazione_fornitore': denominazione,
'importo_totale': importo_totale,
'iva': iva,
'imponibile': imponibile
}
# Generate XML button
if st.button("🔄 Genera XML", type="primary"):
with st.spinner("Generazione XML in corso..."):
xml_root = create_xml_invoice(updated_data)
xml_string = prettify_xml(xml_root)
st.success("XML generato con successo!")
# Display XML
st.subheader("📄 XML Generato")
st.code(xml_string, language='xml')
# Download button
xml_bytes = xml_string.encode('utf-8')
filename = f"fattura_{numero_fattura or 'unknown'}_{data_fattura.replace('/', '-') if data_fattura else 'unknown'}.xml"
st.download_button(
label="⬇️ Scarica XML",
data=xml_bytes,
file_name=filename,
mime="application/xml"
)
# Show summary
st.subheader("📋 Riepilogo")
summary_df = pd.DataFrame([
["Numero Fattura", numero_fattura],
["Data", data_fattura],
["Fornitore", denominazione],
["P.IVA", partita_iva],
["Importo Totale", importo_totale],
["IVA", iva],
["Imponibile", imponibile]
], columns=["Campo", "Valore"])
st.table(summary_df)
# Instructions
st.sidebar.header("ℹ️ Istruzioni")
st.sidebar.write("""
1. **Carica** il file PDF della fattura
2. **Verifica** i dati estratti automaticamente
3. **Modifica** eventuali errori nei campi
4. **Genera** il file XML
5. **Scarica** il risultato
⚠️ **Nota**: I dati vengono estratti automaticamente ma potrebbero richiedere correzioni manuali per garantire la conformità alla Fatturazione Elettronica italiana.
""")
st.sidebar.header("🔧 Formati supportati")
st.sidebar.write("""
- **Input**: PDF
- **Output**: XML (Formato FatturaPA v1.2)
- **Standard**: Agenzia delle Entrate
""")
Hi! I can help you with any questions about Streamlit and Python. What would you like to know?