Add support for complements Pagos and Comercio

This commit is contained in:
el Mau 2022-11-09 12:57:38 -06:00
commit 15f51a8810
15 changed files with 2948 additions and 0 deletions

6
CHANGELOG.md Normal file
View File

@ -0,0 +1,6 @@
# Lista de cambios
## v 0.1.0 [00-Jun-22]
---
* Versión inicial

1
VERSION Normal file
View File

@ -0,0 +1 @@
0.0.1

1
requirements.txt Normal file
View File

@ -0,0 +1 @@
lxml

48
source/cfdi-txt.py Executable file
View File

@ -0,0 +1,48 @@
#!/usr/bin/env python
import argparse
from helper import util
def main(args):
if args.dir_trabajo:
entrada = util.join(args.dir_trabajo, 'entrada')
sellados = util.join(args.dir_trabajo, 'sellados')
timbrados = util.join(args.dir_trabajo, 'timbrados')
else:
entrada = args.dir_entrada
sellados = args.dir_sellados
timbrados = args.dir_timbrados
if args.generar:
util.make_cfdi(entrada, sellados, args.dir_cert, args.nombre)
if args.timbrar:
util.stamp_pac(sellados, timbrados)
return
def _process_command_line_arguments():
parser = argparse.ArgumentParser(
description='CFDI Test')
parser.add_argument('-dc', '--dir-cert', default='')
help = "Nombre de los certificados, el predeterminado es 'cert'"
parser.add_argument('-n', '--nombre', help=help, default='cert')
parser.add_argument('-d', '--dir-trabajo', dest='dir_trabajo', default='')
parser.add_argument('-de', '--dir-entrada', dest='dir_entrada', default='')
parser.add_argument('-ds', '--dir-sellados', dest='dir_sellados', default='')
parser.add_argument('-dt', '--dir-timbrados', dest='dir_timbrados', default='')
parser.add_argument('-g', '--generar', dest='generar',
action='store_true', default=False, required=False)
parser.add_argument('-t', '--timbrar', dest='timbrar',
action='store_true', default=False, required=False)
return parser.parse_args()
if __name__ == '__main__':
args = _process_command_line_arguments()
main(args)

12
source/conf.py Normal file
View File

@ -0,0 +1,12 @@
#!/usr/bin/env python3
DEBUG = True
TIMEOUT = 10
DELETE_FILES = False
PAC_AUTH = {
'user': 'AAA010101AAA',
'pass': 'PWD',
}

5
source/conf.py.ejemplo Normal file
View File

@ -0,0 +1,5 @@
#!/usr/bin/env python3
DEBUG = True
TIMEOUT = 10

417
source/helper/comercio.py Normal file
View File

@ -0,0 +1,417 @@
#!/usr/bin/env python
# ~
# ~ PAC
# ~ Copyright (C) 2018-2019 Mauricio Baeza Servin - public [AT] elmau [DOT] net
# ~
# ~ This program is free software: you can redistribute it and/or modify
# ~ it under the terms of the GNU General Public License as published by
# ~ the Free Software Foundation, either version 3 of the License, or
# ~ (at your option) any later version.
# ~
# ~ This program is distributed in the hope that it will be useful,
# ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
# ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# ~ GNU General Public License for more details.
# ~
# ~ You should have received a copy of the GNU General Public License
# ~ along with this program. If not, see <http://www.gnu.org/licenses/>.
import base64
import logging
import lxml.etree as ET
from . import mureq
from conf import DEBUG
LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
LOG_DATE = '%d/%m/%Y %H:%M:%S'
logging.addLevelName(logging.ERROR, '\033[1;41mERROR\033[1;0m')
logging.addLevelName(logging.DEBUG, '\x1b[33mDEBUG\033[1;0m')
logging.addLevelName(logging.INFO, '\x1b[32mINFO\033[1;0m')
logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT, datefmt=LOG_DATE)
log = logging.getLogger(__name__)
logging.getLogger('requests').setLevel(logging.ERROR)
TIMEOUT = 10
def pretty_print_POST(req):
"""
At this point it is completely built and ready
to be fired; it is "prepared".
However pay attention at the formatting used in
this function because it is programmed to be pretty
printed and may differ from the actual request.
"""
print('{}\n{}\r\n{}\r\n\r\n{}'.format(
'-----------START-----------',
req.method + ' ' + req.url,
'\r\n'.join('{}: {}'.format(k, v) for k, v in req.headers.items()),
req.body,
))
class PACComercioDigital(object):
ws = 'https://{}.comercio-digital.mx/{}'
api = 'https://app2.comercio-digital.mx/{}'
URL = {
'timbra': ws.format('ws', 'timbre4/timbrarV5'),
'cancel': ws.format('cancela', 'cancela4/cancelarUuid'),
'cancelxml': ws.format('cancela', 'cancela4/cancelarXml'),
'status': ws.format('cancela', 'arws/consultaEstatus'),
'client': api.format('x3/altaEmpresa'),
'saldo': api.format('x3/saldo'),
'timbres': api.format('x3/altaTimbres'),
}
CODES = {
'000': '000 Exitoso',
'004': '004 RFC {} ya esta dado de alta con Estatus=A',
'704': '704 Usuario Invalido',
'702': '702 Error rfc/empresa invalido',
}
NS_CFDI = {
'cfdi': 'http://www.sat.gob.mx/cfd/4',
'tdf': 'http://www.sat.gob.mx/TimbreFiscalDigital',
}
if DEBUG:
ws = 'https://pruebas.comercio-digital.mx/{}'
ws6 = 'https://pruebas6.comercio-digital.mx/arws/{}'
URL = {
'timbra': ws.format('timbre4/timbrarV5'),
'cancel': ws.format('cancela4/cancelarUuid'),
'cancelxml': ws.format('cancela4/cancelarXml'),
'status': ws6.format('consultaEstatus'),
'client': api.format('x3/altaEmpresa'),
'saldo': api.format('x3/saldo'),
'timbres': api.format('x3/altaTimbres'),
}
def __init__(self):
self.error = ''
def _error(self, msg):
self.error = str(msg)
# ~ log.error(msg)
return
def _post(self, url, data, headers={}):
result = None
headers['host'] = url.split('/')[2]
headers['Content-type'] = 'text/plain'
headers['Connection'] = 'Keep-Alive'
headers['Expect'] = '100-continue'
# ~ if DEBUG:
# ~ req = requests.Request('POST', url, headers=headers, data=data)
# ~ prepared = req.prepare()
# ~ pretty_print_POST(prepared)
try:
result = mureq.post(url, body=data, headers=headers, timeout=TIMEOUT)
except Exception as e:
self._error(e)
return result
def _validate_cfdi(self, xml):
"""
Comercio Digital solo soporta la declaración con doble comilla
"""
# ~ tree = ET.fromstring(xml.encode())
# ~ xml = ET.tostring(tree,
# ~ pretty_print=True, doctype='<?xml version="1.0" encoding="utf-8"?>')
return xml.encode('utf-8')
def stamp(self, cfdi, auth):
url = self.URL['timbra']
headers = {
'usrws': auth['user'],
'pwdws': auth['pass'],
'tipo': 'XML',
}
cfdi = self._validate_cfdi(cfdi)
result = self._post(url, cfdi, headers)
if result is None:
return ''
if result.status_code != 200:
return ''
if 'errmsg' in result.headers:
self._error(result.headers['errmsg'])
return ''
xml = result.content
tree = ET.fromstring(xml)
cfdi_uuid = tree.xpath(
'string(//cfdi:Complemento/tdf:TimbreFiscalDigital/@UUID)',
namespaces=self.NS_CFDI)
date_stamped = tree.xpath(
'string(//cfdi:Complemento/tdf:TimbreFiscalDigital/@FechaTimbrado)',
namespaces=self.NS_CFDI)
data = {
'xml': xml.decode(),
'uuid': cfdi_uuid,
'date': date_stamped,
}
return data
def _get_data_cancel(self, cfdi, info, auth):
info['tipo'] = 'cfdi'
info['key'] = base64.b64encode(info['key_enc']).decode()
info['cer'] = base64.b64encode(info['cer_ori']).decode()
NS_CFDI = {
'cfdi': 'http://www.sat.gob.mx/cfd/3',
'tdf': 'http://www.sat.gob.mx/TimbreFiscalDigital',
}
tree = ET.fromstring(cfdi.encode())
tipo = tree.xpath(
'string(//cfdi:Comprobante/@TipoDeComprobante)',
namespaces=NS_CFDI)
total = tree.xpath(
'string(//cfdi:Comprobante/@Total)',
namespaces=NS_CFDI)
rfc_emisor = tree.xpath(
'string(//cfdi:Comprobante/cfdi:Emisor/@Rfc)',
namespaces=NS_CFDI)
rfc_receptor = tree.xpath(
'string(//cfdi:Comprobante/cfdi:Receptor/@Rfc)',
namespaces=NS_CFDI)
uid = tree.xpath(
'string(//cfdi:Complemento/tdf:TimbreFiscalDigital/@UUID)',
namespaces=NS_CFDI)
data = (
f"USER={auth['user']}",
f"PWDW={auth['pass']}",
f"RFCE={rfc_emisor}",
f"UUID={uid}",
f"PWDK={info['pass']}",
f"KEYF={info['key']}",
f"CERT={info['cer']}",
f"TIPO1={info['tipo']}",
f"ACUS=SI",
f"RFCR={rfc_receptor}",
f"TIPOC={tipo}",
f"TOTAL={total}",
f"UUIDREL={info['args']['uuid']}",
f"MOTIVO={info['args']['reason']}",
)
return '\n'.join(data)
def cancel(self, cfdi, info, auth):
# ~ if DEBUG or not auth:
# ~ auth = AUTH
url = self.URL['cancel']
data = self._get_data_cancel(cfdi, info, auth)
result = self._post(url, data)
if result is None:
return ''
if result.status_code != 200:
return ''
if result.headers['codigo'] != '000':
self._error(result.headers['errmsg'])
return ''
tree = ET.fromstring(result.text)
date_cancel = tree.xpath('string(//Acuse/@Fecha)')[:19]
data = {
'acuse': result.text,
'date': date_cancel,
}
return data
def _get_headers_cancel_xml(self, cfdi, info, auth):
NS_CFDI = {
'cfdi': 'http://www.sat.gob.mx/cfd/3',
'tdf': 'http://www.sat.gob.mx/TimbreFiscalDigital',
}
tree = ET.fromstring(cfdi.encode())
tipocfdi = tree.xpath(
'string(//cfdi:Comprobante/@TipoDeComprobante)',
namespaces=NS_CFDI)
total = tree.xpath(
'string(//cfdi:Comprobante/@Total)',
namespaces=NS_CFDI)
rfc_receptor = tree.xpath(
'string(//cfdi:Comprobante/cfdi:Receptor/@Rfc)',
namespaces=NS_CFDI)
headers = {
'usrws': auth['user'],
'pwdws': auth['pass'],
'rfcr': rfc_receptor,
'total': total,
'tipocfdi': tipocfdi,
}
headers.update(info)
return headers
def cancel_xml(self, xml, auth, cfdi='', info={'tipo': 'cfdi'}):
# ~ if DEBUG or not auth:
# ~ auth = AUTH
url = self.URL['cancelxml']
headers = self._get_headers_cancel_xml(cfdi, info, auth)
result = self._post(url, xml, headers)
if result is None:
return ''
if result.status_code != 200:
return ''
if result.headers['codigo'] != '000':
self._error(result.headers['errmsg'])
return ''
tree = ET.fromstring(result.text)
date_cancel = tree.xpath('string(//Acuse/@Fecha)')[:19]
data = {
'acuse': result.text,
'date': date_cancel,
}
return data
def status(self, data, auth):
# ~ if not auth:
# ~ auth = AUTH
url = self.URL['status']
data = (
f"USER={auth['user']}",
f"PWDW={auth['pass']}",
f"RFCR={data['rfc_receptor']}",
f"RFCE={data['rfc_emisor']}",
f"TOTAL={data['total']}",
f"UUID={data['uuid']}",
)
data = '\n'.join(data)
result = self._post(url, data)
if result is None:
return ''
if result.status_code != 200:
self._error(result.status_code)
return self.error
return result.text
def _get_data_client(self, auth, values):
data = [f"usr_ws={auth['user']}", f"pwd_ws={auth['pass']}"]
fields = (
'rfc_contribuyente',
'nombre_contribuyente',
'calle',
'noExterior',
'noInterior',
'colonia',
'localidad',
'municipio',
'estado',
'pais',
'cp',
'contacto',
'telefono',
'email',
'rep_nom',
'rep_rfc',
'email_fact',
'pwd_asignado',
)
data += [f"{k}={values[k]}" for k in fields]
return '\n'.join(data)
def client_add(self, data, auth):
# ~ auth = AUTH
url = self.URL['client']
data = self._get_data_client(auth, data)
result = self._post(url, data)
if result is None:
return False
if result.status_code != 200:
self._error(f'Code: {result.status_code}')
return False
if result.text != self.CODES['000']:
self._error(result.text)
return False
return True
def client_balance(self, data, rfc=''):
url = self.URL['saldo']
host = url.split('/')[2]
headers = {
'Content-type': 'text/plain',
'Host': host,
'Connection' : 'Keep-Alive',
}
data = {'usr': data['user'], 'pwd': data['pass']}
try:
result = requests.get(url, params=data, headers=headers, timeout=TIMEOUT)
except ConnectionError as e:
self._error(e)
return ''
if result.status_code != 200:
return ''
if result.text == self.CODES['704']:
self._error(result.text)
return ''
if result.text == self.CODES['702']:
self._error(result.text)
return ''
return result.text
def client_add_timbres(self, data, auth):
# ~ if not auth:
# ~ auth = AUTH
url = self.URL['timbres']
data = '\n'.join((
f"usr_ws={auth['user']}",
f"pwd_ws={auth['pass']}",
f"rfc_recibir={data['rfc']}",
f"num_timbres={data['timbres']}"
))
result = self._post(url, data)
if result is None:
return False
if result.status_code != 200:
self._error(f'Code: {result.status_code}')
return False
if result.text != self.CODES['000']:
self._error(result.text)
return False
return True

393
source/helper/mureq.py Normal file
View File

@ -0,0 +1,393 @@
"""
mureq is a replacement for python-requests, intended to be vendored
in-tree by Linux systems software and other lightweight applications.
mureq is copyright 2021 by its contributors and is released under the
0BSD ("zero-clause BSD") license.
"""
import contextlib
import io
import os.path
import socket
import ssl
import sys
import urllib.parse
from http.client import HTTPConnection, HTTPSConnection, HTTPMessage, HTTPException
__version__ = '0.2.0'
__all__ = ['HTTPException', 'TooManyRedirects', 'Response',
'yield_response', 'request', 'get', 'post', 'head', 'put', 'patch', 'delete']
DEFAULT_TIMEOUT = 15.0
# e.g. "Python 3.8.10"
DEFAULT_UA = "Python " + sys.version.split()[0]
def request(method, url, *, read_limit=None, **kwargs):
"""request performs an HTTP request and reads the entire response body.
:param str method: HTTP method to request (e.g. 'GET', 'POST')
:param str url: URL to request
:param read_limit: maximum number of bytes to read from the body, or None for no limit
:type read_limit: int or None
:param kwargs: optional arguments defined by yield_response
:return: Response object
:rtype: Response
:raises: HTTPException
"""
with yield_response(method, url, **kwargs) as response:
try:
body = response.read(read_limit)
except HTTPException:
raise
except IOError as e:
raise HTTPException(str(e)) from e
return Response(response.url, response.status, _prepare_incoming_headers(response.headers), body)
def get(url, **kwargs):
"""get performs an HTTP GET request."""
return request('GET', url=url, **kwargs)
def post(url, body=None, **kwargs):
"""post performs an HTTP POST request."""
return request('POST', url=url, body=body, **kwargs)
def head(url, **kwargs):
"""head performs an HTTP HEAD request."""
return request('HEAD', url=url, **kwargs)
def put(url, body=None, **kwargs):
"""put performs an HTTP PUT request."""
return request('PUT', url=url, body=body, **kwargs)
def patch(url, body=None, **kwargs):
"""patch performs an HTTP PATCH request."""
return request('PATCH', url=url, body=body, **kwargs)
def delete(url, **kwargs):
"""delete performs an HTTP DELETE request."""
return request('DELETE', url=url, **kwargs)
@contextlib.contextmanager
def yield_response(method, url, *, unix_socket=None, timeout=DEFAULT_TIMEOUT, headers=None,
params=None, body=None, form=None, json=None, verify=True, source_address=None,
max_redirects=None, ssl_context=None):
"""yield_response is a low-level API that exposes the actual
http.client.HTTPResponse via a contextmanager.
Note that unlike mureq.Response, http.client.HTTPResponse does not
automatically canonicalize multiple appearances of the same header by
joining them together with a comma delimiter. To retrieve canonicalized
headers from the response, use response.getheader():
https://docs.python.org/3/library/http.client.html#http.client.HTTPResponse.getheader
:param str method: HTTP method to request (e.g. 'GET', 'POST')
:param str url: URL to request
:param unix_socket: path to Unix domain socket to query, or None for a normal TCP request
:type unix_socket: str or None
:param timeout: timeout in seconds, or None for no timeout (default: 15 seconds)
:type timeout: float or None
:param headers: HTTP headers as a mapping or list of key-value pairs
:param params: parameters to be URL-encoded and added to the query string, as a mapping or list of key-value pairs
:param body: payload body of the request
:type body: bytes or None
:param form: parameters to be form-encoded and sent as the payload body, as a mapping or list of key-value pairs
:param json: object to be serialized as JSON and sent as the payload body
:param bool verify: whether to verify TLS certificates (default: True)
:param source_address: source address to bind to for TCP
:type source_address: str or tuple(str, int) or None
:param max_redirects: maximum number of redirects to follow, or None (the default) for no redirection
:type max_redirects: int or None
:param ssl_context: TLS config to control certificate validation, or None for default behavior
:type ssl_context: ssl.SSLContext or None
:return: http.client.HTTPResponse, yielded as context manager
:rtype: http.client.HTTPResponse
:raises: HTTPException
"""
method = method.upper()
headers = _prepare_outgoing_headers(headers)
enc_params = _prepare_params(params)
body = _prepare_body(body, form, json, headers)
visited_urls = []
while max_redirects is None or len(visited_urls) <= max_redirects:
url, conn, path = _prepare_request(method, url, enc_params=enc_params, timeout=timeout, unix_socket=unix_socket, verify=verify, source_address=source_address, ssl_context=ssl_context)
enc_params = '' # don't reappend enc_params if we get redirected
visited_urls.append(url)
try:
try:
conn.request(method, path, headers=headers, body=body)
response = conn.getresponse()
except HTTPException:
raise
except IOError as e:
# wrap any IOError that is not already an HTTPException
# in HTTPException, exposing a uniform API for remote errors
raise HTTPException(str(e)) from e
redirect_url = _check_redirect(url, response.status, response.headers)
if max_redirects is None or redirect_url is None:
response.url = url # https://bugs.python.org/issue42062
yield response
return
else:
url = redirect_url
if response.status == 303:
# 303 See Other: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/303
method = 'GET'
finally:
conn.close()
raise TooManyRedirects(visited_urls)
class Response:
"""Response contains a completely consumed HTTP response.
:ivar str url: the retrieved URL, indicating whether a redirection occurred
:ivar int status_code: the HTTP status code
:ivar http.client.HTTPMessage headers: the HTTP headers
:ivar bytes body: the payload body of the response
"""
__slots__ = ('url', 'status_code', 'headers', 'body')
def __init__(self, url, status_code, headers, body):
self.url, self.status_code, self.headers, self.body = url, status_code, headers, body
def __repr__(self):
return f"Response(status_code={self.status_code:d})"
@property
def ok(self):
"""ok returns whether the response had a successful status code
(anything other than a 40x or 50x)."""
return not (400 <= self.status_code < 600)
@property
def content(self):
"""content returns the response body (the `body` member). This is an
alias for compatibility with requests.Response."""
return self.body
def raise_for_status(self):
"""raise_for_status checks the response's success code, raising an
exception for error codes."""
if not self.ok:
raise HTTPErrorStatus(self.status_code)
def json(self):
"""Attempts to deserialize the response body as UTF-8 encoded JSON."""
import json as jsonlib
return jsonlib.loads(self.body)
def _debugstr(self):
buf = io.StringIO()
print("HTTP", self.status_code, file=buf)
for k, v in self.headers.items():
print(f"{k}: {v}", file=buf)
print(file=buf)
try:
print(self.body.decode('utf-8'), file=buf)
except UnicodeDecodeError:
print(f"<{len(self.body)} bytes binary data>", file=buf)
return buf.getvalue()
class TooManyRedirects(HTTPException):
"""TooManyRedirects is raised when automatic following of redirects was
enabled, but the server redirected too many times without completing."""
pass
class HTTPErrorStatus(HTTPException):
"""HTTPErrorStatus is raised by Response.raise_for_status() to indicate an
HTTP error code (a 40x or a 50x). Note that a well-formed response with an
error code does not result in an exception unless raise_for_status() is
called explicitly.
"""
def __init__(self, status_code):
self.status_code = status_code
def __str__(self):
return f"HTTP response returned error code {self.status_code:d}"
# end public API, begin internal implementation details
_JSON_CONTENTTYPE = 'application/json'
_FORM_CONTENTTYPE = 'application/x-www-form-urlencoded'
class UnixHTTPConnection(HTTPConnection):
"""UnixHTTPConnection is a subclass of HTTPConnection that connects to a
Unix domain stream socket instead of a TCP address.
"""
def __init__(self, path, timeout=DEFAULT_TIMEOUT):
super(UnixHTTPConnection, self).__init__('localhost', timeout=timeout)
self._unix_path = path
def connect(self):
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
sock.settimeout(self.timeout)
sock.connect(self._unix_path)
except Exception:
sock.close()
raise
self.sock = sock
def _check_redirect(url, status, response_headers):
"""Return the URL to redirect to, or None for no redirection."""
if status not in (301, 302, 303, 307, 308):
return None
location = response_headers.get('Location')
if not location:
return None
parsed_location = urllib.parse.urlparse(location)
if parsed_location.scheme:
# absolute URL
return location
old_url = urllib.parse.urlparse(url)
if location.startswith('/'):
# absolute path on old hostname
return urllib.parse.urlunparse((old_url.scheme, old_url.netloc,
parsed_location.path, parsed_location.params,
parsed_location.query, parsed_location.fragment))
# relative path on old hostname
old_dir, _old_file = os.path.split(old_url.path)
new_path = os.path.join(old_dir, location)
return urllib.parse.urlunparse((old_url.scheme, old_url.netloc,
new_path, parsed_location.params,
parsed_location.query, parsed_location.fragment))
def _prepare_outgoing_headers(headers):
if headers is None:
headers = HTTPMessage()
elif not isinstance(headers, HTTPMessage):
new_headers = HTTPMessage()
if hasattr(headers, 'items'):
iterator = headers.items()
else:
iterator = iter(headers)
for k, v in iterator:
new_headers[k] = v
headers = new_headers
_setdefault_header(headers, 'User-Agent', DEFAULT_UA)
return headers
# XXX join multi-headers together so that get(), __getitem__(),
# etc. behave intuitively, then stuff them back in an HTTPMessage.
def _prepare_incoming_headers(headers):
headers_dict = {}
for k, v in headers.items():
headers_dict.setdefault(k, []).append(v)
result = HTTPMessage()
# note that iterating over headers_dict preserves the original
# insertion order in all versions since Python 3.6:
for k, vlist in headers_dict.items():
result[k] = ','.join(vlist)
return result
def _setdefault_header(headers, name, value):
if name not in headers:
headers[name] = value
def _prepare_body(body, form, json, headers):
if body is not None:
if not isinstance(body, bytes):
raise TypeError('body must be bytes or None', type(body))
return body
if json is not None:
_setdefault_header(headers, 'Content-Type', _JSON_CONTENTTYPE)
import json as jsonlib
return jsonlib.dumps(json).encode('utf-8')
if form is not None:
_setdefault_header(headers, 'Content-Type', _FORM_CONTENTTYPE)
return urllib.parse.urlencode(form, doseq=True)
return None
def _prepare_params(params):
if params is None:
return ''
return urllib.parse.urlencode(params, doseq=True)
def _prepare_request(method, url, *, enc_params='', timeout=DEFAULT_TIMEOUT, source_address=None, unix_socket=None, verify=True, ssl_context=None):
"""Parses the URL, returns the path and the right HTTPConnection subclass."""
parsed_url = urllib.parse.urlparse(url)
is_unix = (unix_socket is not None)
scheme = parsed_url.scheme.lower()
if scheme.endswith('+unix'):
scheme = scheme[:-5]
is_unix = True
if scheme == 'https':
raise ValueError("https+unix is not implemented")
if scheme not in ('http', 'https'):
raise ValueError("unrecognized scheme", scheme)
is_https = (scheme == 'https')
host = parsed_url.hostname
port = 443 if is_https else 80
if parsed_url.port:
port = parsed_url.port
if is_unix and unix_socket is None:
unix_socket = urllib.parse.unquote(parsed_url.netloc)
path = parsed_url.path
if parsed_url.query:
if enc_params:
path = f'{path}?{parsed_url.query}&{enc_params}'
else:
path = f'{path}?{parsed_url.query}'
else:
if enc_params:
path = f'{path}?{enc_params}'
else:
pass # just parsed_url.path in this case
if isinstance(source_address, str):
source_address = (source_address, 0)
if is_unix:
conn = UnixHTTPConnection(unix_socket, timeout=timeout)
elif is_https:
if ssl_context is None:
ssl_context = ssl.create_default_context()
if not verify:
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE
conn = HTTPSConnection(host, port, source_address=source_address, timeout=timeout,
context=ssl_context)
else:
conn = HTTPConnection(host, port, source_address=source_address, timeout=timeout)
munged_url = urllib.parse.urlunparse((parsed_url.scheme, parsed_url.netloc,
path, parsed_url.params,
'', parsed_url.fragment))
return munged_url, conn, path

1181
source/helper/util.py Normal file

File diff suppressed because it is too large Load Diff

17
source/settings.py Normal file
View File

@ -0,0 +1,17 @@
#!/usr/bin/env python3
import logging
import os
from conf import DEBUG, DELETE_FILES, PAC_AUTH
LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
LOG_DATE = '%d/%m/%Y %H:%M:%S'
logging.addLevelName(logging.ERROR, '\033[1;41mERROR\033[1;0m')
logging.addLevelName(logging.DEBUG, '\x1b[33mDEBUG\033[1;0m')
logging.addLevelName(logging.INFO, '\x1b[32mINFO\033[1;0m')
logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT, datefmt=LOG_DATE)
log = logging.getLogger(__name__)
current_path = os.path.dirname(__file__)
PATH_XSLT = os.path.join(current_path, 'xslt', 'cadena.xslt')

403
source/xslt/cadena.xslt Normal file
View File

@ -0,0 +1,403 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:cfdi="http://www.sat.gob.mx/cfd/4" xmlns:cce11="http://www.sat.gob.mx/ComercioExterior11" xmlns:donat="http://www.sat.gob.mx/donat" xmlns:divisas="http://www.sat.gob.mx/divisas" xmlns:implocal="http://www.sat.gob.mx/implocal" xmlns:leyendasFisc="http://www.sat.gob.mx/leyendasFiscales" xmlns:pfic="http://www.sat.gob.mx/pfic" xmlns:tpe="http://www.sat.gob.mx/TuristaPasajeroExtranjero" xmlns:nomina12="http://www.sat.gob.mx/nomina12" xmlns:registrofiscal="http://www.sat.gob.mx/registrofiscal" xmlns:pagoenespecie="http://www.sat.gob.mx/pagoenespecie" xmlns:aerolineas="http://www.sat.gob.mx/aerolineas" xmlns:valesdedespensa="http://www.sat.gob.mx/valesdedespensa" xmlns:notariospublicos="http://www.sat.gob.mx/notariospublicos" xmlns:vehiculousado="http://www.sat.gob.mx/vehiculousado" xmlns:servicioparcial="http://www.sat.gob.mx/servicioparcialconstruccion" xmlns:decreto="http://www.sat.gob.mx/renovacionysustitucionvehiculos" xmlns:destruccion="http://www.sat.gob.mx/certificadodestruccion" xmlns:obrasarte="http://www.sat.gob.mx/arteantiguedades" xmlns:ine="http://www.sat.gob.mx/ine" xmlns:iedu="http://www.sat.gob.mx/iedu" xmlns:ventavehiculos="http://www.sat.gob.mx/ventavehiculos" xmlns:detallista="http://www.sat.gob.mx/detallista" xmlns:ecc12="http://www.sat.gob.mx/EstadoDeCuentaCombustible12" xmlns:consumodecombustibles11="http://www.sat.gob.mx/ConsumoDeCombustibles11" xmlns:gceh="http://www.sat.gob.mx/GastosHidrocarburos10" xmlns:ieeh="http://www.sat.gob.mx/IngresosHidrocarburos10" xmlns:cartaporte20="http://www.sat.gob.mx/CartaPorte20" xmlns:pago20="http://www.sat.gob.mx/Pagos20">
<!-- Con el siguiente método se establece que la salida deberá ser en texto -->
<xsl:output method="text" version="1.0" encoding="UTF-8" indent="no"/>
<xsl:include href="utilerias.xslt"/>
<xsl:include href="leyendasFisc.xslt"/>
<xsl:include href="pagos20.xslt"/>
<xsl:include href="comercioexterior11.xslt"/>
<!--
<xsl:include href="nomina12.xslt"/>
<xsl:include href="divisas.xslt"/>
<xsl:include href="ine11.xslt"/>
<xsl:include href="iedu.xslt"/>
<xsl:include href="cartaporte20.xslt"/>
<xsl:include href="donat11.xslt"/>
<xsl:include href="implocal.xslt"/>
-->
<!--
<xsl:include href="http://www.sat.gob.mx/sitio_internet/cfd/pfic/pfic.xslt"/>
<xsl:include href="http://www.sat.gob.mx/sitio_internet/cfd/TuristaPasajeroExtranjero/TuristaPasajeroExtranjero.xslt"/>
<xsl:include href="http://www.sat.gob.mx/sitio_internet/cfd/cfdiregistrofiscal/cfdiregistrofiscal.xslt"/>
<xsl:include href="http://www.sat.gob.mx/sitio_internet/cfd/pagoenespecie/pagoenespecie.xslt"/>
<xsl:include href="http://www.sat.gob.mx/sitio_internet/cfd/aerolineas/aerolineas.xslt"/>
<xsl:include href="http://www.sat.gob.mx/sitio_internet/cfd/valesdedespensa/valesdedespensa.xslt"/>
<xsl:include href="http://www.sat.gob.mx/sitio_internet/cfd/notariospublicos/notariospublicos.xslt"/>
<xsl:include href="http://www.sat.gob.mx/sitio_internet/cfd/vehiculousado/vehiculousado.xslt"/>
<xsl:include href="http://www.sat.gob.mx/sitio_internet/cfd/servicioparcialconstruccion/servicioparcialconstruccion.xslt"/>
<xsl:include href="http://www.sat.gob.mx/sitio_internet/cfd/renovacionysustitucionvehiculos/renovacionysustitucionvehiculos.xslt"/>
<xsl:include href="http://www.sat.gob.mx/sitio_internet/cfd/certificadodestruccion/certificadodedestruccion.xslt"/>
<xsl:include href="http://www.sat.gob.mx/sitio_internet/cfd/arteantiguedades/obrasarteantiguedades.xslt"/>
<xsl:include href="http://www.sat.gob.mx/sitio_internet/cfd/ventavehiculos/ventavehiculos11.xslt"/>
<xsl:include href="http://www.sat.gob.mx/sitio_internet/cfd/detallista/detallista.xslt"/>
<xsl:include href="http://www.sat.gob.mx/sitio_internet/cfd/EstadoDeCuentaCombustible/ecc12.xslt"/>
<xsl:include href="http://www.sat.gob.mx/sitio_internet/cfd/consumodecombustibles/consumodeCombustibles11.xslt"/>
<xsl:include href="http://www.sat.gob.mx/sitio_internet/cfd/GastosHidrocarburos10/GastosHidrocarburos10.xslt"/>
<xsl:include href="http://www.sat.gob.mx/sitio_internet/cfd/IngresosHidrocarburos10/IngresosHidrocarburos.xslt"/>
-->
<!-- Aquí iniciamos el procesamiento de la cadena original con su | inicial y el terminador || -->
<xsl:template match="/">|<xsl:apply-templates select="/cfdi:Comprobante"/>||</xsl:template>
<!-- Aquí iniciamos el procesamiento de los datos incluidos en el comprobante -->
<xsl:template match="cfdi:Comprobante">
<!-- Iniciamos el tratamiento de los atributos de comprobante -->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Version"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Serie"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Folio"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Fecha"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@FormaPago"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@NoCertificado"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@CondicionesDePago"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@SubTotal"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Descuento"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Moneda"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@TipoCambio"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Total"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@TipoDeComprobante"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Exportacion"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@MetodoPago"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@LugarExpedicion"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Confirmacion"/>
</xsl:call-template>
<!--
Llamadas para procesar al los sub nodos del comprobante
-->
<xsl:apply-templates select="./cfdi:InformacionGlobal"/>
<xsl:apply-templates select="./cfdi:CfdiRelacionados"/>
<xsl:apply-templates select="./cfdi:Emisor"/>
<xsl:apply-templates select="./cfdi:Receptor"/>
<xsl:apply-templates select="./cfdi:Conceptos"/>
<xsl:apply-templates select="./cfdi:Impuestos"/>
<xsl:for-each select="./cfdi:Complemento">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:template>
<!-- Manejador de nodos tipo InformacionGlobal -->
<xsl:template match="cfdi:InformacionGlobal">
<!-- Iniciamos el tratamiento de los atributos del nodo tipo InformacionGlobal -->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Periodicidad"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Meses"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Año"/>
</xsl:call-template>
</xsl:template>
<!-- Manejador de nodos tipo CFDIRelacionados -->
<xsl:template match="cfdi:CfdiRelacionados">
<!-- Iniciamos el tratamiento de los atributos del nodo tipo CFDIRelacionados -->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@TipoRelacion"/>
</xsl:call-template>
<xsl:for-each select="./cfdi:CfdiRelacionado">
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@UUID"/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<!-- Manejador de nodos tipo Emisor -->
<xsl:template match="cfdi:Emisor">
<!-- Iniciamos el tratamiento de los atributos del nodo tipo Emisor -->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Rfc"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Nombre"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@RegimenFiscal"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@FacAtrAdquirente"/>
</xsl:call-template>
</xsl:template>
<!-- Manejador de nodos tipo Receptor -->
<xsl:template match="cfdi:Receptor">
<!-- Iniciamos el tratamiento de los atributos del nodo tipo Receptor -->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Rfc"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Nombre"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@DomicilioFiscalReceptor"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@ResidenciaFiscal"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NumRegIdTrib"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@RegimenFiscalReceptor"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@UsoCFDI"/>
</xsl:call-template>
</xsl:template>
<!-- Manejador de nodos tipo Conceptos -->
<xsl:template match="cfdi:Conceptos">
<!-- Llamada para procesar los distintos nodos tipo Concepto -->
<xsl:for-each select="./cfdi:Concepto">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:template>
<!--Manejador de nodos tipo Concepto-->
<xsl:template match="cfdi:Concepto">
<!-- Iniciamos el tratamiento de los atributos del Concepto -->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@ClaveProdServ"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NoIdentificacion"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Cantidad"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@ClaveUnidad"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Unidad"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Descripcion"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@ValorUnitario"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Importe"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Descuento"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@ObjetoImp"/>
</xsl:call-template>
<!-- Manejo de sub nodos de información Traslado de Conceptos:Concepto:Impuestos:Traslados-->
<xsl:for-each select="./cfdi:Impuestos/cfdi:Traslados/cfdi:Traslado">
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Base"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Impuesto"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@TipoFactor"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@TasaOCuota"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Importe"/>
</xsl:call-template>
</xsl:for-each>
<!-- Manejo de sub nodos de Retencion por cada una de los Conceptos:Concepto:Impuestos:Retenciones-->
<xsl:for-each select="./cfdi:Impuestos/cfdi:Retenciones/cfdi:Retencion">
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Base"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Impuesto"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@TipoFactor"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@TasaOCuota"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Importe"/>
</xsl:call-template>
</xsl:for-each>
<!-- Manejo de los distintos sub nodos a cuenta de terceros de forma indistinta a su grado de dependencia -->
<xsl:for-each select="./cfdi:ACuentaTerceros">
<xsl:apply-templates select="."/>
</xsl:for-each>
<!-- Manejo de los distintos sub nodos de información aduanera de forma indistinta a su grado de dependencia -->
<xsl:for-each select="./cfdi:InformacionAduanera">
<xsl:apply-templates select="."/>
</xsl:for-each>
<!-- Llamada al manejador de nodos de CuentaPredial en caso de existir -->
<xsl:if test="./cfdi:CuentaPredial">
<xsl:apply-templates select="./cfdi:CuentaPredial"/>
</xsl:if>
<!-- Llamada al manejador de nodos de ComplementoConcepto en caso de existir -->
<xsl:if test="./cfdi:ComplementoConcepto">
<xsl:apply-templates select="./cfdi:ComplementoConcepto"/>
</xsl:if>
<!-- Llamada al manejador de nodos de Parte en caso de existir -->
<xsl:for-each select=".//cfdi:Parte">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:template>
<!-- Manejador de nodos tipo ACuentaTerceros -->
<xsl:template match="cfdi:ACuentaTerceros">
<!-- Manejo de los atributos del nodo tipo ACuentaTerceros -->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@RfcACuentaTerceros"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@NombreACuentaTerceros"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@RegimenFiscalACuentaTerceros"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@DomicilioFiscalACuentaTerceros"/>
</xsl:call-template>
</xsl:template>
<!-- Manejador de nodos tipo Información Aduanera -->
<xsl:template match="cfdi:InformacionAduanera">
<!-- Manejo de los atributos de la información aduanera -->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@NumeroPedimento"/>
</xsl:call-template>
</xsl:template>
<!-- Manejador de nodos tipo Información CuentaPredial -->
<xsl:template match="cfdi:CuentaPredial">
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Numero"/>
</xsl:call-template>
</xsl:template>
<!-- Manejador de nodos tipo ComplementoConcepto -->
<xsl:template match="cfdi:ComplementoConcepto">
<xsl:for-each select="./*">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:template>
<!-- Manejador de nodos tipo Parte -->
<xsl:template match="cfdi:Parte">
<!-- Iniciamos el tratamiento de los atributos de Parte-->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@ClaveProdServ"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NoIdentificacion"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Cantidad"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Unidad"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Descripcion"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@ValorUnitario"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Importe"/>
</xsl:call-template>
<!-- Manejador de nodos tipo InformacionAduanera-->
<xsl:for-each select=".//cfdi:InformacionAduanera">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:template>
<!-- Manejador de nodos tipo Complemento -->
<xsl:template match="cfdi:Complemento">
<xsl:for-each select="./*">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:template>
<!-- Manejador de nodos tipo Domicilio fiscal -->
<xsl:template match="cfdi:Impuestos">
<!-- Manejo de sub nodos de Retencion por cada una de los Impuestos:Retenciones-->
<xsl:for-each select="./cfdi:Retenciones/cfdi:Retencion">
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Impuesto"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Importe"/>
</xsl:call-template>
</xsl:for-each>
<!-- Iniciamos el tratamiento de los atributos de TotalImpuestosRetenidos-->
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@TotalImpuestosRetenidos"/>
</xsl:call-template>
<!-- Manejo de sub nodos de información Traslado de Impuestos:Traslados-->
<xsl:for-each select="./cfdi:Traslados/cfdi:Traslado">
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Base"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Impuesto"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@TipoFactor"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@TasaOCuota"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Importe"/>
</xsl:call-template>
</xsl:for-each>
<!-- Iniciamos el tratamiento de los atributos de TotalImpuestosTrasladados-->
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@TotalImpuestosTrasladados"/>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>

View File

@ -0,0 +1,181 @@
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:cce11="http://www.sat.gob.mx/ComercioExterior11">
<xsl:template match="cce11:ComercioExterior">
<!--Manejador de nodos tipo ComercioExterior-->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Version" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@MotivoTraslado" />
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@TipoOperacion" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@ClaveDePedimento" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@CertificadoOrigen" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NumCertificadoOrigen" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NumeroExportadorConfiable" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Incoterm" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Subdivision" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Observaciones" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@TipoCambioUSD" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@TotalUSD" />
</xsl:call-template>
<!-- Iniciamos el manejo de los elementos hijo en la secuencia -->
<xsl:apply-templates select="./cce11:Emisor" />
<xsl:for-each select="./cce11:Propietario">
<xsl:apply-templates select="."/>
</xsl:for-each>
<xsl:apply-templates select="./cce11:Receptor" />
<xsl:for-each select="./cce11:Destinatario">
<xsl:apply-templates select="."/>
</xsl:for-each>
<xsl:apply-templates select="./cce11:Mercancias" />
</xsl:template>
<xsl:template match="cce11:Emisor">
<!-- Iniciamos el tratamiento de los atributos de cce11:Emisor-->
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Curp" />
</xsl:call-template>
<xsl:apply-templates select="./cce11:Domicilio" />
</xsl:template>
<xsl:template match="cce11:Propietario">
<!-- Tratamiento de los atributos de cce11:Propietario-->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@NumRegIdTrib" />
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@ResidenciaFiscal" />
</xsl:call-template>
</xsl:template>
<xsl:template match="cce11:Receptor">
<!-- Tratamiento de los atributos de cce11:Receptor-->
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NumRegIdTrib" />
</xsl:call-template>
<xsl:apply-templates select="./cce11:Domicilio" />
</xsl:template>
<xsl:template match="cce11:Destinatario">
<!-- Tratamiento de los atributos de cce11:Destinatario-->
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NumRegIdTrib" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Nombre" />
</xsl:call-template>
<!-- Manejo de los nodos dependientes -->
<xsl:for-each select="./cce11:Domicilio">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:template>
<xsl:template match="cce11:Mercancias">
<!-- Iniciamos el manejo de los nodos dependientes -->
<xsl:for-each select="./cce11:Mercancia">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:template>
<xsl:template match="cce11:Domicilio">
<!-- Iniciamos el tratamiento de los atributos de cce11:Domicilio-->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Calle" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NumeroExterior" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NumeroInterior" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Colonia" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Localidad" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Referencia" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Municipio" />
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Estado" />
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Pais" />
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@CodigoPostal" />
</xsl:call-template>
</xsl:template>
<xsl:template match="cce11:Mercancia">
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@NoIdentificacion" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@FraccionArancelaria" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@CantidadAduana" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@UnidadAduana" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@ValorUnitarioAduana" />
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@ValorDolares" />
</xsl:call-template>
<xsl:for-each select="./cce11:DescripcionesEspecificas">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:template>
<xsl:template match="cce11:DescripcionesEspecificas">
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Marca" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Modelo" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@SubModelo" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NumeroSerie" />
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:leyendasFisc="http://www.sat.gob.mx/leyendasFiscales">
<xsl:output method="text" version="1.0" encoding="UTF-8" indent="no"/>
<!-- Manejador de nodos tipo leyendasFiscales -->
<xsl:template match="leyendasFisc:LeyendasFiscales">
<!--Iniciamos el tratamiento de los atributos del complemento LeyendasFiscales -->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@version"/>
</xsl:call-template>
<!-- Manejo de los atributos de las leyendas Fiscales-->
<xsl:for-each select="./leyendasFisc:Leyenda">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:template>
<!-- Manejador de nodos tipo Información de las leyendas -->
<xsl:template match="leyendasFisc:Leyenda">
<!-- Manejo de los atributos de la leyenda -->
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@disposicionFiscal"/>
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@norma"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@textoLeyenda"/>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>

233
source/xslt/pagos20.xslt Normal file
View File

@ -0,0 +1,233 @@
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:pago20="http://www.sat.gob.mx/Pagos20">
<xsl:template match="pago20:Pagos">
<!--Manejador de Atributos Pagos-->
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Version" />
</xsl:call-template>
<!-- Iniciamos el manejo de los elementos de tipo Totales. -->
<xsl:for-each select="./pago20:Totales">
<xsl:apply-templates select="."/>
</xsl:for-each>
<!-- Iniciamos el manejo de los elementos de tipo Pago. -->
<xsl:for-each select="./pago20:Pago">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:template>
<!-- Iniciamos el tratamiento de los atributos de pago20:Totales. -->
<xsl:template match="pago20:Totales">
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@TotalRetencionesIVA" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@TotalRetencionesISR" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@TotalRetencionesIEPS" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@TotalTrasladosBaseIVA16" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@TotalTrasladosImpuestoIVA16" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@TotalTrasladosBaseIVA8" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@TotalTrasladosImpuestoIVA8" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@TotalTrasladosBaseIVA0" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@TotalTrasladosImpuestoIVA0" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@TotalTrasladosBaseIVAExento" />
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@MontoTotalPagos" />
</xsl:call-template>
</xsl:template>
<!-- Iniciamos el tratamiento de los atributos de pago20:Pago -->
<xsl:template match="pago20:Pago">
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@FechaPago" />
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@FormaDePagoP" />
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@MonedaP" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@TipoCambioP" />
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@Monto" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NumOperacion" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@RfcEmisorCtaOrd" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@NomBancoOrdExt" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@CtaOrdenante" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@RfcEmisorCtaBen" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@CtaBeneficiario" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@TipoCadPago" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@CertPago" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@CadPago" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@SelloPago" />
</xsl:call-template>
<!-- Iniciamos el tratamiento de los atributos de pago20:DocumentoRelacionado. -->
<xsl:for-each select="./pago20:DoctoRelacionado">
<xsl:apply-templates select="."/>
</xsl:for-each>
<!-- Iniciamos el tratamiento de los atributos de pago20:ImpuestosP. -->
<xsl:for-each select="./pago20:ImpuestosP">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:template>
<!-- Iniciamos el tratamiento de los atributos de pago20:DoctoRelacionado. -->
<xsl:template match="pago20:DoctoRelacionado">
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@IdDocumento" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Serie" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@Folio" />
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@MonedaDR" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@EquivalenciaDR" />
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@NumParcialidad" />
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@ImpSaldoAnt" />
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@ImpPagado" />
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@ImpSaldoInsoluto" />
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@ObjetoImpDR" />
</xsl:call-template>
<!-- Iniciamos el tratamiento de los atributos del subnodo ImpuestosDR-RetencionesDR-RetencionDR. -->
<xsl:for-each select="./pago20:ImpuestosDR/pago20:RetencionesDR/pago20:RetencionDR">
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@BaseDR"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@ImpuestoDR" />
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@TipoFactorDR" />
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@TasaOCuotaDR" />
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@ImporteDR" />
</xsl:call-template>
</xsl:for-each>
<!-- Iniciamos el tratamiento de los atributos del subnodo ImpuestosDR-TrasladosDR-TrasladoDR. -->
<xsl:for-each select="./pago20:ImpuestosDR/pago20:TrasladosDR/pago20:TrasladoDR">
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@BaseDR"/>
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@ImpuestoDR" />
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@TipoFactorDR" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@TasaOCuotaDR" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@ImporteDR" />
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<!-- Iniciamos el tratamiento de los atributos de pago20:ImpuestosP. -->
<xsl:template match="pago20:ImpuestosP">
<xsl:apply-templates select="./pago20:RetencionesP"/>
<xsl:apply-templates select="./pago20:TrasladosP"/>
</xsl:template>
<xsl:template match="pago20:RetencionesP">
<xsl:for-each select="./pago20:RetencionP">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:template>
<xsl:template match="pago20:TrasladosP">
<xsl:for-each select="./pago20:TrasladoP">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:template>
<xsl:template match="pago20:RetencionP">
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@ImpuestoP" />
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@ImporteP" />
</xsl:call-template>
</xsl:template>
<xsl:template match="pago20:TrasladoP">
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@BaseP" />
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@ImpuestoP" />
</xsl:call-template>
<xsl:call-template name="Requerido">
<xsl:with-param name="valor" select="./@TipoFactorP" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@TasaOCuotaP" />
</xsl:call-template>
<xsl:call-template name="Opcional">
<xsl:with-param name="valor" select="./@ImporteP" />
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<!-- Manejador de datos requeridos -->
<xsl:template name="Requerido">
<xsl:param name="valor"/>|<xsl:call-template name="ManejaEspacios">
<xsl:with-param name="s" select="$valor"/>
</xsl:call-template>
</xsl:template>
<!-- Manejador de datos opcionales -->
<xsl:template name="Opcional">
<xsl:param name="valor"/>
<xsl:if test="$valor">|<xsl:call-template name="ManejaEspacios"><xsl:with-param name="s" select="$valor"/></xsl:call-template></xsl:if>
</xsl:template>
<!-- Normalizador de espacios en blanco -->
<xsl:template name="ManejaEspacios">
<xsl:param name="s"/>
<xsl:value-of select="normalize-space(string($s))"/>
</xsl:template>
</xsl:stylesheet>