From 1972723d9e8e91dcecb2665e9d72c23bfbc67c3e Mon Sep 17 00:00:00 2001 From: el Mau Date: Mon, 12 Sep 2022 19:26:23 -0500 Subject: [PATCH] Ejemplo Complemento de Pagos en CFDI 4.0 --- ejemplos/cfdi_pagos.json | 81 +++++++++++++++++++++++++++++----------- source/util.py | 59 +++++++++++++++++++++++------ 2 files changed, 106 insertions(+), 34 deletions(-) diff --git a/ejemplos/cfdi_pagos.json b/ejemplos/cfdi_pagos.json index 197095f..9a6f790 100644 --- a/ejemplos/cfdi_pagos.json +++ b/ejemplos/cfdi_pagos.json @@ -5,15 +5,20 @@ "TipoDeComprobante": "P", "LugarExpedicion": "06850", "SubTotal": "0", - "Total": "0" + "Total": "0", + "Exportacion": "01" }, "emisor": { - "Rfc": "LAN7008173R5", + "Rfc": "EKU9003173C9", + "Nombre": "ESCUELA KEMPER URGATE", "RegimenFiscal": "601" }, "receptor": { "Rfc": "BASM740115RW0", - "UsoCFDI": "P01" + "Nombre": "MAURICIO BAEZA SERVIN", + "DomicilioFiscalReceptor": "06850", + "RegimenFiscalReceptor": "612" , + "UsoCFDI": "CP01" }, "conceptos": [ { @@ -22,26 +27,58 @@ "ClaveUnidad": "ACT", "Descripcion": "Pago", "ValorUnitario": "0", - "Importe": "0" + "Importe": "0", + "ObjetoImp": "01" } ], - "pagos": [ - { - "FechaPago": "2020-08-07T12:00:00", - "FormaDePagoP": "03", - "MonedaP": "MXN", - "Monto": "6073.60", - "documentos": [ - { - "IdDocumento": "5243138D-EE4B-4B1B-A648-320DCC4E3BD2", - "ImpPagado": "6073.60", - "ImpSaldoAnt": "6073.60", - "ImpSaldoInsoluto": "0.00", - "MetodoDePagoDR": "PPD", - "MonedaDR": "MXN", - "NumParcialidad": "1" + "pagos": { + "totales": { + "TotalTrasladosBaseIVA16": "1000.00", + "TotalTrasladosImpuestoIVA16": "160.00", + "MontoTotalPagos": "1160.00" + }, + "pagos": [ + { + "FechaPago": "2020-08-07T12:00:00", + "FormaDePagoP": "03", + "MonedaP": "MXN", + "TipoCambioP": "1", + "Monto": "1160.00", + "documentos": [ + { + "IdDocumento": "5243138D-EE4B-4B1B-A648-320DCC4E3BD2", + "ImpPagado": "1160.00", + "ImpSaldoAnt": "1160.00", + "ImpSaldoInsoluto": "0.00", + "MonedaDR": "MXN", + "EquivalenciaDR": "1", + "NumParcialidad": "1", + "ObjetoImpDR": "02", + "impuestos": { + "traslados": [ + { + "BaseDR": "1000.00", + "ImpuestoDR": "002", + "TipoFactorDR": "Tasa", + "TasaOCuotaDR": "0.160000", + "ImporteDR": "160.00" + } + ] + } + } + ], + "impuestos": { + "traslados": [ + { + "BaseP": "1000.00", + "ImpuestoP": "002", + "TipoFactorP": "Tasa", + "TasaOCuotaP": "0.160000", + "ImporteP": "160.00" + } + ] } - ] - } - ] + } + ] + } } diff --git a/source/util.py b/source/util.py index f5ba6c6..7dfba22 100644 --- a/source/util.py +++ b/source/util.py @@ -26,7 +26,7 @@ log = logging.getLogger(__name__) PACs = { - # ~ 'finkok': PACFinkok, + 'finkok': PACFinkok, 'comercio': PACComercioDigital, } NS_CFDI = { @@ -56,12 +56,12 @@ class CFDI(object): _prefix = 'cfdi' _xmlns = 'http://www.sat.gob.mx/cfd/4' schema = f'{_xmlns} http://www.sat.gob.mx/sitio_internet/cfd/4/cfdv40.xsd' - _pagos = 'http://www.sat.gob.mx/Pagos' + _pagos = 'http://www.sat.gob.mx/Pagos20' PAGOS = { - 'version': '1.0', + 'version': '2.0', 'prefix': _pagos, - 'ns': {'pago10': _pagos}, - 'schema': f' {_pagos} http://www.sat.gob.mx/sitio_internet/cfd/Pagos/Pagos10.xsd', + 'ns': {'pago20': _pagos}, + 'schema': f' {_pagos} http://www.sat.gob.mx/sitio_internet/cfd/Pagos/Pagos20.xsd', } _nomina = 'http://www.sat.gob.mx/nomina12' NOMINA = { @@ -237,16 +237,51 @@ class CFDI(object): return def _pagos(self, complemento, data): - node_name = f"{{{self.PAGOS['prefix']}}}Pagos" + pre = self.PAGOS['prefix'] + totales = data['totales'] + pagos = data['pagos'] + + node_name = f"{{{pre}}}Pagos" attr = {'Version': self.PAGOS['version']} node_pagos = ET.SubElement(complemento, node_name, attr) - for pago in data: - documentos = pago.pop('documentos') - node_name = f"{{{self.PAGOS['prefix']}}}Pago" + + node_name = f"{{{pre}}}Totales" + ET.SubElement(node_pagos, node_name, totales) + + for pago in pagos: + documents = pago.pop('documentos') + taxes_pago = pago.pop('impuestos') + node_name = f"{{{pre}}}Pago" node_pago = ET.SubElement(node_pagos, node_name, pago) - node_name = f"{{{self.PAGOS['prefix']}}}DoctoRelacionado" - for doc in documentos: - ET.SubElement(node_pago, node_name, doc) + node_name = f"{{{pre}}}DoctoRelacionado" + for doc in documents: + taxes = doc.pop('impuestos', {}) + traslados = taxes.get('traslados', []) + retenciones = taxes.get('retenciones', []) + node_doc = ET.SubElement(node_pago, node_name, doc) + if traslados or retenciones: + node_name = f"{{{pre}}}ImpuestosDR" + node_taxes = ET.SubElement(node_doc, node_name) + if traslados: + node_name = f"{{{pre}}}TrasladosDR" + node_tax = ET.SubElement(node_taxes, node_name) + for traslado in traslados: + node_name = f"{{{pre}}}TrasladoDR" + ET.SubElement(node_tax, node_name, traslado) + + traslados = taxes_pago.get('traslados', []) + retenciones = taxes_pago.get('retenciones', []) + if traslados or retenciones: + node_name = f"{{{pre}}}ImpuestosP" + node_taxes = ET.SubElement(node_pago, node_name) + if traslados: + node_name = f"{{{pre}}}TrasladosP" + node_tax = ET.SubElement(node_taxes, node_name) + for traslado in traslados: + node_name = f"{{{pre}}}TrasladoP" + ET.SubElement(node_tax, node_name, traslado) + + return def _nomina(self, complemento, data):