Leggere dettaglio linee da fatture elettroniche con xmltodict
In questo articolo avevamo visto come esportare in csv i dati di intestazione di una fattura elettronica per poi poterla leggere con Excel.
Il programma utilizzava la libreria eTree per scorrere i valori per poi scriverli su un file di testo csv.
Il problema si pone sulle righe di dettaglio, che naturalmente possono essere più di una.
Qui il problema principale è come accedervi ma anche come successivamente scriverle nel file in uscita.
Utilizzando una libreria diversa, xmltodict possiamo accedere direttamente agli oggetti dell’xml in vari formati o utilizzando degli xpath.
Nel nostro caso ho scelto di scorrere il dictionary creato e individuare il tipo di valore ottenuto dalle singole interrogazioni.
Siccome gli oggetti del dictionary sono raggruppati in liste o liste di dictionary, parlando per esempio dei valori DettagliLinee, della fatturazione elettronica.
Possiamo avere un dictionary con all’interno i campi di dettaglio di una sola riga oppure una lista di dictionary nel caso di fatture con più righe.
L’idea è quindi di ricreare una struttura Python che contenga ogni fattura in un oggetto lista, dove ogni valore di DettaglioLinee sia contenuto in un dictionary facilmente accessibile.
#!/usr/bin/env python3 import os, glob import xmltodict DatiFatture = [] StrutturaCampi = [] # Creiamo una struttura dei campi che ci interessa esportare seguendo la struttura xml, se i campi sono composti da più righe, # come nel caso degli articoli, prendiamo il primo campo 'padre' necessario (DettaglioLinee) il programma si occuperà di prendere # tutti i sottocampi contenuti. StrutturaCampi = [['FatturaElettronicaHeader', 'DatiTrasmissione', 'ProgressivoInvio'], ['FatturaElettronicaHeader', 'DatiTrasmissione', 'CodiceDestinatario'], ['FatturaElettronicaHeader', 'CedentePrestatore', 'DatiAnagrafici', 'Anagrafica', 'Denominazione'], ['FatturaElettronicaHeader', 'CessionarioCommittente', 'Sede', 'Indirizzo'], ['FatturaElettronicaBody', 'DatiPagamento', 'CondizioniPagamento'], ['FatturaElettronicaBody', 'DatiBeniServizi', 'DettaglioLinee']] # Qui indichiamo la certella con i file XML da elaborare path = "C:\\Users\\user\\Downloads" # Leggiamo tutti i file for filename in glob.glob(os.path.join(path, "*.xml")): Fattura = [] with open(filename) as fd: doc = xmltodict.parse(fd.read()) # Prendiamo la chiave radice principale in automatico (es.: p:FatturaElettronica or ns1:FatturaElettronica) root = list(doc.keys())[0] # Facciamo il parse della struttura campi for fieldpath in StrutturaCampi: fieldname = '' # Prepariamo la stringa da interrogare successivamente con il metodo eval string_2_eval = 'doc["' + root + '"]' row_dict = {} for field in fieldpath: # memorizziamo il nome dell'ultimo campo della lista (Es.ProgressivoInvio): fieldname = fieldpath[-1] # accodiamo il percorso per andare a recuperare il valore nell'xml string_2_eval += '["' + field.replace('\n', '') + '"]' # Verifichiamo se il valore esiste e che non dia errori try: value = eval(string_2_eval) except: value = False # Verifichiamo che tipo di dato abbiamo: # se è un dictionary vuol dire che è una fattura con una sola riga di dettaglio if isinstance(value, dict) and value: print("E' UN DICTIONARY !") for k, v in value.items(): row_dict[k] = v Fattura.append(('Righe fattura', row_dict)) # se è una lista vuol dire che è una fattura con una più righe di dettaglio elif isinstance(value, list) and value: print("E' UNA LISTA !") for l in value: for k, v in l.items(): row_dict[k] = v Fattura.append(('Righe fattura', row_dict)) row_dict = {} # se è un valore vuol dire che è un valore singolo elif not isinstance(value, dict) and value: print("VALORE NORMALE") Fattura.append((fieldname, value)) # se è un valore ma non contiene dati aggiungiamo comunque il campo vuoto per non perdere la formattazione delle colonne nel file in uscita elif not isinstance(value, dict) and not value: Fattura.append((fieldname, '')) # Fine lettura campi fattura # A questo punto in Fattura[] abbiamo la lista di tutti i campi che ci interessano, dove ogni valore di 'Righe Fattura' conterrà # il dictionary con i campi della riga. # Volendo possiamo accodare a un'altra lista che conterrà tutte le fatture in formato lista. DatiFatture.append(Fattura) print('Dati fatture') print('------------') print(DatiFatture)
OUTPUT SPIEGATO:
Prima fattura [ [('ProgressivoInvio', '00002'), ('CodiceDestinatario', 'XXXXXX'), ('Denominazione', 'Roberto Zanardo'), ('Indirizzo', 'Via dei Tigli,23'), ('CondizioniPagamento', 'TP02'), Questa è una tuple che contiene come primo dato il nome del campo(Riga fattura) e come secondo dato il dictionary con tutte le chiavi/valori della riga. ('Riga fattura', {'NumeroLinea': '1', 'Descrizione': 'CONSULENZA/SVILUPPO SOFTWARE ODOO', 'Quantita': '1.00', 'PrezzoUnitario': '1600.00', 'PrezzoTotale': '1600.00', 'AliquotaIVA': '1.00', 'Natura': 'N2'}), ('Riga fattura', {'NumeroLinea': '2', 'Descrizione': 'cassa previdenziale 4', 'Quantita': '1.00', 'PrezzoUnitario': '64.00', 'PrezzoTotale': '64.00', 'AliquotaIVA': '2.00', 'Natura': 'N2'}) ] Fine prima fattura , Seconda fattura [ ('ProgressivoInvio', '00002'), ('CodiceDestinatario', 'SUXXXN'), ('Denominazione', 'Roberto Zanardo'), ('Indirizzo', 'Via Alpi 34'), ('CondizioniPagamento', 'TP02'), Questa è la tuple della prima riga che contiene come primo dato il nome del campo(Riga fattura) e come secondo dato il dictionary con tutte le chiavi/valori della riga. ('Riga fattura', {'NumeroLinea': '1', 'Descrizione': 'ORE CONSULENZA E SVILUPPO SOFTWARE', 'Quantita': '1.00', 'PrezzoUnitario': '1200.00', 'PrezzoTotale': '1200.00', 'AliquotaIVA': '0.00', 'Natura': 'N2'}), Questa è la tuple della prima seconda che contiene come primo dato il nome del campo(Riga fattura) e come secondo dato il dictionary con tutte le chiavi/valori della riga. ('Riga fattura', {'NumeroLinea': '2', 'Descrizione': 'Spese', 'Quantita': '1.00', 'PrezzoUnitario': '48.00', 'PrezzoTotale': '48.00', 'AliquotaIVA': '0.00', 'Natura': 'N2'})], ] Fine seconda fattura
Da questo punto sarà quindi necessario rileggere la struttura creata per disporla nel file di output come preferiamo.
Un commento su “Leggere dettaglio linee da fatture elettroniche con xmltodict”