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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +