diff --git a/source/helper/util.py b/source/helper/util.py
index 8551d03..e80d443 100644
--- a/source/helper/util.py
+++ b/source/helper/util.py
@@ -74,6 +74,18 @@ class DictToCfdi():
'xmlns': 'http://www.sat.gob.mx/leyendasFiscales',
'schema': ' http://www.sat.gob.mx/leyendasFiscales http://www.sat.gob.mx/sitio_internet/cfd/leyendasFiscales/leyendasFisc.xsd',
}
+ _PAGOS = {
+ 'version': '2.0',
+ 'prefix': '',
+ 'xmlns': 'http://www.sat.gob.mx/',
+ 'schema': ' http://www.sat.gob.mx/ http://www.sat.gob.mx/sitio_internet/cfd//.xsd',
+ }
+ _COMERCIO = {
+ 'version': '1.1',
+ 'prefix': 'cce11',
+ 'xmlns': 'http://www.sat.gob.mx/ComercioExterior11',
+ 'schema': ' http://www.sat.gob.mx/ComercioExterior11 http://www.sat.gob.mx/sitio_internet/cfd/ComercioExterior11/ComercioExterior11.xsd',
+ }
def __init__ (self, data):
self._data = data
@@ -111,6 +123,12 @@ class DictToCfdi():
self._attr_complementos['leyendas'] = {
self._LEYENDAS['prefix']: self._LEYENDAS['xmlns']
}
+ if 'comercio' in self._data['complementos']:
+ self._schema += self._COMERCIO['schema']
+ self._attr_complementos['comercio'] = {
+ self._COMERCIO['prefix']: self._COMERCIO['xmlns']
+ }
+
return
def _comprobante(self):
@@ -223,6 +241,12 @@ class DictToCfdi():
if 'leyendas' in self._data['complementos']:
self._complemento_leyendas(self._data['complementos']['leyendas'], node)
+
+ if 'pagos' in self._data['complementos']:
+ self._complemento_pagos(self._data['complementos']['pagos'], node)
+ elif 'comercio' in self._data['complementos']:
+ self._complemento_comercio(self._data['complementos']['comercio'], node)
+
return
def _complemento_leyendas(self, data, node):
@@ -235,6 +259,69 @@ class DictToCfdi():
ET.SubElement(node_leyendas, node_name, leyenda)
return
+ def _complemento_pagos(self, data, node):
+ attr = {'version': self._PAGOS['version']}
+ return
+
+ def _complemento_comercio(self, data, node):
+ mercancias = data.pop('mercancias', {})
+ emisor = data.pop('emisor', {})
+ propietario = data.pop('propietario', {})
+ receptor = data.pop('receptor', {})
+ destinatario = data.pop('destinatario', {})
+ attr = data
+
+ node_name = f"{{{self._COMERCIO['xmlns']}}}ComercioExterior"
+ node_comercio = ET.SubElement(node, node_name, attr)
+
+ if emisor:
+ node_name = f"{{{self._COMERCIO['xmlns']}}}Emisor"
+ attr = {}
+ if 'Curp' in emisor:
+ attr = {'Curp': emisor.pop('Curp')}
+ node_emisor = ET.SubElement(node_comercio, node_name, attr)
+
+ node_name = f"{{{self._COMERCIO['xmlns']}}}Domicilio"
+ ET.SubElement(node_emisor, node_name, emisor)
+
+ if propietario:
+ node_name = f"{{{self._COMERCIO['xmlns']}}}Propietario"
+ ET.SubElement(node_comercio, node_name, propietario)
+
+ if receptor:
+ node_name = f"{{{self._COMERCIO['xmlns']}}}Receptor"
+ attr = {}
+ if 'NumRegIdTrib' in receptor:
+ attr = {'NumRegIdTrib': emisor.pop('NumRegIdTrib')}
+ node_receptor = ET.SubElement(node_comercio, node_name, attr)
+
+ node_name = f"{{{self._COMERCIO['xmlns']}}}Domicilio"
+ ET.SubElement(node_receptor, node_name, receptor)
+
+ if destinatario:
+ node_name = f"{{{self._COMERCIO['xmlns']}}}Destinatario"
+ attr = {}
+ if 'NumRegIdTrib' in destinatario:
+ attr['NumRegIdTrib'] = destinatario.pop('NumRegIdTrib')
+ if 'Nombre' in destinatario:
+ attr['Nombre'] = destinatario.pop('Nombre')
+ node_destinatario = ET.SubElement(node_comercio, node_name, attr)
+
+ node_name = f"{{{self._COMERCIO['xmlns']}}}Domicilio"
+ ET.SubElement(node_destinatario, node_name, destinatario)
+
+ if mercancias:
+ node_name = f"{{{self._COMERCIO['xmlns']}}}Mercancias"
+ node_mercancias = ET.SubElement(node_comercio, node_name)
+ for mercancia in mercancias:
+ description = mercancia.pop('description', {})
+ node_name = f"{{{self._COMERCIO['xmlns']}}}Mercancia"
+ node_mercancia = ET.SubElement(node_mercancias, node_name, mercancia)
+ if description:
+ node_name = f"{{{self._COMERCIO['xmlns']}}}DescripcionesEspecificas"
+ ET.SubElement(node_mercancia, node_name, description)
+ return
+
def _addenda(self):
type_boveda = self._data['addenda'].get('type', '')
type_client = self._data['addenda'].get('type_client', '')
@@ -339,6 +426,13 @@ class DataToDict():
'05': '_conceptos',
'06': '_impuestos',
'10': '_leyendas',
+ '11': '_complemento',
+ '12': '_complemento_12',
+ '13': '_complemento_13',
+ '14': '_complemento_14',
+ '15': '_complemento_15',
+ '16': '_complemento_16',
+ '17': '_complemento_17',
'49': '_addenda_lotes',
'50': '_boveda',
'51': '_addenda',
@@ -348,9 +442,11 @@ class DataToDict():
def __init__ (self, data):
self._data = data
self._cfdi = {'conceptos': [], 'complementos': {}, 'addenda': {}}
+ self._complement = ''
self._type_header = ''
self._partes = []
self._lotes = []
+ self._ce_mercancias = []
self._process_data()
@property
@@ -372,6 +468,10 @@ class DataToDict():
self._cfdi['addenda']['partes'] = self._partes
self._cfdi['addenda']['lotes'] = self._lotes
+
+ if self._ce_mercancias:
+ self._cfdi['complementos']['comercio']['mercancias'] = \
+ self._ce_mercancias
return
def _comprobante(self, data):
@@ -538,6 +638,181 @@ class DataToDict():
self._cfdi['complementos']['leyendas'] = leyendas
return
+ def _fields_to_dict(self, fields, data):
+ attr = {}
+ for index, field in enumerate(fields):
+ if not data[index]:
+ continue
+ attr[field] = data[index]
+ return attr
+
+ def _complemento(self, data):
+ if not data:
+ return
+
+ self._complement = data[0]
+ version = {'Version': data[1]}
+ if self._complement == '1':
+ self._cfdi['complementos']['pagos'] = version
+ elif self._complement == '2':
+ self._cfdi['complementos']['comercio'] = version
+
+ return
+
+ def _complemento_12(self, data):
+ if not data:
+ return
+
+ if self._complement == '1':
+ fields = (
+ 'TotalRetencionesIVA',
+ 'TotalRetencionesISR',
+ 'TotalRetencionesIEPS',
+ 'TotalTrasladosBaseIVA16',
+ 'TotalTrasladosImpuestoIVA16',
+ 'TotalTrasladosBaseIVA8',
+ 'TotalTrasladosImpuestoIVA8',
+ 'TotalTrasladosBaseIVA0',
+ 'TotalTrasladosImpuestoIVA0',
+ 'TotalTrasladosBaseIVAExento',
+ 'MontoTotalPagos',
+ )
+ attr = self._fields_to_dict(fields, data)
+ self._cfdi['complementos']['pagos'].update(attr)
+ elif self._complement == '2':
+ fields = (
+ 'MotivoTraslado',
+ 'TipoOperacion',
+ 'ClaveDePedimento',
+ 'CertificadoOrigen',
+ 'NumCertificadoOrigen',
+ 'NumeroExportadorConfiable',
+ 'Incoterm',
+ 'Subdivision',
+ 'Observaciones',
+ 'TipoCambioUSD',
+ 'TotalUSD',
+ )
+ attr = self._fields_to_dict(fields, data)
+ self._cfdi['complementos']['comercio'].update(attr)
+
+ return
+
+ def _complemento_13(self, data):
+ if not data:
+ return
+
+ if self._complement == '1':
+ pass
+ elif self._complement == '2':
+ fields = (
+ 'Curp',
+ 'Calle',
+ 'NumeroExterior',
+ 'NumeroInterior',
+ 'Colonia',
+ 'Localidad',
+ 'Referencia',
+ 'Municipio',
+ 'Estado',
+ 'Pais',
+ 'CodigoPostal',
+ )
+ attr = self._fields_to_dict(fields, data)
+ self._cfdi['complementos']['comercio']['emisor'] = attr
+ return
+
+ def _complemento_14(self, data):
+ if not data:
+ return
+
+ if self._complement == '1':
+ pass
+ elif self._complement == '2':
+ fields = ('NumRegIdTrib', 'ResidenciaFiscal')
+ attr = self._fields_to_dict(fields, data)
+ if attr:
+ self._cfdi['complementos']['comercio']['propietario'] = attr
+ return
+
+ def _complemento_15(self, data):
+ if not data:
+ return
+
+ if self._complement == '1':
+ pass
+ elif self._complement == '2':
+ fields = (
+ 'NumRegIdTrib',
+ 'Calle',
+ 'NumeroExterior',
+ 'NumeroInterior',
+ 'Colonia',
+ 'Localidad',
+ 'Referencia',
+ 'Municipio',
+ 'Estado',
+ 'Pais',
+ 'CodigoPostal',
+ )
+ attr = self._fields_to_dict(fields, data)
+ self._cfdi['complementos']['comercio']['receptor'] = attr
+ return
+
+ def _complemento_16(self, data):
+ if not data:
+ return
+
+ if self._complement == '1':
+ pass
+ elif self._complement == '2':
+ fields = (
+ 'NumRegIdTrib',
+ 'Nombre',
+ 'Calle',
+ 'NumeroExterior',
+ 'NumeroInterior',
+ 'Colonia',
+ 'Localidad',
+ 'Referencia',
+ 'Municipio',
+ 'Estado',
+ 'Pais',
+ 'CodigoPostal',
+ )
+ attr = self._fields_to_dict(fields, data)
+ self._cfdi['complementos']['comercio']['destinatario'] = attr
+ return
+
+ def _complemento_17(self, data):
+ if not data:
+ return
+
+ if self._complement == '1':
+ pass
+ elif self._complement == '2':
+ fields = (
+ 'NoIdentificacion',
+ 'FraccionArancelaria',
+ 'CantidadAduana',
+ 'UnidadAduana',
+ 'ValorUnitarioAduana',
+ 'ValorDolares',
+ )
+ mercancia = self._fields_to_dict(fields, data)
+ fields = (
+ 'Marca',
+ 'Modelo',
+ 'SubModelo',
+ 'NumeroSerie',
+ )
+ description = self._fields_to_dict(fields, data[6:])
+ if description:
+ mercancia['description'] = description
+ self._ce_mercancias.append(mercancia)
+
+ return
+
def _boveda(self, data):
type_addenda = data[0]
if type_addenda == '01':
diff --git a/source/xslt/cadena.xslt b/source/xslt/cadena.xslt
index 44de891..2179e83 100644
--- a/source/xslt/cadena.xslt
+++ b/source/xslt/cadena.xslt
@@ -6,13 +6,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+