Merge branch 'falcon'

Timbrando
This commit is contained in:
Mauricio Baeza 2017-10-11 15:41:56 -05:00
commit c85bd0826e
69 changed files with 58839 additions and 1 deletions

15
.gitignore vendored
View File

@ -6,6 +6,19 @@ __pycache__/
# Django stuff:
*.log
local_settings.py
conf.py
source/fixtures
source/media
# Sphinx documentation
docs/_build/
docs/
*.ods
*.xlsx
credenciales.conf
*.sqlite
*.sql
rfc.db
configpac.py

9
requirements.txt Normal file
View File

@ -0,0 +1,9 @@
falcon
falcon-multipart
Beaker
Mako
peewee
click
logbook
bcrypt
python-dateutil

View File

@ -0,0 +1,10 @@
#!/usr/bin/env python
from peewee import SqliteDatabase
DEBUG = True
ID_SUPPORT = ''
DATABASE = None
if DEBUG:
DATABASE = SqliteDatabase('empresalibre.sqlite')

View File

@ -0,0 +1,2 @@
#!/usr/bin/env python

View File

@ -0,0 +1,308 @@
#!/usr/bin/env python
import datetime
from xml.etree import ElementTree as ET
from xml.dom.minidom import parseString
from logbook import Logger
#~ from settings import DEBUG
log = Logger('XML')
CFDI_ACTUAL = 'cfdi33'
NOMINA_ACTUAL = 'nomina12'
SAT = {
'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
'cfdi32': {
'version': '3.2',
'prefix': 'cfdi',
'xmlns': 'http://www.sat.gob.mx/cfd/3',
'schema': 'http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv32.xsd',
},
'cfdi33': {
'version': '3.3',
'prefix': 'cfdi',
'xmlns': 'http://www.sat.gob.mx/cfd/3',
'schema': 'http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv33.xsd',
},
'nomina11': {
'version': '1.1',
'prefix': 'nomina',
'xmlns': 'http://www.sat.gob.mx/nomina',
'schema': 'http://www.sat.gob.mx/nomina http://www.sat.gob.mx/sitio_internet/cfd/nomina/nomina11.xsd',
},
'nomina12': {
'version': '1.2',
'prefix': 'nomina',
'xmlns': 'http://www.sat.gob.mx/nomina12',
'schema': 'http://www.sat.gob.mx/nomina12 http://www.sat.gob.mx/sitio_internet/cfd/nomina/nomina12.xsd',
},
}
class CFDI(object):
def __init__(self, version=CFDI_ACTUAL):
self._sat_cfdi = SAT[version]
self._xsi = SAT['xsi']
self._pre = self._sat_cfdi['prefix']
self._cfdi = None
self.error = ''
def _now(self):
return datetime.datetime.now().isoformat()[:19]
def get_xml(self, datos):
if not self._validate(datos):
return ''
self._comprobante(datos['comprobante'])
self._emisor(datos['emisor'])
self._receptor(datos['receptor'])
self._conceptos(datos['conceptos'])
self._impuestos(datos['impuestos'])
if 'nomina' in datos:
self._nomina(datos['nomina'])
if 'complementos' in datos:
self._complementos(datos['complementos'])
return self._to_pretty_xml(ET.tostring(self._cfdi, encoding='utf-8'))
def add_sello(self, sello):
self._cfdi.attrib['Sello'] = sello
return self._to_pretty_xml(ET.tostring(self._cfdi, encoding='utf-8'))
def _to_pretty_xml(self, source):
tree = parseString(source)
xml = tree.toprettyxml(encoding='utf-8').decode('utf-8')
return xml
def _validate(self, datos):
if 'nomina' in datos:
return self._validate_nomina(datos)
return True
def _validate_nomina(self, datos):
comprobante = datos['comprobante']
validators = (
('MetodoDePago', 'NA'),
('TipoCambio', '1'),
('Moneda', 'MXN'),
('TipoDeComprobante', 'egreso'),
)
for f, v in validators:
if f in comprobante:
if v != comprobante[f]:
msg = 'El atributo: {}, debe ser: {}'.format(f, v)
self.error = msg
return False
return True
def _comprobante(self, datos):
attributes = {}
attributes['xmlns:{}'.format(self._pre)] = self._sat_cfdi['xmlns']
attributes['xmlns:xsi'] = self._xsi
attributes['xsi:schemaLocation'] = self._sat_cfdi['schema']
attributes.update(datos)
#~ if DEBUG:
#~ attributes['Fecha'] = self._now()
#~ attributes['NoCertificado'] = CERT_NUM
if not 'Version' in attributes:
attributes['Version'] = self._sat_cfdi['version']
if not 'Fecha' in attributes:
attributes['Fecha'] = self._now()
self._cfdi = ET.Element('{}:Comprobante'.format(self._pre), attributes)
return
def _emisor(self, datos):
#~ if DEBUG:
#~ datos['Rfc'] = RFC_TEST
node_name = '{}:Emisor'.format(self._pre)
emisor = ET.SubElement(self._cfdi, node_name, datos)
return
def _receptor(self, datos):
node_name = '{}:Receptor'.format(self._pre)
emisor = ET.SubElement(self._cfdi, node_name, datos)
return
def _conceptos(self, datos):
conceptos = ET.SubElement(self._cfdi, '{}:Conceptos'.format(self._pre))
for row in datos:
complemento = {}
if 'complemento' in row:
complemento = row.pop('complemento')
taxes = {}
if 'impuestos' in row:
taxes = row.pop('impuestos')
node_name = '{}:Concepto'.format(self._pre)
concepto = ET.SubElement(conceptos, node_name, row)
if taxes:
node_name = '{}:Impuestos'.format(self._pre)
impuestos = ET.SubElement(concepto, node_name)
if 'traslados' in taxes and taxes['traslados']:
node_name = '{}:Traslados'.format(self._pre)
traslados = ET.SubElement(impuestos, node_name)
for traslado in taxes['traslados']:
ET.SubElement(
traslados, '{}:Traslado'.format(self._pre), traslado)
if 'retenciones' in taxes and taxes['retenciones']:
node_name = '{}:Retenciones'.format(self._pre)
retenciones = ET.SubElement(impuestos, node_name)
for retencion in taxes['retenciones']:
ET.SubElement(
retenciones, '{}:Retencion'.format(self._pre), retencion)
if 'InformacionAduanera' in row:
for field in fields:
if field in row['InformacionAduanera']:
attributes[field] = row['InformacionAduanera'][field]
if attributes:
node_name = '{}:InformacionAduanera'.format(self._pre)
ET.SubElement(concepto, node_name, attributes)
if 'CuentaPredial' in row:
attributes = {'numero': row['CuentaPredial']}
node_name = '{}:CuentaPredial'.format(self._pre)
ET.SubElement(concepto, node_name, attributes)
if 'autRVOE' in row:
fields = (
'version',
'nombreAlumno',
'CURP',
'nivelEducativo',
'autRVOE',
)
for field in fields:
if field in row['autRVOE']:
attributes[field] = row['autRVOE'][field]
node_name = '{}:ComplementoConcepto'.format(self._pre)
complemento = ET.SubElement(concepto, node_name)
ET.SubElement(complemento, 'iedu:instEducativas', attributes)
return
def _impuestos(self, datos):
if not datos:
node_name = '{}:Impuestos'.format(self._pre)
ET.SubElement(self._cfdi, node_name)
return
attributes = {}
fields = ('TotalImpuestosTrasladados', 'TotalImpuestosRetenidos')
for field in fields:
if field in datos:
attributes[field] = datos[field]
node_name = '{}:Impuestos'.format(self._pre)
impuestos = ET.SubElement(self._cfdi, node_name, attributes)
if 'retenciones' in datos and datos['retenciones']:
retenciones = ET.SubElement(impuestos, '{}:Retenciones'.format(self._pre))
for row in datos['retenciones']:
ET.SubElement(retenciones, '{}:Retencion'.format(self._pre), row)
if 'traslados' in datos and datos['traslados']:
traslados = ET.SubElement(impuestos, '{}:Traslados'.format(self._pre))
for row in datos['traslados']:
ET.SubElement(traslados, '{}:Traslado'.format(self._pre), row)
return
def _nomina(self, datos):
sat_nomina = SAT[NOMINA_ACTUAL]
pre = sat_nomina['prefix']
complemento = ET.SubElement(self._cfdi, '{}:Complemento'.format(self._pre))
emisor = datos.pop('Emisor', None)
receptor = datos.pop('Receptor', None)
percepciones = datos.pop('Percepciones', None)
deducciones = datos.pop('Deducciones', None)
attributes = {}
attributes['xmlns:{}'.format(pre)] = sat_nomina['xmlns']
attributes['xsi:schemaLocation'] = sat_nomina['schema']
attributes.update(datos)
if not 'Version' in attributes:
attributes['Version'] = sat_nomina['version']
nomina = ET.SubElement(complemento, '{}:Nomina'.format(pre), attributes)
if emisor:
ET.SubElement(nomina, '{}:Emisor'.format(pre), emisor)
if receptor:
ET.SubElement(nomina, '{}:Receptor'.format(pre), receptor)
if percepciones:
detalle = percepciones.pop('detalle', None)
percepciones = ET.SubElement(nomina, '{}:Percepciones'.format(pre), percepciones)
for row in detalle:
ET.SubElement(percepciones, '{}:Percepcion'.format(pre), row)
if deducciones:
detalle = deducciones.pop('detalle', None)
deducciones = ET.SubElement(nomina, '{}:Deducciones'.format(pre), deducciones)
for row in detalle:
ET.SubElement(deducciones, '{}:Deduccion'.format(pre), row)
return
def _complementos(self, datos):
complemento = ET.SubElement(self._cfdi, '{}:Complemento'.format(self._pre))
if 'ce' in datos:
pre = 'cce11'
datos = datos.pop('ce')
emisor = datos.pop('emisor')
propietario = datos.pop('propietario')
receptor = datos.pop('receptor')
destinatario = datos.pop('destinatario')
conceptos = datos.pop('conceptos')
attributes = {}
attributes['xmlns:{}'.format(pre)] = \
'http://www.sat.gob.mx/ComercioExterior11'
attributes['xsi:schemaLocation'] = \
'http://www.sat.gob.mx/ComercioExterior11 ' \
'http://www.sat.gob.mx/sitio_internet/cfd/ComercioExterior11/ComercioExterior11.xsd'
attributes.update(datos)
ce = ET.SubElement(
complemento, '{}:ComercioExterior'.format(pre), attributes)
attributes = {}
if 'Curp' in emisor:
attributes = {'Curp': emisor.pop('Curp')}
node = ET.SubElement(ce, '{}:Emisor'.format(pre), attributes)
ET.SubElement(node, '{}:Domicilio'.format(pre), emisor)
if propietario:
ET.SubElement(ce, '{}:Propietario'.format(pre), propietario)
attributes = {}
if 'NumRegIdTrib' in receptor:
attributes = {'NumRegIdTrib': receptor.pop('NumRegIdTrib')}
node = ET.SubElement(ce, '{}:Receptor'.format(pre), attributes)
ET.SubElement(node, '{}:Domicilio'.format(pre), receptor)
attributes = {}
if 'NumRegIdTrib' in destinatario:
attributes = {'NumRegIdTrib': destinatario.pop('NumRegIdTrib')}
if 'Nombre' in destinatario:
attributes.update({'Nombre': destinatario.pop('Nombre')})
node = ET.SubElement(ce, '{}:Destinatario'.format(pre), attributes)
ET.SubElement(node, '{}:Domicilio'.format(pre), destinatario)
node = ET.SubElement(ce, '{}:Mercancias'.format(pre))
fields = ('Marca', 'Modelo', 'SubModelo', 'NumeroSerie')
for row in conceptos:
detalle = {}
for f in fields:
if f in row:
detalle[f] = row.pop(f)
concepto = ET.SubElement(node, '{}:Mercancia'.format(pre), row)
if detalle:
ET.SubElement(
concepto, '{}:DescripcionesEspecificas'.format(pre), detalle)
return

View File

@ -0,0 +1,12 @@
#!/usr/bin/env python3
import falcon
from models.main import get_cp
class AppPostalCode(object):
def on_get(self, req, resp):
values = req.params
req.context['result'] = get_cp(values['cp'])
resp.status = falcon.HTTP_200

View File

@ -0,0 +1,213 @@
#!/usr/bin/env python3
import falcon
from middleware import get_template
class AppLogin(object):
template = 'login.html'
def __init__(self, db):
self._db = db
@falcon.after(get_template)
def on_get(self, req, resp):
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
session = req.env['beaker.session']
values = req.params
result = self._db.authenticate(values)
if result['login']:
session.save()
session['user'] = result['user']
session['rfc'] = values['rfc']
req.context['result'] = result
resp.status = falcon.HTTP_200
class AppLogout(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
session = req.env['beaker.session']
session.delete()
resp.status = falcon.HTTP_200
raise falcon.HTTPTemporaryRedirect('/')
class AppAdmin(object):
template = 'admin.html'
def __init__(self, db):
self._db = db
@falcon.after(get_template)
def on_get(self, req, resp):
resp.status = falcon.HTTP_200
class AppMain(object):
template = 'main.html'
def __init__(self, db):
self._db = db
@falcon.after(get_template)
def on_get(self, req, resp):
resp.status = falcon.HTTP_200
class AppValues(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp, table):
values = req.params
req.context['result'] = self._db.get_values(table, values)
resp.status = falcon.HTTP_200
def on_post(self, req, resp, table):
file_object = req.get_param('upload')
if file_object is None:
session = req.env['beaker.session']
values = req.params
req.context['result'] = self._db.validate_cert(values, session)
else:
req.context['result'] = self._db.add_cert(file_object)
resp.status = falcon.HTTP_200
class AppPartners(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
req.context['result'] = self._db.get_partners(values)
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
req.context['result'] = self._db.partner(values)
resp.status = falcon.HTTP_200
def on_delete(self, req, resp):
values = req.params
if self._db.delete('partner', values['id']):
resp.status = falcon.HTTP_200
else:
resp.status = falcon.HTTP_204
class AppProducts(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
req.context['result'] = self._db.get_products(values)
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
req.context['result'] = self._db.product(values)
resp.status = falcon.HTTP_200
def on_delete(self, req, resp):
values = req.params
if self._db.delete('product', values['id']):
resp.status = falcon.HTTP_200
else:
resp.status = falcon.HTTP_204
class AppInvoices(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
req.context['result'] = self._db.get_invoices(values)
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
req.context['result'] = self._db.invoice(values)
resp.status = falcon.HTTP_200
def on_delete(self, req, resp):
values = req.params
if self._db.delete('invoice', values['id']):
resp.status = falcon.HTTP_200
else:
resp.status = falcon.HTTP_204
class AppEmisor(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
session = req.env['beaker.session']
req.context['result'] = self._db.get_emisor(session['rfc'])
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
req.context['result'] = self._db.emisor(values)
resp.status = falcon.HTTP_200
def on_delete(self, req, resp):
values = req.params
if self._db.delete('invoice', values['id']):
resp.status = falcon.HTTP_200
else:
resp.status = falcon.HTTP_204
class AppFolios(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
req.context['result'] = self._db.get_folios()
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
req.context['result'] = self._db.add_folios(values)
resp.status = falcon.HTTP_200
def on_delete(self, req, resp):
values = req.params
if self._db.delete('folios', values['id']):
resp.status = falcon.HTTP_200
else:
resp.status = falcon.HTTP_204
class AppDocumentos(object):
def __init__(self, db):
self._db = db
#~ self._not_json = True
def on_get(self, req, resp, type_doc, id_doc):
req.context['result'], file_name, content_type = \
self._db.get_doc(type_doc, id_doc)
resp.append_header('Content-Disposition',
'attachment; filename={}'.format(file_name))
resp.content_type = content_type
resp.status = falcon.HTTP_200

View File

@ -0,0 +1,609 @@
#!/usr/bin/env python
#~ import re
#~ from xml.etree import ElementTree as ET
#~ from requests import Request, Session, exceptions
import datetime
import hashlib
import os
import requests
import time
from lxml import etree
from xml.dom.minidom import parseString
from xml.sax.saxutils import escape, unescape
from uuid import UUID
from logbook import Logger
from zeep import Client
from zeep.plugins import HistoryPlugin
from zeep.cache import SqliteCache
from zeep.transports import Transport
from zeep.exceptions import Fault, TransportError
from .configpac import DEBUG, TIMEOUT, AUTH, URL
log = Logger('PAC')
#~ node = client.create_message(client.service, SERVICE, **args)
#~ print(etree.tostring(node, pretty_print=True).decode())
class Ecodex(object):
def __init__(self):
self.codes = URL['codes']
self.error = ''
self.message = ''
self._transport = Transport(cache=SqliteCache(), timeout=TIMEOUT)
self._plugins = None
self._history = None
if DEBUG:
self._history = HistoryPlugin()
self._plugins = [self._history]
def _get_token(self, rfc):
client = Client(URL['seguridad'],
transport=self._transport, plugins=self._plugins)
try:
result = client.service.ObtenerToken(rfc, self._get_epoch())
except Fault as e:
self.error = str(e)
log.error(self.error)
return ''
s = '{}|{}'.format(AUTH['ID'], result.Token)
return hashlib.sha1(s.encode()).hexdigest()
def _get_token_rest(self, rfc):
data = {
'rfc': rfc,
'grant_type': 'authorization_token',
}
headers = {'Content-type': 'application/x-www-form-urlencoded'}
result = requests.post(URL['token'], data=data, headers=headers)
data = result.json()
s = '{}|{}'.format(AUTH['ID'], data['service_token'])
return hashlib.sha1(s.encode()).hexdigest(), data['access_token']
def _validate_xml(self, xml):
NS_CFDI = {'cfdi': 'http://www.sat.gob.mx/cfd/3'}
if os.path.isfile(xml):
tree = etree.parse(xml).getroot()
else:
tree = etree.fromstring(xml.encode())
fecha = tree.get('Fecha')
rfc = tree.xpath('string(//cfdi:Emisor/@Rfc)', namespaces=NS_CFDI)
data = {
'ComprobanteXML': etree.tostring(tree).decode(),
'RFC': rfc,
'Token': self._get_token(rfc),
'TransaccionID': self._get_epoch(fecha),
}
return data
def _get_by_hash(self, sh, rfc):
token, access_token = self._get_token_rest(rfc)
url = URL['hash'].format(sh)
headers = {
'Authorization': 'Bearer {}'.format(access_token),
'X-Auth-Token': token,
}
result = requests.get(url, headers=headers)
if result.status_code == 200:
print (result.json())
return
def timbra_xml(self, xml):
data = self._validate_xml(xml)
client = Client(URL['timbra'],
transport=self._transport, plugins=self._plugins)
try:
result = client.service.TimbraXML(**data)
except Fault as e:
error = str(e)
if self.codes['HASH'] in error:
sh = error.split(' ')[3]
return self._get_by_hash(sh[:40], data['RFC'])
self.error = error
return ''
tree = parseString(result.ComprobanteXML.DatosXML)
xml = tree.toprettyxml(encoding='utf-8').decode('utf-8')
return xml
def _get_epoch(self, date=None):
if isinstance(date, str):
f = '%Y-%m-%dT%H:%M:%S'
e = int(time.mktime(time.strptime(date, f)))
else:
date = datetime.datetime.now()
e = int(time.mktime(date.timetuple()))
return e
def estatus_cuenta(self, rfc):
#~ Codigos:
#~ 100 = Cuenta encontrada
#~ 101 = RFC no dado de alta en el sistema ECODEX
token = self._get_token(rfc)
if not token:
return {}
data = {
'RFC': rfc,
'Token': token,
'TransaccionID': self._get_epoch()
}
client = Client(URL['clients'],
transport=self._transport, plugins=self._plugins)
try:
result = client.service.EstatusCuenta(**data)
except Fault as e:
log.error(str(e))
return
#~ print (result)
return result.Estatus
class Finkok(object):
def __init__(self):
self.codes = URL['codes']
self.error = ''
self.message = ''
self._transport = Transport(cache=SqliteCache(), timeout=TIMEOUT)
self._plugins = None
self._history = None
self.uuid = ''
self.fecha = None
if DEBUG:
self._history = HistoryPlugin()
self._plugins = [self._history]
def _debug(self):
if not DEBUG:
return
print('SEND', self._history.last_sent)
print('RESULT', self._history.last_received)
return
def _check_result(self, method, result):
#~ print ('CODE', result.CodEstatus)
#~ print ('INCIDENCIAS', result.Incidencias)
self.message = ''
MSG = {
'OK': 'Comprobante timbrado satisfactoriamente',
'307': 'Comprobante timbrado previamente',
}
status = result.CodEstatus
if status is None and result.Incidencias:
for i in result.Incidencias['Incidencia']:
self.error += 'Error: {}\n{}'.format(
i['CodigoError'], i['MensajeIncidencia'])
return ''
if method == 'timbra' and status in (MSG['OK'], MSG['307']):
#~ print ('UUID', result.UUID)
#~ print ('FECHA', result.Fecha)
if status == MSG['307']:
self.message = MSG['307']
tree = parseString(result.xml)
response = tree.toprettyxml(encoding='utf-8').decode('utf-8')
self.uuid = result.UUID
self.fecha = result.Fecha
return response
def _load_file(self, path):
try:
with open(path, 'rb') as f:
data = f.read()
except Exception as e:
self.error = str(e)
return
return data
def _validate_xml(self, file_xml):
if os.path.isfile(file_xml):
try:
with open(file_xml, 'rb') as f:
xml = f.read()
except Exception as e:
self.error = str(e)
return False, ''
else:
xml = file_xml.encode('utf-8')
return True, xml
def _validate_uuid(self, uuid):
try:
UUID(uuid)
return True
except ValueError:
self.error = 'UUID no válido: {}'.format(uuid)
return False
def timbra_xml(self, file_xml):
self.error = ''
method = 'timbra'
ok, xml = self._validate_xml(file_xml)
if not ok:
return ''
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
args = {
'username': AUTH['USER'],
'password': AUTH['PASS'],
'xml': xml,
}
if URL['quick_stamp']:
try:
result = client.service.quick_stamp(**args)
except Fault as e:
self.error = str(e)
return
else:
try:
result = client.service.stamp(**args)
except Fault as e:
self.error = str(e)
return
return self._check_result(method, result)
def _get_xml(self, uuid):
if not self._validate_uuid(uuid):
return ''
method = 'util'
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
args = {
'username': AUTH['USER'],
'password': AUTH['PASS'],
'uuid': uuid,
'taxpayer_id': self.rfc,
'invoice_type': 'I',
}
try:
result = client.service.get_xml(**args)
except Fault as e:
self.error = str(e)
return ''
except TransportError as e:
self.error = str(e)
return ''
if result.error:
self.error = result.error
return ''
tree = parseString(result.xml)
xml = tree.toprettyxml(encoding='utf-8').decode('utf-8')
return xml
def recupera_xml(self, file_xml='', uuid=''):
self.error = ''
if uuid:
return self._get_xml(uuid)
method = 'timbra'
ok, xml = self._validate_xml(file_xml)
if not ok:
return ''
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
try:
result = client.service.stamped(xml, AUTH['USER'], AUTH['PASS'])
except Fault as e:
self.error = str(e)
return ''
return self._check_result(method, result)
def estatus_xml(self, uuid):
method = 'timbra'
if not self._validate_uuid(uuid):
return ''
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
try:
result = client.service.query_pending(AUTH['USER'], AUTH['PASS'], uuid)
#~ print (result.date)
#~ tree = parseString(unescape(result.xml))
#~ response = tree.toprettyxml(encoding='utf-8').decode('utf-8')
return result.status
except Fault as e:
self.error = str(e)
return ''
def cancel_xml(self, rfc, uuids, path_cer, path_key):
for u in uuids:
if not self._validate_uuid(u):
return ''
cer = self._load_file(path_cer)
key = self._load_file(path_key)
method = 'cancel'
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
uuid_type = client.get_type('ns1:UUIDS')
sa = client.get_type('ns0:stringArray')
args = {
'UUIDS': uuid_type(uuids=sa(string=uuids)),
'username': AUTH['USER'],
'password': AUTH['PASS'],
'taxpayer_id': rfc,
'cer': cer,
'key': key,
'store_pending': True,
}
try:
result = client.service.cancel(**args)
except Fault as e:
self.error = str(e)
return ''
if result.CodEstatus and self.codes['205'] in result.CodEstatus:
self.error = result.CodEstatus
return ''
return result
def cancel_signature(self, file_xml):
method = 'cancel'
if os.path.isfile(file_xml):
root = etree.parse(file_xml).getroot()
else:
root = etree.fromstring(file_xml)
xml = etree.tostring(root)
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
args = {
'username': AUTH['USER'],
'password': AUTH['PASS'],
'xml': xml,
'store_pending': True,
}
result = client.service.cancel_signature(**args)
return result
def get_acuse(self, rfc, uuids, type_acuse='C'):
for u in uuids:
if not self._validate_uuid(u):
return ''
method = 'cancel'
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
args = {
'username': AUTH['USER'],
'password': AUTH['PASS'],
'taxpayer_id': rfc,
'uuid': '',
'type': type_acuse,
}
try:
result = []
for u in uuids:
args['uuid'] = u
r = client.service.get_receipt(**args)
result.append(r)
except Fault as e:
self.error = str(e)
return ''
return result
def estatus_cancel(self, uuids):
for u in uuids:
if not self._validate_uuid(u):
return ''
method = 'cancel'
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
args = {
'username': AUTH['USER'],
'password': AUTH['PASS'],
'uuid': '',
}
try:
result = []
for u in uuids:
args['uuid'] = u
r = client.service.query_pending_cancellation(**args)
result.append(r)
except Fault as e:
self.error = str(e)
return ''
return result
def add_token(self, rfc, email):
"""
Se requiere cuenta de reseller para usar este método
"""
method = 'util'
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
args = {
'username': AUTH['USER'],
'password': AUTH['PASS'],
'name': rfc,
'token_username': email,
'taxpayer_id': rfc,
'status': True,
}
result = client.service.add_token(**args)
return result
def get_date(self):
method = 'util'
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
try:
result = client.service.datetime(AUTH['USER'], AUTH['PASS'])
except Fault as e:
self.error = str(e)
return ''
if result.error:
self.error = result.error
return
return result.datetime
def add_client(self, rfc, type_user=False):
"""
Se requiere cuenta de reseller para usar este método
type_user: False == 'P' == Prepago or True == 'O' == On demand
"""
tu = {False: 'P', True: 'O'}
method = 'client'
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
args = {
'reseller_username': AUTH['USER'],
'reseller_password': AUTH['PASS'],
'taxpayer_id': rfc,
'type_user': tu[type_user],
'added': datetime.datetime.now().isoformat()[:19],
}
try:
result = client.service.add(**args)
except Fault as e:
self.error = str(e)
return ''
return result
def edit_client(self, rfc, status=True):
"""
Se requiere cuenta de reseller para usar este método
status = 'A' or 'S'
"""
sv = {False: 'S', True: 'A'}
method = 'client'
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
args = {
'reseller_username': AUTH['USER'],
'reseller_password': AUTH['PASS'],
'taxpayer_id': rfc,
'status': sv[status],
}
try:
result = client.service.edit(**args)
except Fault as e:
self.error = str(e)
return ''
return result
def get_client(self, rfc):
"""
Se requiere cuenta de reseller para usar este método
"""
method = 'client'
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
args = {
'reseller_username': AUTH['USER'],
'reseller_password': AUTH['PASS'],
'taxpayer_id': rfc,
}
try:
result = client.service.get(**args)
except Fault as e:
self.error = str(e)
return ''
except TransportError as e:
self.error = str(e)
return ''
return result
def assign_client(self, rfc, credit):
"""
Se requiere cuenta de reseller para usar este método
"""
method = 'client'
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
args = {
'username': AUTH['USER'],
'password': AUTH['PASS'],
'taxpayer_id': rfc,
'credit': credit,
}
try:
result = client.service.assign(**args)
except Fault as e:
self.error = str(e)
return ''
return result
def _get_data_sat(path):
BF = 'string(//*[local-name()="{}"]/@{})'
NS_CFDI = {'cfdi': 'http://www.sat.gob.mx/cfd/3'}
try:
if os.path.isfile(path):
tree = etree.parse(path).getroot()
else:
tree = etree.fromstring(path)
data = {}
emisor = escape(
tree.xpath('string(//cfdi:Emisor/@rfc)', namespaces=NS_CFDI) or
tree.xpath('string(//cfdi:Emisor/@Rfc)', namespaces=NS_CFDI)
)
receptor = escape(
tree.xpath('string(//cfdi:Receptor/@rfc)', namespaces=NS_CFDI) or
tree.xpath('string(//cfdi:Receptor/@Rfc)', namespaces=NS_CFDI)
)
data['total'] = tree.get('total') or tree.get('Total')
data['emisor'] = emisor
data['receptor'] = receptor
data['uuid'] = tree.xpath(BF.format('TimbreFiscalDigital', 'UUID'))
except Exception as e:
print (e)
return {}
return '?re={emisor}&rr={receptor}&tt={total}&id={uuid}'.format(**data)
def get_status_sat(xml):
data = _get_data_sat(xml)
if not data:
return
URL = 'https://consultaqr.facturaelectronica.sat.gob.mx/ConsultaCFDIService.svc?wsdl'
client = Client(URL, transport=Transport(cache=SqliteCache()))
try:
result = client.service.Consulta(expresionImpresa=data)
except Exception as e:
return 'Error: {}'.format(str(e))
return result.Estado
def main():
return
if __name__ == '__main__':
main()

View File

@ -0,0 +1,401 @@
#!/usr/bin/env python
import datetime
import getpass
import hashlib
import json
import mimetypes
import os
import re
import sqlite3
import subprocess
import tempfile
import unicodedata
import uuid
from dateutil import parser
from settings import DEBUG, log, template_lookup, COMPANIES, DB_SAT, \
PATH_XSLT, PATH_XSLTPROC, PATH_OPENSSL
#~ def _get_hash(password):
#~ return bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
#~ def validate_password(hashed, password):
#~ return bcrypt.hashpw(password.encode(), hashed.encode()) == hashed.encode()
def _call(args):
return subprocess.check_output(args, shell=True).decode()
def _get_md5(data):
return hashlib.md5(data.encode()).hexdigest()
def _save_temp(data, modo='wb'):
path = tempfile.mkstemp()[1]
with open(path, modo) as f:
f.write(data)
return path
def _join(*paths):
return os.path.join(*paths)
def _kill(path):
try:
os.remove(path)
except:
pass
return
def get_pass():
password = getpass.getpass('Introduce la contraseña: ')
pass2 = getpass.getpass('Confirma la contraseña: ')
if password != pass2:
msg = 'Las contraseñas son diferentes'
return False, msg
password = password.strip()
if not password:
msg = 'La contraseña es necesaria'
return False, msg
return True, password
def get_value(arg):
value = input('Introduce el {}: '.format(arg)).strip()
if not value:
msg = 'El {} es requerido'.format(arg)
log.error(msg)
return ''
return value
def _get_args(rfc):
con = sqlite3.connect(COMPANIES)
cursor = con.cursor()
sql = "SELECT con FROM names WHERE rfc=?"
cursor.execute(sql, (rfc,))
values = cursor.fetchone()
if values is None:
msg = 'No se encontró el RFC'
log.error(msg)
return ''
cursor.close()
con.close()
return values[0]
def get_con(rfc=''):
if not rfc:
rfc = get_value('RFC').upper()
if not rfc:
return False
args = _get_args(rfc.upper())
if not args:
return False
return loads(args)
def get_rfcs():
con = sqlite3.connect(COMPANIES)
cursor = con.cursor()
sql = "SELECT * FROM names"
cursor.execute(sql)
values = cursor.fetchall()
cursor.close()
con.close()
return values
def get_sat_key(table, key):
con = sqlite3.connect(DB_SAT)
cursor = con.cursor()
sql = 'SELECT key, description FROM {} WHERE key=?'.format(table)
cursor.execute(sql, (key,))
data = cursor.fetchone()
cursor.close()
con.close()
if data is None:
return {'ok': False, 'text': 'No se encontró la clave'}
return {'ok': True, 'text': data[1]}
def now():
return datetime.datetime.now().replace(microsecond=0)
def get_token():
return _get_hash(uuid.uuid4().hex)
def get_mimetype(path):
mt = mimetypes.guess_type(path)[0]
return mt or 'application/octet-stream'
def is_file(path):
return os.path.isfile(path)
def get_stream(path):
return get_file(path), get_size(path)
def get_file(path):
return open(path, 'rb')
def get_size(path):
return os.path.getsize(path)
def get_template(name, data={}):
#~ print ('NAME', name, data)
template = template_lookup.get_template(name)
return template.render(**data)
def dumps(data):
return json.dumps(data, default=str)
def loads(data):
return json.loads(data)
def clean(values):
for k, v in values.items():
if isinstance(v, str):
values[k] = v.strip()
return values
def parse_con(values):
data = values.split('|')
try:
con = {'type': data[0]}
if con['type'] == 'sqlite':
con['name'] = data[1]
else:
if data[1]:
con['host'] = data[1]
if data[2]:
con['port'] = data[2]
con['name'] = data[3]
con['user'] = data[4]
con['password'] = data[5]
return con
except IndexError:
return {}
def spaces(value):
return ' '.join(value.split())
def to_slug(string):
value = (unicodedata.normalize('NFKD', string)
.encode('ascii', 'ignore')
.decode('ascii').lower())
return value
class Certificado(object):
def __init__(self, key, cer):
self._key = key
self._cer = cer
self._modulus = ''
self._save_files()
self.error = ''
def _save_files(self):
try:
self._path_key = _save_temp(self._key)
self._path_cer = _save_temp(self._cer)
except:
self._path_key = ''
self._path_cer = ''
return
def _kill(self, path):
try:
os.remove(path)
except:
pass
return
def _get_info_cer(self, session_rfc):
data = {}
args = 'openssl x509 -inform DER -in {}'
try:
cer_pem = _call(args.format(self._path_cer))
except Exception as e:
self.error = 'No se pudo convertir el CER en PEM'
return data
args = 'openssl enc -base64 -in {}'
try:
cer_txt = _call(args.format(self._path_cer))
except Exception as e:
self.error = 'No se pudo convertir el CER en TXT'
return data
args = 'openssl x509 -inform DER -in {} -noout -{}'
try:
result = _call(args.format(self._path_cer, 'purpose')).split('\n')[3]
except Exception as e:
self.error = 'No se puede saber si es FIEL'
return data
if result == 'SSL server : No':
self.error = 'El certificado es FIEL'
return data
result = _call(args.format(self._path_cer, 'serial'))
serie = result.split('=')[1].split('\n')[0][1::2]
result = _call(args.format(self._path_cer, 'subject'))
rfc = result.split('=')[5].split('/')[0].strip()
if not DEBUG:
if not rfc == session_rfc:
self.error = 'El RFC del certificado no corresponde.'
return data
dates = _call(args.format(self._path_cer, 'dates')).split('\n')
desde = parser.parse(dates[0].split('=')[1])
hasta = parser.parse(dates[1].split('=')[1])
self._modulus = _call(args.format(self._path_cer, 'modulus'))
data['cer'] = self._cer
data['cer_tmp'] = None
data['cer_pem'] = cer_pem
data['cer_txt'] = cer_txt.replace('\n', '')
data['serie'] = serie
data['rfc'] = rfc
data['desde'] = desde
data['hasta'] = hasta
return data
def _get_p12(self, password, rfc):
tmp_cer = tempfile.mkstemp()[1]
tmp_key = tempfile.mkstemp()[1]
tmp_p12 = tempfile.mkstemp()[1]
args = 'openssl x509 -inform DER -in "{}" -out "{}"'
_call(args.format(self._path_cer, tmp_cer))
args = 'openssl pkcs8 -inform DER -in "{}" -passin pass:{} -out "{}"'
_call(args.format(self._path_key, password, tmp_key))
args = 'openssl pkcs12 -export -in "{}" -inkey "{}" -name "{}" -passout ' \
'pass:"{}" -out "{}"'
_call(args.format(tmp_cer, tmp_key, rfc,
hashlib.md5(rfc.encode()).hexdigest(), tmp_p12))
data = open(tmp_p12, 'rb').read()
self._kill(tmp_cer)
self._kill(tmp_key)
self._kill(tmp_p12)
return data
def _get_info_key(self, password, rfc):
data = {}
args = 'openssl pkcs8 -inform DER -in "{}" -passin pass:{}'
try:
result = _call(args.format(self._path_key, password))
except Exception as e:
self.error = 'Contraseña incorrecta'
return data
args = 'openssl pkcs8 -inform DER -in "{}" -passin pass:{} | ' \
'openssl rsa -noout -modulus'
mod_key = _call(args.format(self._path_key, password))
if self._modulus != mod_key:
self.error = 'Los archivos no son pareja'
return data
args = 'openssl pkcs8 -inform DER -in "{}" -passin pass:{} | ' \
'openssl rsa -des3 -passout pass:{}'.format(
self._path_key, password, _get_md5(rfc))
key_enc = _call(args)
data['key'] = self._key
data['key_tmp'] = None
data['key_enc'] = key_enc
data['p12'] = self._get_p12(password, rfc)
return data
def validate(self, password, rfc):
if not self._path_key or not self._path_cer:
self.error = 'Error al cargar el certificado'
return {}
data = self._get_info_cer(rfc)
llave = self._get_info_key(password, data['rfc'])
if not llave:
return {}
data.update(llave)
self._kill(self._path_key)
self._kill(self._path_cer)
return data
def make_xml(data, certificado):
from .cfdi_xml import CFDI
if DEBUG:
data['emisor']['Rfc'] = certificado.rfc
data['emisor']['RegimenFiscal'] = '603'
cfdi = CFDI()
xml = cfdi.get_xml(data)
data = {
'xsltproc': PATH_XSLTPROC,
'xslt': _join(PATH_XSLT, 'cadena.xslt'),
'xml': _save_temp(xml, 'w'),
'openssl': PATH_OPENSSL,
'key': _save_temp(certificado.key_enc, 'w'),
'pass': _get_md5(certificado.rfc)
}
args = '"{xsltproc}" "{xslt}" "{xml}" | ' \
'"{openssl}" dgst -sha256 -sign "{key}" -passin pass:{pass} | ' \
'"{openssl}" enc -base64 -A'.format(**data)
sello = _call(args)
_kill(data['xml'])
_kill(data['key'])
return cfdi.add_sello(sello)
def timbra_xml(xml):
from .pac import Finkok as PAC
result = {'ok': True, 'error': ''}
pac = PAC()
xml = pac.timbra_xml(xml)
if not xml:
result['ok'] = False
result['error'] = pac.error
return result
result['xml'] = xml
result['uuid'] = pac.uuid
result['fecha'] = pac.fecha
return result

13
source/app/main.ini Normal file
View File

@ -0,0 +1,13 @@
[uwsgi]
socket = 127.0.0.1:3032
uid = nginx
gid = nginx
chdir = /srv/app/empresalibre/app
wsgi-file = main.py
callable = app
master = true
processes = 4
threads = 4
thunder-lock = true
#~ stats = 127.0.0.1:9191
logger = file:/srv/log/empresalibre-uwsgi.log

59
source/app/main.py Normal file
View File

@ -0,0 +1,59 @@
#!/usr/bin/env python3
import falcon
from falcon_multipart.middleware import MultipartMiddleware
from beaker.middleware import SessionMiddleware
from middleware import (
AuthMiddleware,
JSONTranslator,
ConnectionMiddleware,
static,
handle_404
)
from models.db import StorageEngine
from controllers.main import (
AppLogin, AppLogout, AppAdmin, AppEmisor,
AppMain, AppValues, AppPartners, AppProducts, AppInvoices, AppFolios,
AppDocumentos
)
from settings import DEBUG
db = StorageEngine()
api = falcon.API(middleware=[
AuthMiddleware(),
JSONTranslator(),
ConnectionMiddleware(),
MultipartMiddleware(),
])
api.req_options.auto_parse_form_urlencoded = True
api.add_sink(handle_404, '')
api.add_route('/', AppLogin(db))
api.add_route('/logout', AppLogout(db))
api.add_route('/admin', AppAdmin(db))
api.add_route('/emisor', AppEmisor(db))
api.add_route('/folios', AppFolios(db))
api.add_route('/main', AppMain(db))
api.add_route('/values/{table}', AppValues(db))
api.add_route('/doc/{type_doc}/{id_doc}', AppDocumentos(db))
api.add_route('/partners', AppPartners(db))
api.add_route('/products', AppProducts(db))
api.add_route('/invoices', AppInvoices(db))
if DEBUG:
api.add_sink(static, '/static')
session_options = {
'session.type': 'file',
'session.cookie_expires': True,
'session.data_dir': '/tmp/cache/data',
'session.lock_dir': '/tmp/cache/lock',
}
app = SessionMiddleware(api, session_options)

View File

@ -0,0 +1,8 @@
[uwsgi]
http = 127.0.0.1:8000
wsgi-file = main.py
callable = app
master = true
processes = 4
threads = 4
py-autoreload = 1

68
source/app/middleware.py Normal file
View File

@ -0,0 +1,68 @@
#!/usr/bin/env python
import falcon
from controllers import util
from models import main
from settings import PATH_STATIC
def handle_404(req, resp):
id_session = req.cookies.get('beaker.session.id', '')
if id_session:
raise falcon.HTTPTemporaryRedirect('/main')
raise falcon.HTTPTemporaryRedirect('/')
def get_template(req, resp, resource):
session = req.env['beaker.session']
resp.content_type = 'text/html'
data = {'username': session.get('user', '')}
resp.body = util.get_template(resource.template, data)
def static(req, res):
path = PATH_STATIC + req.path
if util.is_file(path):
res.content_type = util.get_mimetype(path)
res.stream, res.stream_len = util.get_stream(path)
res.status = falcon.HTTP_200
else:
res.status = falcon.HTTP_404
class AuthMiddleware(object):
def process_resource(self, req, resp, resource, params):
id_session = req.cookies.get('beaker.session.id', '')
if not id_session and req.path != '/':
raise falcon.HTTPTemporaryRedirect('/')
class JSONTranslator(object):
#~ def process_request(self, req, resp):
#~ pass
def process_response(self, req, resp, resource):
if 'result' not in req.context:
return
if '/doc/' in req.path:
resp.body = req.context['result']
return
resp.body = util.dumps(req.context['result'])
class ConnectionMiddleware(object):
#~ def process_request(self, req, resp):
def process_resource(self, req, resp, resource, params):
id_session = req.cookies.get('beaker.session.id', '')
session = req.env['beaker.session']
rfc = session.get('rfc', '')
if id_session and rfc:
opt = util.get_con(rfc)
main.conectar(opt)
def process_response(self, req, resp, resource):
main.desconectar()

View File

@ -0,0 +1,2 @@
#!/usr/bin/env python

123
source/app/models/db.py Normal file
View File

@ -0,0 +1,123 @@
#!/usr/bin/env python
from . import main
class StorageEngine(object):
def __init__(self):
pass
def authenticate(self, args):
return main.authenticate(args)
def get_values(self, table, values=None):
return getattr(self, '_get_{}'.format(table))(values)
def add_cert(self, file_object):
return main.Certificado.add(file_object)
def validate_cert(self, values, session):
return main.Certificado.validate(values, session)
def _get_cert(self, values):
return main.Certificado.get_data()
def _get_cp(self, values):
return main.get_cp(values['cp'])
def _get_formapago(self, values):
return main.SATFormaPago.get_activos(values)
def _get_categorias(self, values):
return main.Categorias.get_all()
def _get_newkey(self, values):
return main.Productos.next_key()
def _get_unidades(self, values):
return main.SATUnidades.get_activos()
def _get_taxes(self, values):
return main.SATImpuestos.get_activos()
def _get_satkey(self, values):
return main.get_sat_key(values['key'])
def _get_series(self, values):
return main.Folios.get_all()
def _get_monedas(self, values):
return main.SATMonedas.get_activos()
def _get_regimenes(self, values):
return main.Emisor.get_regimenes()
def _get_usocfdi(self, values):
return main.SATUsoCfdi.get_activos(values)
def delete(self, table, id):
if table == 'partner':
return main.Socios.remove(id)
if table == 'product':
return main.Productos.remove(id)
if table == 'invoice':
return main.Facturas.remove(id)
if table == 'folios':
return main.Folios.remove(id)
return False
def _get_client(self, values):
return main.Socios.get_by_client(values)
def _get_product(self, values):
return main.Productos.get_by(values)
def get_partners(self, values):
return main.Socios.get_(values)
def partner(self, values):
id = int(values.pop('id', '0'))
if id:
return main.Socios.actualizar(values, id)
return main.Socios.add(values)
def get_products(self, values):
return main.Productos.get_(values)
def product(self, values):
id = int(values.pop('id', '0'))
if id:
return main.Productos.actualizar(values, id)
return main.Productos.add(values)
def invoice(self, values):
id = int(values.pop('id', '0'))
if id:
return main.Facturas.actualizar(values, id)
return main.Facturas.add(values)
def get_invoices(self, values):
return main.Facturas.get_(values)
def _get_timbrar(self, values):
return main.Facturas.timbrar(int(values['id']))
def get_emisor(self, rfc):
return main.Emisor.get_(rfc)
def emisor(self, values):
return main.Emisor.add(values)
def get_folios(self):
return main.Folios.get_()
def add_folios(self, values):
return main.Folios.add(values)
def get_doc(self, type_doc, id):
if type_doc == 'xml':
data, file_name = main.Facturas.get_xml(id)
content_type = 'application.xml'
return data, file_name, content_type

1608
source/app/models/main.py Normal file

File diff suppressed because it is too large Load Diff

61
source/app/settings.py Normal file
View File

@ -0,0 +1,61 @@
#!/usr/bin/env python
import logbook
import os
import sys
from mako.lookup import TemplateLookup
from logbook import Logger, StreamHandler, RotatingFileHandler
logbook.set_datetime_format('local')
DEBUG = True
VERSION = '0.1.0'
EMAIL_SUPPORT = ('soporte@empresalibre.net',)
BASE_DIR = os.path.abspath(os.path.dirname(__file__))
PATH_STATIC = os.path.abspath(os.path.join(BASE_DIR, '..'))
PATH_TEMPLATES = os.path.abspath(os.path.join(BASE_DIR, '..', 'templates'))
PATH_CP = os.path.abspath(os.path.join(BASE_DIR, '..', 'db', 'cp.db'))
COMPANIES = os.path.abspath(os.path.join(BASE_DIR, '..', 'db', 'rfc.db'))
DB_SAT = os.path.abspath(os.path.join(BASE_DIR, '..', 'db', 'sat.db'))
PATH_XSLT = os.path.abspath(os.path.join(BASE_DIR, '..', 'xslt'))
PATH_BIN = os.path.abspath(os.path.join(BASE_DIR, '..', 'bin'))
template_lookup = TemplateLookup(directories=[PATH_TEMPLATES],
input_encoding='utf-8',
output_encoding='utf-8')
LOG_PATH = 'empresalibre.log'
LOG_NAME = 'API'
LOG_LEVEL = 'INFO'
format_string = '[{record.time:%d-%b-%Y %H:%M:%S}] ' \
'{record.level_name}: ' \
'{record.channel}: ' \
'{record.message}'
if DEBUG:
LOG_LEVEL = 'DEBUG'
StreamHandler(
sys.stdout,
level=LOG_LEVEL,
format_string=format_string).push_application()
else:
LOG_PATH = '/srv/log/empresalibre.log'
RotatingFileHandler(
LOG_PATH,
backup_count=10,
max_size=1073741824,
level=LOG_LEVEL,
format_string=format_string).push_application()
log = Logger(LOG_NAME)
PATH_XSLTPROC = 'xsltproc'
PATH_OPENSSL = 'openssl'
if 'win' in sys.platform:
PATH_XSLTPROC = os.path.join(PATH_BIN, 'xsltproc.exe')
PATH_OPENSSL = os.path.join(PATH_BIN, 'openssl.exe')

BIN
source/db/cp.db Normal file

Binary file not shown.

BIN
source/db/rfc.db Normal file

Binary file not shown.

BIN
source/db/sat.db Normal file

Binary file not shown.

View File

@ -0,0 +1,116 @@
[
{
"tabla": "Categorias",
"datos": [
{"categoria": "Productos"},
{"categoria": "Servicios"}
]
},
{
"tabla": "SATImpuestos",
"datos": [
{"key": "002", "name": "IVA", "tasa": 0.16, "activo": true, "default": true},
{"key": "001", "name": "ISR", "tasa": 0.10, "tipo": "R", "activo": true},
{"key": "002", "name": "IVA", "tasa": 0.0, "activo": true},
{"key": "002", "name": "IVA", "tasa": 0.10, "tipo": "R", "activo": true},
{"key": "002", "name": "IVA", "tasa": 0.666667, "tipo": "R", "activo": true},
{"key": "000", "name": "Exento", "tipo": "E", "factor": "E", "activo": true}
]
},
{
"tabla": "SATUnidades",
"datos": [
{"key": "HUR", "name": "Hora", "activo": true},
{"key": "H87", "name": "Pieza", "activo": true},
{"key": "E48", "name": "Servicio", "activo": true},
{"key": "E51", "name": "Trabajo", "activo": false}
]
},
{
"tabla": "SATMonedas",
"datos": [
{"key": "MXN", "name": "Peso Mexicano", "activo": true, "default": true},
{"key": "USD", "name": "Dólar americano", "activo": false},
{"key": "EUR", "name": "Euro", "activo": false}
]
},
{
"tabla": "SATFormaPago",
"datos": [
{"key": "01", "name": "Efectivo", "activo": true},
{"key": "02", "name": "Cheque nominativo", "activo": true},
{"key": "03", "name": "Transferencia electrónica de fondos", "activo": true, "default": true},
{"key": "04", "name": "Tarjeta de crédito", "activo": true},
{"key": "05", "name": "Monedero electrónico", "activo": false},
{"key": "06", "name": "Dinero electrónico", "activo": false},
{"key": "08", "name": "Vales de despensa", "activo": false},
{"key": "12", "name": "Dación en pago", "activo": false},
{"key": "13", "name": "Pago por subrogación", "activo": false},
{"key": "14", "name": "Pago por consignación", "activo": false},
{"key": "15", "name": "Condonación", "activo": false},
{"key": "17", "name": "Compensación", "activo": false},
{"key": "23", "name": "Novación", "activo": false},
{"key": "24", "name": "Confusión", "activo": false},
{"key": "25", "name": "Remisión de deuda", "activo": false},
{"key": "26", "name": "Prescripción o caducidad", "activo": false},
{"key": "27", "name": "A satisfacción del acreedor", "activo": false},
{"key": "28", "name": "Tarjeta de débito", "activo": true},
{"key": "29", "name": "Tarjeta de servicios", "activo": false},
{"key": "30", "name": "Aplicación de anticipos", "activo": false},
{"key": "99", "name": "Por definir", "activo": true}
]
},
{
"tabla": "SATRegimenes",
"datos": [
{"key": "601", "name": "General de Ley Personas Morales", "moral": true, "activo": true, "default": true},
{"key": "603", "name": "Personas Morales con Fines no Lucrativos", "moral": true, "activo": true},
{"key": "605", "name": "Sueldos y Salarios e Ingresos Asimilados a Salarios", "fisica": true, "activo": true},
{"key": "606", "name": "Arrendamiento", "fisica": true, "activo": true},
{"key": "608", "name": "Demás ingresos", "fisica": true, "activo": false},
{"key": "609", "name": "Consolidación", "moral": true, "activo": false},
{"key": "610", "name": "Residentes en el Extranjero sin Establecimiento Permanente en México", "fisica": true, "moral": true, "activo": false},
{"key": "611", "name": "Ingresos por Dividendos (socios y accionistas)", "fisica": true, "activo": false},
{"key": "612", "name": "Personas Físicas con Actividades Empresariales y Profesionales", "fisica": true, "activo": true, "default": true},
{"key": "614", "name": "Ingresos por intereses", "fisica": true, "activo": false},
{"key": "616", "name": "Sin obligaciones fiscales", "fisica": true, "activo": false},
{"key": "620", "name": "Sociedades Cooperativas de Producción que optan por diferir sus ingresos", "moral": true, "activo": false},
{"key": "621", "name": "Incorporación Fiscal", "fisica": true, "activo": true},
{"key": "622", "name": "Actividades Agrícolas, Ganaderas, Silvícolas y Pesqueras", "fisica": true, "moral": true, "activo": false},
{"key": "623", "name": "Opcional para Grupos de Sociedades", "moral": true, "activo": false},
{"key": "624", "name": "Coordinados", "moral": true, "activo": false},
{"key": "628", "name": "Hidrocarburos", "moral": true, "activo": false},
{"key": "607", "name": "Régimen de Enajenación o Adquisición de Bienes", "moral": true, "activo": false},
{"key": "629", "name": "De los Regímenes Fiscales Preferentes y de las Empresas Multinacionales", "fisica": true, "activo": false},
{"key": "630", "name": "Enajenación de acciones en bolsa de valores", "fisica": true, "activo": false},
{"key": "615", "name": "Régimen de los ingresos por obtención de premios", "fisica": true, "activo": false}
]
},
{
"tabla": "SATUsoCfdi",
"datos": [
{"key": "G01", "name": "Adquisición de mercancias", "moral": true, "activo": true},
{"key": "G02", "name": "Devoluciones, descuentos o bonificaciones", "moral": true, "activo": true},
{"key": "G03", "name": "Gastos en general", "moral": true, "activo": true, "default": true},
{"key": "I01", "name": "Construcciones", "moral": true, "activo": false},
{"key": "I02", "name": "Mobilario y equipo de oficina por inversiones", "moral": true, "activo": false},
{"key": "I03", "name": "Equipo de transporte", "moral": true, "activo": false},
{"key": "I04", "name": "Equipo de computo y accesorios", "moral": true, "activo": false},
{"key": "I05", "name": "Dados, troqueles, moldes, matrices y herramental", "moral": true, "activo": false},
{"key": "I06", "name": "Comunicaciones telefónicas", "moral": true, "activo": false},
{"key": "I07", "name": "Comunicaciones satelitales", "moral": true, "activo": false},
{"key": "I08", "name": "Otra maquinaria y equipo", "moral": true, "activo": false},
{"key": "D01", "name": "Honorarios médicos, dentales y gastos hospitalarios.", "activo": false},
{"key": "D02", "name": "Gastos médicos por incapacidad o discapacidad", "activo": false},
{"key": "D03", "name": "Gastos funerales.", "activo": false},
{"key": "D04", "name": "Donativos.", "activo": true},
{"key": "D05", "name": "Intereses reales efectivamente pagados por créditos hipotecarios (casa habitación).", "activo": false},
{"key": "D06", "name": "Aportaciones voluntarias al SAR.", "activo": false},
{"key": "D07", "name": "Primas por seguros de gastos médicos.", "activo": false},
{"key": "D08", "name": "Gastos de transportación escolar obligatoria.", "activo": false},
{"key": "D09", "name": "Depósitos en cuentas para el ahorro, primas que tengan como base planes de pensiones.", "activo": false},
{"key": "D10", "name": "Pagos por servicios educativos (colegiaturas)", "activo": true},
{"key": "P01", "name": "Por definir", "moral": true, "activo": true}
]
}
]

1403
source/static/css/air.css Normal file

File diff suppressed because it is too large Load Diff

61
source/static/css/app.css Normal file
View File

@ -0,0 +1,61 @@
.login_header {
text-align: center;
font-weight: bold;
font-size: 150%;
font-family: Arial, Helvetica, sans-serif;
}
.cmd_edit_parter {
max-width: 32px,
padding-left: 5px;
}
.center {
text-align: center;
}
.right {
text-align: right;
}
.right_footer {
text-align: right;
font-weight: bold;
font-size: 125%;
color: DarkRed;
}
.lbl_partner {
font-weight: bold;
font-size: 125%;
}
.delete {
text-align: center;
font-weight: bold;
font-size: 250%;
color: red;
}
.cmd_close_partner div button {
background-color: red !important;
border-color: red !important;
border-bottom: 1px solid red !important;
}
/*
.webix_message_area {
position: absolute;
margin-top: 25%;
left: 50%;
transform: translate(-50%, -50%);
width: 40%;
}
*/
.webix_success div {
background-color: #00a65a !important;
font-size: 1vw;
color: white;
}

View File

@ -0,0 +1,95 @@
/* SideBar*/
.webix_sidebar{
background: #ECEFF1;
}
.webix_sidebar .webix_tree_item {
color: #454545;
height: 35px;
line-height: 35px;
}
.webix_sidebar .webix_scroll_cont > .webix_tree_leaves {
padding: 0;
}
.webix_sidebar .webix_tree_leaves .webix_tree_leaves {
margin-left: 0px;
}
.webix_sidebar_selected,
.webix_sidebar_expanded .webix_tree_item:hover{
background-color: rgba(0,0,0,0.02);
}
.webix_sidebar .webix_tree_item.webix_selected,
.webix_sidebar .webix_tree_item.webix_selected span{
background-color: #27ae60;
padding-right:0;
}
.webix_sidebar .webix_tree_branch_1 .webix_tree_item{
padding-left:40px;
}
.webix_sidebar .webix_tree_branch_1>.webix_tree_item{
height: 40px;
line-height: 40px;
padding-left:0;
}
.webix_sidebar .webix_tree_branch_1{
border-bottom:1px solid #e5e5e5;
}
.webix_sidebar .webix_tree_item.webix_selected span,
.webix_sidebar .webix_tree_item span{
margin:0;
padding:0px;
}
.webix_sidebar_icon{
width: 40px;
text-align: center;
}
.webix_sidebar_dir_icon{
float: right;
line-height: inherit;
}
/*SubMenu (Popup) */
.webix_sidebar_popup{
border:none !important;
box-shadow: 2px 3px 3px #ddd;
}
.webix_sidebar_popup, .webix_sidebar_popup .webix_list_item{
border-radius:0;
}
.webix_sidebar_popup_right{
box-shadow: -1px 3px 3px #ddd;
}
/*SubMenu: title*/
.webix_sidebar_popup_title{
background: #ECEFF1;
}
.webix_sidebar_popup_title.webix_selected{
border-left-color: #27ae60;
background: #27ae60;
}
.webix_sidebar_popup_title .webix_template{
line-height: 40px;
padding: 0 10px;
border: 1px solid #E5E5E5;
border-left:none;
}
.webix_sidebar_selected.webix_sidebar_popup_title .webix_template{
background: rgba(0,0,0,0.03);
border-left: none;
}
.webix_sidebar_popup_list .webix_list_item{
border-left: 1px solid #E5E5E5;
border-right: 1px solid #E5E5E5;
}
/*SubMenu: list*/
.webix_sidebar_popup_list .webix_list_item:first-child{
border-top: 1px solid #E5E5E5;
}
.webix_sidebar_popup_list .webix_list_item:hover{
background: #f6f9fb;
}
.webix_sidebar_popup_list .webix_list_item.webix_selected:hover{
background: #27ae60;
}

View File

@ -0,0 +1,94 @@
/* SideBar*/
.webix_sidebar{
background: #ECEFF1;
}
.webix_sidebar .webix_tree_item {
color: #454545;
height: 35px;
line-height: 35px;
}
.webix_sidebar .webix_scroll_cont > .webix_tree_leaves {
padding: 0;
}
.webix_sidebar .webix_tree_leaves .webix_tree_leaves {
margin-left: 0px;
}
.webix_sidebar_selected,
.webix_sidebar_expanded .webix_tree_item:hover{
background-color: rgba(0,0,0,0.02);
}
.webix_sidebar .webix_tree_item.webix_selected,
.webix_sidebar .webix_tree_item.webix_selected span{
background-color: #ffdb8f;
padding-right:0;
}
.webix_sidebar .webix_tree_branch_1 .webix_tree_item{
padding-left:40px;
}
.webix_sidebar .webix_tree_branch_1>.webix_tree_item{
height: 40px;
line-height: 40px;
padding-left:0;
}
.webix_sidebar .webix_tree_branch_1{
border-bottom:1px solid #e5e5e5;
}
.webix_sidebar .webix_tree_item.webix_selected span,
.webix_sidebar .webix_tree_item span{
margin:0;
padding:0px;
}
.webix_sidebar_icon{
width: 40px;
text-align: center;
}
.webix_sidebar_dir_icon{
float: right;
line-height: inherit;
}
/*SubMenu (Popup) */
.webix_sidebar_popup{
border:none !important;
box-shadow: 2px 3px 3px #ddd;
}
.webix_sidebar_popup, .webix_sidebar_popup .webix_list_item{
border-radius:0;
}
.webix_sidebar_popup_right{
box-shadow: -1px 3px 3px #ddd;
}
/*SubMenu: title*/
.webix_sidebar_popup_title{
background: #ECEFF1;
}
.webix_sidebar_popup_title.webix_selected{
border-left-color: #ffdb8f;
background: #ffdb8f;
}
.webix_sidebar_popup_title .webix_template{
line-height: 40px;
padding: 0 10px;
border: 1px solid #E5E5E5;
border-left:none;
}
.webix_sidebar_selected.webix_sidebar_popup_title .webix_template{
background: rgba(0,0,0,0.03);
border-left: none;
}
.webix_sidebar_popup_list .webix_list_item{
border-left: 1px solid #E5E5E5;
border-right: 1px solid #E5E5E5;
}
/*SubMenu: list*/
.webix_sidebar_popup_list .webix_list_item:first-child{
border-top: 1px solid #E5E5E5;
}
.webix_sidebar_popup_list .webix_list_item:hover{
background: #f6f9fb;
}
.webix_sidebar_popup_list .webix_list_item.webix_selected:hover{
background: #ffdb8f;
}

1742
source/static/css/webix.css Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,14 @@
## Font Awesome
Copyright (C) 2012 by Dave Gandy
Author: Dave Gandy
License: SIL (http://scripts.sil.org/OFL)
Homepage: http://fortawesome.github.com/Font-Awesome/
## PT Sans Free
Copyright © 2009 ParaType Ltd.
License: Free Font License v1.00

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 754 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,448 @@
var msg = ''
var controllers = {
init: function(){
//~ Admin
$$('menu_user').attachEvent('onMenuItemClick', menu_user_click)
$$('multi_admin').attachEvent('onViewChange', multi_admin_change)
//~ Emisor
$$('cmd_save_emisor').attachEvent('onItemClick', cmd_save_emisor_click)
$$('emisor_cp').attachEvent('onKeyPress', emisor_postal_code_key_press)
$$('emisor_cp').attachEvent('onTimedKeyPress', emisor_postal_code_key_up)
$$('chk_escuela').attachEvent('onChange', chk_escuela_change)
$$('chk_ong').attachEvent('onChange', chk_ong_change)
$$('cmd_subir_certificado').attachEvent('onItemClick', cmd_subir_certificado_click)
$$('up_cert').attachEvent('onUploadComplete', up_cert_upload_complete)
$$('cmd_agregar_serie').attachEvent('onItemClick', cmd_agregar_serie_click)
$$('grid_folios').attachEvent('onItemClick', grid_folios_click)
}
}
function menu_user_click(id, e, node){
if (id == 1){
window.location = '/logout'
return
}
}
function cmd_home_click(){
window.location = '/main'
}
function cmd_save_emisor_click(){
var valid_cp = false
var form = $$('form_emisor')
if (!form.validate()){
msg = 'Valores inválidos'
msg_error(msg)
return
}
var values = form.getValues()
var ids = $$('lst_emisor_regimen').getSelectedId()
if(!ids){
msg = 'Selecciona al menos un Regimen Fiscal'
msg_error(msg)
return
}
if(values.emisor_cp && values.emisor_cp.length != 5){
msg = 'Longitud inválida del C.P.'
msg_error(msg)
return
}
if(values.emisor_cp2 && values.emisor_cp2.length != 5){
msg = 'Longitud inválida del C.P. de Expedición'
msg_error(msg)
return
}else if(values.emisor_cp2){
webix.ajax().sync().get('/values/cp', {cp: values.emisor_cp2}, {
error: function(text, data, xhr) {
msg = 'Error al consultar el C.P. de Expedición'
msg_error(msg)
},
success: function(text, data, xhr) {
var values = data.json();
if (values.estado == undefined){
msg = 'No se encontró el C.P., asegurate de que sea correcto'
msg_error(msg)
}else{
valid_cp = true
}
}
})
}
if(!valid_cp){
return
}
if(values.es_ong){
if(!values.ong_autorizacion){
msg = 'Si es ONG, el Número de Autorización del SAT es requerido'
msg_error(msg)
return
}
if(!values.ong_fecha){
msg = 'Si es ONG, la Fecha de Autorización del SAT es requerida'
msg_error(msg)
return
}
if(!values.ong_fecha_dof){
msg = 'Si es ONG, la Fecha de Publicación en DOF es requerida'
msg_error(msg)
return
}
}
values['regimenes'] = ids
webix.ajax().post('/emisor', values, {
error:function(text, data, XmlHttpRequest){
msg = 'Ocurrio un error, consulta a soporte técnico'
msg_error(msg)
},
success:function(text, data, XmlHttpRequest){
var values = data.json()
if(values.ok){
msg_sucess('Emisor guardado correctamente')
}else{
msg_error(values.msg)
}
}
})
}
function get_emisor(){
var form = $$('form_emisor')
webix.ajax().get("/emisor", {}, {
error: function(text, data, xhr) {
msg = 'Error al consultar'
msg_error(msg)
},
success: function(text, data, xhr) {
var values = data.json()
if (values.ok){
var emisor = values.row.emisor
$$('lst_emisor_regimen').parse(values.row.regimenes)
form.setValues(emisor, true)
$$('lst_emisor_regimen').select(emisor.regimenes)
}else{
msg_error(values.msg)
}
}
})
}
function get_certificado(){
var form = $$('form_cert')
webix.ajax().get("/values/cert", {}, {
error: function(text, data, xhr) {
msg = 'Error al consultar'
msg_error(msg)
},
success: function(text, data, xhr) {
var values = data.json()
form.setValues(values)
}
})
}
function get_table_folios(){
webix.ajax().get("/folios", {}, {
error: function(text, data, xhr) {
msg = 'Error al consultar'
msg_error(msg)
},
success: function(text, data, xhr) {
var values = data.json()
$$('grid_folios').parse(values)
}
})
}
function multi_admin_change(prevID, nextID){
//~ webix.message(nextID)
if(nextID == 'app_emisor'){
$$('tab_emisor').setValue('Datos Fiscales')
get_emisor()
get_certificado()
return
}
if(nextID == 'app_folios'){
get_table_folios()
return
}
}
function emisor_postal_code_key_up(){
var value = this.getValue()
if( value.length == 5 ){
webix.ajax().get('/values/cp', {cp: value}, {
error: function(text, data, xhr) {
msg = 'Error al consultar el C.P.'
msg_error(msg)
},
success: function(text, data, xhr) {
var values = data.json();
if (values.estado == undefined){
msg = 'No se encontró el C.P., asegurate de que sea correcto'
msg_error(msg)
} else {
$$('form_emisor').setValues({
emisor_cp2: value,
emisor_estado: values.estado,
emisor_municipio: values.municipio,
emisor_colonia: ''}, true)
$$('emisor_colonia').define('suggest', [])
if (webix.isArray(values.colonia)){
$$('emisor_colonia').define('suggest', values.colonia)
}else{
$$('form_emisor').setValues(
{emisor_colonia: values.colonia}, true)
}
$$('emisor_colonia').refresh()
}
}
})
}
}
function emisor_postal_code_key_press(code, e){
var data = [8, 9, 37, 39, 46]
if ( data.indexOf(code) >= 0 ){
return true;
}
if ( code < 48 || code > 57){
return false;
}
}
function chk_escuela_change(new_value, old_value){
var value = Boolean(new_value)
if (value){
$$('cmd_niveles').enable()
} else {
$$('cmd_niveles').disable()
}
}
function chk_ong_change(new_value, old_value){
var value = Boolean(new_value)
if (value){
$$('ong_autorizacion').enable()
$$('ong_fecha').enable()
$$('ong_fecha_dof').enable()
} else {
$$('ong_autorizacion').disable()
$$('ong_fecha').disable()
$$('ong_fecha_dof').disable()
}
}
function cmd_subir_certificado_click(){
var form = $$('form_upload')
if (!form.validate()){
msg = 'Valores inválidos'
msg_error(msg)
return
}
var values = form.getValues()
if(!values.contra.trim()){
msg = 'La contraseña no puede estar vacía'
msg_error(msg)
return
}
if($$('lst_cert').count() < 2){
msg = 'Selecciona al menos dos archivos: CER y KEY del certificado.'
msg_error(msg)
return
}
if($$('lst_cert').count() > 2){
msg = 'Selecciona solo dos archivos: CER y KEY del certificado.'
msg_error(msg)
return
}
var fo1 = $$('up_cert').files.getItem($$('up_cert').files.getFirstId())
var fo2 = $$('up_cert').files.getItem($$('up_cert').files.getLastId())
var ext = ['key', 'cer']
if(ext.indexOf(fo1.type.toLowerCase()) == -1 || ext.indexOf(fo2.type.toLowerCase()) == -1){
msg = 'Archivos inválidos, se requiere un archivo CER y un KEY.'
msg_error(msg)
return
}
if(fo1.type == fo2.type && fo1.size == fo2.size){
msg = 'Selecciona archivos diferentes: un archivo CER y un KEY.'
msg_error(msg)
return
}
var rfc = $$('form_cert').getValues()['cert_rfc']
if(rfc){
msg = 'Ya existe un certificado guardado<BR><BR>¿Deseas reemplazarlo?'
webix.confirm({
title: 'Certificado Existente',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if(result){
$$('up_cert').send()
}
}
})
}
}
function up_cert_upload_complete(response){
if(response.status != 'server'){
msg = 'Ocurrio un error al subir los archivos'
msg_error(msg)
return
}
msg = 'Archivos subidos correctamente. Esperando validación'
msg_sucess(msg)
var values = $$('form_upload').getValues()
$$('form_upload').setValues({})
$$('up_cert').files.data.clearAll()
webix.ajax().post('/values/cert', values, {
error:function(text, data, XmlHttpRequest){
msg = 'Ocurrio un error, consulta a soporte técnico'
msg_error(msg)
},
success:function(text, data, XmlHttpRequest){
var values = data.json()
if(values.ok){
$$('form_cert').setValues(values.data)
msg_sucess(values.msg)
}else{
msg_error(values.msg)
}
}
})
}
function cmd_agregar_serie_click(){
var form = $$('form_folios')
var grid = $$('grid_folios')
if (!form.validate()){
msg = 'Valores inválidos'
msg_error(msg)
return
}
var values = form.getValues()
var reg = /^[a-z]+$/i
if(!reg.test(values.folio_serie)){
msg = 'Introduce una serie válida. Solo letras.'
msg_error(msg)
return
}
var pre = '1'
if(grid.count() > 0){
pre = ''
}
var usarcon = ''
if(values.folio_usarcon != 'S'){
usarcon = values.folio_usarcon
}
var values = {
serie: values.folio_serie.trim().toUpperCase(),
inicio: values.folio_inicio,
usarcon: usarcon,
default: pre,
}
webix.ajax().post('/folios', values, {
error:function(text, data, XmlHttpRequest){
msg = 'Ocurrio un error, consulta a soporte técnico'
msg_error(msg)
},
success:function(text, data, XmlHttpRequest){
var values = data.json()
if(values.ok){
form.setValues(
{folio_serie: '', folio_inicio: 1, folio_usarcon: 'S'})
grid.add(values.row)
msg = 'Serie agregada correctamente'
msg_sucess(msg)
}else{
msg_error(values.msg)
}
}
})
}
function grid_folios_click(id, e, node){
if(id.column != 'delete'){
return
}
msg = '¿Estás seguro de borrar esta serie?<BR><BR>ESTA ACCIÓN NO SE PUEDE DESHACER'
webix.confirm({
title: 'Borrar Serie',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if(result){
webix.ajax().del('/folios', {id: id.row}, function(text, xml, xhr){
msg = 'Serie eliminada correctamente'
if(xhr.status == 200){
$$('grid_folios').remove(id.row)
msg_sucess(msg)
}else{
msg = 'No se pudo eliminar'
msg_error(msg)
}
})
}
}
})
}

View File

@ -0,0 +1,651 @@
var query = []
var grid = null
var msg = ''
function get_series(){
webix.ajax().get('/values/series', function(text, data){
var values = data.json()
table_series.clear()
table_series.insert(values)
pre = values[0]
$$('lst_serie').getList().parse(values)
$$('lst_serie').setValue(pre.id)
if(pre.usar_con){
$$('lst_tipo_comprobante').setValue(pre.usar_con)
$$('lst_tipo_comprobante').config.readonly = true
$$('lst_tipo_comprobante').refresh()
}
if(values.length == 1){
$$('lst_serie').config.readonly = true
$$('lst_serie').refresh()
}
})
}
function get_forma_pago(){
webix.ajax().get('/values/formapago', {key: true}, function(text, data){
var values = data.json()
//~ pre = values[0]
$$('lst_forma_pago').getList().parse(values)
//~ $$('lst_forma_pago').setValue(pre.id)
})
}
function get_monedas(){
webix.ajax().get('/values/monedas', function(text, data){
var values = data.json()
pre = values[0]
$$('lst_moneda').getList().parse(values)
$$('lst_moneda').setValue(pre.id)
if(values.length == 1){
$$('fs_moneda').hide()
$$('fs_moneda').refresh()
}
})
}
function get_uso_cfdi(){
get_uso_cfdi_to_table({key: true})
query = table_usocfdi.chain().data()
$$('lst_uso_cfdi').getList().parse(query)
}
function get_regimen_fiscal(){
webix.ajax().get('/values/regimenes', function(text, data){
var values = data.json()
pre = values[0]
$$('lst_regimen_fiscal').getList().parse(values)
$$('lst_regimen_fiscal').setValue(pre.id)
if(values.length == 1){
$$('fs_regimen_fiscal').hide()
$$('fs_regimen_fiscal').refresh()
}
})
}
function default_config(){
webix.ajax().sync().get('/values/taxes', function(text, data){
var values = data.json()
table_taxes.clear()
table_taxes.insert(values)
})
get_series()
get_forma_pago()
get_monedas()
get_uso_cfdi()
get_regimen_fiscal()
table_pt.clear()
table_totals.clear()
}
function cmd_new_invoice_click(id, e, node){
var form = $$('form_invoice')
var grid_totals = $$('grid_totals')
grid = $$('grid_details')
default_config()
form.adjust()
form.setValues({id: 0, id_partner: 0, lbl_client: 'Ninguno'})
grid.clearAll()
grid_totals.clearAll()
grid_totals.add({id: 1, concepto: 'SubTotal', importe: 0})
$$('multi_invoices').setValue('invoices_new')
form.focus('search_client_id')
}
function cmd_edit_invoice_click(id, e, node){
$$("multi_invoices").setValue("invoices_new")
}
function delete_invoice(id){
webix.ajax().del('/invoices', {id: id}, function(text, xml, xhr){
if(xhr.status == 200){
gi.remove(id)
msg_sucess('Factura eliminada correctamente')
}else{
msg_error('No se pudo eliminar')
}
})
}
function cmd_delete_invoice_click(id, e, node){
var row = gi.getSelectedItem()
if (row == undefined){
msg_error('Selecciona una factura')
return
}
if(row.uuid){
msg_error('Solo se pueden eliminar facturas sin timbrar')
return
}
var msg = '¿Estás seguro de eliminar la siguiente Factura?<BR><BR>'
msg += '(' + row['folio'] + ') ' + row['cliente']
msg += '<BR><BR>ESTA ACCIÓN NO SE PUEDE DESHACER'
webix.confirm({
title:'Eliminar Factura',
ok:'Si',
cancel:'No',
type:'confirm-error',
text:msg,
callback:function(result){
if (result){
delete_invoice(row['id'])
}
}
})
}
function validate_invoice(values){
if(values.id_partner == 0){
webix.UIManager.setFocus('search_client_name')
msg = 'Selecciona un cliente'
msg_error(msg)
return false
}
if(!grid.count()){
webix.UIManager.setFocus('search_product_id')
msg = 'Agrega al menos un producto o servicio'
msg_error(msg)
return false
}
var uso_cfdi = $$('lst_uso_cfdi').getValue()
if(uso_cfdi.trim() == ""){
webix.UIManager.setFocus('lst_uso_cfdi')
msg = 'El Uso del CFDI es requerido'
msg_error(msg)
return false
}
var forma_pago = $$('lst_forma_pago').getValue()
if(forma_pago.trim() == ""){
webix.UIManager.setFocus('lst_forma_pago')
msg = 'La Forma de pago es requerida'
msg_error(msg)
return false
}
var tipo_cambio = $$('txt_tipo_cambio').getValue()
if(tipo_cambio.trim() == ""){
webix.UIManager.setFocus('txt_tipo_cambio')
msg = 'El Tipo de Cambio es requerido'
msg_error(msg)
return false
}
if(isNaN(tipo_cambio * 1)){
webix.UIManager.setFocus('txt_tipo_cambio')
msg = 'El Tipo de Cambio debe ser un valor númerico'
msg_error(msg)
return false
}
var moneda = $$('lst_moneda').getValue()
if(moneda == 'MXN' && tipo_cambio != 1){
webix.UIManager.setFocus('txt_tipo_cambio')
msg = 'Si la moneda es MXN, el Tipo de Cambio debe ser 1.00'
msg_error(msg)
return false
}
if(moneda != 'MXN' && tipo_cambio == 1){
webix.UIManager.setFocus('txt_tipo_cambio')
msg = 'Si la moneda no es MXN, el Tipo de Cambio debe ser diferente de 1.00'
msg_error(msg)
return false
}
return true
}
function update_grid_invoices(values){
if(values.new){
gi.add(values.row)
}else{
gi.updateItem(values.row['id'], values.row)
}
}
function send_timbrar(id){
webix.ajax().get('/values/timbrar', {id: id}, function(text, data){
var values = data.json()
if(values.ok){
msg_sucess(values.msg)
gi.updateItem(id, values.row)
}else{
webix.alert({
title: 'Error al Timbrar',
text: values.msg,
type: 'alert-error'
})
}
})
}
function save_invoice(data){
var result = false
var values = NaN
webix.ajax().sync().post('invoices', data, {
error:function(text, data, XmlHttpRequest){
msg = 'Ocurrio un error, consulta a soporte técnico'
msg_error(msg)
},
success:function(text, data, XmlHttpRequest){
values = data.json();
if(values.ok){
msg_sucess('Factura guardada correctamente. Enviando a timbrar')
update_grid_invoices(values)
send_timbrar(values.row['id'])
result = true
}else{
msg_error(values.msg)
}
}
})
if(result){
table_pt.clear()
table_totals.clear()
grid.clearAll()
$$('grid_totals').clearAll()
}
return result
}
function cmd_timbrar_click(id, e, node){
var form = this.getFormView();
if(!form.validate()) {
webix.message({type:'error', text:'Valores inválidos'})
return
}
var values = form.getValues();
if(!validate_invoice(values)){
return
}
var rows = grid.data.getRange()
for (i = 0; i < rows.length; i++) {
delete rows[i]['delete']
delete rows[i]['clave']
delete rows[i]['descripcion']
delete rows[i]['unidad']
delete rows[i]['importe']
rows[i]['valor_unitario'] = parseFloat(rows[i]['valor_unitario'])
}
var data = new Object()
data['id'] = values.id
data['cliente'] = values.id_partner
data['productos'] = rows
data['serie'] = $$('lst_serie').getText()
data['forma_pago'] = $$('lst_forma_pago').getValue()
data['condiciones_pago'] = $$('txt_condicion_pago').getValue().trim()
data['moneda'] = $$('lst_moneda').getValue()
data['tipo_cambio'] = $$('txt_tipo_cambio').getValue()
data['tipo_comprobante'] = $$('lst_tipo_comprobante').getValue()
data['metodo_pago'] = $$('lst_metodo_pago').getValue()
data['uso_cfdi'] = $$('lst_uso_cfdi').getValue()
data['regimen_fiscal'] = $$('lst_regimen_fiscal').getValue()
if(!save_invoice(data)){
return
}
form.setValues({id_partner: 0, lbl_partner: 'Ninguno'})
$$('multi_invoices').setValue('invoices_home')
}
function cmd_close_invoice_click(id, e, node){
$$('multi_invoices').setValue('invoices_home')
}
function search_client_by_id(id){
var msg = ''
webix.ajax().get('/values/client', {'id': id}, {
error: function(text, data, xhr) {
webix.message({type: 'error', text: 'Error al consultar'})
},
success: function(text, data, xhr){
var values = data.json()
if (values.ok){
set_client(values.row)
}else{
msg = 'No se encontró un cliente con la clave: ' + id
webix.message({type:'error', text: msg})
}
}
})
}
function set_client(row){
var form = $$('form_invoice')
var html = '<span class="webix_icon fa-user"></span><span class="lbl_partner">'
form.setValues({
id_partner:row.id, search_client_id:'', search_client_name:'',
forma_pago: row.forma_pago, uso_cfdi: row.uso_cfdi}, true)
html += row.nombre + ' (' + row.rfc + ')</span>'
$$('lbl_client').setValue(html)
form.focus('search_product_id')
}
function grid_clients_found_click(obj){
set_client(obj)
}
function search_client_id_key_press(code, e){
var value = this.getValue()
if(code == 13 && value.length > 0){
var id = parseInt(value, 10)
if (isNaN(id)){
webix.message({type:'error', text:'Captura una clave válida'});
}else{
search_client_by_id(id)
}
}
}
function calculate_taxes(){
var tmp = null
table_totals.clear()
var subtotal = 0
var total_iva = 0
var id = 2
var grid_totals = $$('grid_totals')
grid_totals.clearAll()
grid_totals.add({id: 1, concepto: 'SubTotal', importe: 0})
grid.eachRow(function(row){
var product = grid.getItem(row)
subtotal += parseFloat(product.importe)
query = table_pt.chain().find({'product': product.id}).data()
for(var tax of query){
tmp = table_totals.findOne({'tax': tax.tax})
if(tmp === null){
table_totals.insert(
{'tax': tax.tax, 'importe': parseFloat(product.importe)})
tmp = table_totals.findOne({'tax': tax.tax})
}else{
tmp.importe += parseFloat(product.importe)
table_totals.update(tmp)
}
}
})
var tax = null
var tipo = 'Traslado '
var concepto = ''
var total_tax = 0
query = table_totals.chain().data()
for(var t of query){
tax = table_taxes.findOne({'id': t.tax})
if(tax.tipo == 'E' || tax.tipo == 'R'){
continue
}
concepto = tipo + tax.name + ' (' + tax.tasa + ')'
total_tax = (tax.tasa * t.importe).round(DECIMALES)
grid_totals.add({id: id, concepto: concepto, importe: total_tax})
id += 1
if(tax.name == 'IVA'){
total_iva += total_tax
}
}
tipo = 'Retención '
for(var t of query){
tax = table_taxes.findOne({'id': t.tax})
if(tax.tipo == 'E' || tax.tipo == 'T'){
continue
}
concepto = tipo + tax.name + ' (' + tax.tasa + ')'
if(tax.tasa == (2/3).round(6)){
total_tax = (tax.tasa * total_iva * -1).round(DECIMALES)
concepto = tipo + tax.name + ' (2/3)'
}else{
total_tax = (tax.tasa * t.importe * -1).round(DECIMALES)
}
grid_totals.add({id: id, concepto: concepto, importe: total_tax})
id += 1
}
var row = {importe: subtotal}
grid_totals.updateItem(1, row)
}
function set_product(values){
var taxes = values.taxes
var values = values.row
var form = $$('form_invoice')
var row = grid.getItem(values.id)
values['delete'] = '-'
if (row == undefined){
values['cantidad'] = 1
values['importe'] = values['valor_unitario']
grid.add(values)
} else {
values['cantidad'] = parseFloat(row.cantidad) + 1
values['importe'] = values['valor_unitario'] * values['cantidad']
grid.updateItem(row.id, values)
}
form.setValues({search_product_id:'', search_product_name:''}, true)
for(var v of taxes){
var pt = table_pt.findOne(v)
if(pt === null){
table_pt.insert(v)
}
}
calculate_taxes()
}
function grid_products_found_click(obj){
search_product_by_id(obj.id)
}
function search_product_by_id(id){
var msg = ''
webix.ajax().get('/values/product', {'id': id}, {
error: function(text, data, xhr) {
webix.message({type: 'error', text: 'Error al consultar'})
},
success: function(text, data, xhr){
var values = data.json()
if (values.ok){
set_product(values)
} else {
msg = 'No se encontró un producto con la clave: ' + id
webix.message({type: 'error', text: msg})
}
}
})
}
function search_product_id_key_press(code, e){
var value = this.getValue()
if(code == 13 && value.length > 0){
var id = parseInt(value, 10)
if (isNaN(id)){
webix.message({type: 'error', text: 'Captura una clave válida'});
}else{
search_product_by_id(id)
}
}
}
function grid_details_before_edit_start(id){
var columns = ['', 'descripcion', 'cantidad', 'valor_unitario']
if(!columns.indexOf(id.column)){
return !this.getItem(id.row)[id.column]
}
}
function grid_details_before_edit_stop(state, editor){
var row = grid.getItem(editor.row)
if(editor.column == 'descripcion'){
if(!state.value.trim()){
msg = 'La descripción no puede estar vacía'
webix.message({type:'error', text: msg})
grid.blockEvent()
state.value = state.old
grid.editCancel()
grid.unblockEvent()
return true
}
state.value = state.value.trim()
return true
}
if(editor.column == 'cantidad'){
var cantidad = parseFloat(state.value)
if(isNaN(cantidad)){
msg = 'La cantidad debe ser un número'
webix.message({type:'error', text: msg})
grid.blockEvent()
state.value = state.old
grid.editCancel()
grid.unblockEvent()
return true
}
var valor_unitario = row['valor_unitario']
}
if(editor.column == 'valor_unitario'){
var valor_unitario = parseFloat(state.value)
if(isNaN(valor_unitario)){
msg = 'El valor unitario debe ser un número'
webix.message({type:'error', text: msg})
grid.blockEvent()
state.value = state.old
grid.editCancel()
grid.unblockEvent()
return true
}
var cantidad = row['cantidad']
}
row['importe'] = (cantidad * valor_unitario).round(DECIMALES)
grid.refresh()
calculate_taxes()
}
function grid_details_click(id, e, node){
if(id.column != 'delete'){
return
}
grid.remove(id.row)
calculate_taxes()
}
function grid_details_header_click(id){
if(id.column != 'delete'){
return
}
var msg = '¿Estás seguro de quitar todos los productos?'
webix.confirm({
title: 'Quitar todos',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if (result){
grid.clearAll()
calculate_taxes()
}
}
})
}
function cmd_refacturar_click(){
show('Refacturar')
}
function cmd_invoice_timbrar_click(){
if(gi.count() == 0){
return
}
var row = gi.getSelectedItem()
if (row == undefined){
msg_error('Selecciona una factura')
return
}
if(row.uuid){
msg_error('La factura ya esta timbrada')
return
}
msg = '¿Estás seguro de enviar a timbrar esta factura?'
webix.confirm({
title: 'Timbrar Factura',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if(result){
send_timbrar(row.id)
}
}
})
}
function grid_invoices_click(id, e, node){
var row = this.getItem(id)
if(id.column == 'xml'){
location = '/doc/xml/' + row.id
}
}

View File

@ -0,0 +1,157 @@
var gi = null
var controllers = {
init: function(){
//~ Main
$$('menu_user').attachEvent('onMenuItemClick', menu_user_click);
//~ Partner
$$('cmd_new_partner').attachEvent('onItemClick', cmd_new_partner_click);
$$('cmd_new_contact').attachEvent('onItemClick', cmd_new_contact_click);
$$('cmd_edit_partner').attachEvent('onItemClick', cmd_edit_partner_click);
$$('cmd_delete_partner').attachEvent('onItemClick', cmd_delete_partner_click);
$$('cmd_save_partner').attachEvent('onItemClick', cmd_save_partner_click);
$$('cmd_cancel_partner').attachEvent('onItemClick', cmd_cancel_partner_click);
$$('cmd_cancel_contact').attachEvent('onItemClick', cmd_cancel_contact_click);
$$('codigo_postal').attachEvent('onKeyPress', postal_code_key_press);
$$('codigo_postal').attachEvent('onTimedKeyPress', postal_code_key_up);
$$('colonia').attachEvent('onFocus', colonia_on_focus)
$$("tipo_persona").attachEvent( "onChange", opt_tipo_change)
$$("es_cliente").attachEvent( "onChange", is_client_change)
$$("es_proveedor").attachEvent( "onChange", is_supplier_change)
$$("rfc").attachEvent( "onBlur", rfc_lost_focus)
$$('multi').attachEvent('onViewChange', multi_change)
//~ Products
$$("cmd_new_product").attachEvent("onItemClick", cmd_new_product_click)
$$("cmd_edit_product").attachEvent("onItemClick", cmd_edit_product_click)
$$("cmd_delete_product").attachEvent("onItemClick", cmd_delete_product_click)
$$("cmd_save_product").attachEvent("onItemClick", cmd_save_product_click)
$$("cmd_cancel_product").attachEvent("onItemClick", cmd_cancel_product_click)
$$("chk_automatica").attachEvent("onChange", chk_automatica_change)
$$("valor_unitario").attachEvent("onChange", valor_unitario_change)
//~ Invoices
$$('cmd_new_invoice').attachEvent("onItemClick", cmd_new_invoice_click)
$$('cmd_refacturar').attachEvent("onItemClick", cmd_refacturar_click)
$$('cmd_delete_invoice').attachEvent("onItemClick", cmd_delete_invoice_click)
$$('cmd_timbrar').attachEvent('onItemClick', cmd_timbrar_click)
$$('cmd_close_invoice').attachEvent('onItemClick', cmd_close_invoice_click)
$$('search_client_id').attachEvent('onKeyPress', search_client_id_key_press)
$$('grid_clients_found').attachEvent('onValueSuggest', grid_clients_found_click)
$$('search_product_id').attachEvent('onKeyPress', search_product_id_key_press)
$$('grid_products_found').attachEvent('onValueSuggest', grid_products_found_click)
$$('grid_details').attachEvent('onItemClick', grid_details_click)
$$('grid_details').attachEvent('onHeaderClick', grid_details_header_click)
$$('grid_details').attachEvent('onBeforeEditStart', grid_details_before_edit_start)
$$('grid_details').attachEvent('onBeforeEditStop', grid_details_before_edit_stop)
$$('cmd_invoice_timbrar').attachEvent('onItemClick', cmd_invoice_timbrar_click)
$$('grid_invoices').attachEvent('onItemClick', grid_invoices_click)
}
}
function get_uso_cfdi_to_table(args){
webix.ajax().sync().get('/values/usocfdi', args, function(text, data){
var values = data.json()
table_usocfdi.clear()
table_usocfdi.insert(values)
})
}
function get_partners(){
webix.ajax().get("/partners", {}, {
error: function(text, data, xhr) {
webix.message({ type:"error", text: "Error al consultar"});
},
success: function(text, data, xhr) {
var values = data.json();
$$("grid_partners").clearAll();
if (values.ok){
$$("grid_partners").parse(values.rows, 'json');
};
}
});
}
function get_products(){
var grid = $$('grid_products')
webix.ajax().get('/products', {}, {
error: function(text, data, xhr) {
webix.message({type: 'error', text: 'Error al consultar'})
},
success: function(text, data, xhr) {
var values = data.json();
grid.clearAll();
if (values.ok){
grid.parse(values.rows, 'json');
};
}
});
}
function get_invoices(){
var grid = $$('grid_invoices')
webix.ajax().get('/invoices', {}, {
error: function(text, data, xhr) {
webix.message({type: 'error', text: 'Error al consultar'})
},
success: function(text, data, xhr) {
var values = data.json();
grid.clearAll();
if (values.ok){
grid.parse(values.rows, 'json');
};
}
});
}
function menu_user_click(id, e, node){
if (id == 1){
window.location = '/logout';
return
}
}
function multi_change(prevID, nextID){
//~ webix.message(nextID)
if(nextID == 'app_partners'){
active = $$('multi_partners').getActiveId()
if(active == 'partners_home'){
get_partners()
}
return
}
if(nextID == 'app_products'){
active = $$('multi_products').getActiveId()
if(active == 'products_home'){
get_products()
}
return
}
if(nextID == 'app_invoices'){
active = $$('multi_invoices').getActiveId()
if(active == 'invoices_home'){
get_invoices()
}
gi = $$('grid_invoices')
return
}
}
function get_taxes(){
webix.ajax().sync().get('/values/taxes', function(text, data){
var values = data.json()
table_taxes.clear()
table_taxes.insert(values)
$$("grid_product_taxes").clearAll()
$$("grid_product_taxes").parse(values, 'json')
})
}

View File

@ -0,0 +1,329 @@
function cmd_new_partner_click(id, e, node){
$$('form_partner').setValues({
id: 0, pais: 'México', tipo_persona: 1, es_activo: true})
$$('forma_pago').getList().load('/values/formapago')
$$('grid_partners').clearSelection()
$$('multi_partners').setValue('partners_new')
$$('tab_partner').setValue('Datos Fiscales')
get_uso_cfdi_to_table({})
query = table_usocfdi.chain().find({fisica: true}).data()
$$('lst_uso_cfdi_socio').getList().parse(query)
}
function cmd_new_contact_click(id, e, node){
$$('grid_contacts').clearSelection()
$$('multi_contacts').setValue('contacts_new')
}
function cmd_edit_partner_click(id, e, node){
var msg = ''
var row = $$('grid_partners').getSelectedItem()
if (row == undefined){
msg = 'Selecciona un Socio de Negocio'
webix.message({type:'error', text: msg})
return
}
webix.ajax().get("/partners", {id: row['id']}, {
error: function(text, data, xhr) {
webix.message({type:"error", text: "Error al consultar"})
},
success: function(text, data, xhr){
var values = data.json()
$$('form_partner').setValues(values)
$$('forma_pago').getList().load('/values/formapago')
get_uso_cfdi_to_table({})
if(values.tipo_persona == 1){
query = table_usocfdi.chain().find({fisica: true}).data()
}else if(values.tipo_persona == 2){
query = table_usocfdi.chain().find({moral: true}).data()
}else{
query = [{id: 'P01', value: 'Por definir'}]
}
$$('lst_uso_cfdi_socio').getList().parse(query)
if(values.es_cliente){
$$('cuenta_cliente').enable()
}
if(values.es_proveedor){
$$('cuenta_proveedor').enable()
}
}
})
$$('multi_partners').setValue('partners_new')
$$('tab_partner').setValue('Datos Fiscales')
};
function cmd_delete_partner_click(id, e, node){
var msg = ''
var row = $$('grid_partners').getSelectedItem()
if (row == undefined){
msg = 'Selecciona un Cliente o Proveedor'
webix.message({type:'error', text: msg})
return
}
msg = '¿Estás seguro de eliminar al cliente?<BR><BR>'
msg += row['nombre'] + ' (' + row['rfc'] + ')'
msg += '<BR><BR>ESTA ACCIÓN NO SE PUEDE DESHACER<BR><BR>'
msg += 'Solo se pueden eliminar clientes o proveedores sin documentos '
msg += 'relacionados. Se recomienda solo desactivar en vez de eliminar'
webix.confirm({
title:'Eliminar Cliente',
ok:'Si',
cancel:'No',
type:'confirm-error',
text:msg,
callback:function(result){
if (result){
delete_partner(row['id'])
}
}
})
};
function delete_partner(id){
webix.ajax().del('/partners', {id: id}, function(text, xml, xhr){
var msg = 'Socio eliminado correctamente'
if (xhr.status == 200){
$$('grid_partners').remove(id);
webix.message({type: 'success', text: msg})
} else {
msg = 'No se pudo eliminar. Asegurate de que no tenga documentos relacionados'
webix.message({type: 'error', text: msg})
}
})
}
function cmd_save_partner_click(id, e, node){
var msg = 'Valores inválidos'
var form = this.getFormView();
if (!form.validate()) {
webix.message({type: 'error', text: msg})
return
}
var values = form.getValues();
if(values.codigo_postal && values.codigo_postal.length != 5){
msg = 'Longitud inválida del C.P.'
msg_error(msg)
return
}
if (!values.es_cliente && !values.es_proveedor){
msg = 'Selecciona si es cliente, proveedor o ambos'
msg_error(msg)
$$('tab_partner').setValue('Otros Datos')
return
}
webix.ajax().post('/partners', values, {
error:function(text, data, XmlHttpRequest){
msg = 'Ocurrio un error, consulta a soporte técnico';
webix.message({type:'error', text:msg});
},
success:function(text, data, XmlHttpRequest){
var values = data.json();
if (values.ok) {
update_grid_partner(values)
} else {
webix.message({type:'error', text:values.msg});
}
}
})
}
function update_grid_partner(values){
var msg = 'Socio de negocio agregado correctamente'
if (values.new){
$$('form_partner').clear()
$$('grid_partners').add(values.row)
}else{
msg = 'Socio de negocio actualizado correctamente'
$$("grid_partners").updateItem(values.row['id'], values.row)
}
$$('multi_partners').setValue('partners_home')
webix.message({type:'success', text: msg})
}
function cmd_cancel_partner_click(id, e, node){
$$('multi_partners').setValue('partners_home')
}
function cmd_cancel_contact_click(id, e, node){
$$('multi_contacts').setValue('contacts_home')
}
function postal_code_key_up(){
var value = this.getValue()
var msg = ''
if( value.length == 5 ){
webix.ajax().get('/values/cp', {cp: value}, {
error: function(text, data, xhr) {
webix.message({type:'error', text:'Error al consultar el C.P.'})
},
success: function(text, data, xhr) {
var values = data.json();
if (values.estado == undefined){
msg = 'No se encontró el C.P., asegurate de que sea correcto'
webix.message({type:'error', text:msg})
} else {
$$('form_partner').setValues({
estado: values.estado,
municipio: values.municipio,
colonia: ''}, true)
$$('colonia').define('suggest', [])
if (webix.isArray(values.colonia)){
$$('colonia').define('suggest', values.colonia)
}else{
$$('form_partner').setValues({colonia: values.colonia}, true)
}
$$('colonia').refresh()
$$('form_partner').focus('colonia')
}
}
})
}
}
function postal_code_key_press(code, e){
var data = [8, 9, 37, 39, 46]
if ( data.indexOf(code) >= 0 ){
return true;
}
if ( code < 48 || code > 57){
return false;
}
}
function colonia_on_focus(){
if ($$(this.config.suggest).getList().config.height > 2){
$$(this.config.suggest).show(this.getInputNode())
}
}
function opt_tipo_change(new_value, old_value){
$$("nombre").define("value", "")
$$("pais").define("readonly", true)
$$("pais").define("value", PAIS)
if (new_value == 1 || new_value == 2){
$$("rfc").define("value", "");
$$("rfc").define("readonly", false);
} else if (new_value == 3) {
$$("rfc").define("value", RFC_PUBLICO);
$$("nombre").define("value", PUBLICO);
$$("rfc").define("readonly", true);
} else if (new_value == 4) {
$$("rfc").define("value", RFC_EXTRANJERO);
$$("rfc").define("readonly", true);
$$("pais").define("readonly", false);
$$("pais").define("value", "");
}
$$("nombre").refresh();
$$("rfc").refresh();
$$("pais").refresh();
if (new_value == 3) {
$$("calle").focus();
} else {
$$("rfc").focus();
}
$$('lst_uso_cfdi_socio').define('suggest', [])
if (new_value == 1){
query = table_usocfdi.chain().find({fisica: true}).data()
}else if (new_value == 2){
query = table_usocfdi.chain().find({moral: true}).data()
}else{
query = [{id: 'P01', value: 'Por definir'}]
}
$$('lst_uso_cfdi_socio').getList().parse(query)
$$('lst_uso_cfdi_socio').refresh()
}
function is_client_change(new_value, old_value){
var value = Boolean(new_value)
if (value){
$$("cuenta_cliente").enable();
} else {
$$("cuenta_cliente").disable();
}
}
function is_supplier_change(new_value, old_value){
var value = Boolean(new_value)
if (value){
$$("cuenta_proveedor").enable();
} else {
$$("cuenta_proveedor").disable();
}
}
function rfc_lost_focus(prev_view){
//~ var form = this.getFormView()
//~ var values = form.getValues()
//~ if (values.rfc.trim() == ""){
//~ return
//~ }
//~ if (values.id == undefined){
//~ exclude = ''
//~ } else {
//~ exclude = {'id': values.id}
//~ }
//~ values = {
//~ 'table': 'partner',
//~ 'filter': {'rfc': values.rfc.trim().toUpperCase()},
//~ 'exclude': exclude,
//~ }
//~ webix.message(values)
//~ webix.ajax().get("/values/validate", values, {
//~ error:function(text, data, XmlHttpRequest){
//~ msg = "No se pudo validar el RFC"
//~ webix.message({ type:"error", text: msg });
//~ },
//~ success:function(text, data, XmlHttpRequest){
//~ var values = data.json();
//~ if (values.exists) {
//~ msg = "El RFC ya existe"
//~ webix.message({ type:"error", text: msg });
//~ }
//~ }
//~ })
}
function multi_partners_change(prevID, nextID){
webix.message(prevID)
webix.message(nextID)
}

View File

@ -0,0 +1,203 @@
function cmd_new_product_click(id, e, node){
$$('form_product').setValues({
id: 0, es_activo_producto: true})
add_config({'key': 'id_product', 'value': ''})
get_new_key()
get_taxes()
$$('grid_products').clearSelection()
$$('categoria').getList().load('/values/categorias')
$$('unidad').getList().load('/values/unidades')
$$("multi_products").setValue("product_new")
}
function cmd_edit_product_click(id, e, node){
var grid = $$('grid_products')
var row = grid.getSelectedItem()
if(row == undefined){
webix.message({type: 'error', text: 'Selecciona un Producto'})
return
}
get_taxes()
$$('categoria').getList().load('/values/categorias')
$$('unidad').getList().load('/values/unidades')
webix.ajax().get('/products', {id:row['id']}, {
error: function(text, data, xhr) {
webix.message({type: 'error', text: 'Error al consultar'})
},
success: function(text, data, xhr){
var values = data.json()
$$('form_product').setValues(values.row)
add_config({'key': 'id_product', 'value': values.row.id})
for(i = 0; i < values.taxes.length; i++){
$$('grid_product_taxes').select(values.taxes[i], true)
}
}
})
$$('multi_products').setValue('product_new')
};
function delete_product(id){
webix.ajax().del('/products', {id:id}, function(text, xml, xhr){
var msg = 'Producto eliminado correctamente'
if(xhr.status == 200){
$$('grid_products').remove(id)
webix.message({type:'success', text:msg})
}else{
msg = 'No se pudo eliminar'
webix.message({type:'error', text:msg})
}
})
}
function cmd_delete_product_click(id, e, node){
var row = $$('grid_products').getSelectedItem()
if (row == undefined){
webix.message({type:'error', text: 'Selecciona un Producto'})
return
}
var msg = '¿Estás seguro de eliminar el Producto?<BR><BR>'
msg += '(' + row['clave'] + ') ' + row['descripcion']
msg += '<BR><BR>ESTA ACCIÓN NO SE PUEDE DESHACER<BR><BR>Se recomienda '
msg += 'solo desactivar el producto en vez de eliminar'
webix.confirm({
title: 'Eliminar Producto',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if (result){
delete_product(row['id'])
}
}
})
}
function validate_sat_key_product(key, text){
var result = false
webix.ajax().sync().get('/values/satkey', {key:key}, function(text, data){
result = data.json()
})
if(text){
if(result.ok){
return '<b>' + result.text + '</b>'
}else{
return '<b><font color="red">' + result.text + '</font></b>'
}
}
return result.ok
}
function update_grid_products(values){
var msg = 'Producto agregado correctamente'
if(values.new){
$$('form_product').clear()
$$('grid_products').add(values.row)
}else{
msg = 'Producto actualizado correctamente'
$$("grid_products").updateItem(values.row['id'], values.row)
}
$$('multi_products').setValue('products_home')
webix.message({type: 'success', text: msg})
}
function cmd_save_product_click(id, e, node){
var msg = ''
var form = this.getFormView()
if(!form.validate()){
webix.message({type: 'error', text: 'Valores inválidos'})
return
}
var rows = $$('grid_product_taxes').getSelectedId(true, true)
if (rows.length == 0){
webix.message({type: 'error', text: 'Selecciona un impuesto'})
return
}
var values = form.getValues();
if (!validate_sat_key_product(values.clave_sat, false)){
webix.message({ type:'error', text:'La clave SAT no existe' })
return
}
values['taxes'] = JSON.stringify(rows)
webix.ajax().sync().post('products', values, {
error:function(text, data, XmlHttpRequest){
msg = 'Ocurrio un error, consulta a soporte técnico'
webix.message({type: 'error', text: msg})
},
success:function(text, data, XmlHttpRequest){
var values = data.json();
if (values.ok) {
update_grid_products(values)
}else{
webix.message({type:'error', text:values.msg})
}
}
})
}
function cmd_cancel_product_click(id, e, node){
$$("multi_products").setValue("products_home")
};
function chk_automatica_change(new_value, old_value){
var value = Boolean(new_value)
if (value){
var value = get_config('id_product')
if(value){
$$("clave").setValue(value)
$$("clave").refresh()
}else{
get_new_key()
}
$$("clave").config.readonly = true
$$('form_product').focus('clave_sat')
} else {
$$("clave").setValue('')
$$("clave").config.readonly = false
$$('form_product').focus('clave')
}
$$("clave").refresh()
}
function get_new_key(){
webix.ajax().get('/values/newkey', {
error: function(text, data, xhr) {
webix.message({type:'error', text: text})
},
success: function(text, data, xhr) {
var values = data.json();
$$("clave").setValue(values.value)
$$("clave").refresh()
}
})
}
function valor_unitario_change(new_value, old_value){
if(!isFinite(new_value)){
this.config.value = old_value
this.refresh()
}
}

View File

@ -0,0 +1,149 @@
var PUBLICO = "Público en general";
var RFC_PUBLICO = "XAXX010101000";
var RFC_EXTRANJERO = "XEXX010101000";
var PAIS = "México";
var DECIMALES = 2;
var db = new loki('data.db');
var table_config = db.addCollection('config')
var table_taxes = db.addCollection('taxes')
var table_pt = db.addCollection('productstaxes')
var table_totals = db.addCollection('totals', {unique: ['tax']})
var table_series = db.addCollection('series')
var table_usocfdi = db.addCollection('usocfdi')
function show(values){
webix.message(JSON.stringify(values, null, 2))
}
function msg_error(msg){
webix.message({type: 'error', text: msg})
}
function msg_sucess(msg){
webix.message({type: 'sucess', text: msg})
}
Number.prototype.round = function(decimals){
return Number((Math.round(this + "e" + decimals) + "e-" + decimals))
}
webix.protoUI({
$cssName: "text",
name: "currency",
$init:function(){
this.attachEvent("onItemClick", function(){
this.$setValue(this.config.raw, true)
})
this.attachEvent("onBlur", function(){
this.$setValue(this.config.value)
})
},
$render:function(){
this.$setValue(this.config.value)
},
$setValue:function(value, raw){
this.config.raw = value
if(!raw){
value = webix.i18n.priceFormat(value)
}
this.getInputNode().value = value
}
}, webix.ui.text)
webix.ui.datafilter.rowCount = webix.extend({
refresh:function(master, node, value){
node.firstChild.innerHTML = master.count();
}
}, webix.ui.datafilter.summColumn)
function validate_rfc(value){
rfc = value.trim().toUpperCase();
if ( rfc == ""){
webix.message({ type:"error", text:"El RFC no puede estar vacío" });
return false
}
var tipo_persona = $$('tipo_persona').getValue()
var length = 13
var start = 4
if(tipo_persona==2){
length = 12
start = 2
}
if (rfc.length != length){
webix.message({ type:"error", text:"Longitud incorrecta del RFC" });
return false
}
if (tipo_persona < 3 && (rfc == RFC_PUBLICO || rfc == RFC_EXTRANJERO)){
webix.message({ type:"error", text:"RFC incorrecto" });
return false
}
var part = rfc.slice(0, start);
var re = new RegExp('[a-z&Ñ]{' + start + '}', 'i');
if (!part.match(re)){
webix.message({ type:"error", text: "El RFC tiene caractéres inválidos al inicio" });
return false
}
part = rfc.slice(-3);
re = new RegExp('[a-z0-9]{3}', 'i');
if (!part.match(re)){
webix.message({ type:"error", text: "El RFC tiene caractéres inválidos al final" });
return false
}
part = rfc.slice(-9, -3);
re = new RegExp('[0-9]{6}', 'i');
if (!part.match(re)){
webix.message({ type:"error", text: "Fecha inválida" });
return false
}
var month = parseInt(part.slice(-4, -2))
if (month == 0 || month > 12 ){
webix.message({ type:"error", text: "Fecha inválida" });
return false
}
var day = parseInt(part.slice(-2))
if (day == 0 || day > 31 ){
webix.message({ type:"error", text: "Fecha inválida" });
return false
}
return true
}
function validate_email(email){
var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
return re.test(email)
}
function add_config(args){
var key = table_config.findOne({key: args.key})
if(key===null){
table_config.insert(args)
}else{
key.value = args.value
table_config.update(key)
}
}
function get_config(value){
var key = table_config.findOne({key: value})
if(key===null){
return ''
}else{
return key.value
}
}

26
source/static/js/es-MX.js Normal file
View File

@ -0,0 +1,26 @@
/*Spanish (Mexico) locale*/
webix.i18n.locales["es-MX"] = {
groupDelimiter:",",
groupSize:3,
decimalDelimiter:".",
decimalSize:2,
dateFormat:"%d/%n/%Y",
timeFormat:"%h:%i %a",
longDateFormat:"%l, %d' %je '%F' %je '%Y",
fullDateFormat:"%l, %d' %je '%F' %je '%Y %h:%i %a",
am:["a.m.","A.M."],
pm:["p.m.","P.M."],
price:"${obj}",
priceSettings:{
groupDelimiter:",",
groupSize:3,
decimalDelimiter:".",
decimalSize:2
},
calendar:{
monthFull:["enero","febrero","marzo","abril","mayo","junio","julio","agosto","septiembre","octubre","noviembre","diciembre"],
monthShort:["ene","feb","mar","abr","may","jun","jul","ago","sep","oct","nov","dic"],
dayFull:["domingo","lunes","martes","miércoles","jueves","viernes","sábado"],
dayShort:["dom","lun","mar","mié","jue","vie","sáb"]
}
};

26
source/static/js/es.js Normal file
View File

@ -0,0 +1,26 @@
/*Spanish locale*/
webix.i18n.locales["es"] = {
groupDelimiter:".",
groupSize:3,
decimalDelimiter:",",
decimalSize:2,
dateFormat:"%d/%n/%Y",
timeFormat:"%G:%i",
longDateFormat:"%l, %d' %je '%F' %je '%Y",
fullDateFormat:"%l, %d' %je '%F' %je '%Y %G:%i",
am:null,
pm:null,
price:"{obj} €",
priceSettings:{
groupDelimiter:".",
groupSize:3,
decimalDelimiter:",",
decimalSize:2
},
calendar:{
monthFull:["enero","febrero","marzo","abril","mayo","junio","julio","agosto","septiembre","octubre","noviembre","diciembre"],
monthShort:["ene","feb","mar","abr","may","jun","jul","ago","sep","oct","nov","dic"],
dayFull:["domingo","lunes","martes","miércoles","jueves","viernes","sábado"],
dayShort:["dom","lun","mar","mié","jue","vie","sáb"]
}
};

3
source/static/js/lokijs.min.js vendored Normal file

File diff suppressed because one or more lines are too long

275
source/static/js/sidebar.js Normal file
View File

@ -0,0 +1,275 @@
webix.protoUI({
name: "sidebar",
defaults:{
titleHeight: 40,
type: "sideBar",
activeTitle: true,
select: true,
scroll: false,
collapsed: false,
collapsedWidth: 41,
position: "left",
width: 250,
mouseEventDelay: 10
},
$init: function(config){
this.$ready.push(this._initSidebar);
this.$ready.push(this._initContextMenu);
},
on_context:{},
on_mouse_move:{},
_initSidebar: function(){
this._fullWidth = this.config.width;
this.attachEvent("onBeforeOpen", function(id){
if(!this.config.multipleOpen)
this.closeAll();
return !this.config.collapsed;
});
this.attachEvent("onItemClick", function(id, ev, node){
if(this.getPopup() && !this.getPopup().config.hidden)
ev.showpopup = true;
if(webix.env.touch)
this._showPopup(id, node);
});
this.attachEvent("onBeforeSelect", function(id){
if(!this.getItem(id).$count){
var selected = this.getSelectedId();
if(selected && id!= selected){
var parentId = this.getParentId(selected);
this.removeCss(parentId, "webix_sidebar_selected");
}
return true;
}
return false;
});
this.attachEvent("onAfterSelect", function(id){
var parentId = this.getParentId(id);
this.addCss(parentId, "webix_sidebar_selected");
var title = this.getPopupTitle();
title.callEvent("onMasterSelect",[id]);
});
this.attachEvent("onMouseMove", function(id, ev, node){
this._showPopup(id, node);
});
if(this.config.collapsed)
this.collapse();
},
_showPopup: function(id, node){
if(this.config.collapsed){
var popup = this.getPopup();
if(popup){
var title = this.getPopupTitle();
if(title){
this._updateTitle(id);
}
var list = this.getPopupList();
if(list){
this._updateList(id);
}
var x = (this.config.position == "left"?this.config.collapsedWidth:-popup.config.width);
popup.show(node, {x: x , y:-1});
}
}
},
_updateTitle: function(id){
var title = this.getPopupTitle();
title.masterId = id;
title.parse(this.getItem(id));
var selectedId = this.getSelectedId();
if(selectedId && this.getParentId(selectedId) == id){
webix.html.addCss(title.$view, "webix_sidebar_selected", true);
}
else{
webix.html.removeCss(title.$view, "webix_sidebar_selected");
}
if(selectedId == id){
webix.html.addCss(title.$view, "webix_selected", true);
}
else{
webix.html.removeCss(title.$view, "webix_selected");
}
},
_updateList: function(id){
var list = this.getPopupList();
list.masterId = id;
var selectedId = this.getSelectedId();
var data = [].concat(webix.copy(this.data.getBranch(id)));
list.unselect();
if(data.length){
list.show();
list.data.importData(data);
if(list.exists(selectedId))
list.select(selectedId);
}
else
list.hide();
},
_initContextMenu: function(){
var config = this.config,
popup;
if(config.popup){
popup = webix.$$(config.popup);
}
if(!popup){
var dirClassName = (config.position=="left"?"webix_sidebar_popup_left":"webix_sidebar_popup_right");
var popupConfig = {
view:"popup",
css: "webix_sidebar_popup "+dirClassName,
autofit: false,
width: this._fullWidth - this.config.collapsedWidth,
borderless: true,
padding:0,
body:{
rows:[
{
view: "template", borderless: true, css: "webix_sidebar_popup_title",
template: "#value#", height: this.config.titleHeight+2,
on:{
onMasterSelect: function(id){
var master = this.getTopParentView().master;
if( master && master.getParentId(id) == this.masterId){
webix.html.addCss(this.$view, "webix_sidebar_selected", true);
}
if(master.config.collapsed && master.getItem(id).$level ==1){
webix.html.addCss(this.$view, "webix_selected", true);
}
}
},
onClick:{
webix_template: function(){
var id = this.masterId;
var master = this.getTopParentView().master;
if(!master.getItem(id).$count)
master.select(id);
}
}
},
{ view: "list", select: true, borderless: true, css: "webix_sidebar_popup_list", autoheight: true,
on:{
onAfterSelect: function(id){
this.getTopParentView().master.select(id);
}
}
}
]
}
};
webix.extend(popupConfig, config.popup||{}, true);
popup = webix.ui(popupConfig);
popup.master = this;
}
popup.attachEvent("onBeforeShow",function(){
return config.collapsed;
});
var master = this;
var h = webix.event(document.body,"mousemove", function(e){
var trg = e.target || e.srcElement;
if(!popup.config.hidden && !popup.$view.contains(trg) && !master.$view.firstChild.contains(trg)){
popup.hide();
}
});
this.attachEvent("onDestruct", function(){
if(webix.removeEvent)
webix.removeEvent(h);
if(popup)
popup.destructor();
});
config.popupId = popup.config.id;
},
getPopup: function(){
return webix.$$(this.config.popupId);
},
getPopupTitle: function(){
var popup = this.getPopup();
return popup.getBody().getChildViews()[0];
},
getPopupList: function(){
var popup = this.getPopup();
return popup.getBody().getChildViews()[1];
},
position_setter:function(value){
var newPos = value;
var oldPos = value=="left"?"right":"left";
webix.html.removeCss(this.$view, "webix_sidebar_"+oldPos);
webix.html.addCss(this.$view, "webix_sidebar_"+newPos, true);
var popup = this.getPopup();
if(popup){
var popupEl = popup.$view;
webix.html.removeCss(popupEl, "webix_sidebar_popup_"+oldPos);
webix.html.addCss(popupEl, "webix_sidebar_popup_"+newPos, true);
}
return value;
},
collapse: function(){
this.define("collapsed", true);
},
expand: function(){
this.define("collapsed", false);
},
toggle: function(){
var collapsed = !this.config.collapsed;
this.define("collapsed", collapsed);
},
collapsed_setter: function(value){
var width;
if(!value){
width = this._fullWidth;
}
else{
width = this.config.collapsedWidth;
this.closeAll();
}
if(!value){
this.type.collapsed = false;
webix.html.addCss(this.$view, "webix_sidebar_expanded", true);
}
else{
this.type.collapsed = true;
webix.html.removeCss(this.$view, "webix_sidebar_expanded");
}
this.define("width",width);
this.resize();
return value;
}
}, webix.ui.tree);
webix.type(webix.ui.tree, {
name:"sideBar",
height: "auto",
css: "webix_sidebar",
template: function(obj, common){
if(common.collapsed)
return common.icon(obj, common);
return common.arrow(obj, common)+common.icon(obj, common) +"<span>"+obj.value+"</span>";
},
arrow: function(obj, common){
var html = "";
var open = "";
for (var i=1; i<=obj.$level; i++){
if (i==obj.$level && obj.$count){
var className = "webix_sidebar_dir_icon webix_icon fa-angle-"+(obj.open?"down":"left");
html+="<span class='"+className+"'></span>";
}
}
return html;
},
icon:function(obj, common){
if(obj.icon)
return "<span class='webix_icon webix_sidebar_icon fa-"+obj.icon+"'></span>";
return "";
}
});

View File

@ -0,0 +1,319 @@
var menu_data = [
{id: 'app_home', icon: 'dashboard', value: 'Inicio'},
{id: 'app_emisor', icon: 'user-circle', value: 'Emisor'},
{id: 'app_folios', icon: 'sort-numeric-asc', value: 'Folios'},
]
var sidebar = {
view: 'sidebar',
data: menu_data,
ready: function(){
this.select('app_home');
this.open(this.getParentId('app_home'));
},
on:{
onAfterSelect: function(id){
$$('multi_admin').setValue(id)
}
}
}
var emisor_datos_fiscales = [
{template: 'Datos SAT', type: 'section'},
{cols: [{view: 'text', id: 'emisor_rfc', name: 'emisor_rfc', label: 'RFC: ',
width: 300, required: true, invalidMessage: 'RFC inválido',
readonly: true, attributes: {maxlength: 13}}, {}]},
{view: 'text', id: 'emisor_nombre', name: 'emisor_nombre',
label: 'Razón Social: ', required: true,
invalidMessage: 'La Razón Social es requerida'},
{cols: [
{view: 'search', id: 'emisor_cp', name: 'emisor_cp', width: 300,
label: 'C.P.: ', required: true, attributes: {maxlength: 5},
invalidMessage: 'El C.P. es requerido'},
{view: 'text', id: 'emisor_cp2', name: 'emisor_cp2', width: 300,
label: 'C.P. de Expedición: ', attributes: {maxlength: 5}},
{}]},
{cols: [
{view: 'label', label: 'Regimenes Fiscales *', required: true}, {}]},
{cols: [{view: 'list', id: 'lst_emisor_regimen', select: 'multiselect',
name: 'lst_emisor_regimen', width: 600, height: 125, required: true,
data: []}, {}]},
{template: 'Dirección Fiscal', type: 'section'},
{view: 'text', id: 'emisor_calle', name: 'emisor_calle', label: 'Calle: '},
{cols: [{view: 'text', id: 'emisor_no_exterior', name: 'emisor_no_exterior',
width: 300, label: 'No Exterior: '},{}]},
{cols: [{view: 'text', id: 'emisor_no_interior', name: 'emisor_no_interior',
width: 300, label: 'No Interior: '},{}]},
{view: 'text', id: 'emisor_colonia', name: 'emisor_colonia',
label: 'Colonia: '},
{view: 'text', id: 'emisor_municipio', name: 'emisor_municipio',
label: 'Municipio: '},
{view: 'text', id: 'emisor_estado', name: 'emisor_estado',
label: 'Estado: '},
{view: 'text', id: 'emisor_pais', name: 'emisor_pais', label: 'País: ',
value: 'México', readonly: true},
{template: '', type: 'section', minHeight: 25},
]
var emisor_otros_datos= [
{template: 'Generales', type: 'section'},
{view: 'text', id: 'emisor_nombre_comercial',
name: 'emisor_nombre_comercial', label: 'Nombre comercial: '},
{view: 'text', id: 'emisor_telefono', name: 'emisor_telefono',
label: 'Teléfonos: '},
{view: 'text', id: 'emisor_correo', name: 'emisor_correo',
label: 'Correos: '},
{view: 'text', id: 'emisor_web', name: 'emisor_web',
label: 'Página Web: '},
{template: 'Escuela', type: 'section'},
{cols: [{view: 'checkbox', id: 'chk_escuela', name: 'es_escuela',
label: 'Es Escuela'},
{view: 'button', id: 'cmd_niveles', label: 'Niveles Escolares',
type: 'form', align: 'center', autowidth: true, disabled: true},
{}, {}]},
{template: 'ONG', type: 'section'},
{view: 'checkbox', id: 'chk_ong', name: 'es_ong', label: 'Es ONG'},
{cols: [{view: 'text', id: 'ong_autorizacion', name: 'ong_autorizacion',
label: 'Autorización: ', disabled: true,
placeholder: 'Número de autorización del SAT'}, {}]},
{cols: [{view: 'datepicker', id: 'ong_fecha', name: 'ong_fecha',
label: 'Fecha de Autorización: ', disabled: true, format: '%d-%M-%Y',
placeholder: 'Fecha de autorización en el SAT'}, {}]},
{cols: [{view: 'datepicker', id: 'ong_fecha_dof', name: 'ong_fecha_dof',
label: 'Fecha de DOF: ', disabled: true, format: '%d-%M-%Y',
placeholder: 'Fecha de publicación en el DOF'}, {}]},
]
var emisor_certificado = [
{template: 'Certificado actual', type: 'section'},
{view: 'form', id: 'form_cert', rows: [
{cols: [{view: 'text', id: 'cert_rfc', name: 'cert_rfc',
label: 'RFC: ', readonly: true, placeholder: 'Ninguno'}, {}]},
{cols: [{view: 'text', id: 'cert_serie', name: 'cert_serie',
label: 'Serie: ', readonly: true, placeholder: 'Ninguno'}, {}]},
{cols: [{view: 'text', id: 'cert_desde', name: 'cert_desde',
label: 'Vigente desde: ', readonly: true}, {}]},
{cols: [{view: 'text', id: 'cert_hasta', name: 'cert_hasta',
label: 'Vigente hasta: ', readonly: true}, {}]},
]},
{template: 'Cargar Certificado', type: 'section'},
{view: 'form', id: 'form_upload', rows: [
{cols: [{},
{view: 'uploader', id: 'up_cert', autosend: false, link: 'lst_cert',
value: 'Seleccionar certificado', upload: '/values/files'}, {}]},
{cols: [{},
{view: 'list', id: 'lst_cert', name: 'certificado',
type: 'uploader', autoheight:true, borderless: true}, {}]},
{cols: [{},
{view: 'text', id: 'txt_contra', name: 'contra',
label: 'Contraseña KEY', labelPosition: 'top',
labelAlign: 'center', type: 'password', required: true}, {}]},
{cols: [{}, {view: 'button', id: 'cmd_subir_certificado',
label: 'Subir certificado'}, {}]},
]},
]
var controls_emisor = [
{
view: 'tabview',
id: 'tab_emisor',
tabbar: {options: [
'Datos Fiscales',
'Otros Datos',
'Certificado']},
animate: true,
cells: [
{id: 'Datos Fiscales', rows: emisor_datos_fiscales},
{id: 'Otros Datos', rows: emisor_otros_datos},
{id: 'Certificado', rows: emisor_certificado},
{},
]
}
]
var form_emisor = {
type: 'space',
cols: [{
view: 'form',
id: 'form_emisor',
complexData: true,
elements: controls_emisor,
elementsConfig: {
labelWidth: 150,
labelAlign: 'right'
},
autoheight: true,
rules: {
emisor_nombre: function(value){return value.trim() != ''},
}
}]
}
var options_usarcon = [
{id: 'S', value: 'Todos'},
{id: 'I', value: 'Ingreso'},
{id: 'E', value: 'Egreso'},
{id: 'T', value: 'Traslado'},
]
var grid_folios_cols = [
{id: 'delete', header: '', width: 30, css: 'delete'},
{id: 'serie', header: 'Serie', fillspace: 1},
{id: 'inicio', header: 'Inicio', fillspace: 1},
{id: 'usarcon', header: 'Usar Con', fillspace: 1},
{id: 'pre', header: 'Predeterminado', fillspace: 1}
]
var grid_folios = {
view: 'datatable',
id: 'grid_folios',
select: 'row',
adjust: true,
headermenu: true,
columns: grid_folios_cols
}
var emisor_folios = [
{template: 'Nueva serie', type: 'section'},
{cols: [
{view: 'text', id: 'folio_serie', name: 'folio_serie', label: 'Serie: ',
required: true, attributes: {maxlength: 15}},
{view: 'counter', id: 'folio_inicio', name: 'folio_inicio', value: 1,
required: true, label: 'Inicio: ', step: 1, min: 1},
{view: 'richselect', id: 'folio_usarcon', name: 'folio_usarcon',
label: 'Usar Con: ', value: 'S', required: true,
options: options_usarcon},
]},
{maxHeight: 20},
{cols: [{},
{view: 'button', id: 'cmd_agregar_serie', label: 'Agregar Serie',
autowidth: true, type: 'form'},
{}]},
{template: 'Series guardadas', type: 'section'},
grid_folios
]
var controls_folios = [
{
view: 'tabview',
id: 'tab_folios',
tabbar: {options: ['Folios']},
animate: true,
cells: [
{id: 'Folios', rows: emisor_folios},
{},
]
}
]
var form_folios = {
type: 'space',
cols: [{
view: 'form',
id: 'form_folios',
complexData: true,
elements: controls_folios,
elementsConfig: {
labelWidth: 100,
labelAlign: 'right'
},
autoheight: true,
rules: {
folio_serie: function(value){return value.trim() != ''},
folio_inicio: function(value){return value > 0},
}
}]
}
var app_emisor = {
id: 'app_emisor',
rows:[
{view: 'template', id: 'th_emisor', type: 'header',
template: 'Emisor'},
form_emisor,
{maxHeight: 20},
{margin: 10, cols: [{},
{view: 'button', id: 'cmd_save_emisor', label: 'Guardar' ,
type: 'form', autowidth: true, align: 'center'},
{}]
},
{},
]
}
var app_folios = {
id: 'app_folios',
rows:[
{view: 'template', id: 'th_folios', type: 'header',
template: 'Folios'},
form_folios,
{},
]
}
var multi_admin = {
id: 'multi_admin',
animate: true,
cells:[
{
id: 'app_admin_home',
view: 'template',
template: 'HOME'
},
app_emisor,
app_folios,
]
}
var menu_user = {
view: 'menu',
id: 'menu_user',
width: 150,
autowidth: true,
data: [
{id: '0', value: 'User...', submenu:[{id:1, value:'Cerrar Sesión'}]},
],
type: {
subsign: true,
},
};
var ui_admin = {
rows: [
{view: 'toolbar', padding: 3, elements: [
{view: 'button', type: 'icon', icon: 'bars',
width: 37, align: 'left', css: 'app_button', click: function(){
$$('$sidebar1').toggle()
}
},
{view: 'label', label: 'Empresa Libre - Configuración'},
{},
{view: 'button', type: 'icon', width: 40, css: 'app_button',
icon: 'home', click: 'cmd_home_click'},
menu_user
]},
{
cols:[
sidebar,
multi_admin,
]
}
]
};

View File

@ -0,0 +1,331 @@
var toolbar_invoices = [
{view: "button", id: "cmd_new_invoice", label: "Nueva", type: "iconButton",
autowidth: true, icon: "plus"},
{view: "button", id: "cmd_refacturar", label: "Refacturar", type: "iconButton",
autowidth: true, icon: "pencil"},
{},
{view: "button", id: "cmd_delete_invoice", label: "Eliminar", type: "iconButton",
autowidth: true, icon: "minus"},
]
var toolbar_invoices_util = [
{view: 'button', id: 'cmd_invoice_timbrar', label: 'Timbrar',
type: 'iconButton', autowidth: true, icon: 'ticket'},
]
function get_icon(tipo){
var node = "<img src='/static/img/file-" + tipo + ".png' height='20' width='17' style='margin: 5px 0px'/>"
return node
}
var grid_invoices_cols = [
{id: "id", header:"ID", hidden:true},
{id: "serie", header: ["Serie", {content: "selectFilter"}], adjust: "data",
sort:"string"},
{id: "folio", header: ["Folio", {content: "numberFilter"}], adjust: "data",
sort:"int", css: "cell_right"},
{id: "uuid", header: ["UUID", {content: "textFilter"}], adjust: "data",
sort:"string", hidden:true},
{id: "fecha", header: ["Fecha y Hora"], adjust: "data", sort:"string"},
{id: "tipo_comprobante", header: ["Tipo", {content: "selectFilter"}],
adjust: 'header', sort: 'string'},
{id: "estatus", header: ["Estatus", {content: "selectFilter"}],
adjust: "data", sort:"string"},
{id: 'total_mn', header: ['Total M.N.', {content: 'numberFilter'}], width: 150,
sort: 'int', format: webix.i18n.priceFormat, css: 'right'},
{id: "cliente", header: ["Razón Social", {content: "selectFilter"}],
fillspace:true, sort:"string"},
{id: 'xml', header: 'XML', adjust: 'data', template: get_icon('xml')},
{id: 'pdf', header: 'PDF', adjust: 'data', template: get_icon('pdf')},
{id: 'zip', header: 'ZIP', adjust: 'data', template: get_icon('zip')},
{id: 'email', header: '', adjust: 'data', template: get_icon('email')}
]
var grid_invoices = {
view: 'datatable',
id: 'grid_invoices',
select: 'row',
adjust: true,
footer: true,
resizeColumn: true,
headermenu: true,
columns: grid_invoices_cols,
};
var grid_details_cols = [
{id: "id", header:"ID", hidden: true},
{id: 'delete', header: '', width: 30, css: 'delete'},
{id: "clave", header:{text: 'Clave', css: 'center'}, width: 100},
{id: "descripcion", header:{text: 'Descripción', css: 'center'},
fillspace: true, editor: 'text'},
{id: "unidad", header:{text: 'Unidad', css: 'center'}, width: 100},
{id: 'cantidad', header: {text: 'Cantidad', css: 'center'}, width: 100,
format: webix.i18n.numberFormat, css:'right', editor: 'text'},
{id: "valor_unitario", header:{text: 'Valor Unitario', css: 'center'}, width: 100,
format: webix.i18n.priceFormat, css:'right', editor: 'text'},
{id: "importe", header:{text: 'Importe', css: 'center'}, width: 150, format: webix.i18n.priceFormat, css:'right'},
]
var grid_details = {
view: 'datatable',
id: 'grid_details',
select: 'row',
adjust: true,
autoheight: true,
editable: true,
columns: grid_details_cols,
data: []
}
var grid_totals_cols = [
{id: 'id', header: 'ID', hidden: true},
{id: 'concepto', header: 'Concepto', width: 200,
footer: {text: 'TOTAL', css:'right_footer'}, css:'right'},
{id: 'importe', header: 'Importe', width: 150,
footer: {content: 'summColumn', css:'right_footer'},
format: webix.i18n.priceFormat, css:'right'},
]
var grid_totals = {
view: 'datatable',
id: 'grid_totals',
select: false,
width: 350,
header: false,
footer: true,
autoheight: true,
columns: grid_totals_cols,
data: [{id: 1, concepto: 'SubTotal', importe: 0}]
}
var suggest_partners = {
view: 'gridsuggest',
id: 'grid_clients_found',
name: 'grid_clients_found',
body: {
autoConfig: false,
header: false,
columns: [
{id: 'id', hidden: true},
{id: 'nombre', adjust: 'data'},
{id: 'rfc', adjust: 'data'},
{id: 'forma_pago', hidden: true},
{id: 'uso_cfdi', hidden: true},
],
dataFeed:function(text){
if (text.length > 2){
this.load('/values/client?name=' + text)
}else{
this.hide()
}
}
}
}
var suggest_products = {
view: 'gridsuggest',
id: 'grid_products_found',
name: 'grid_products_found',
body: {
autoConfig: false,
header: true,
columns: [
{id: 'id', hidden: true},
{id: 'clave', adjust: 'data'},
{id: 'descripcion', adjust: 'data'},
{id: 'unidad', adjust: 'data'},
{id: 'valor_unitario', adjust: 'data',
format: webix.i18n.priceFormat}
],
dataFeed:function(text){
if (text.length > 2){
this.load('/values/product?name=' + text)
}else{
this.hide()
}
}
}
}
var body_comprobante = {rows: [{
cols: [
{
view: 'richselect',
id: 'lst_tipo_comprobante',
label: 'Tipo',
labelPosition: 'top',
required: true,
value: 'I',
options: [
{id: 'I', value: 'Ingreso'},
{id: 'E', value: 'Egreso'},
{id: 'T', value: 'Traslado'}
]
},
{
view: 'richselect',
id: 'lst_serie',
label: 'Serie',
labelPosition: 'top',
options: [],
},
]},
{view: 'richselect', id: 'lst_uso_cfdi', name: 'uso_cfdi', required: true,
labelPosition: 'top', label: 'Uso del CFDI', options: []},
]}
var opt_metodo_pago = [
{id: 'PUE', value: 'Pago en una sola exhibición'},
{id: 'PPD', value: 'Pago en parcialidades o diferido'}
]
var body_opciones = {rows: [
{view: 'richselect', id: 'lst_forma_pago', name: 'forma_pago',
label: 'Forma de Pago', labelPosition: 'top', required: true,
options: []},
{view: 'richselect', id: 'lst_metodo_pago', label: 'Método de Pago',
labelPosition: 'top', options: opt_metodo_pago, readonly: true,
value: 'PUE',
required: true},
{view: 'text', id: 'txt_condicion_pago', label: 'Condiciones de Pago',
labelPosition: 'top'},
]}
var body_moneda = {cols: [
{view: 'richselect', id: 'lst_moneda', label: 'Nombre',
labelPosition: 'top', required: true, options: []},
{view: 'text', type: 'text', id: 'txt_tipo_cambio', value: '1.00',
label: 'Tipo de Cambio', labelPosition: 'top', required: true,
invalidMessage: 'Captura un valor númerico', inputAlign: 'right',
readonly: true}
]}
var body_regimen_fiscal = {
view: 'richselect',
id: 'lst_regimen_fiscal',
required: true,
options: []
}
var controls_generate = [
{cols: [ {rows:[
{view: 'fieldset', label: 'Buscar Cliente', body: {rows: [
{cols: [
{view:"search", id:"search_client_id", name:"search_client_id",
label:"por Clave", labelPosition:'top', maxWidth:200,
placeholder:'Captura la clave'},
{view: 'search', id: 'search_client_name',
name: 'search_client_name', label: 'por Nombre o RFC',
labelPosition: 'top', suggest: suggest_partners,
placeholder: 'Captura al menos tres letras'},
]},
{cols: [{
view: 'label', id: 'lbl_client_title',
name: "lbl_client_title", label: 'Seleccionado: ',
autowidth:true},
{view: 'label', id: 'lbl_client', name: 'lbl_client',
label: 'Ninguno'},
]}
]}},
{view: 'fieldset', label: 'Buscar Producto', body: {rows: [
{cols: [
{view: "search", id: "search_product_id",
name: "search_product_id", label: "por Clave",
labelPosition:'top', maxWidth:200,
placeholder:'Captura la clave'},
{view: "search", id: "search_product_name",
name: "search_product_name", label: "por Descripción",
labelPosition:'top', suggest: suggest_products,
placeholder:'Captura al menos tres letras'},
]},
]}}
]},
{maxWidth: 10},
{maxWidth: 300, rows: [
{view: 'fieldset', label: 'Comprobante', body: body_comprobante},
{view: 'fieldset', label: 'Opciones de Pago', body: body_opciones},
{view: 'fieldset', id: 'fs_moneda', label: 'Moneda', body: body_moneda},
{view: 'fieldset', id: 'fs_regimen_fiscal', label: 'Regimen Fiscal',
body: body_regimen_fiscal},
]}
]},
{view: 'label', label: 'Detalle', height: 30, align: 'left'},
grid_details,
{minHeight: 15, maxHeight: 15},
{cols: [{}, grid_totals]}
]
var controls_invoices = [
{
view: "tabview",
tabbar: {options: ["Generar"]}, animate: true,
cells: [
{id: "Generar", rows: controls_generate},
]
},
{rows: [
{template:"", type: "section" },
{margin: 10, cols: [{},
{view: "button", id: "cmd_timbrar", label: "Timbrar",
type: "form", autowidth: true, align:"center"},
{view: 'button', id: 'cmd_close_invoice', label: 'Cancelar',
type: 'danger', autowidth: true, align: 'center'},
{}]
},
]}
]
var form_invoice = {
type: 'space',
cols: [{
view: 'form',
id: 'form_invoice',
complexData: true,
elements: controls_invoices,
}]
}
var multi_invoices = {
id: 'multi_invoices',
view: 'multiview',
animate: true,
cells:[
{id: 'invoices_home', rows:[
{view: 'toolbar', elements: toolbar_invoices},
{view: 'toolbar', elements: toolbar_invoices_util},
grid_invoices,
]},
{id: 'invoices_new', rows:[form_invoice, {}]}
]
}
var app_invoices = {
id: "app_invoices",
rows:[
{view: "template", id: "th_invoices", type: "header", template:"Administración de Facturas" },
multi_invoices
]
}

View File

@ -0,0 +1,44 @@
var msg_rfc = 'El RFC es requerido'
var msg_user = 'El usuario es requerido'
var msg_pass = 'La contraseña es requerida'
var form_controls = [
{view: 'text', label: 'RFC', id: 'txt_rfc', name: 'rfc',
labelPosition: 'top', required: true, invalidMessage: msg_rfc},
{view: 'text', label: 'Usuario', id: 'txt_usuario', name: 'usuario',
labelPosition: 'top', required: true, invalidMessage: msg_user},
{view: 'text', label: 'Contraseña', id: 'txt_contra', name: 'contra',
type: 'password', required: true, labelPosition: 'top',
invalidMessage: msg_pass},
{margin: 10, cols:[{}, {view: 'button', value: 'Iniciar Sesión',
click: 'validate_login', hotkey: 'enter'}, {}]}
]
var msg_header = 'Bienvenido a Empresa Libre'
var ui_login = {
rows: [
{maxHeight: 50},
{view: 'template', template: msg_header, maxHeight: 50, css: 'login_header'},
{maxHeight: 50},
{cols: [{}, {type: 'space', padding: 5,
rows: [
{view: 'template', template: 'Acceso al sistema', type: 'header'},
{
container: 'form_login',
view: 'form',
id: 'form_login',
width: 400,
elements: form_controls,
rules:{
rfc:function(value){ return value.trim() != '';},
usuario:function(value){ return value.trim() != '';},
contra:function(value){ return value.trim() != '';},
}
},
]}, {}, ]
},
]
}

View File

@ -0,0 +1,75 @@
var menu_data = [
{id: 'app_home', icon: 'dashboard', value: 'Inicio'},
{id: 'app_partners', icon: 'users', value: 'Clientes y Proveedores'},
{id: 'app_products', icon: 'server', value: 'Productos y Servicios'},
{id: 'app_invoices', icon: 'cart-plus', value: 'Facturas'},
];
var sidebar = {
view: 'sidebar',
data: menu_data,
ready: function(){
this.select('app_home');
this.open(this.getParentId('app_home'));
},
on:{
onAfterSelect: function(id){
$$('multi').setValue(id)
}
}
};
var multi_main = {
id: 'multi',
animate: true,
cells:[
{
id: 'app_home',
view: 'template',
template: 'HOME'
},
app_partners,
app_products,
app_invoices,
]
};
var menu_user = {
view: 'menu',
id: 'menu_user',
width: 150,
autowidth: true,
data: [
{id: '0', value: 'User...', submenu:[{id:1, value:'Cerrar Sesión'}]},
],
type: {
subsign: true,
},
};
var ui_main = {
rows: [
{view: 'toolbar', padding: 3, elements: [
{view: 'button', type: 'icon', icon: 'bars',
width: 37, align: 'left', css: 'app_button', click: function(){
$$('$sidebar1').toggle()
}
},
{view: 'label', label: 'Empresa Libre'},
{},
menu_user,
{view: 'button', type: 'icon', width: 45, css: 'app_button', icon: 'bell-o', badge: 1}
]
},
{
cols:[
sidebar,
multi_main,
]
}
]
};

View File

@ -0,0 +1,323 @@
var toolbar_partners = [
{view: 'button', id: 'cmd_new_partner', label: 'Nuevo', type: 'iconButton',
autowidth: true, icon: 'user-plus'},
{view: 'button', id: 'cmd_edit_partner', label: 'Editar', type: 'iconButton',
autowidth: true, icon: 'user'},
{view: 'button', id: 'cmd_delete_partner', label: 'Eliminar', type: 'iconButton',
autowidth: true, icon: 'user-times'},
]
var grid_partners_cols = [
{id: 'index', header:'#', adjust:'data', css: 'right',
footer: {content: 'rowCount', colspan: 2, css: 'right'}},
{id: 'id', header: 'Clave', adjust:'data', sort: 'int', css: 'right'},
{id: 'rfc', header: ['RFC', {content: 'textFilter'}], adjust:'data',
sort: 'string', footer: {text: 'Clientes y Proveedores', colspan: 2}},
{id: 'nombre', header: ['Razón Social', {content: 'textFilter'}],
fillspace:true, sort: 'string'},
]
var grid_partners = {
view: 'datatable',
id: 'grid_partners',
select: 'row',
adjust: true,
footer: true,
resizeColumn: true,
headermenu: true,
columns: grid_partners_cols,
on:{
'data->onStoreUpdated':function(){
this.data.each(function(obj, i){
obj.index = i+1;
})
}
},
}
var persons_type = [
{id: 1, value: 'Física'},
{id: 2, value: 'Moral'},
{id: 3, value: 'Público'},
{id: 4, value: 'Extranjero'},
]
var controls_fiscales = [
{template: 'Tipo de Persona', type: 'section'},
{view: 'radio', id: 'tipo_persona', name: 'tipo_persona', label: 'Tipos: ',
labelWidth: 150, value: 1, options: persons_type, required: true,
invalidMessage: 'El Tipo de Persona es requerido'},
{template: 'Dirección Fiscal', type: 'section'},
{cols: [{view: 'text', id: 'rfc', name: 'rfc', label: 'RFC: ', width: 300,
required: true, invalidMessage: 'RFC inválido', attributes: {maxlength: 13}},{}]},
{view: 'text', id: 'nombre', name: 'nombre', label: 'Razón Social: ', required: true,
invalidMessage: 'La Razón Social es requerida'},
{view: 'text', id: 'calle', name: 'calle', label: 'Calle: '},
{cols: [{view: 'text', id: 'no_exterior', name: 'no_exterior', width: 300,
label: 'No Exterior: '},{}]},
{cols: [{view: 'text', id: 'no_interior', name: 'no_interior', width: 300,
label: 'No Interior: '},{}]},
{cols: [{view: 'search', id: 'codigo_postal', name: 'codigo_postal',
width: 300, label: 'C.P.: ', attributes: {maxlength: 5}},{}]},
{view: 'text', id: 'colonia', name: 'colonia', label: 'Colonia: '},
{view: 'text', id: 'municipio', name: 'municipio', label: 'Municipio: '},
{view: 'text', id: 'estado', name: 'estado', label: 'Estado: '},
{view: 'text', id: 'pais', name: 'pais', label: 'País: ',
value: 'México', readonly: true},
{template: 'Condiciones Comerciales', type: 'section'},
{cols: [
{view: 'richselect', id: 'forma_pago', name: 'forma_pago',
label: 'Forma de Pago: ', required: true, options: [],
invalidMessage: 'La Forma de pago es requerida'},
{view: 'text', id: 'condicion_pago', name: 'condicion_pago',
label: 'Condiciones de Pago: '},
]},
{cols: [
{view: 'counter', id: 'dias_pago', name: 'dias_pago',
label: 'Días de pago', step: 5, value: 0, min: 0, max: 365,
tooltip: 'Permite calcular las fechas de pago', width: 250},
{view: 'checkbox', id: 'dias_habiles', name: 'dias_habiles',
label: 'Hábiles: ', value: false, width: 180},
{},
]},
{cols: [
{view: 'richselect', id: 'lst_uso_cfdi_socio', name: 'uso_cfdi_socio',
label: 'Uso del CFDI', options: []},
{},
]}
]
var controls_others = [
{view: 'checkbox', id: 'es_activo', name: 'es_activo', label: 'Activo: ',
value: true, bottomLabel: '&nbsp;&nbsp;&nbsp;Se recomienda solo desactivar y no eliminar'},
{view: 'text', id: 'commercial_name', name: 'nombre_comercial',
label: 'Nombre Comercial: '},
{view: 'text', id: 'telefonos', name: 'telefonos', label: 'Teléfonos: '},
{view: 'text', id: 'web', name: 'web', label: 'Página Web: '},
{view: 'text', id: 'correo_facturas', name: 'correo_facturas',
label: 'Correos para Facturas: ', tooltip: 'Separados por comas',
bottomLabel: 'Uno o más correos electrónicos separados por comas'},
{cols: [
{view: 'checkbox', id: 'es_cliente', name: 'es_cliente',
label: 'Es Cliente: ', value: true, width: 180},
{view: 'text', id: 'cuenta_cliente', name: 'cuenta_cliente',
label: 'Cuenta Cliente: ', disabled: true}, {}]
},
{cols: [
{view: 'checkbox', id: 'es_proveedor', name: 'es_proveedor',
label: 'Es Proveedor: ', value: false, width: 180},
{view: 'text', id: 'cuenta_proveedor', name: 'cuenta_proveedor',
label: 'Cuenta Proveedor: ', disabled: true}, {}]
},
{view: 'checkbox', name: 'es_ong', label: 'Es ONG: ', value: false},
{view: 'text', name: 'tags', label: 'Etiquetas',
tooltip: 'Utiles para filtrados rápidos. Separa por comas.'},
{view: 'textarea' , height: 200, name: 'notas', label: 'Notas'},
]
var toolbar_contacts = [
{view: 'button', id: 'cmd_new_contact', label: 'Nuevo', type: 'iconButton',
autowidth: true, icon: 'user-plus'},
]
var grid_contacts_cols = [
{id: 'index', header:'#', adjust:'data', css:'right',
footer: {content: 'rowCount'}},
{id: 'id', header: '', hidden: true},
{id: 'title', header: 'Título', adjust:'data', sort: 'string',
footer: 'Contactos'},
{id: 'first_name', header: ['Nombre', {content: 'textFilter'}], adjust:'data',
sort: 'string'},
{id: 'paterno', header: ['Apellido Paterno', {content: 'textFilter'}],
fillspace:true, sort: 'string'},
{id: 'materno', header: ['Apellido Materno', {content: 'textFilter'}],
fillspace:true, sort: 'string'},
{id: 'date_born', header: ['Fecha de Nacimiento'],
adjust: 'data'},
]
var grid_contacts = {
view: 'datatable',
id: 'grid_contacts',
select: 'row',
adjust: true,
height: 400,
footer: true,
resizeColumn: true,
headermenu: true,
columns: grid_contacts_cols,
on:{
'data->onStoreUpdated':function(){
this.data.each(function(obj, i){
obj.index = i+1;
})
}
},
}
var grid_email = {
view: 'datatable',
id: 'grid_email',
select: 'row',
adjust: true,
height: 200,
resizeColumn: true,
columns: [
{id: 'id', header: '', hidden: true},
{id: 'type', header: 'Tipo', adjust:'data', sort: 'string'},
{id: 'email', header: ['Correo Electrónico'], fillspace:true, sort: 'string'},
],
}
var grid_phone = {
view: 'datatable',
id: 'grid_phone',
select: 'row',
adjust: true,
height: 200,
resizeColumn: true,
columns: [
{id: 'id', header: '', hidden: true},
{id: 'type', header: 'Tipo', adjust:'data', sort: 'string'},
{id: 'phone', header: ['Teléfono'], fillspace:true, sort: 'string'},
],
}
var controls_contact = [
{template: 'Datos Generales', type: 'section' },
{cols: [
{view: 'combo', id: 'cbo_title_contact', name: 'cbo_title_contact',
label: 'Título: ', width: 150},
{view: 'text', id: 'name_contact', name: 'name_contact',
label: 'Nombre: '},
{view: 'text', id: 'paterno_contact', name: 'paterno_contact',
label: 'A. Paterno: '},
{view: 'text', id: 'materno_contact', name: 'materno_contact',
label: 'A. Materno: '},
{view: 'datepicker', id: 'date_contact', name: 'date_contact',
label: 'Fecha de Nacimiento: ', format: '%d-%M-%Y'},
]},
{template: 'Correos y Teléfonos', type: 'section' },
{cols: [
grid_email,
{maxWidth: 10},
grid_phone,
]},
{},
{ template:"", type: "section" },
{ margin: 10, cols: [{},
{view: "button", id: "cmd_save_conctact", label: "Guardar Contacto" , type: "form", autowidth: true, align:"center"},
{view: "button", id: "cmd_cancel_contact", label: "Regresar" , type: "danger", autowidth: true, align:"center"},
{}]
},
]
var form_contact = {
type: 'space',
cols: [{
view: 'form',
id: 'form_contact',
complexData: true,
elements: controls_contact,
elementsConfig: {
labelPosition: 'top',
},
autoheight: true,
}]
}
var multi_contacts = {
id: 'multi_contacts',
animate: true,
cells:[
{id: 'contacts_home', rows:[
{view: 'toolbar', elements: toolbar_contacts},
grid_contacts,
]},
{id: 'contacts_new', rows:[form_contact, {}]}
]
};
var controls_partner = [
{
view: 'tabview',
id: 'tab_partner',
tabbar: {options: ['Datos Fiscales', 'Otros Datos', 'Contactos']}, animate: true,
cells: [
{id: 'Datos Fiscales', rows: controls_fiscales},
{id: 'Otros Datos', rows: controls_others},
{id: 'Contactos', rows: [multi_contacts]},
]
},
{rows: [
{ template:"", type: "section" },
{ margin: 10, cols: [{},
{view: "button", id: "cmd_save_partner", label: "Guardar" , type: "form", autowidth: true, align:"center"},
{view: "button", id: "cmd_cancel_partner", label: "Cancelar" , type: "danger", autowidth: true, align:"center"},
{}]
},
]}
]
var form_partner = {
type: 'space',
cols: [{
view: 'form',
id: 'form_partner',
complexData: true,
elements: controls_partner,
elementsConfig: {
labelWidth: 150,
labelAlign: 'right'
},
autoheight: true,
rules: {
nombre: function(value){ return value.trim() != '';},
rfc: validate_rfc,
}
}]
}
var multi_partners = {
id: 'multi_partners',
animate: true,
cells:[
{id: 'partners_home', rows:[
{view: 'toolbar', elements: toolbar_partners},
grid_partners,
]},
{id: 'partners_new', rows:[form_partner, {}]}
]
}
var title_partners = 'Administración de Clientes y Proveedores'
var app_partners = {
id: 'app_partners',
rows:[
{view: 'template', id: 'th_partner', type: 'header', template: title_partners},
multi_partners
]
}

View File

@ -0,0 +1,171 @@
var toolbar_products = [
{view: "button", id: "cmd_new_product", label: "Nuevo", type: "iconButton",
autowidth: true, icon: "plus"},
{view: "button", id: "cmd_edit_product", label: "Editar", type: "iconButton",
autowidth: true, icon: "pencil"},
{view: "button", id: "cmd_delete_product", label: "Eliminar",
type: "iconButton", autowidth: true, icon: "minus"},
]
var grid_products_cols = [
{ id: "id", header: "ID", width: 75},
{ id: "clave", header: ["Clave", {content: "textFilter"}], width: 100,
sort:"string" },
{ id: "descripcion", header: ["Descripción", {content: "textFilter"}],
fillspace:true, sort:"string" },
{ id: "unidad", header: ["Unidad", {content: "selectFilter"}], width: 150,
sort:"string" },
{ id: "valor_unitario", header: ["Precio", {content: "numberFilter"}], width: 150,
sort:"int", format: webix.i18n.priceFormat, css: "right" },
]
var grid_products = {
view: "datatable",
id: "grid_products",
select: "row",
adjust: true,
footer: true,
resizeColumn: true,
headermenu: true,
columns: grid_products_cols,
}
var suggest_categories = {
view: "datasuggest",
type: "tree",
width: 400,
body: { data: [] },
}
var grid_product_taxes_cols = [
{id:"id", header:"ID", hidden: true},
{id:"name", header:'Nombre'},
{id:"tipo", header:'Tipo'},
{id:"tasa", header:'Tasa'},
]
var grid_product_taxes = {
view: 'datatable',
id: 'grid_product_taxes',
select: 'row',
multiselect: true,
adjust: true,
autoheight: true,
autowidth: true,
headermenu: true,
columns: grid_product_taxes_cols,
}
var controls_generals = [
{view: 'checkbox', id: 'es_activo_producto', name: 'es_activo_producto',
label: 'Activo: ', value: true,
bottomLabel: 'Se recomienda solo desactivar y no eliminar'},
{cols: [
{view: 'combo', id: 'categoria', name: 'categoria', label: 'Categoría',
labelPosition: 'top', options: suggest_categories},
{view: 'text', id: 'clave', name: 'clave', label: 'Clave',
labelPosition: 'top', readonly: true, required: true},
{view: 'checkbox', id: 'chk_automatica', label: 'Automática',
labelPosition: 'top', value: true, maxWidth: 80},
{view: 'search', id: 'clave_sat', name: 'clave_sat', label: 'Clave SAT',
labelPosition: 'top', required: true, placeholder: 'Buscar clave...'},
]},
{cols: [
{view: 'text', id: 'codigo_barras', name: 'codigo_barras',
label: 'Código de Barras', labelPosition: 'top', hidden: true},
{view: 'text', id: 'cuenta_predial', name: 'cuenta_predial',
label: 'Cuenta Predial', labelPosition: 'top', hidden: true},
{id: 'txt_col1'}]},
{view: "textarea", id: "descripcion", name: "descripcion", height: 200,
label: "Descripción", required: true, labelPosition: "top",
invalidMessage: "La Descripción es requerida" },
{minHeight: 5},
{cols: [
{view: "richselect", id: "unidad", name: "unidad", label: "Unidad",
width: 300, labelWidth: 130, labelAlign: "right", required: true,
invalidMessage: "La Unidad es requerida", options: []},
{view: 'text', id: 'tags_producto', name: 'tags_producto',
labelAlign: 'right', label: 'Etiquetas',
placeholder: 'Separadas por comas'}
]},
{cols: [{view: "currency", type: "text", id: "valor_unitario",
name: "valor_unitario", label: "Valor Unitario", width: 300,
labelWidth: 130, labelAlign: "right", required: true,
invalidMessage: "Captura un valor númerico", inputAlign: "right" },{}]},
{cols: [
{view: 'checkbox', id: 'inventario', name: 'inventario', hidden: true,
label: 'Inventario', labelAlign: 'right', labelWidth: 130},
{view: 'counter', id: 'existencia', name: 'existencia', hidden: true,
label: 'Existencia', step: 5, value: 0, min: 0, disabled: true},
{view: 'counter', id: 'minimo', name: 'minimo', hidden: true,
label: 'Mínimo', step: 5, value: 0, min: 0, disabled: true},
{id: 'txt_col2'}]},
{cols:[{view:'label', label:'Impuestos', width: 300, align:'center'}, {}]},
{cols:[grid_product_taxes, {}]}
]
var controls_products = [
{
view: "tabview",
tabbar: { options: ["Datos"]}, animate: true,
cells: [
{id: "Datos", rows: controls_generals},
],
},
{rows: [
{ template:"", type: "section" },
{ margin: 10, cols: [{},
{view: "button", id: "cmd_save_product", label: "Guardar" , type: "form", autowidth: true, align:"center"},
{view: "button", id: "cmd_cancel_product", label: "Cancelar" , type: "danger", autowidth: true, align:"center"},
{}]
},
]}
]
var form_product = {
type: "space",
cols: [{
view: "form",
id: "form_product",
//~ width: 600,
complexData: true,
elements: controls_products,
rules: {
descripcion: function(value){ return value.trim() != ""; },
valor_unitario: function(value){ return value.trim() != "$"; },
}
}],
}
var multi_products = {
id: "multi_products",
animate: true,
cells:[
{id: "products_home", rows:[
{view:"toolbar", elements: toolbar_products},
grid_products,
]},
{id: "product_new", rows:[form_product, {}]}
],
}
var app_products = {
id: "app_products",
rows:[
{view: "template", id: "th_products", type: "header", template:"Administración de Productos" },
multi_products
],
}

1985
source/static/js/webix.js Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,30 @@
<%inherit file="base.html"/>
<%block name="media">
<link rel="stylesheet" href="/static/css/sidebar_air.css" type="text/css">
<link rel="stylesheet" href="/static/css/app.css" type="text/css">
<script src="/static/js/sidebar.js" type="text/javascript" ></script>
<script src="/static/js/controller/util.js" type="text/javascript" ></script>
<script src="/static/js/ui/admin.js" type="text/javascript" ></script>
<script src="/static/js/controller/admin.js" type="text/javascript" ></script>
</%block>
<%block name="content">
<input type=hidden id="username" value="${username}"/>
<script type="text/javascript" charset="utf-8">
webix.ready(function(){
webix.CustomScroll.init()
webix.ui(ui_admin)
controllers.init()
var user = document.getElementById("username").value
$$('menu_user').getMenu(0).updateItem(0, {value:user})
})
</script>
</%block>

View File

@ -0,0 +1,28 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Empresa Libre</title>
<meta charset="utf-8" />
<link rel="shortcut icon" href="/static/img/favicon.png">
<link rel="stylesheet" href="/static/css/air.css" type="text/css">
<link rel="stylesheet" href="/static/css/app.css" type="text/css">
<script src="/static/js/webix_debug.js" type="text/javascript" ></script>
<script src="/static/js/es-MX.js" type="text/javascript" ></script>
<script src="/static/js/lokijs.min.js" type="text/javascript" ></script>
<%block name="media"/>
</head>
<body>
<script type="text/javascript" charset="utf-8">
webix.debug = true;
webix.i18n.setLocale("es-MX");
</script>
<%block name="content"/>
</body>
</html>

View File

@ -0,0 +1,43 @@
<%inherit file="base.html"/>
<%block name="media">
<script src="/static/js/ui/login.js" type="text/javascript" ></script>
</%block>
<%block name="content">
<div id="form_login"></div>
<script type="text/javascript" charset="utf-8">
function validate_login(){
var form = this.getFormView();
if (!form.validate()) {
webix.message({ type:"error", text:"Valores inválidos" });
return
}
var values = form.getValues()
webix.ajax().post("/", values, function(text, data, xhr) {
var values = data.json();
if (values.login) {
if (values.super) {
window.location = "/admin"
}else{
window.location = "/main"
}
} else {
webix.message({ type:"error", text: values.msg })
}
});
};
webix.ready(function(){
webix.ui(ui_login);
});
</script>
</%block>

View File

@ -0,0 +1,16 @@
<%inherit file="base.html"/>
<%block name="media">
</%block>
<%block name="content">
<script type="text/javascript" charset="utf-8">
window.location = "/";
};
</script>
</%block>

View File

@ -0,0 +1,36 @@
<%inherit file="base.html"/>
<%block name="media">
<link rel="stylesheet" href="/static/css/sidebar_air.css" type="text/css">
<script src="/static/js/sidebar.js" type="text/javascript" ></script>
<script src="/static/js/controller/util.js" type="text/javascript" ></script>
<script src="/static/js/ui/partners.js" type="text/javascript" ></script>
<script src="/static/js/ui/products.js" type="text/javascript" ></script>
<script src="/static/js/ui/invoices.js" type="text/javascript" ></script>
<script src="/static/js/ui/main.js" type="text/javascript" ></script>
<script src="/static/js/controller/partners.js" type="text/javascript" ></script>
<script src="/static/js/controller/products.js" type="text/javascript" ></script>
<script src="/static/js/controller/invoices.js" type="text/javascript" ></script>
<script src="/static/js/controller/main.js" type="text/javascript" ></script>
</%block>
<%block name="content">
<input type=hidden id="username" value="${username}"/>
<script type="text/javascript" charset="utf-8">
webix.ready(function(){
webix.CustomScroll.init()
webix.ui(ui_main)
controllers.init()
var user = document.getElementById("username").value
$$('menu_user').getMenu(0).updateItem(0, {value:user})
})
</script>
</%block>

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

@ -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="ecc11.xslt"/>
<xsl:include href="donat11.xslt"/>
<xsl:include href="Divisas.xslt"/>
<xsl:include href="implocal.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="ine11.xslt"/>
<xsl:include href="iedu.xslt"/>
<xsl:include href="ventavehiculos11.xslt"/>
<xsl:include href="terceros11.xslt"/>
<xsl:include href="Pagos10.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>

View File

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

View File

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

412
source/xslt/nomina12.xslt Normal file
View File

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

View File

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