diff --git a/CHANGELOG.md b/CHANGELOG.md index 75e154c..aa37d48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Lista de cambios +## v 0.6.0 [27-Mar-22] +--- +* Fix - Cambios del SAT + ## v 0.5.1 [09-Feb-22] --- diff --git a/VERSION b/VERSION index 4b9fcbe..a918a2a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.5.1 +0.6.0 diff --git a/doc/Servicio+de+Descarga+de+Solicitudes+Exitosas.pdf b/doc/Servicio+de+Descarga+de+Solicitudes+Exitosas.pdf new file mode 100644 index 0000000..36eafc0 Binary files /dev/null and b/doc/Servicio+de+Descarga+de+Solicitudes+Exitosas.pdf differ diff --git a/doc/Servicio+de+Verificación+de+Descarga+de+Solicitudes+Exitosas.pdf b/doc/Servicio+de+Verificación+de+Descarga+de+Solicitudes+Exitosas.pdf new file mode 100644 index 0000000..173b3f6 Binary files /dev/null and b/doc/Servicio+de+Verificación+de+Descarga+de+Solicitudes+Exitosas.pdf differ diff --git a/doc/Solicitud_Descarga_Masiva.pdf b/doc/Solicitud_Descarga_Masiva.pdf new file mode 100644 index 0000000..7c87e49 Binary files /dev/null and b/doc/Solicitud_Descarga_Masiva.pdf differ diff --git a/doc/URLs.pdf b/doc/URLs.pdf new file mode 100644 index 0000000..b13ab37 Binary files /dev/null and b/doc/URLs.pdf differ diff --git a/source/cfdi-descarga.py b/source/cfdi-descarga.py index 3e85452..9e70eec 100755 --- a/source/cfdi-descarga.py +++ b/source/cfdi-descarga.py @@ -51,7 +51,7 @@ def _process_command_line_arguments(): dest='month', default=0, type=int, choices=range(13)) help = "Día de la descarga, de forma predeterminada no se usa" parser.add_argument('-d', '--dia', help=help, - dest='day', default=0, type=int, choices=range(32)) + dest='day', default=0, type=int, choices=range(31)) help = "Intervalo de días a partir de la fecha actual y hacia a atras" parser.add_argument('-ud', '--ultimos-dias', help=help, dest='last_days', default=0, type=int, choices=range(30)) diff --git a/source/sat/sat_web.py b/source/sat/sat_web.py index 7f4b92c..47df420 100644 --- a/source/sat/sat_web.py +++ b/source/sat/sat_web.py @@ -8,6 +8,8 @@ from datetime import datetime, timedelta import httpx import lxml.etree as ET +from conf import TIMEOUT + class SATWebService(): BASE = 'https://cfdidescargamasivasolicitud.clouda.sat.gob.mx' @@ -29,6 +31,8 @@ class SATWebService(): 'Content-type': 'text/xml;charset="utf-8"', 'Accept': 'text/xml', 'Cache-Control': 'no-cache', + 'Expect': '100-continue', + 'Accept-Encoding': 'gzip, deflate', } NS = { 's': 'http://schemas.xmlsoap.org/soap/envelope/', @@ -148,12 +152,14 @@ class SATWebService(): nsmap = {'s': self.NS['s'], None: self.XMLNS} node_name = 's:Body/AutenticaResponse/AutenticaResult' token = result.find(node_name, namespaces=nsmap).text - print(f'Token: {token}') + # ~ print(f'Token: {token}') return token def _get_data_req(self, args): - NSMAP = {'s': self.NS['s'], 'des': self.NS['des'], 'xd': self.NS['xd']} + # ~ NSMAP = {'s': self.NS['s'], 'des': self.NS['des'], 'xd': self.NS['xd']} + # ~ NSMAP = {'s': self.NS['s'], 'xd': self.NS['xd']} + NSMAP = {'s': self.NS['s']} FORMAT = '%Y-%m-%dT%H:%M:%S' date_start = args['date_start'] @@ -165,28 +171,50 @@ class SATWebService(): root = ET.Element(node_name, nsmap=NSMAP) node_name = f"{{{self.NS['s']}}}Header" - body = ET.SubElement(root, node_name) + header = ET.SubElement(root, node_name) + + node_name = 'ActivityId' + attr = {'CorrelationId': '806aad0d-ef46-443b-9741-040c8e8e8c7d'} + nsmap = {None: 'http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics'} + activity = ET.SubElement(header, node_name, attr, nsmap=nsmap) + activity.text = 'e906cfb4-f706-43de-94d0-5cc935be1aaa' node_name = f"{{{self.NS['s']}}}Body" - body = ET.SubElement(root, node_name) + nsmap = { + 'xsi': 'http://www.w3.org/2001/XMLSchema-instance', + 'xsd': 'http://www.w3.org/2001/XMLSchema', + } + body = ET.SubElement(root, node_name, nsmap=nsmap) - node_name = f"{{{self.NS['des']}}}SolicitaDescarga" - request_down = ET.SubElement(body, node_name) + # ~ node_name = f"{{{self.NS['des']}}}SolicitaDescarga" + node_name = "SolicitaDescarga" + nsmap = {None: 'http://DescargaMasivaTerceros.sat.gob.mx'} + request_down = ET.SubElement(body, node_name, nsmap=nsmap) - node_name = f"{{{self.NS['des']}}}solicitud" + # ~ node_name = f"{{{self.NS['des']}}}solicitud" + node_name = "solicitud" type_request = 'CFDI' if args['metadata']: type_request = 'Metadata' attr = { - 'RfcSolicitante': self._cert.rfc, - 'FechaFinal': date_end.strftime(FORMAT), 'FechaInicial': date_start.strftime(FORMAT), + 'FechaFinal': date_end.strftime(FORMAT), + 'RfcSolicitante': self._cert.rfc, 'TipoSolicitud': type_request, - args['rfc']: self._cert.rfc, + # ~ 'RfcACuentaTerceros': '', } + if args['rfc'] == 'RfcEmisor': + attr['RfcEmisor'] = self._cert.rfc request = ET.SubElement(request_down, node_name, attr) + node_name = 'RfcReceptores' + node_receptores = ET.SubElement(request, node_name) + node_name = 'RfcReceptor' + node_receptor = ET.SubElement(node_receptores, node_name) + if args['rfc'] == 'RfcReceptor': + node_receptor.text = self._cert.rfc + nsmap = {None: self.NS['xd']} signature = ET.SubElement(request, 'Signature', nsmap=nsmap) signed_info = ET.SubElement(signature, 'SignedInfo', nsmap=nsmap) @@ -199,7 +227,8 @@ class SATWebService(): attr = {'Algorithm': 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'} signature_method = ET.SubElement(signed_info, node_name, attr) - attr = {'URI': '#_0'} + # ~ attr = {'URI': '#_0'} + attr = {'URI': ''} reference = ET.SubElement(signed_info, 'Reference', attr) transforms = ET.SubElement(reference, 'Transforms') ET.SubElement(transforms, 'Transform', attr1) @@ -227,18 +256,25 @@ class SATWebService(): x_serial_number.text = str(self._cert.serial_number2) x_cert.text = self._cert.cer_txt - # ~ soap = ET.tostring(root, pretty_print=True, encoding='utf-8') - soap = ET.tostring(root) - + # ~ soap = b'\n' + ET.tostring(root, pretty_print=True, encoding='utf-8') + soap = ET.tostring(root, pretty_print=True) + # ~ print(soap.decode()) return soap def request_download(self, args): headers = self.HEADERS.copy() headers['SOAPAction'] = self.ACTIONS['REQ'] + headers['Host'] = 'srvsolicituddescargamaster.cloudapp.net' headers['Authorization'] = f'WRAP access_token="{self._token}"' data = self._get_data_req(args) - response = httpx.post(self.URL['REQ'], data=data, headers=headers) + try: + response = httpx.post(self.URL['REQ'], + data=data, headers=headers, timeout=TIMEOUT) + except httpx.TimeoutException as exc: + print(exc) + return + if response.status_code != httpx.codes.OK: self._error = f'Status: {response.status_code} - {response.text}' return @@ -251,7 +287,7 @@ class SATWebService(): return data def _get_data_verify(self, args): - NSMAP = {'s': self.NS['s'], 'des': self.NS['des'], 'xd': self.NS['xd']} + NSMAP = {'soapenv': self.NS['s'], 'des': self.NS['des'], 'xd': self.NS['xd']} node_name = f"{{{self.NS['s']}}}Envelope" root = ET.Element(node_name, nsmap=NSMAP) @@ -284,7 +320,8 @@ class SATWebService(): attr = {'Algorithm': 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'} signature_method = ET.SubElement(signed_info, node_name, attr) - attr = {'URI': '#_0'} + # ~ attr = {'URI': '#_0'} + attr = {'URI': ''} reference = ET.SubElement(signed_info, 'Reference', attr) transforms = ET.SubElement(reference, 'Transforms') ET.SubElement(transforms, 'Transform', attr1) @@ -337,7 +374,7 @@ class SATWebService(): return data def _get_data_download(self, args): - NSMAP = {'s': self.NS['s'], 'des': self.NS['des'], 'xd': self.NS['xd']} + NSMAP = {'soapenv': self.NS['s'], 'des': self.NS['des'], 'xd': self.NS['xd']} node_name = f"{{{self.NS['s']}}}Envelope" root = ET.Element(node_name, nsmap=NSMAP) @@ -370,7 +407,8 @@ class SATWebService(): attr = {'Algorithm': 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'} signature_method = ET.SubElement(signed_info, node_name, attr) - attr = {'URI': '#_0'} + # ~ attr = {'URI': '#_0'} + attr = {'URI': ''} reference = ET.SubElement(signed_info, 'Reference', attr) transforms = ET.SubElement(reference, 'Transforms') ET.SubElement(transforms, 'Transform', attr1) @@ -409,7 +447,13 @@ class SATWebService(): headers['Authorization'] = f'WRAP access_token="{self._token}"' data = self._get_data_download(args) - response = httpx.post(self.URL['DOWN'], data=data, headers=headers) + try: + response = httpx.post(self.URL['DOWN'], + data=data, headers=headers, timeout=TIMEOUT) + except httpx.TimeoutException as exc: + print(exc) + return + if response.status_code != httpx.codes.OK: self._error = f'Status: {response.status_code} - {response.text}' return diff --git a/source/sat/util.py b/source/sat/util.py index 3328694..0cf37a2 100644 --- a/source/sat/util.py +++ b/source/sat/util.py @@ -342,6 +342,10 @@ def descargar_archivos(args): else: result = sat.verify(data) files = result['files'] + if result['EstadoSolicitud'] in ('1', '2'): + msg = 'Solicitud aún no aceptada...' + log.error(msg) + return for f in files: data['id_file'] = f