Compare commits
5 Commits
Author | SHA1 | Date |
---|---|---|
Mauricio Baeza | 719c5f78da | |
Mauricio Baeza | cdd223ffc8 | |
Mauricio Baeza | ce57ea169a | |
Mauricio Baeza | b12525e475 | |
Mauricio Baeza | bb3e921a54 |
|
@ -1,3 +1,3 @@
|
|||
# cfdi-trimbra
|
||||
|
||||
Webservice para timbrado de CFDI
|
||||
Webservice para timbrado de CFDI con Django
|
||||
|
|
|
@ -2,3 +2,4 @@ xmlsec
|
|||
cryptography
|
||||
lxml
|
||||
httpx
|
||||
django
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from .models import Clients
|
||||
|
||||
|
||||
@admin.register(Clients)
|
||||
class AdminClients(admin.ModelAdmin):
|
||||
actions_on_top = True
|
|
@ -0,0 +1,6 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ApiConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'api'
|
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# ~ Establece un token personalizado para encriptar las claves
|
||||
# ~ from secrets import token_hex
|
||||
# ~ token_hex(32)
|
||||
|
||||
TOKEN = ''
|
||||
|
||||
# ~ Token maestro
|
||||
API_TOKEN = ''
|
|
@ -0,0 +1,35 @@
|
|||
# Generated by Django 3.2 on 2021-05-04 19:15
|
||||
|
||||
import api.models
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Clients',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('rfc', api.models.RFCField(max_length=13, unique=True, verbose_name='RFC')),
|
||||
('token', models.CharField(max_length=100, verbose_name='Token')),
|
||||
('key', models.BinaryField(blank=True, default=b'', verbose_name='Key')),
|
||||
('cer', models.BinaryField(blank=True, default=b'', verbose_name='Cer')),
|
||||
('serial_number', models.CharField(blank=True, default='', max_length=100, verbose_name='Fiel Serie')),
|
||||
('date_from', models.DateTimeField(blank=True, null=True, verbose_name='Desde')),
|
||||
('date_to', models.DateTimeField(blank=True, null=True, verbose_name='Hasta')),
|
||||
('user', models.CharField(blank=True, default='', max_length=50, verbose_name='Usuario')),
|
||||
('contra', models.CharField(blank=True, default='', max_length=50, verbose_name='Contraseña')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Cliente',
|
||||
'verbose_name_plural': 'Clientes',
|
||||
'ordering': ['rfc'],
|
||||
},
|
||||
),
|
||||
]
|
|
@ -0,0 +1,64 @@
|
|||
import re
|
||||
from datetime import datetime
|
||||
|
||||
from django.db import models
|
||||
from django.core.validators import MinLengthValidator
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
|
||||
def validate_rfc(value):
|
||||
l = 4
|
||||
if len(value)==12:
|
||||
l = 3
|
||||
s = value[0:l]
|
||||
r = re.match('[A-ZÑ&]{%s}' % l, s)
|
||||
if not r:
|
||||
raise ValidationError('Caracteres inválidos al inicio del RFC')
|
||||
|
||||
s = value[-3:]
|
||||
r = re.match('[A-Z0-9]{3}', s)
|
||||
if not r:
|
||||
raise ValidationError('Caracteres inválidos al final del RFC')
|
||||
|
||||
s = value[l:l+6]
|
||||
r = re.match('[0-9]{6}', s)
|
||||
msg = 'Fecha inválida en el RFC'
|
||||
if not r:
|
||||
raise ValidationError(msg)
|
||||
|
||||
try:
|
||||
datetime.strptime(s,"%y%m%d")
|
||||
except:
|
||||
raise ValidationError(msg)
|
||||
|
||||
|
||||
class RFCField(models.CharField):
|
||||
description = 'Field to RFC of México'
|
||||
default_validators = [MinLengthValidator(12), validate_rfc]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs['max_length'] = 13
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def to_python(self, value):
|
||||
return value.upper()
|
||||
|
||||
|
||||
class Clients(models.Model):
|
||||
rfc = RFCField('RFC', unique=True)
|
||||
token = models.CharField('Token', max_length=100)
|
||||
key = models.BinaryField('Key', default=b'', blank=True)
|
||||
cer = models.BinaryField('Cer', default=b'', blank=True)
|
||||
serial_number = models.CharField('Fiel Serie', default='', blank=True, max_length=100)
|
||||
date_from = models.DateTimeField('Desde', null=True, blank=True)
|
||||
date_to = models.DateTimeField('Hasta', null=True, blank=True)
|
||||
user = models.CharField('Usuario', default='', blank=True, max_length=50)
|
||||
contra = models.CharField('Contraseña', default='', blank=True, max_length=50)
|
||||
|
||||
class Meta:
|
||||
ordering = ['rfc']
|
||||
verbose_name = 'Cliente'
|
||||
verbose_name_plural = 'Clientes'
|
||||
|
||||
def __str__(self):
|
||||
return self.rfc
|
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from django.urls import path
|
||||
from api.views import ViewClients, ViewCfdi
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path('clients/', ViewClients.as_view()),
|
||||
path('cfdi/', ViewCfdi.as_view()),
|
||||
]
|
|
@ -0,0 +1,261 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import base64
|
||||
import datetime
|
||||
import getpass
|
||||
from pathlib import Path
|
||||
|
||||
import xmlsec
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from cryptography import x509
|
||||
from cryptography.x509.oid import NameOID
|
||||
from cryptography.x509.oid import ExtensionOID
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives.asymmetric import padding
|
||||
|
||||
from ..conf import TOKEN
|
||||
|
||||
|
||||
class SATCertificate(object):
|
||||
|
||||
def __init__(self, cer=b'', key=b'', password=''):
|
||||
self._error = ''
|
||||
self._init_values()
|
||||
self._get_data_cer(cer)
|
||||
self._get_data_key(key, password)
|
||||
|
||||
def _init_values(self):
|
||||
self._rfc = ''
|
||||
self._serial_number = ''
|
||||
self._not_before = None
|
||||
self._not_after = None
|
||||
self._is_fiel = False
|
||||
self._are_couple = False
|
||||
self._is_valid_time = False
|
||||
self._cer = b''
|
||||
self._cer_pem = ''
|
||||
self._cer_txt = ''
|
||||
self._key_enc = b''
|
||||
self._p12 = b''
|
||||
self._cer_modulus = 0
|
||||
self._key_modulus = 0
|
||||
return
|
||||
|
||||
def __str__(self):
|
||||
msg = '\tRFC: {}\n'.format(self.rfc)
|
||||
msg += '\tNo de Serie: {}\n'.format(self.serial_number)
|
||||
msg += '\tVálido desde: {}\n'.format(self.not_before)
|
||||
msg += '\tVálido hasta: {}\n'.format(self.not_after)
|
||||
msg += '\tEs vigente: {}\n'.format(self.is_valid_time)
|
||||
msg += '\tSon pareja: {}\n'.format(self.are_couple)
|
||||
msg += '\tEs FIEL: {}\n'.format(self.is_fiel)
|
||||
return msg
|
||||
|
||||
def __bool__(self):
|
||||
return self.is_valid
|
||||
|
||||
def _get_hash(self):
|
||||
digest = hashes.Hash(hashes.SHA512(), default_backend())
|
||||
digest.update(self._rfc.encode())
|
||||
digest.update(self._serial_number.encode())
|
||||
digest.update(TOKEN.encode())
|
||||
return digest.finalize()
|
||||
|
||||
def _get_data_cer(self, cer):
|
||||
obj = x509.load_der_x509_certificate(cer, default_backend())
|
||||
self._rfc = obj.subject.get_attributes_for_oid(
|
||||
NameOID.X500_UNIQUE_IDENTIFIER)[0].value.split(' ')[0]
|
||||
self._serial_number = '{0:x}'.format(obj.serial_number)[1::2]
|
||||
self._not_before = obj.not_valid_before
|
||||
self._not_after = obj.not_valid_after
|
||||
now = datetime.datetime.utcnow()
|
||||
self._is_valid_time = (now > self.not_before) and (now < self.not_after)
|
||||
if not self._is_valid_time:
|
||||
msg = 'El certificado no es vigente'
|
||||
self._error = msg
|
||||
|
||||
self._is_fiel = obj.extensions.get_extension_for_oid(
|
||||
ExtensionOID.KEY_USAGE).value.key_agreement
|
||||
|
||||
self._cer = cer
|
||||
self._cer_pem = obj.public_bytes(serialization.Encoding.PEM).decode()
|
||||
self._cer_txt = ''.join(self._cer_pem.split('\n')[1:-2])
|
||||
self._cer_modulus = obj.public_key().public_numbers().n
|
||||
return
|
||||
|
||||
def _get_data_key(self, key, password):
|
||||
self._key_enc = key
|
||||
if not key or not password:
|
||||
return
|
||||
|
||||
try:
|
||||
obj = serialization.load_der_private_key(
|
||||
key, password.encode(), default_backend())
|
||||
except ValueError:
|
||||
msg = 'La contraseña es incorrecta'
|
||||
self._error = msg
|
||||
return
|
||||
|
||||
p = self._get_hash()
|
||||
self._key_enc = obj.private_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PrivateFormat.PKCS8,
|
||||
encryption_algorithm=serialization.BestAvailableEncryption(p)
|
||||
)
|
||||
|
||||
self._key_modulus = obj.public_key().public_numbers().n
|
||||
self._are_couple = self._cer_modulus == self._key_modulus
|
||||
if not self._are_couple:
|
||||
msg = 'El CER y el KEY no son pareja'
|
||||
self._error = msg
|
||||
return
|
||||
|
||||
def _get_key(self, password):
|
||||
if not password:
|
||||
password = self._get_hash()
|
||||
private_key = serialization.load_pem_private_key(
|
||||
self._key_enc, password=password, backend=default_backend())
|
||||
return private_key
|
||||
|
||||
def _get_key_pem(self):
|
||||
obj = self._get_key('')
|
||||
key_pem = obj.private_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PrivateFormat.PKCS8,
|
||||
encryption_algorithm=serialization.NoEncryption()
|
||||
)
|
||||
return key_pem
|
||||
|
||||
# Not work
|
||||
def _get_p12(self):
|
||||
obj = serialization.pkcs12.serialize_key_and_certificates('test',
|
||||
self.key_pem, self.cer_pem, None,
|
||||
encryption_algorithm=serialization.NoEncryption()
|
||||
)
|
||||
return obj
|
||||
|
||||
def sign(self, data, password=''):
|
||||
private_key = self._get_key(password)
|
||||
firma = private_key.sign(data, padding.PKCS1v15(), hashes.SHA256())
|
||||
return base64.b64encode(firma).decode()
|
||||
|
||||
def sign_xml(self, tree):
|
||||
node = xmlsec.tree.find_node(tree, xmlsec.constants.NodeSignature)
|
||||
ctx = xmlsec.SignatureContext()
|
||||
key = xmlsec.Key.from_memory(self.key_pem, xmlsec.constants.KeyDataFormatPem)
|
||||
ctx.key = key
|
||||
ctx.sign(node)
|
||||
node = xmlsec.tree.find_node(tree, 'X509Certificate')
|
||||
node.text = self.cer_txt
|
||||
return tree
|
||||
|
||||
@property
|
||||
def rfc(self):
|
||||
return self._rfc
|
||||
|
||||
@property
|
||||
def serial_number(self):
|
||||
return self._serial_number
|
||||
|
||||
@property
|
||||
def not_before(self):
|
||||
return self._not_before
|
||||
|
||||
@property
|
||||
def not_after(self):
|
||||
return self._not_after
|
||||
|
||||
@property
|
||||
def is_fiel(self):
|
||||
return self._is_fiel
|
||||
|
||||
@property
|
||||
def are_couple(self):
|
||||
return self._are_couple
|
||||
|
||||
@property
|
||||
def is_valid(self):
|
||||
return not bool(self.error)
|
||||
|
||||
@property
|
||||
def is_valid_time(self):
|
||||
return self._is_valid_time
|
||||
|
||||
@property
|
||||
def cer(self):
|
||||
return self._cer
|
||||
|
||||
@property
|
||||
def cer_pem(self):
|
||||
return self._cer_pem.encode()
|
||||
|
||||
@property
|
||||
def cer_txt(self):
|
||||
return self._cer_txt
|
||||
|
||||
@property
|
||||
def key_pem(self):
|
||||
return self._get_key_pem()
|
||||
|
||||
@property
|
||||
def key_enc(self):
|
||||
return self._key_enc
|
||||
|
||||
@property
|
||||
def p12(self):
|
||||
return self._get_p12()
|
||||
|
||||
@property
|
||||
def error(self):
|
||||
return self._error
|
||||
|
||||
|
||||
def main(args):
|
||||
contra = getpass.getpass('Introduce la contraseña del archivo KEY: ')
|
||||
#contra = '12345678a'
|
||||
if not contra.strip():
|
||||
msg = 'La contraseña es requerida'
|
||||
print(msg)
|
||||
return
|
||||
|
||||
path_cer = Path(args.cer)
|
||||
path_key = Path(args.key)
|
||||
|
||||
if not path_cer.is_file():
|
||||
msg = 'El archivo CER es necesario'
|
||||
print(msg)
|
||||
return
|
||||
|
||||
if not path_key.is_file():
|
||||
msg = 'El archivo KEY es necesario'
|
||||
print(msg)
|
||||
return
|
||||
|
||||
cer = path_cer.read_bytes()
|
||||
key = path_key.read_bytes()
|
||||
cert = SATCertificate(cer, key, contra)
|
||||
|
||||
if cert.error:
|
||||
print(cert.error)
|
||||
else:
|
||||
print(cert)
|
||||
return
|
||||
|
||||
|
||||
def _process_command_line_arguments():
|
||||
parser = argparse.ArgumentParser(description='CFDI Certificados')
|
||||
|
||||
help = 'Archivo CER'
|
||||
parser.add_argument('-c', '--cer', help=help, default='')
|
||||
help = 'Archivo KEY'
|
||||
parser.add_argument('-k', '--key', help=help, default='')
|
||||
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = _process_command_line_arguments()
|
||||
main(args)
|
|
@ -0,0 +1,271 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from datetime import datetime
|
||||
import lxml.etree as ET
|
||||
|
||||
|
||||
class CFDI(object):
|
||||
_version = '3.3'
|
||||
_prefix = 'cfdi'
|
||||
_xmlns = 'http://www.sat.gob.mx/cfd/3'
|
||||
_schema = f'{_xmlns} http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv33.xsd'
|
||||
_pagos = 'http://www.sat.gob.mx/Pagos'
|
||||
NS = {'cfdi': 'http://www.sat.gob.mx/cfd/3'}
|
||||
PAGOS = {
|
||||
'version': '1.0',
|
||||
'prefix': _pagos,
|
||||
'ns': {'pago10': _pagos},
|
||||
'schema': f' {_pagos} http://www.sat.gob.mx/sitio_internet/cfd/Pagos/Pagos10.xsd',
|
||||
}
|
||||
_nomina = 'http://www.sat.gob.mx/nomina12'
|
||||
NOMINA = {
|
||||
'version': '1.2',
|
||||
'prefix': _nomina,
|
||||
'ns': {'nomina12': _nomina},
|
||||
'schema': f' {_nomina} http://www.sat.gob.mx/sitio_internet/cfd/nomina/nomina12.xsd',
|
||||
}
|
||||
_tax_locales = 'http://www.sat.gob.mx/implocal'
|
||||
TAX_LOCALES = {
|
||||
'version': '1.0',
|
||||
'prefix': _tax_locales,
|
||||
'ns': {'implocal': _tax_locales},
|
||||
'schema': f' {_tax_locales} http://www.sat.gob.mx/sitio_internet/cfd/implocal/implocal.xsd',
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.error = ''
|
||||
self._root = None
|
||||
|
||||
@property
|
||||
def xml(self):
|
||||
cfdi = ET.tostring(self._root,
|
||||
pretty_print=True, xml_declaration=True, encoding='utf-8')
|
||||
return cfdi.decode()
|
||||
|
||||
def make_xml(self, data):
|
||||
self._validate_data(data)
|
||||
self._comprobante(data['comprobante'])
|
||||
self._relacionados(data.get('relacionados', {}))
|
||||
self._emisor(data['emisor'])
|
||||
self._receptor(data['receptor'])
|
||||
self._conceptos(data['conceptos'])
|
||||
self._impuestos(data.get('impuestos', {}))
|
||||
self._complementos(data.get('complementos', {}))
|
||||
return
|
||||
|
||||
def stamp(self, cert, path_xslt):
|
||||
xslt = open(path_xslt, 'rb')
|
||||
transfor = ET.XSLT(ET.parse(xslt))
|
||||
|
||||
self._root.attrib['NoCertificado'] = cert.serial_number
|
||||
self._root.attrib['Certificado'] = cert.cer_txt
|
||||
|
||||
cadena = str(transfor(self._root)).encode()
|
||||
self._root.attrib['Sello'] = cert.sign(cadena)
|
||||
xslt.close()
|
||||
return
|
||||
|
||||
def _validate_data(self, data):
|
||||
self._node_complement = False
|
||||
self._exists_pagos = False
|
||||
self._exists_nomina = False
|
||||
self._exists_tax_locales = False
|
||||
if not 'complementos' in data:
|
||||
return
|
||||
|
||||
complements = data['complementos']
|
||||
self._exists_pagos = 'pagos' in complements
|
||||
self._exists_nomina = 'nomina' in complements
|
||||
self._exists_tax_locales = 'impuestos_locales' in complements
|
||||
|
||||
if self._exists_pagos:
|
||||
self._node_complement = True
|
||||
self._schema += self.PAGOS['schema']
|
||||
if self._exists_nomina:
|
||||
self._node_complement = True
|
||||
self._schema += self.NOMINA['schema']
|
||||
if self._exists_tax_locales:
|
||||
self._node_complement = True
|
||||
self._schema += self.TAX_LOCALES['schema']
|
||||
return
|
||||
|
||||
def _comprobante(self, attr):
|
||||
NSMAP = {
|
||||
self._prefix: self._xmlns,
|
||||
'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
|
||||
}
|
||||
if self._exists_pagos:
|
||||
NSMAP.update(self.PAGOS['ns'])
|
||||
if self._exists_nomina:
|
||||
NSMAP.update(self.NOMINA['ns'])
|
||||
if self._exists_tax_locales:
|
||||
NSMAP.update(self.TAX_LOCALES['ns'])
|
||||
|
||||
attr_qname = ET.QName(
|
||||
'http://www.w3.org/2001/XMLSchema-instance', 'schemaLocation')
|
||||
schema = {attr_qname: self._schema}
|
||||
|
||||
if not 'Version' in attr:
|
||||
attr['Version'] = self._version
|
||||
if not 'Fecha' in attr:
|
||||
attr['Fecha'] = datetime.now().isoformat()[:19]
|
||||
|
||||
node_name = f'{{{self._xmlns}}}Comprobante'
|
||||
self._root = ET.Element(node_name, schema, **attr, nsmap=NSMAP)
|
||||
return
|
||||
|
||||
def _relacionados(self, attr):
|
||||
return
|
||||
|
||||
def _emisor(self, attr):
|
||||
node_name = f'{{{self._xmlns}}}Emisor'
|
||||
emisor = ET.SubElement(self._root, node_name, attr)
|
||||
return
|
||||
|
||||
def _receptor(self, attr):
|
||||
node_name = f'{{{self._xmlns}}}Receptor'
|
||||
emisor = ET.SubElement(self._root, node_name, attr)
|
||||
return
|
||||
|
||||
def _conceptos(self, data):
|
||||
node_name = f'{{{self._xmlns}}}Conceptos'
|
||||
conceptos = ET.SubElement(self._root, node_name)
|
||||
for row in data:
|
||||
complemento = row.pop('complemento', {})
|
||||
taxes = row.pop('impuestos', {})
|
||||
|
||||
node_name = f'{{{self._xmlns}}}Concepto'
|
||||
concepto = ET.SubElement(conceptos, node_name, row)
|
||||
|
||||
if not taxes:
|
||||
continue
|
||||
|
||||
if taxes['traslados'] or taxes['retenciones']:
|
||||
node_name = f'{{{self._xmlns}}}Impuestos'
|
||||
impuestos = ET.SubElement(concepto, node_name)
|
||||
if 'traslados' in taxes and taxes['traslados']:
|
||||
node_name = f'{{{self._xmlns}}}Traslados'
|
||||
traslados = ET.SubElement(impuestos, node_name)
|
||||
node_name = f'{{{self._xmlns}}}Traslado'
|
||||
for traslado in taxes['traslados']:
|
||||
ET.SubElement(traslados, node_name, traslado)
|
||||
if 'retenciones' in taxes and taxes['retenciones']:
|
||||
node_name = f'{{{self._xmlns}}}Retenciones'
|
||||
retenciones = ET.SubElement(impuestos, node_name)
|
||||
node_name = f'{{{self._xmlns}}}Retencion'
|
||||
for retencion in taxes['retenciones']:
|
||||
ET.SubElement(retenciones, node_name, retencion)
|
||||
return
|
||||
|
||||
def _impuestos(self, data):
|
||||
if not data:
|
||||
return
|
||||
|
||||
node_name = f'{{{self._xmlns}}}Impuestos'
|
||||
retenciones = data.pop('retenciones', ())
|
||||
traslados = data.pop('traslados', ())
|
||||
taxes = ET.SubElement(self._root, node_name, data)
|
||||
|
||||
if retenciones:
|
||||
node_name = f'{{{self._xmlns}}}Retenciones'
|
||||
subnode = ET.SubElement(taxes, node_name)
|
||||
node_name = f'{{{self._xmlns}}}Retencion'
|
||||
for row in retenciones:
|
||||
ET.SubElement(subnode, node_name, row)
|
||||
|
||||
if traslados:
|
||||
node_name = f'{{{self._xmlns}}}Traslados'
|
||||
subnode = ET.SubElement(taxes, node_name)
|
||||
node_name = f'{{{self._xmlns}}}Traslado'
|
||||
for row in traslados:
|
||||
ET.SubElement(subnode, node_name, row)
|
||||
|
||||
return
|
||||
|
||||
def _complementos(self, data):
|
||||
if not self._node_complement:
|
||||
return
|
||||
|
||||
node_name = f'{{{self._xmlns}}}Complemento'
|
||||
complemento = ET.SubElement(self._root, node_name)
|
||||
|
||||
if self._exists_pagos:
|
||||
self._pagos(complemento, data['pagos'])
|
||||
if self._exists_nomina:
|
||||
self._nomina(complemento, data['nomina'])
|
||||
if self._exists_tax_locales:
|
||||
self._tax_locales(complemento, data['impuestos_locales'])
|
||||
return
|
||||
|
||||
def _pagos(self, complemento, data):
|
||||
node_name = f"{{{self.PAGOS['prefix']}}}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_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)
|
||||
return
|
||||
|
||||
def _nomina(self, complemento, data):
|
||||
emisor = data.pop('emisor')
|
||||
receptor = data.pop('receptor')
|
||||
percepciones = data.pop('percepciones', {})
|
||||
deducciones = data.pop('deducciones', {})
|
||||
otros = data.pop('otros', ())
|
||||
|
||||
if not 'Version' in data:
|
||||
data['Version'] = self.NOMINA['version']
|
||||
|
||||
node_name = f"{{{self.NOMINA['prefix']}}}Nomina"
|
||||
node_nomina = ET.SubElement(complemento, node_name, data)
|
||||
node_name = f"{{{self.NOMINA['prefix']}}}Emisor"
|
||||
ET.SubElement(node_nomina, node_name, emisor)
|
||||
node_name = f"{{{self.NOMINA['prefix']}}}Receptor"
|
||||
ET.SubElement(node_nomina, node_name, receptor)
|
||||
|
||||
if percepciones:
|
||||
attr = percepciones
|
||||
percepciones = attr.pop('percepciones')
|
||||
node_name = f"{{{self.NOMINA['prefix']}}}Percepciones"
|
||||
node = ET.SubElement(node_nomina, node_name, attr)
|
||||
node_name = f"{{{self.NOMINA['prefix']}}}Percepcion"
|
||||
for percepcion in percepciones:
|
||||
ET.SubElement(node, node_name, percepcion)
|
||||
|
||||
if deducciones:
|
||||
attr = deducciones
|
||||
deducciones = attr.pop('deducciones')
|
||||
node_name = f"{{{self.NOMINA['prefix']}}}Deducciones"
|
||||
node = ET.SubElement(node_nomina, node_name, attr)
|
||||
node_name = f"{{{self.NOMINA['prefix']}}}Deduccion"
|
||||
for deduccion in deducciones:
|
||||
ET.SubElement(node, node_name, deduccion)
|
||||
|
||||
if otros:
|
||||
node_name = f"{{{self.NOMINA['prefix']}}}OtrosPagos"
|
||||
node = ET.SubElement(node_nomina, node_name)
|
||||
node_name = f"{{{self.NOMINA['prefix']}}}OtroPago"
|
||||
for otro in otros:
|
||||
subsidio = otro.pop('subsidio', {})
|
||||
sub_node = ET.SubElement(node, node_name, otro)
|
||||
if subsidio:
|
||||
sub_name = f"{{{self.NOMINA['prefix']}}}SubsidioAlEmpleo"
|
||||
ET.SubElement(sub_node, sub_name, subsidio)
|
||||
return
|
||||
|
||||
def _tax_locales(self, complemento, data):
|
||||
traslados = data.pop('traslados', ())
|
||||
retenciones = data.pop('retenciones', ())
|
||||
|
||||
node_name = f"{{{self.TAX_LOCALES['prefix']}}}ImpuestosLocales"
|
||||
attr = {'version': self.TAX_LOCALES['version']}
|
||||
attr.update(data)
|
||||
node_tax = ET.SubElement(complemento, node_name, attr)
|
||||
for traslado in traslados:
|
||||
node_name = f"{{{self.TAX_LOCALES['prefix']}}}TrasladosLocales"
|
||||
ET.SubElement(node_tax, node_name, traslado)
|
||||
return
|
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
from .comerciodigital import PACComercioDigital
|
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from .comercio import PACComercioDigital
|
|
@ -0,0 +1,383 @@
|
|||
#!/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 logging
|
||||
|
||||
import lxml.etree as ET
|
||||
import requests
|
||||
from requests.exceptions import ConnectionError
|
||||
|
||||
from .conf import DEBUG, 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__)
|
||||
|
||||
logging.getLogger('requests').setLevel(logging.ERROR)
|
||||
|
||||
|
||||
TIMEOUT = 10
|
||||
|
||||
|
||||
class PACComercioDigital(object):
|
||||
ws = 'https://{}.comercio-digital.mx/{}'
|
||||
api = 'https://app2.comercio-digital.mx/{}'
|
||||
URL = {
|
||||
'timbra': ws.format('ws', 'timbre/timbrarV5.aspx'),
|
||||
'cancel': ws.format('cancela', 'cancela3/cancelarUuid'),
|
||||
'cancelxml': ws.format('cancela', 'cancela3/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/3',
|
||||
'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('timbre/timbrarV5.aspx'),
|
||||
'cancel': ws.format('cancela3/cancelarUuid'),
|
||||
'cancelxml': ws.format('cancela3/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'
|
||||
|
||||
try:
|
||||
result = requests.post(url, data=data, headers=headers, timeout=TIMEOUT)
|
||||
except ConnectionError 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
|
||||
|
||||
def stamp(self, cfdi, auth={}):
|
||||
if DEBUG or not auth:
|
||||
auth = 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):
|
||||
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"TIPO={info['tipo']}",
|
||||
f"ACUS=SI",
|
||||
f"RFCR={rfc_receptor}",
|
||||
f"TIPOC={tipo}",
|
||||
f"TOTAL={total}",
|
||||
)
|
||||
return '\n'.join(data)
|
||||
|
||||
def cancel(self, cfdi, info, auth={}):
|
||||
if 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 ''
|
||||
|
||||
return result.text
|
||||
|
||||
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': 'cfdi3.3'}):
|
||||
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
|
||||
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
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
#!/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/>.
|
||||
|
||||
|
||||
# ~ Siempre consulta la documentación de PAC
|
||||
# ~ AUTH = Las credenciales de timbrado proporcionadas por el PAC
|
||||
# ~ NO cambies las credenciales de prueba
|
||||
|
||||
|
||||
DEBUG = False
|
||||
|
||||
|
||||
AUTH = {
|
||||
'user': '',
|
||||
'pass': '',
|
||||
}
|
||||
|
||||
|
||||
if DEBUG:
|
||||
AUTH = {
|
||||
'user': 'AAA010101AAA',
|
||||
'pass': 'PWD',
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from secrets import token_hex
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.utils.timezone import now
|
||||
|
||||
from ..conf import API_TOKEN
|
||||
from .cfdi_cert import SATCertificate
|
||||
from .cfdi_xml import CFDI
|
||||
from .pacs import PACComercioDigital
|
||||
|
||||
from ..models import Clients
|
||||
|
||||
|
||||
CURRENT_DIR = Path(__file__).resolve().parent
|
||||
CADENA = 'xslt/cadena.xslt'
|
||||
|
||||
|
||||
def validate_token(token):
|
||||
return token == API_TOKEN
|
||||
|
||||
|
||||
def validate_client(post, files):
|
||||
rfc = post.get('rfc', '')
|
||||
if not rfc:
|
||||
msg = 'El RFC es requerido'
|
||||
return False, msg
|
||||
|
||||
contra = post.get('password', '')
|
||||
if not contra:
|
||||
msg = 'La contraseña es requerida'
|
||||
return False, msg
|
||||
|
||||
file_cer = files.get('cer', b'')
|
||||
if not file_cer:
|
||||
msg = 'El archivo CER es requerido'
|
||||
return False, msg
|
||||
|
||||
file_key = files.get('key', b'')
|
||||
if not file_key:
|
||||
msg = 'El archivo KEY es requerido'
|
||||
return False, msg
|
||||
|
||||
cert = SATCertificate(file_cer.read(), file_key.read(), contra)
|
||||
if not cert.is_valid:
|
||||
return False, cert.error
|
||||
|
||||
if cert.is_fiel:
|
||||
msg = 'El certificado es FIEL'
|
||||
return False, msg
|
||||
|
||||
if rfc.upper() != cert.rfc:
|
||||
msg = 'El certificado no corresponde al RFC'
|
||||
return False, msg
|
||||
|
||||
|
||||
data = {
|
||||
'rfc': rfc,
|
||||
'token': token_hex(32),
|
||||
'key': cert.key_enc,
|
||||
'cer': cert.cer,
|
||||
'serial_number': cert.serial_number,
|
||||
'date_from': cert.not_before,
|
||||
'date_to': cert.not_after,
|
||||
}
|
||||
return True, data
|
||||
|
||||
|
||||
def validate_cfdi(post):
|
||||
data = json.loads(post)
|
||||
rfc = data['emisor']['Rfc']
|
||||
|
||||
try:
|
||||
obj = Clients.objects.get(rfc=rfc)
|
||||
except ObjectDoesNotExist:
|
||||
msg = 'Emisor no existe'
|
||||
return False, msg
|
||||
|
||||
if obj.date_to < now():
|
||||
msg = 'Certificado vencido'
|
||||
return False, msg
|
||||
|
||||
if rfc != obj.rfc:
|
||||
msg = 'Documento no corresponde al RFC del emisor'
|
||||
return False, msg
|
||||
|
||||
data = {'cfdi': data, 'emisor': obj}
|
||||
|
||||
return True, data
|
||||
|
||||
|
||||
def send_to_pac(data):
|
||||
pac = PACComercioDigital()
|
||||
xml = pac.stamp(data)
|
||||
if pac.error:
|
||||
print(pac.error)
|
||||
return '', pac.error
|
||||
|
||||
return xml, ''
|
||||
|
||||
|
||||
def send_stamp(data):
|
||||
msg = ''
|
||||
emisor = data['emisor']
|
||||
path_xslt = CURRENT_DIR / CADENA
|
||||
|
||||
cert = SATCertificate(emisor.cer, emisor.key)
|
||||
|
||||
cfdi = CFDI()
|
||||
try:
|
||||
cfdi.make_xml(data['cfdi'])
|
||||
cfdi.stamp(cert, path_xslt)
|
||||
except Exception as e:
|
||||
return '', str(e)
|
||||
|
||||
xml, msg = send_to_pac(cfdi.xml)
|
||||
|
||||
return xml, msg
|
|
@ -0,0 +1,345 @@
|
|||
<?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:cfdi="http://www.sat.gob.mx/cfd/3" 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:consumodecombustibles="http://www.sat.gob.mx/consumodecombustibles" 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:terceros="http://www.sat.gob.mx/terceros" xmlns:pago10="http://www.sat.gob.mx/Pagos">
|
||||
|
||||
<!-- 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="comercioexterior11.xslt"/>
|
||||
<xsl:include href="leyendasFisc.xslt"/>
|
||||
<xsl:include href="nomina12.xslt"/>
|
||||
<xsl:include href="implocal.xslt"/>
|
||||
<xsl:include href="donat11.xslt"/>
|
||||
<xsl:include href="ine11.xslt"/>
|
||||
<xsl:include href="iedu.xslt"/>
|
||||
<xsl:include href="pagos10.xslt"/>
|
||||
<xsl:include href="divisas.xslt"/>
|
||||
|
||||
<!--
|
||||
<xsl:include href="ecc11.xslt"/>
|
||||
<xsl:include href="pfic.xslt"/>
|
||||
<xsl:include href="TuristaPasajeroExtranjero.xslt"/>
|
||||
<xsl:include href="cfdiregistrofiscal.xslt"/>
|
||||
<xsl:include href="pagoenespecie.xslt"/>
|
||||
<xsl:include href="aerolineas.xslt"/>
|
||||
<xsl:include href="valesdedespensa.xslt"/>
|
||||
<xsl:include href="consumodecombustibles.xslt"/>
|
||||
<xsl:include href="notariospublicos.xslt"/>
|
||||
<xsl:include href="vehiculousado.xslt"/>
|
||||
<xsl:include href="servicioparcialconstruccion.xslt"/>
|
||||
<xsl:include href="renovacionysustitucionvehiculos.xslt"/>
|
||||
<xsl:include href="certificadodedestruccion.xslt"/>
|
||||
<xsl:include href="obrasarteantiguedades.xslt"/>
|
||||
<xsl:include href="ventavehiculos11.xslt"/>
|
||||
<xsl:include href="terceros11.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="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: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 CFDIRelacionados -->
|
||||
<xsl:template match="cfdi:CfdiRelacionados">
|
||||
<!-- Iniciamos el tratamiento de los atributos del 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 Emisor -->
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@Rfc"/>
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<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:template>
|
||||
|
||||
<!-- Manejador de nodos tipo Receptor -->
|
||||
<xsl:template match="cfdi:Receptor">
|
||||
<!-- Iniciamos el tratamiento de los atributos del Receptor -->
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@Rfc"/>
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@Nombre"/>
|
||||
</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="./@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>
|
||||
|
||||
<!-- 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 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 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="./@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>
|
||||
<!-- 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>
|
|
@ -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>
|
|
@ -0,0 +1,13 @@
|
|||
<?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:divisas="http://www.sat.gob.mx/divisas">
|
||||
<!-- Manejador de nodos tipo divisas:Divisas -->
|
||||
<xsl:template match="divisas:Divisas">
|
||||
<!-- Iniciamos el tratamiento de los atributos de divisas:Divisas -->
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@version"/>
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@tipoOperacion"/>
|
||||
</xsl:call-template>
|
||||
</xsl:template>
|
||||
</xsl:stylesheet>
|
|
@ -0,0 +1,13 @@
|
|||
<?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:donat="http://www.sat.gob.mx/donat">
|
||||
<xsl:output method="text" version="1.0" encoding="UTF-8" indent="no"/>
|
||||
<!-- Manejador de nodos tipo donat:Donatarias -->
|
||||
<xsl:template match="donat:Donatarias">
|
||||
<!-- Iniciamos el tratamiento de los atributos de donat:Donatarias -->
|
||||
<xsl:call-template name="Requerido"><xsl:with-param name="valor" select="./@version"/></xsl:call-template>
|
||||
<xsl:call-template name="Requerido"><xsl:with-param name="valor" select="./@noAutorizacion"/></xsl:call-template>
|
||||
<xsl:call-template name="Requerido"><xsl:with-param name="valor" select="./@fechaAutorizacion"/></xsl:call-template>
|
||||
<xsl:call-template name="Requerido"><xsl:with-param name="valor" select="./@leyenda"/></xsl:call-template>
|
||||
</xsl:template>
|
||||
|
||||
</xsl:stylesheet>
|
|
@ -0,0 +1,26 @@
|
|||
<?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:iedu="http://www.sat.gob.mx/iedu">
|
||||
<xsl:output method="text" version="1.0" encoding="UTF-8" indent="no"/>
|
||||
<!-- Manejador de nodos tipo iedu -->
|
||||
<xsl:template match="iedu:instEducativas">
|
||||
<!--Iniciamos el tratamiento de los atributos de instEducativas -->
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@version"/>
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@nombreAlumno"/>
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@CURP"/>
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@nivelEducativo"/>
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@autRVOE"/>
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@rfcPago"/>
|
||||
</xsl:call-template>
|
||||
</xsl:template>
|
||||
</xsl:stylesheet>
|
|
@ -0,0 +1,39 @@
|
|||
<?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:implocal="http://www.sat.gob.mx/implocal">
|
||||
<xsl:output method="text" version="1.0" encoding="UTF-8" indent="no"/>
|
||||
<!-- Manejador de nodos tipo implocal -->
|
||||
<xsl:template match="implocal:ImpuestosLocales">
|
||||
<!--Iniciamos el tratamiento de los atributos de ImpuestosLocales -->
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@version"/>
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@TotaldeRetenciones"/>
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@TotaldeTraslados"/>
|
||||
</xsl:call-template>
|
||||
<xsl:for-each select="implocal:RetencionesLocales">
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@ImpLocRetenido"/>
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@TasadeRetencion"/>
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@Importe"/>
|
||||
</xsl:call-template>
|
||||
</xsl:for-each>
|
||||
<xsl:for-each select="implocal:TrasladosLocales">
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@ImpLocTrasladado"/>
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@TasadeTraslado"/>
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@Importe"/>
|
||||
</xsl:call-template>
|
||||
</xsl:for-each>
|
||||
</xsl:template>
|
||||
</xsl:stylesheet>
|
|
@ -0,0 +1,51 @@
|
|||
<?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:ine="http://www.sat.gob.mx/ine">
|
||||
|
||||
<xsl:template match="ine:INE">
|
||||
<!--Manejador de nodos tipo INE-->
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@Version" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@TipoProceso" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@TipoComite" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@IdContabilidad" />
|
||||
</xsl:call-template>
|
||||
|
||||
<!-- Iniciamos el manejo de los elementos hijo en la secuencia -->
|
||||
<xsl:for-each select="./ine:Entidad">
|
||||
<xsl:apply-templates select="."/>
|
||||
</xsl:for-each>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="ine:Entidad">
|
||||
<!--Manejador de nodos tipo Entidad-->
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@ClaveEntidad" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@Ambito" />
|
||||
</xsl:call-template>
|
||||
|
||||
<!-- Iniciamos el tratamiento de los atributos de ine:Contabilidad-->
|
||||
<xsl:for-each select="./ine:Contabilidad">
|
||||
<xsl:apply-templates select="."/>
|
||||
</xsl:for-each>
|
||||
|
||||
</xsl:template>
|
||||
|
||||
<!-- Iniciamos el manejo de los elementos hijo en la secuencia Contabilidad-->
|
||||
<xsl:template match="ine:Contabilidad">
|
||||
<!-- Iniciamos el manejo de los nodos dependientes -->
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@IdContabilidad" />
|
||||
</xsl:call-template>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
|
||||
</xsl:stylesheet>
|
|
@ -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>
|
|
@ -0,0 +1,412 @@
|
|||
<?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:nomina12="http://www.sat.gob.mx/nomina12">
|
||||
|
||||
<xsl:template match="nomina12:Nomina">
|
||||
<!--Manejador de nodos tipo Nomina-->
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@Version" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@TipoNomina" />
|
||||
</xsl:call-template>
|
||||
<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="./@FechaInicialPago" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@FechaFinalPago" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@NumDiasPagados" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@TotalPercepciones" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@TotalDeducciones" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@TotalOtrosPagos" />
|
||||
</xsl:call-template>
|
||||
|
||||
<!-- Iniciamos el manejo de los elementos hijo en la secuencia -->
|
||||
<xsl:for-each select="./nomina12:Emisor">
|
||||
<xsl:apply-templates select="."/>
|
||||
</xsl:for-each>
|
||||
|
||||
<xsl:for-each select="./nomina12:Receptor">
|
||||
<xsl:apply-templates select="."/>
|
||||
</xsl:for-each>
|
||||
|
||||
<xsl:for-each select="./nomina12:Percepciones">
|
||||
<xsl:apply-templates select="."/>
|
||||
</xsl:for-each>
|
||||
|
||||
<xsl:for-each select="./nomina12:Deducciones">
|
||||
<xsl:apply-templates select="."/>
|
||||
</xsl:for-each>
|
||||
|
||||
<xsl:for-each select="./nomina12:OtrosPagos">
|
||||
<xsl:apply-templates select="."/>
|
||||
</xsl:for-each>
|
||||
|
||||
<xsl:for-each select="./nomina12:Incapacidades">
|
||||
<xsl:apply-templates select="."/>
|
||||
</xsl:for-each>
|
||||
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="nomina12:Emisor">
|
||||
<!--Manejador de nodos tipo nomina12:Emisor-->
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@Curp" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@RegistroPatronal" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@RfcPatronOrigen" />
|
||||
</xsl:call-template>
|
||||
|
||||
<!-- Iniciamos el tratamiento de los atributos de nomina12:EntidadSNCF-->
|
||||
<xsl:for-each select="./nomina12:EntidadSNCF">
|
||||
<xsl:apply-templates select="."/>
|
||||
</xsl:for-each>
|
||||
|
||||
</xsl:template>
|
||||
|
||||
<!-- Iniciamos el manejo de los elementos hijo en la secuencia EntidadSNCF-->
|
||||
<xsl:template match="nomina12:EntidadSNCF">
|
||||
<!-- Iniciamos el manejo de los nodos dependientes -->
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@OrigenRecurso" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@MontoRecursoPropio" />
|
||||
</xsl:call-template>
|
||||
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="nomina12:Receptor">
|
||||
<!--Manejador de nodos tipo nomina12:Receptor-->
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@Curp" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@NumSeguridadSocial" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@FechaInicioRelLaboral" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@Antigüedad" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@TipoContrato" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@Sindicalizado" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@TipoJornada" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@TipoRegimen" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@NumEmpleado" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@Departamento" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@Puesto" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@RiesgoPuesto" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@PeriodicidadPago" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@Banco" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@CuentaBancaria" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@SalarioBaseCotApor" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@SalarioDiarioIntegrado" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@ClaveEntFed" />
|
||||
</xsl:call-template>
|
||||
|
||||
<!-- Iniciamos el tratamiento de los atributos de nomina12:SubContratacion-->
|
||||
<xsl:for-each select="./nomina12:SubContratacion">
|
||||
<xsl:apply-templates select="."/>
|
||||
</xsl:for-each>
|
||||
|
||||
</xsl:template>
|
||||
|
||||
<!-- Iniciamos el manejo de los elementos hijo en la secuencia SubContratacion-->
|
||||
<xsl:template match="nomina12:SubContratacion">
|
||||
<!-- Iniciamos el manejo de los nodos dependientes -->
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@RfcLabora" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@PorcentajeTiempo" />
|
||||
</xsl:call-template>
|
||||
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="nomina12:Percepciones">
|
||||
<!--Manejador de nodos tipo nomina12:Percepciones-->
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@TotalSueldos" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@TotalSeparacionIndemnizacion" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@TotalJubilacionPensionRetiro" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@TotalGravado" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@TotalExento" />
|
||||
</xsl:call-template>
|
||||
|
||||
<!-- Iniciamos el tratamiento de los atributos de nomina12:Percepcion-->
|
||||
<xsl:for-each select="./nomina12:Percepcion">
|
||||
<xsl:apply-templates select="."/>
|
||||
</xsl:for-each>
|
||||
|
||||
<!-- Iniciamos el tratamiento de los atributos de nomina12:JubilacionPensionRetiro-->
|
||||
<xsl:for-each select="./nomina12:JubilacionPensionRetiro">
|
||||
<xsl:apply-templates select="."/>
|
||||
</xsl:for-each>
|
||||
|
||||
<!-- Iniciamos el tratamiento de los atributos de nomina12:SeparacionIndemnizacion-->
|
||||
<xsl:for-each select="./nomina12:SeparacionIndemnizacion">
|
||||
<xsl:apply-templates select="."/>
|
||||
</xsl:for-each>
|
||||
|
||||
</xsl:template>
|
||||
<!-- Iniciamos el manejo de los elementos hijo en la secuencia Percepcion-->
|
||||
<xsl:template match="nomina12:Percepcion">
|
||||
<!--Manejador de nodos tipo nomina12:Percepcion-->
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@TipoPercepcion" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@Clave" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@Concepto" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@ImporteGravado" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@ImporteExento" />
|
||||
</xsl:call-template>
|
||||
|
||||
<!-- Iniciamos el tratamiento de los atributos de nomina12:AccionesOTitulos-->
|
||||
<xsl:for-each select="./nomina12:AccionesOTitulos">
|
||||
<xsl:apply-templates select="."/>
|
||||
</xsl:for-each>
|
||||
<!-- Iniciamos el tratamiento de los atributos de nomina12:HorasExtra-->
|
||||
<xsl:for-each select="./nomina12:HorasExtra">
|
||||
<xsl:apply-templates select="."/>
|
||||
</xsl:for-each>
|
||||
|
||||
</xsl:template>
|
||||
|
||||
<!-- Iniciamos el manejo de los elementos hijo en la secuencia AccionesOTitulos-->
|
||||
<xsl:template match="nomina12:AccionesOTitulos">
|
||||
<!-- Iniciamos el manejo de los nodos dependientes -->
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@ValorMercado" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@PrecioAlOtorgarse" />
|
||||
</xsl:call-template>
|
||||
</xsl:template>
|
||||
|
||||
<!-- Iniciamos el manejo de los elementos hijo en la secuencia HorasExtra-->
|
||||
<xsl:template match="nomina12:HorasExtra">
|
||||
<!-- Iniciamos el manejo de los nodos dependientes -->
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@Dias" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@TipoHoras" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@HorasExtra" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@ImportePagado" />
|
||||
</xsl:call-template>
|
||||
</xsl:template>
|
||||
|
||||
<!-- Iniciamos el manejo de los elementos hijo en la secuencia JubilacionPensionRetiro-->
|
||||
<xsl:template match="nomina12:JubilacionPensionRetiro">
|
||||
<!--Manejador de nodos tipo nomina12:JubilacionPensionRetiro-->
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@TotalUnaExhibicion" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@TotalParcialidad" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@MontoDiario" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@IngresoAcumulable" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@IngresoNoAcumulable" />
|
||||
</xsl:call-template>
|
||||
</xsl:template>
|
||||
|
||||
<!-- Iniciamos el manejo de los elementos hijo en la secuencia SeparacionIndemnizacion-->
|
||||
<xsl:template match="nomina12:SeparacionIndemnizacion">
|
||||
<!--Manejador de nodos tipo nomina12:JubilacionPensionRetiro-->
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@TotalPagado" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@NumAñosServicio" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@UltimoSueldoMensOrd" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@IngresoAcumulable" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@IngresoNoAcumulable" />
|
||||
</xsl:call-template>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="nomina12:Deducciones">
|
||||
<!--Manejador de nodos tipo nomina12:Deducciones-->
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@TotalOtrasDeducciones" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@TotalImpuestosRetenidos" />
|
||||
</xsl:call-template>
|
||||
|
||||
<!-- Iniciamos el tratamiento de los atributos de nomina12:Deduccion-->
|
||||
<xsl:for-each select="./nomina12:Deduccion">
|
||||
<xsl:apply-templates select="."/>
|
||||
</xsl:for-each>
|
||||
|
||||
</xsl:template>
|
||||
|
||||
<!-- Iniciamos el manejo de los elementos hijo en la secuencia Deduccion-->
|
||||
<xsl:template match="nomina12:Deduccion">
|
||||
<!--Manejador de nodos tipo nomina12:Deduccion-->
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@TipoDeduccion" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@Clave" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@Concepto" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@Importe" />
|
||||
</xsl:call-template>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="nomina12:OtrosPagos">
|
||||
|
||||
<!-- Iniciamos el tratamiento de los atributos de nomina12:OtroPago-->
|
||||
<xsl:for-each select="./nomina12:OtroPago">
|
||||
<xsl:apply-templates select="."/>
|
||||
</xsl:for-each>
|
||||
|
||||
</xsl:template>
|
||||
|
||||
<!-- Iniciamos el manejo de los elementos hijo en la secuencia OtroPago-->
|
||||
<xsl:template match="nomina12:OtroPago">
|
||||
<!--Manejador de nodos tipo nomina12:OtroPago-->
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@TipoOtroPago" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@Clave" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@Concepto" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@Importe" />
|
||||
</xsl:call-template>
|
||||
|
||||
<!-- Iniciamos el tratamiento de los atributos de nomina12:SubsidioAlEmpleo-->
|
||||
<xsl:for-each select="./nomina12:SubsidioAlEmpleo">
|
||||
<xsl:apply-templates select="."/>
|
||||
</xsl:for-each>
|
||||
<!-- Iniciamos el tratamiento de los atributos de nomina12:CompensacionSaldosAFavor-->
|
||||
<xsl:for-each select="./nomina12:CompensacionSaldosAFavor">
|
||||
<xsl:apply-templates select="."/>
|
||||
</xsl:for-each>
|
||||
|
||||
</xsl:template>
|
||||
|
||||
<!-- Iniciamos el manejo de los elementos hijo en la secuencia SubsidioAlEmpleo-->
|
||||
<xsl:template match="nomina12:SubsidioAlEmpleo">
|
||||
<!--Manejador de nodos tipo nomina12:SubsidioAlEmpleo-->
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@SubsidioCausado" />
|
||||
</xsl:call-template>
|
||||
</xsl:template>
|
||||
|
||||
<!-- Iniciamos el manejo de los elementos hijo en la secuencia CompensacionSaldosAFavor-->
|
||||
<xsl:template match="nomina12:CompensacionSaldosAFavor">
|
||||
<!--Manejador de nodos tipo nomina12:CompensacionSaldosAFavor-->
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@SaldoAFavor" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@Año" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@RemanenteSalFav" />
|
||||
</xsl:call-template>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="nomina12:Incapacidades">
|
||||
<!-- Iniciamos el tratamiento de los atributos de nomina12:Incapacidades-->
|
||||
<xsl:for-each select="./nomina12:Incapacidad">
|
||||
<xsl:apply-templates select="."/>
|
||||
</xsl:for-each>
|
||||
</xsl:template>
|
||||
|
||||
<!-- Iniciamos el manejo de los elementos hijo en la secuencia Incapacidad-->
|
||||
<xsl:template match="nomina12:Incapacidad">
|
||||
<!--Manejador de nodos tipo nomina12:Incapacidad-->
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@DiasIncapacidad" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@TipoIncapacidad" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@ImporteMonetario" />
|
||||
</xsl:call-template>
|
||||
|
||||
</xsl:template>
|
||||
</xsl:stylesheet>
|
|
@ -0,0 +1,165 @@
|
|||
<?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:pago10="http://www.sat.gob.mx/Pagos">
|
||||
|
||||
<xsl:template match="pago10: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 hijo en la secuencia -->
|
||||
<xsl:for-each select="./pago10:Pago">
|
||||
<xsl:apply-templates select="."/>
|
||||
</xsl:for-each>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="pago10: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 pago10:DocumentoRelacionado-->
|
||||
<xsl:for-each select="./pago10:DoctoRelacionado">
|
||||
<xsl:apply-templates select="."/>
|
||||
</xsl:for-each>
|
||||
<xsl:for-each select="./pago10:Impuestos">
|
||||
<xsl:apply-templates select="."/>
|
||||
</xsl:for-each>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="pago10: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="./@TipoCambioDR" />
|
||||
</xsl:call-template>
|
||||
|
||||
<xsl:call-template name="Requerido">
|
||||
<xsl:with-param name="valor" select="./@MetodoDePagoDR" />
|
||||
</xsl:call-template>
|
||||
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@NumParcialidad" />
|
||||
</xsl:call-template>
|
||||
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@ImpSaldoAnt" />
|
||||
</xsl:call-template>
|
||||
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@ImpPagado" />
|
||||
</xsl:call-template>
|
||||
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@ImpSaldoInsoluto" />
|
||||
</xsl:call-template>
|
||||
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="pago10:Impuestos">
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@TotalImpuestosRetenidos" />
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="Opcional">
|
||||
<xsl:with-param name="valor" select="./@TotalImpuestosTrasladados" />
|
||||
</xsl:call-template>
|
||||
|
||||
<xsl:apply-templates select="./pago10:Retenciones"/>
|
||||
<xsl:apply-templates select="./pago10:Traslados"/>
|
||||
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="pago10:Retenciones">
|
||||
<xsl:for-each select="./pago10:Retencion">
|
||||
<xsl:apply-templates select="."/>
|
||||
</xsl:for-each>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="pago10:Traslados">
|
||||
<xsl:for-each select="./pago10:Traslado">
|
||||
<xsl:apply-templates select="."/>
|
||||
</xsl:for-each>
|
||||
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="pago10: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:template>
|
||||
|
||||
<xsl:template match="pago10:Traslado">
|
||||
<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:template>
|
||||
</xsl:stylesheet>
|
|
@ -0,0 +1,22 @@
|
|||
<?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">
|
||||
|
||||
<!-- 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>
|
|
@ -0,0 +1,84 @@
|
|||
from django.http import HttpResponse
|
||||
from django.http import JsonResponse
|
||||
from django.views import View
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from .util import util
|
||||
|
||||
from .models import Clients
|
||||
|
||||
|
||||
def _validate_token(request):
|
||||
if not 'Token' in request.headers:
|
||||
return False
|
||||
|
||||
token = request.headers['Token']
|
||||
result = util.validate_token(token)
|
||||
return result
|
||||
|
||||
|
||||
class ViewClients(View):
|
||||
|
||||
def get(self, request):
|
||||
if not _validate_token(request):
|
||||
return HttpResponse(status=401)
|
||||
|
||||
rfc = request.GET['rfc']
|
||||
try:
|
||||
obj = Clients.objects.filter(rfc=rfc).values()[0]
|
||||
except IndexError:
|
||||
msg = {'error': 'Cliente no existe'}
|
||||
return JsonResponse(msg, safe=False, status=202)
|
||||
|
||||
del obj['key']
|
||||
del obj['cer']
|
||||
return JsonResponse(obj, safe=False)
|
||||
|
||||
def post(self, request):
|
||||
if not _validate_token(request):
|
||||
return HttpResponse(status=401)
|
||||
|
||||
post = request.POST
|
||||
files = request.FILES
|
||||
|
||||
result, data = util.validate_client(post, files)
|
||||
if not result:
|
||||
return HttpResponse(data, status=202)
|
||||
|
||||
if Clients.objects.filter(rfc=data['rfc']).exists():
|
||||
msg = 'Cliente ya existe'
|
||||
return HttpResponse(msg, status=202)
|
||||
|
||||
obj = Clients.objects.create(**data)
|
||||
|
||||
return HttpResponse(status=201)
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
if not _validate_token(request):
|
||||
return HttpResponse(status=401)
|
||||
|
||||
rfc = request.GET['rfc']
|
||||
try:
|
||||
obj = Clients.objects.get(rfc=rfc)
|
||||
except ObjectDoesNotExist:
|
||||
msg = 'Cliente no existe'
|
||||
return HttpResponse(msg, status=202)
|
||||
|
||||
obj.delete()
|
||||
return HttpResponse()
|
||||
|
||||
|
||||
class ViewCfdi(View):
|
||||
|
||||
def post(self, request):
|
||||
if not _validate_token(request):
|
||||
return HttpResponse(status=401)
|
||||
|
||||
result, data = util.validate_cfdi(request.body)
|
||||
if not result:
|
||||
return HttpResponse(data, status=202)
|
||||
|
||||
xml, msg = util.send_stamp(data)
|
||||
if msg:
|
||||
return HttpResponse(msg, status=202)
|
||||
|
||||
return JsonResponse(xml, status=201)
|
|
@ -0,0 +1,16 @@
|
|||
"""
|
||||
ASGI config for cfditimbra project.
|
||||
|
||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'cfditimbra.settings')
|
||||
|
||||
application = get_asgi_application()
|
|
@ -0,0 +1,126 @@
|
|||
"""
|
||||
Django settings for cfditimbra project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 3.2.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.2/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/3.2/ref/settings/
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'django-insecure-0(14)u68lkquc^pw+dpq^_^q_*uh+ho&g087)88#aq_ms=vue#'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'api',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
# ~ 'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'cfditimbra.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'cfditimbra.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/3.2/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/3.2/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
|
@ -0,0 +1,22 @@
|
|||
"""cfditimbra URL Configuration
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/3.2/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import include, path
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('api/', include('api.urls')),
|
||||
]
|
|
@ -0,0 +1,16 @@
|
|||
"""
|
||||
WSGI config for cfditimbra project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'cfditimbra.settings')
|
||||
|
||||
application = get_wsgi_application()
|
|
@ -0,0 +1,22 @@
|
|||
#!/usr/bin/env python
|
||||
"""Django's command-line utility for administrative tasks."""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
"""Run administrative tasks."""
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'cfditimbra.settings')
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
) from exc
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,227 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import unittest
|
||||
import warnings
|
||||
import httpx
|
||||
from uuid import UUID
|
||||
|
||||
|
||||
URL_API = 'http://127.0.0.1:8000/api/{}'
|
||||
PATH_CERT = 'certificados/comercio.{}'
|
||||
CFDI_MINIMO = {
|
||||
"comprobante": {
|
||||
"TipoCambio": "1",
|
||||
"Moneda": "MXN",
|
||||
"TipoDeComprobante": "I",
|
||||
"LugarExpedicion": "06850",
|
||||
"SubTotal": "1000.00",
|
||||
"Total": "1160.00",
|
||||
"FormaPago": "03",
|
||||
"MetodoPago": "PUE",
|
||||
},
|
||||
"emisor": {
|
||||
"Rfc": "EKU9003173C9",
|
||||
"RegimenFiscal": "601",
|
||||
},
|
||||
"receptor": {
|
||||
"Rfc": "BASM740115RW0",
|
||||
"UsoCFDI": "G01"
|
||||
},
|
||||
"conceptos": [
|
||||
{
|
||||
"ClaveProdServ": "60121001",
|
||||
"Cantidad": "1.0",
|
||||
"ClaveUnidad": "KGM",
|
||||
"Descripcion": "Asesoría en desarrollo",
|
||||
"ValorUnitario": "1000.00",
|
||||
"Importe": "1000.00",
|
||||
"impuestos": {
|
||||
"traslados": [
|
||||
{
|
||||
"Base": "1000.00",
|
||||
"Impuesto": "002",
|
||||
"TipoFactor": "Tasa",
|
||||
"TasaOCuota": "0.160000",
|
||||
"Importe": "160.00"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"impuestos": {
|
||||
"TotalImpuestosTrasladados": "160.00",
|
||||
"traslados": [
|
||||
{
|
||||
"Impuesto": "002",
|
||||
"TipoFactor": "Tasa",
|
||||
"TasaOCuota": "0.160000",
|
||||
"Importe": "160.00"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
CFDI_ISH = {
|
||||
"comprobante": {
|
||||
"TipoCambio": "1",
|
||||
"Moneda": "MXN",
|
||||
"TipoDeComprobante": "I",
|
||||
"LugarExpedicion": "06850",
|
||||
"SubTotal": "1409.64",
|
||||
"Total": "1677.47",
|
||||
"FormaPago": "03",
|
||||
"MetodoPago": "PUE"
|
||||
},
|
||||
"emisor": {
|
||||
"Rfc": "EKU9003173C9",
|
||||
"RegimenFiscal": "601"
|
||||
},
|
||||
"receptor": {
|
||||
"Rfc": "BASM740115RW0",
|
||||
"UsoCFDI": "G01"
|
||||
},
|
||||
"conceptos": [
|
||||
{
|
||||
"ClaveProdServ": "81112106",
|
||||
"Cantidad": "1.0",
|
||||
"ClaveUnidad": "18",
|
||||
"Descripcion": "Proveedores de servicios de aplicación",
|
||||
"ValorUnitario": "1409.64",
|
||||
"Importe": "1409.64",
|
||||
"impuestos": {
|
||||
"traslados": [
|
||||
{
|
||||
"Base": "1409.64",
|
||||
"Impuesto": "002",
|
||||
"TipoFactor": "Tasa",
|
||||
"TasaOCuota": "0.160000",
|
||||
"Importe": "225.54"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"impuestos": {
|
||||
"TotalImpuestosTrasladados": "225.54",
|
||||
"traslados": [
|
||||
{
|
||||
"Impuesto": "002",
|
||||
"TipoFactor": "Tasa",
|
||||
"TasaOCuota": "0.160000",
|
||||
"Importe": "225.54"
|
||||
}
|
||||
]
|
||||
},
|
||||
"complementos": {
|
||||
"impuestos_locales": {
|
||||
"TotaldeTraslados": "42.29",
|
||||
"TotaldeRetenciones": "0.00",
|
||||
"traslados": [
|
||||
{
|
||||
"ImpLocTrasladado": "ISH",
|
||||
"TasadeTraslado": "3.00",
|
||||
"Importe": "42.29"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def ignore_warnings(test_func):
|
||||
def do_test(self, *args, **kwargs):
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
test_func(self, *args, **kwargs)
|
||||
return do_test
|
||||
|
||||
|
||||
class TestClients(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
print(f'In method: {self._testMethodName}')
|
||||
self.url = URL_API.format('clients/')
|
||||
|
||||
def test_unauthorized_without_token(self):
|
||||
expected = 401
|
||||
result = httpx.post(self.url)
|
||||
self.assertEqual(expected, result.status_code)
|
||||
|
||||
def test_unauthorized_with_token(self):
|
||||
expected = 401
|
||||
result = httpx.post(self.url, headers={'Token': '123'})
|
||||
self.assertEqual(expected, result.status_code)
|
||||
|
||||
@ignore_warnings
|
||||
def test_01_add_client(self):
|
||||
expected = 201
|
||||
headers = {'Token': '12345'}
|
||||
data = {
|
||||
'rfc': 'EKU9003173C9',
|
||||
'password': '12345678a',
|
||||
}
|
||||
files = {
|
||||
'cer': open(PATH_CERT.format('cer'), 'rb'),
|
||||
'key': open(PATH_CERT.format('key'), 'rb'),
|
||||
}
|
||||
result = httpx.post(self.url, headers=headers, data=data, files=files)
|
||||
self.assertEqual(expected, result.status_code)
|
||||
|
||||
def test_02_get_client(self):
|
||||
expected = 200
|
||||
headers = {'Token': '12345'}
|
||||
params = {'rfc': 'EKU9003173C9'}
|
||||
result = httpx.get(self.url, headers=headers, params=params)
|
||||
self.assertEqual(expected, result.status_code)
|
||||
|
||||
def test_03_delete_client(self):
|
||||
expected = 200
|
||||
headers = {'Token': '12345'}
|
||||
params = {'rfc': 'EKU9003173C9'}
|
||||
result = httpx.delete(self.url, headers=headers, params=params)
|
||||
self.assertEqual(expected, result.status_code)
|
||||
|
||||
def test_04_delete_client_not_exists(self):
|
||||
expected = 202
|
||||
headers = {'Token': '12345'}
|
||||
params = {'rfc': 'EKU900317321'}
|
||||
result = httpx.delete(self.url, headers=headers, params=params)
|
||||
self.assertEqual(expected, result.status_code)
|
||||
|
||||
|
||||
class TestCfdi(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
print(f'In method: {self._testMethodName}')
|
||||
self.url = URL_API.format('cfdi/')
|
||||
|
||||
# ~ @unittest.skip('temp')
|
||||
def test_stamp_cfdi_emisor_not_exists(self):
|
||||
expected = 202
|
||||
msg = 'Emisor no existe'
|
||||
headers = {'Token': '12345'}
|
||||
cfdi = CFDI_MINIMO.copy()
|
||||
cfdi['emisor']['Rfc'] = 'No_exists'
|
||||
result = httpx.post(self.url, headers=headers, json=cfdi)
|
||||
self.assertEqual(result.text, msg)
|
||||
self.assertEqual(expected, result.status_code)
|
||||
|
||||
def test_stamp_cfdi(self):
|
||||
expected = 201
|
||||
headers = {'Token': '12345'}
|
||||
result = httpx.post(self.url, headers=headers, json=CFDI_MINIMO)
|
||||
data = result.json()
|
||||
self.assertEqual(expected, result.status_code)
|
||||
self.assertTrue(bool(UUID(data['uuid'])))
|
||||
|
||||
def test_stamp_cfdi_ish(self):
|
||||
expected = 201
|
||||
headers = {'Token': '12345'}
|
||||
result = httpx.post(self.url, headers=headers, json=CFDI_ISH)
|
||||
data = result.json()
|
||||
self.assertEqual(expected, result.status_code)
|
||||
self.assertTrue(bool(UUID(data['uuid'])))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Loading…
Reference in New Issue