88 lines
2.2 KiB
Python
88 lines
2.2 KiB
Python
#!/usr/bin/env python3
|
|
|
|
from decimal import Decimal, getcontext
|
|
# ~ getcontext().prec = 6
|
|
|
|
import lxml.etree as ET
|
|
from requests.structures import CaseInsensitiveDict as CIDict
|
|
|
|
|
|
NS_CFDI = {
|
|
'cfdi': 'http://www.sat.gob.mx/cfd/3',
|
|
'tfd': 'http://www.sat.gob.mx/TimbreFiscalDigital',
|
|
'nomina12': 'http://www.sat.gob.mx/nomina12',
|
|
}
|
|
PRE = '/cfdi:Comprobante'
|
|
|
|
|
|
class CfdiRead(object):
|
|
|
|
def __init__(self, source):
|
|
self._source = source
|
|
self._data = {}
|
|
self._error = ''
|
|
self._rfc_emisor = ''
|
|
self._rfc_receptor = ''
|
|
self._parse()
|
|
|
|
@property
|
|
def source(self):
|
|
return self._source
|
|
|
|
@property
|
|
def data(self):
|
|
return self._data
|
|
|
|
@property
|
|
def rfc_emisor(self):
|
|
return self._rfc_emisor
|
|
|
|
@property
|
|
def rfc_receptor(self):
|
|
return self._rfc_receptor
|
|
|
|
@property
|
|
def error(self):
|
|
return self._error
|
|
|
|
def _parse(self):
|
|
self._tree = ET.fromstring(self.source)
|
|
self._data['cfdi'] = dict(self._tree.attrib)
|
|
|
|
node_name = f'{PRE}/cfdi:Emisor'
|
|
self._data['emisor'] = self._get_attr(node_name)
|
|
self._rfc_emisor = self._data['emisor']['Rfc']
|
|
|
|
node_name = f'{PRE}/cfdi:Receptor'
|
|
self._data['receptor'] = self._get_attr(node_name)
|
|
self._rfc_receptor = self._data['receptor']['Rfc']
|
|
|
|
node_name = f'{PRE}/cfdi:Complemento/tfd:TimbreFiscalDigital'
|
|
self._data['timbre'] = self._get_attr(node_name)
|
|
|
|
self._parse_details()
|
|
return
|
|
|
|
def _get_attr(self, node_name):
|
|
node = self._tree.xpath(node_name, namespaces=NS_CFDI)[0]
|
|
attr = dict(node.attrib)
|
|
return attr
|
|
|
|
def _parse_details(self):
|
|
node_name = f'{PRE}/cfdi:Conceptos/cfdi:Concepto'
|
|
details = self._tree.xpath(node_name, namespaces=NS_CFDI)
|
|
rows = []
|
|
for detail in details:
|
|
row = dict(detail.attrib)
|
|
for k, v in row.items():
|
|
if k in ('Cantidad', 'ValorUnitario', 'Descuento', 'Importe'):
|
|
row[k] = Decimal(v)
|
|
# ~ row['taxes'] = self._get_taxes(detail)
|
|
rows.append(row)
|
|
self._data['conceptos'] = rows
|
|
return
|
|
|
|
|
|
class CfdiWrite(object):
|
|
pass
|