commit
60528d8be1
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
Este proyecto está en continuo desarrollo, contratar un esquema de soporte,
|
Este proyecto está en continuo desarrollo, contratar un esquema de soporte,
|
||||||
nos ayuda a continuar su desarrollo. Ponte en contacto con nosotros para
|
nos ayuda a continuar su desarrollo. Ponte en contacto con nosotros para
|
||||||
contratar.
|
contratar: administracion@empresalibre.net
|
||||||
|
|
||||||
|
|
||||||
### Requerimientos:
|
### Requerimientos:
|
||||||
|
@ -16,6 +16,9 @@ contratar.
|
||||||
* Servidor web, recomendado Nginx
|
* Servidor web, recomendado Nginx
|
||||||
* uwsgi
|
* uwsgi
|
||||||
* python3
|
* python3
|
||||||
|
* xsltproc
|
||||||
|
* openssl
|
||||||
|
* xmlsec
|
||||||
|
|
||||||
Debería de funcionar con cualquier combinación servidor-wsgi que soporte
|
Debería de funcionar con cualquier combinación servidor-wsgi que soporte
|
||||||
aplicaciones Python.
|
aplicaciones Python.
|
||||||
|
|
|
@ -1,10 +1,4 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
from peewee import SqliteDatabase
|
|
||||||
|
|
||||||
|
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
ID_SUPPORT = ''
|
|
||||||
|
|
||||||
DATABASE = None
|
|
||||||
if DEBUG:
|
|
||||||
DATABASE = SqliteDatabase('empresalibre.sqlite')
|
|
||||||
|
|
|
@ -371,7 +371,7 @@ class Finkok(object):
|
||||||
if os.path.isfile(file_xml):
|
if os.path.isfile(file_xml):
|
||||||
root = etree.parse(file_xml).getroot()
|
root = etree.parse(file_xml).getroot()
|
||||||
else:
|
else:
|
||||||
root = etree.fromstring(file_xml)
|
root = etree.fromstring(file_xml.encode())
|
||||||
|
|
||||||
xml = etree.tostring(root)
|
xml = etree.tostring(root)
|
||||||
|
|
||||||
|
@ -385,8 +385,12 @@ class Finkok(object):
|
||||||
'store_pending': True,
|
'store_pending': True,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
result = client.service.cancel_signature(**args)
|
result = client.service.cancel_signature(**args)
|
||||||
return result
|
return result
|
||||||
|
except Fault as e:
|
||||||
|
self.error = str(e)
|
||||||
|
return ''
|
||||||
|
|
||||||
def get_acuse(self, rfc, uuids, type_acuse='C'):
|
def get_acuse(self, rfc, uuids, type_acuse='C'):
|
||||||
for u in uuids:
|
for u in uuids:
|
||||||
|
@ -443,21 +447,41 @@ class Finkok(object):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def add_token(self, rfc, email):
|
def add_token(self, rfc, email):
|
||||||
"""
|
"""Agrega un nuevo token al cliente para timbrado.
|
||||||
Se requiere cuenta de reseller para usar este método
|
Se requiere cuenta de reseller para usar este método
|
||||||
|
|
||||||
|
Args:
|
||||||
|
rfc (str): El RFC del cliente, ya debe existir
|
||||||
|
email (str): El correo del cliente, funciona como USER al timbrar
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict
|
||||||
|
'username': 'username',
|
||||||
|
'status': True or False
|
||||||
|
'name': 'name',
|
||||||
|
'success': True or False
|
||||||
|
'token': 'Token de timbrado',
|
||||||
|
'message': None
|
||||||
"""
|
"""
|
||||||
|
auth = AUTH['RESELLER']
|
||||||
|
|
||||||
method = 'util'
|
method = 'util'
|
||||||
client = Client(
|
client = Client(
|
||||||
URL[method], transport=self._transport, plugins=self._plugins)
|
URL[method], transport=self._transport, plugins=self._plugins)
|
||||||
args = {
|
args = {
|
||||||
'username': AUTH['USER'],
|
'username': auth['USER'],
|
||||||
'password': AUTH['PASS'],
|
'password': auth['PASS'],
|
||||||
'name': rfc,
|
'name': rfc,
|
||||||
'token_username': email,
|
'token_username': email,
|
||||||
'taxpayer_id': rfc,
|
'taxpayer_id': rfc,
|
||||||
'status': True,
|
'status': True,
|
||||||
}
|
}
|
||||||
|
try:
|
||||||
result = client.service.add_token(**args)
|
result = client.service.add_token(**args)
|
||||||
|
except Fault as e:
|
||||||
|
self.error = str(e)
|
||||||
|
return ''
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def get_date(self):
|
def get_date(self):
|
||||||
|
@ -477,17 +501,31 @@ class Finkok(object):
|
||||||
return result.datetime
|
return result.datetime
|
||||||
|
|
||||||
def add_client(self, rfc, type_user=False):
|
def add_client(self, rfc, type_user=False):
|
||||||
"""
|
"""Agrega un nuevo cliente para timbrado.
|
||||||
Se requiere cuenta de reseller para usar este método
|
Se requiere cuenta de reseller para usar este método
|
||||||
type_user: False == 'P' == Prepago or True == 'O' == On demand
|
|
||||||
|
Args:
|
||||||
|
rfc (str): El RFC del nuevo cliente
|
||||||
|
|
||||||
|
Kwargs:
|
||||||
|
type_user (bool): False == 'P' == Prepago or True == 'O' == On demand
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict
|
||||||
|
'message':
|
||||||
|
'Account Created successfully'
|
||||||
|
'Account Already exists'
|
||||||
|
'success': True or False
|
||||||
"""
|
"""
|
||||||
|
auth = AUTH['RESELLER']
|
||||||
|
|
||||||
tu = {False: 'P', True: 'O'}
|
tu = {False: 'P', True: 'O'}
|
||||||
method = 'client'
|
method = 'client'
|
||||||
client = Client(
|
client = Client(
|
||||||
URL[method], transport=self._transport, plugins=self._plugins)
|
URL[method], transport=self._transport, plugins=self._plugins)
|
||||||
args = {
|
args = {
|
||||||
'reseller_username': AUTH['USER'],
|
'reseller_username': auth['USER'],
|
||||||
'reseller_password': AUTH['PASS'],
|
'reseller_password': auth['PASS'],
|
||||||
'taxpayer_id': rfc,
|
'taxpayer_id': rfc,
|
||||||
'type_user': tu[type_user],
|
'type_user': tu[type_user],
|
||||||
'added': datetime.datetime.now().isoformat()[:19],
|
'added': datetime.datetime.now().isoformat()[:19],
|
||||||
|
@ -505,13 +543,15 @@ class Finkok(object):
|
||||||
Se requiere cuenta de reseller para usar este método
|
Se requiere cuenta de reseller para usar este método
|
||||||
status = 'A' or 'S'
|
status = 'A' or 'S'
|
||||||
"""
|
"""
|
||||||
|
auth = AUTH['RESELLER']
|
||||||
|
|
||||||
sv = {False: 'S', True: 'A'}
|
sv = {False: 'S', True: 'A'}
|
||||||
method = 'client'
|
method = 'client'
|
||||||
client = Client(
|
client = Client(
|
||||||
URL[method], transport=self._transport, plugins=self._plugins)
|
URL[method], transport=self._transport, plugins=self._plugins)
|
||||||
args = {
|
args = {
|
||||||
'reseller_username': AUTH['USER'],
|
'reseller_username': auth['USER'],
|
||||||
'reseller_password': AUTH['PASS'],
|
'reseller_password': auth['PASS'],
|
||||||
'taxpayer_id': rfc,
|
'taxpayer_id': rfc,
|
||||||
'status': sv[status],
|
'status': sv[status],
|
||||||
}
|
}
|
||||||
|
@ -524,15 +564,35 @@ class Finkok(object):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def get_client(self, rfc):
|
def get_client(self, rfc):
|
||||||
"""
|
"""Regresa el estatus del cliente
|
||||||
|
.
|
||||||
Se requiere cuenta de reseller para usar este método
|
Se requiere cuenta de reseller para usar este método
|
||||||
|
|
||||||
|
Args:
|
||||||
|
rfc (str): El RFC del emisor
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict
|
||||||
|
'message': None,
|
||||||
|
'users': {
|
||||||
|
'ResellerUser': [
|
||||||
|
{
|
||||||
|
'status': 'A',
|
||||||
|
'counter': 0,
|
||||||
|
'taxpayer_id': '',
|
||||||
|
'credit': 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
} or None si no existe
|
||||||
"""
|
"""
|
||||||
|
auth = AUTH['RESELLER']
|
||||||
|
|
||||||
method = 'client'
|
method = 'client'
|
||||||
client = Client(
|
client = Client(
|
||||||
URL[method], transport=self._transport, plugins=self._plugins)
|
URL[method], transport=self._transport, plugins=self._plugins)
|
||||||
args = {
|
args = {
|
||||||
'reseller_username': AUTH['USER'],
|
'reseller_username': auth['USER'],
|
||||||
'reseller_password': AUTH['PASS'],
|
'reseller_password': auth['PASS'],
|
||||||
'taxpayer_id': rfc,
|
'taxpayer_id': rfc,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -548,15 +608,30 @@ class Finkok(object):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def assign_client(self, rfc, credit):
|
def assign_client(self, rfc, credit):
|
||||||
"""
|
"""Agregar credito a un emisor
|
||||||
|
|
||||||
Se requiere cuenta de reseller para usar este método
|
Se requiere cuenta de reseller para usar este método
|
||||||
|
|
||||||
|
Args:
|
||||||
|
rfc (str): El RFC del emisor, debe existir
|
||||||
|
credit (int): Cantidad de folios a agregar
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict
|
||||||
|
'success': True or False,
|
||||||
|
'credit': nuevo credito despues de agregar or None
|
||||||
|
'message':
|
||||||
|
'Success, added {credit} of credit to {RFC}'
|
||||||
|
'RFC no encontrado'
|
||||||
"""
|
"""
|
||||||
|
auth = AUTH['RESELLER']
|
||||||
|
|
||||||
method = 'client'
|
method = 'client'
|
||||||
client = Client(
|
client = Client(
|
||||||
URL[method], transport=self._transport, plugins=self._plugins)
|
URL[method], transport=self._transport, plugins=self._plugins)
|
||||||
args = {
|
args = {
|
||||||
'username': AUTH['USER'],
|
'username': auth['USER'],
|
||||||
'password': AUTH['PASS'],
|
'password': auth['PASS'],
|
||||||
'taxpayer_id': rfc,
|
'taxpayer_id': rfc,
|
||||||
'credit': credit,
|
'credit': credit,
|
||||||
}
|
}
|
||||||
|
@ -577,7 +652,7 @@ def _get_data_sat(path):
|
||||||
if os.path.isfile(path):
|
if os.path.isfile(path):
|
||||||
tree = etree.parse(path).getroot()
|
tree = etree.parse(path).getroot()
|
||||||
else:
|
else:
|
||||||
tree = etree.fromstring(path)
|
tree = etree.fromstring(path.encode())
|
||||||
|
|
||||||
data = {}
|
data = {}
|
||||||
emisor = escape(
|
emisor = escape(
|
||||||
|
@ -602,7 +677,7 @@ def _get_data_sat(path):
|
||||||
def get_status_sat(xml):
|
def get_status_sat(xml):
|
||||||
data = _get_data_sat(xml)
|
data = _get_data_sat(xml)
|
||||||
if not data:
|
if not data:
|
||||||
return
|
return 'XML inválido'
|
||||||
|
|
||||||
URL = 'https://consultaqr.facturaelectronica.sat.gob.mx/ConsultaCFDIService.svc?wsdl'
|
URL = 'https://consultaqr.facturaelectronica.sat.gob.mx/ConsultaCFDIService.svc?wsdl'
|
||||||
client = Client(URL, transport=Transport(cache=SqliteCache()))
|
client = Client(URL, transport=Transport(cache=SqliteCache()))
|
||||||
|
|
|
@ -33,7 +33,8 @@ from dateutil import parser
|
||||||
|
|
||||||
from .helper import CaseInsensitiveDict, NumLet, SendMail, TemplateInvoice
|
from .helper import CaseInsensitiveDict, NumLet, SendMail, TemplateInvoice
|
||||||
from settings import DEBUG, log, template_lookup, COMPANIES, DB_SAT, \
|
from settings import DEBUG, log, template_lookup, COMPANIES, DB_SAT, \
|
||||||
PATH_XSLT, PATH_XSLTPROC, PATH_OPENSSL, PATH_TEMPLATES, PATH_MEDIA, PRE
|
PATH_XSLT, PATH_XSLTPROC, PATH_OPENSSL, PATH_TEMPLATES, PATH_MEDIA, PRE, \
|
||||||
|
PATH_XMLSEC, TEMPLATE_CANCEL
|
||||||
|
|
||||||
|
|
||||||
#~ def _get_hash(password):
|
#~ def _get_hash(password):
|
||||||
|
@ -52,7 +53,7 @@ def _get_md5(data):
|
||||||
return hashlib.md5(data.encode()).hexdigest()
|
return hashlib.md5(data.encode()).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
def _save_temp(data, modo='wb'):
|
def save_temp(data, modo='wb'):
|
||||||
path = tempfile.mkstemp()[1]
|
path = tempfile.mkstemp()[1]
|
||||||
with open(path, modo) as f:
|
with open(path, modo) as f:
|
||||||
f.write(data)
|
f.write(data)
|
||||||
|
@ -276,21 +277,22 @@ def to_slug(string):
|
||||||
|
|
||||||
class Certificado(object):
|
class Certificado(object):
|
||||||
|
|
||||||
def __init__(self, key, cer):
|
def __init__(self, paths):
|
||||||
self._key = key
|
self._path_key = paths['path_key']
|
||||||
self._cer = cer
|
self._path_cer = paths['path_cer']
|
||||||
self._modulus = ''
|
self._modulus = ''
|
||||||
self._save_files()
|
#~ self._save_files()
|
||||||
self.error = ''
|
self.error = ''
|
||||||
|
|
||||||
def _save_files(self):
|
#~ def _save_files(self):
|
||||||
try:
|
#~ try:
|
||||||
self._path_key = _save_temp(self._key)
|
#~ self._path_key = _save_temp(bytes(self._key))
|
||||||
self._path_cer = _save_temp(self._cer)
|
#~ self._path_cer = _save_temp(bytes(self._cer))
|
||||||
except:
|
#~ except Exception as e:
|
||||||
self._path_key = ''
|
#~ log.error(e)
|
||||||
self._path_cer = ''
|
#~ self._path_key = ''
|
||||||
return
|
#~ self._path_cer = ''
|
||||||
|
#~ return
|
||||||
|
|
||||||
def _kill(self, path):
|
def _kill(self, path):
|
||||||
try:
|
try:
|
||||||
|
@ -341,8 +343,7 @@ class Certificado(object):
|
||||||
hasta = parser.parse(dates[1].split('=')[1])
|
hasta = parser.parse(dates[1].split('=')[1])
|
||||||
self._modulus = _call(args.format(self._path_cer, 'modulus'))
|
self._modulus = _call(args.format(self._path_cer, 'modulus'))
|
||||||
|
|
||||||
data['cer'] = self._cer
|
data['cer'] = read_file(self._path_cer)
|
||||||
data['cer_tmp'] = None
|
|
||||||
data['cer_pem'] = cer_pem
|
data['cer_pem'] = cer_pem
|
||||||
data['cer_txt'] = cer_txt.replace('\n', '')
|
data['cer_txt'] = cer_txt.replace('\n', '')
|
||||||
data['serie'] = serie
|
data['serie'] = serie
|
||||||
|
@ -365,7 +366,8 @@ class Certificado(object):
|
||||||
'pass:"{}" -out "{}"'
|
'pass:"{}" -out "{}"'
|
||||||
_call(args.format(tmp_cer, tmp_key, rfc,
|
_call(args.format(tmp_cer, tmp_key, rfc,
|
||||||
hashlib.md5(rfc.encode()).hexdigest(), tmp_p12))
|
hashlib.md5(rfc.encode()).hexdigest(), tmp_p12))
|
||||||
data = open(tmp_p12, 'rb').read()
|
#~ data = open(tmp_p12, 'rb').read()
|
||||||
|
data = read_file(tmp_p12)
|
||||||
|
|
||||||
self._kill(tmp_cer)
|
self._kill(tmp_cer)
|
||||||
self._kill(tmp_key)
|
self._kill(tmp_key)
|
||||||
|
@ -396,18 +398,20 @@ class Certificado(object):
|
||||||
self._path_key, password, _get_md5(rfc))
|
self._path_key, password, _get_md5(rfc))
|
||||||
key_enc = _call(args)
|
key_enc = _call(args)
|
||||||
|
|
||||||
data['key'] = self._key
|
data['key'] = read_file(self._path_key)
|
||||||
data['key_tmp'] = None
|
|
||||||
data['key_enc'] = key_enc
|
data['key_enc'] = key_enc
|
||||||
data['p12'] = self._get_p12(password, rfc)
|
data['p12'] = self._get_p12(password, rfc)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def validate(self, password, rfc):
|
def validate(self, password, rfc):
|
||||||
if not self._path_key or not self._path_cer:
|
if not self._path_key or not self._path_cer:
|
||||||
self.error = 'Error al cargar el certificado'
|
self.error = 'Error en las rutas temporales del certificado'
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
data = self._get_info_cer(rfc)
|
data = self._get_info_cer(rfc)
|
||||||
|
if not data:
|
||||||
|
return {}
|
||||||
|
|
||||||
llave = self._get_info_key(password, data['rfc'])
|
llave = self._get_info_key(password, data['rfc'])
|
||||||
if not llave:
|
if not llave:
|
||||||
return {}
|
return {}
|
||||||
|
@ -432,9 +436,9 @@ def make_xml(data, certificado):
|
||||||
data = {
|
data = {
|
||||||
'xsltproc': PATH_XSLTPROC,
|
'xsltproc': PATH_XSLTPROC,
|
||||||
'xslt': _join(PATH_XSLT, 'cadena.xslt'),
|
'xslt': _join(PATH_XSLT, 'cadena.xslt'),
|
||||||
'xml': _save_temp(xml, 'w'),
|
'xml': save_temp(xml, 'w'),
|
||||||
'openssl': PATH_OPENSSL,
|
'openssl': PATH_OPENSSL,
|
||||||
'key': _save_temp(certificado.key_enc, 'w'),
|
'key': save_temp(certificado.key_enc, 'w'),
|
||||||
'pass': _get_md5(certificado.rfc)
|
'pass': _get_md5(certificado.rfc)
|
||||||
}
|
}
|
||||||
args = '"{xsltproc}" "{xslt}" "{xml}" | ' \
|
args = '"{xsltproc}" "{xslt}" "{xml}" | ' \
|
||||||
|
@ -473,6 +477,11 @@ def timbra_xml(xml, auth):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def get_sat(xml):
|
||||||
|
from .pac import get_status_sat
|
||||||
|
return get_status_sat(xml)
|
||||||
|
|
||||||
|
|
||||||
class LIBO(object):
|
class LIBO(object):
|
||||||
HOST = 'localhost'
|
HOST = 'localhost'
|
||||||
PORT = '8100'
|
PORT = '8100'
|
||||||
|
@ -1036,6 +1045,59 @@ def get_path_temp():
|
||||||
return tempfile.mkstemp()[1]
|
return tempfile.mkstemp()[1]
|
||||||
|
|
||||||
|
|
||||||
|
def get_date(value, next_day=False):
|
||||||
|
d = parser.parse(value)
|
||||||
|
if next_day:
|
||||||
|
return d + datetime.timedelta(days=1)
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
|
def cancel_cfdi(uuid, pk12, rfc, auth):
|
||||||
|
from .pac import Finkok as PAC
|
||||||
|
|
||||||
|
template = read_file(TEMPLATE_CANCEL, 'r')
|
||||||
|
data = {
|
||||||
|
'rfc': rfc,
|
||||||
|
'fecha': datetime.datetime.now().isoformat()[:19],
|
||||||
|
'uuid': str(uuid).upper(),
|
||||||
|
}
|
||||||
|
template = template.format(**data)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'xmlsec': PATH_XMLSEC,
|
||||||
|
'pk12': save_temp(pk12),
|
||||||
|
'pass': _get_md5(rfc),
|
||||||
|
'template': save_temp(template, 'w'),
|
||||||
|
}
|
||||||
|
args = '"{xmlsec}" --sign --pkcs12 "{pk12}" --pwd {pass} ' \
|
||||||
|
'"{template}"'.format(**data)
|
||||||
|
xml_sign = _call(args)
|
||||||
|
|
||||||
|
if DEBUG:
|
||||||
|
auth = {}
|
||||||
|
else:
|
||||||
|
if not auth:
|
||||||
|
msg = 'Sin datos para cancelar'
|
||||||
|
result = {'ok': False, 'error': msg}
|
||||||
|
return result
|
||||||
|
|
||||||
|
msg = 'Factura cancelada correctamente'
|
||||||
|
data = {'ok': True, 'msg': msg, 'row': {'estatus': 'Cancelada'}}
|
||||||
|
pac = PAC(auth)
|
||||||
|
result = pac.cancel_signature(xml_sign)
|
||||||
|
if result:
|
||||||
|
codes = {None: '',
|
||||||
|
'Could not get UUID Text': 'UUID no encontrado'}
|
||||||
|
if not result['CodEstatus'] is None:
|
||||||
|
data['ok'] = False
|
||||||
|
data['msg'] = codes.get(result['CodEstatus'], result['CodEstatus'])
|
||||||
|
else:
|
||||||
|
data['ok'] = False
|
||||||
|
data['msg'] = pac.error
|
||||||
|
|
||||||
|
return data, result
|
||||||
|
|
||||||
|
|
||||||
class ImportFacturaLibre(object):
|
class ImportFacturaLibre(object):
|
||||||
|
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
socket = 127.0.0.1:3033
|
socket = 127.0.0.1:3033
|
||||||
uid = nginx
|
uid = nginx
|
||||||
gid = nginx
|
gid = nginx
|
||||||
|
#~ Establece una ruta accesible para nginx o el servidor web que uses
|
||||||
chdir = /srv/app/empresa-libre/app
|
chdir = /srv/app/empresa-libre/app
|
||||||
wsgi-file = main.py
|
wsgi-file = main.py
|
||||||
callable = app
|
callable = app
|
||||||
|
@ -10,4 +11,5 @@ processes = 4
|
||||||
threads = 4
|
threads = 4
|
||||||
thunder-lock = true
|
thunder-lock = true
|
||||||
#~ stats = 127.0.0.1:9191
|
#~ stats = 127.0.0.1:9191
|
||||||
|
#~ Establece una ruta accesible para nginx o el servidor web que uses
|
||||||
logger = file:/srv/log/empresalibre-uwsgi.log
|
logger = file:/srv/log/empresalibre-uwsgi.log
|
||||||
|
|
|
@ -45,7 +45,6 @@ api.add_route('/products', AppProducts(db))
|
||||||
api.add_route('/invoices', AppInvoices(db))
|
api.add_route('/invoices', AppInvoices(db))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
api.add_sink(static, '/static')
|
api.add_sink(static, '/static')
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
[uwsgi]
|
[uwsgi]
|
||||||
http = 127.0.0.1:8000
|
http = 127.0.0.1:8000
|
||||||
#~ http = 37.228.132.181:9000
|
|
||||||
wsgi-file = main.py
|
wsgi-file = main.py
|
||||||
callable = app
|
callable = app
|
||||||
master = true
|
master = true
|
||||||
|
|
|
@ -20,8 +20,8 @@ class StorageEngine(object):
|
||||||
def add_config(self, values):
|
def add_config(self, values):
|
||||||
return main.Configuracion.add(values)
|
return main.Configuracion.add(values)
|
||||||
|
|
||||||
def add_cert(self, file_object):
|
def add_cert(self, file_obj):
|
||||||
return main.Certificado.add(file_object)
|
return main.Certificado.add(file_obj)
|
||||||
|
|
||||||
def validate_cert(self, values, session):
|
def validate_cert(self, values, session):
|
||||||
return main.Certificado.validate(values, session)
|
return main.Certificado.validate(values, session)
|
||||||
|
@ -32,6 +32,15 @@ class StorageEngine(object):
|
||||||
def send_email(self, values, session):
|
def send_email(self, values, session):
|
||||||
return main.Facturas.send(values['id'], session['rfc'])
|
return main.Facturas.send(values['id'], session['rfc'])
|
||||||
|
|
||||||
|
def _get_cancelinvoice(self, values):
|
||||||
|
return main.Facturas.cancel(values['id'])
|
||||||
|
|
||||||
|
def _get_statussat(self, values):
|
||||||
|
return main.Facturas.get_status_sat(values['id'])
|
||||||
|
|
||||||
|
def _get_filteryears(self, values):
|
||||||
|
return main.Facturas.filter_years()
|
||||||
|
|
||||||
def _get_cert(self, values):
|
def _get_cert(self, values):
|
||||||
return main.Certificado.get_data()
|
return main.Certificado.get_data()
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,9 @@ class Configuracion(BaseModel):
|
||||||
clave = TextField(unique=True)
|
clave = TextField(unique=True)
|
||||||
valor = TextField(default='')
|
valor = TextField(default='')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '{} = {}'.format(self.clave, self.valor)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_(cls, keys):
|
def get_(cls, keys):
|
||||||
if keys['fields'] == 'correo':
|
if keys['fields'] == 'correo':
|
||||||
|
@ -74,6 +77,13 @@ class Configuracion(BaseModel):
|
||||||
.select()
|
.select()
|
||||||
.where(Configuracion.clave.in_(fields))
|
.where(Configuracion.clave.in_(fields))
|
||||||
)
|
)
|
||||||
|
elif keys['fields'] == 'path_cer':
|
||||||
|
fields = ('path_key', 'path_cer')
|
||||||
|
data = (Configuracion
|
||||||
|
.select()
|
||||||
|
.where(Configuracion.clave.in_(fields))
|
||||||
|
)
|
||||||
|
|
||||||
values = {r.clave: r.valor for r in data}
|
values = {r.clave: r.valor for r in data}
|
||||||
return values
|
return values
|
||||||
|
|
||||||
|
@ -289,10 +299,8 @@ class Emisor(BaseModel):
|
||||||
|
|
||||||
class Certificado(BaseModel):
|
class Certificado(BaseModel):
|
||||||
key = BlobField(null=True)
|
key = BlobField(null=True)
|
||||||
key_tmp = BlobField(null=True)
|
|
||||||
key_enc = TextField(default='')
|
key_enc = TextField(default='')
|
||||||
cer = BlobField(null=True)
|
cer = BlobField(null=True)
|
||||||
cer_tmp = BlobField(null=True)
|
|
||||||
cer_pem = TextField(default='')
|
cer_pem = TextField(default='')
|
||||||
cer_txt = TextField(default='')
|
cer_txt = TextField(default='')
|
||||||
p12 = BlobField(null=True)
|
p12 = BlobField(null=True)
|
||||||
|
@ -316,28 +324,26 @@ class Certificado(BaseModel):
|
||||||
return row
|
return row
|
||||||
|
|
||||||
def get_(cls):
|
def get_(cls):
|
||||||
if Certificado.select().count():
|
return Certificado.select()[0]
|
||||||
obj = Certificado.select()[0]
|
|
||||||
else:
|
|
||||||
obj = Certificado()
|
|
||||||
return obj
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add(cls, file_object):
|
def add(cls, file_obj):
|
||||||
obj = cls.get_(cls)
|
if file_obj.filename.endswith('key'):
|
||||||
if file_object.filename.endswith('key'):
|
path_key = util.save_temp(file_obj.file.read())
|
||||||
obj.key_tmp = file_object.file.read()
|
Configuracion.add({'path_key': path_key})
|
||||||
elif file_object.filename.endswith('cer'):
|
elif file_obj.filename.endswith('cer'):
|
||||||
obj.cer_tmp = file_object.file.read()
|
path_cer = util.save_temp(file_obj.file.read())
|
||||||
obj.save()
|
Configuracion.add({'path_cer': path_cer})
|
||||||
return {'status': 'server'}
|
return {'status': 'server'}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate(cls, values, session):
|
def validate(cls, values, session):
|
||||||
row = {}
|
row = {}
|
||||||
result = False
|
result = False
|
||||||
|
|
||||||
obj = cls.get_(cls)
|
obj = cls.get_(cls)
|
||||||
cert = util.Certificado(obj.key_tmp, obj.cer_tmp)
|
paths = Configuracion.get_({'fields': 'path_cer'})
|
||||||
|
cert = util.Certificado(paths)
|
||||||
data = cert.validate(values['contra'], session['rfc'])
|
data = cert.validate(values['contra'], session['rfc'])
|
||||||
if data:
|
if data:
|
||||||
msg = 'Certificado guardado correctamente'
|
msg = 'Certificado guardado correctamente'
|
||||||
|
@ -352,9 +358,9 @@ class Certificado(BaseModel):
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
msg = cert.error
|
msg = cert.error
|
||||||
obj.key_tmp = None
|
|
||||||
obj.cer_tmp = None
|
Configuracion.add({'path_key': ''})
|
||||||
obj.save()
|
Configuracion.add({'path_cer': ''})
|
||||||
|
|
||||||
return {'ok': result, 'msg': msg, 'data': row}
|
return {'ok': result, 'msg': msg, 'data': row}
|
||||||
|
|
||||||
|
@ -666,8 +672,10 @@ class Socios(BaseModel):
|
||||||
es_proveedor = BooleanField(default=False)
|
es_proveedor = BooleanField(default=False)
|
||||||
cuenta_cliente = TextField(default='')
|
cuenta_cliente = TextField(default='')
|
||||||
cuenta_proveedor = TextField(default='')
|
cuenta_proveedor = TextField(default='')
|
||||||
saldo_cliente = DecimalField(default=0.0, decimal_places=6, auto_round=True)
|
saldo_cliente = DecimalField(default=0.0, max_digits=20, decimal_places=6,
|
||||||
saldo_proveedor = DecimalField(default=0.0, decimal_places=6, auto_round=True)
|
auto_round=True)
|
||||||
|
saldo_proveedor = DecimalField(default=0.0, max_digits=20, decimal_places=6,
|
||||||
|
auto_round=True)
|
||||||
web = TextField(default='')
|
web = TextField(default='')
|
||||||
correo_facturas = TextField(default='')
|
correo_facturas = TextField(default='')
|
||||||
forma_pago = ForeignKeyField(SATFormaPago, null=True)
|
forma_pago = ForeignKeyField(SATFormaPago, null=True)
|
||||||
|
@ -723,7 +731,7 @@ class Socios(BaseModel):
|
||||||
.where((Socios.id==id) & (Socios.es_cliente==True))
|
.where((Socios.id==id) & (Socios.es_cliente==True))
|
||||||
.dicts()
|
.dicts()
|
||||||
)
|
)
|
||||||
print (id, row)
|
#~ print (id, row)
|
||||||
if len(row):
|
if len(row):
|
||||||
return {'ok': True, 'row': row[0]}
|
return {'ok': True, 'row': row[0]}
|
||||||
return {'ok': False}
|
return {'ok': False}
|
||||||
|
@ -801,12 +809,17 @@ class Productos(BaseModel):
|
||||||
clave_sat = TextField(default='')
|
clave_sat = TextField(default='')
|
||||||
descripcion = TextField(index=True)
|
descripcion = TextField(index=True)
|
||||||
unidad = ForeignKeyField(SATUnidades)
|
unidad = ForeignKeyField(SATUnidades)
|
||||||
valor_unitario = DecimalField(default=0.0, decimal_places=6, auto_round=True)
|
valor_unitario = DecimalField(default=0.0, max_digits=18, decimal_places=6,
|
||||||
ultimo_costo = DecimalField(default=0.0, decimal_places=6, auto_round=True)
|
auto_round=True)
|
||||||
descuento = DecimalField(default=0.0, decimal_places=6, auto_round=True)
|
ultimo_costo = DecimalField(default=0.0, max_digits=18, decimal_places=6,
|
||||||
|
auto_round=True)
|
||||||
|
descuento = DecimalField(default=0.0, max_digits=18, decimal_places=6,
|
||||||
|
auto_round=True)
|
||||||
inventario = BooleanField(default=False)
|
inventario = BooleanField(default=False)
|
||||||
existencia = DoubleField(default=0.0)
|
existencia = DecimalField(default=0.0, max_digits=18, decimal_places=2,
|
||||||
minimo = DoubleField(default=0.0)
|
auto_round=True)
|
||||||
|
minimo = DecimalField(default=0.0, max_digits=18, decimal_places=2,
|
||||||
|
auto_round=True)
|
||||||
codigo_barras = TextField(default='')
|
codigo_barras = TextField(default='')
|
||||||
cuenta_predial = TextField(default='')
|
cuenta_predial = TextField(default='')
|
||||||
es_activo = BooleanField(default=True)
|
es_activo = BooleanField(default=True)
|
||||||
|
@ -818,7 +831,11 @@ class Productos(BaseModel):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def next_key(cls):
|
def next_key(cls):
|
||||||
value = Productos.select(fn.Max(Productos.id)).scalar()
|
value = (Productos
|
||||||
|
.select(fn.Max(Productos.id))
|
||||||
|
.group_by(Productos.id)
|
||||||
|
.order_by(Productos.id)
|
||||||
|
.scalar())
|
||||||
if value is None:
|
if value is None:
|
||||||
value = 1
|
value = 1
|
||||||
else:
|
else:
|
||||||
|
@ -982,28 +999,35 @@ class Facturas(BaseModel):
|
||||||
fecha_timbrado = DateTimeField(null=True)
|
fecha_timbrado = DateTimeField(null=True)
|
||||||
forma_pago = TextField(default='')
|
forma_pago = TextField(default='')
|
||||||
condiciones_pago = TextField(default='')
|
condiciones_pago = TextField(default='')
|
||||||
subtotal = DecimalField(default=0.0, decimal_places=6, auto_round=True)
|
subtotal = DecimalField(default=0.0, max_digits=20, decimal_places=6,
|
||||||
descuento = DecimalField(default=0.0, decimal_places=6, auto_round=True)
|
auto_round=True)
|
||||||
|
descuento = DecimalField(default=0.0, max_digits=20, decimal_places=6,
|
||||||
|
auto_round=True)
|
||||||
moneda = TextField(default='MXN')
|
moneda = TextField(default='MXN')
|
||||||
tipo_cambio = DecimalField(default=1.0, decimal_places=6, auto_round=True)
|
tipo_cambio = DecimalField(default=1.0, decimal_places=6, auto_round=True)
|
||||||
total = DecimalField(default=0.0, decimal_places=6, auto_round=True)
|
total = DecimalField(default=0.0, max_digits=20, decimal_places=6,
|
||||||
total_mn = DecimalField(default=0.0, decimal_places=6, auto_round=True)
|
auto_round=True)
|
||||||
|
total_mn = DecimalField(default=0.0, max_digits=20, decimal_places=6,
|
||||||
|
auto_round=True)
|
||||||
tipo_comprobante = TextField(default='I')
|
tipo_comprobante = TextField(default='I')
|
||||||
metodo_pago = TextField(default='PUE')
|
metodo_pago = TextField(default='PUE')
|
||||||
lugar_expedicion = TextField(default='')
|
lugar_expedicion = TextField(default='')
|
||||||
confirmacion = TextField(default='')
|
confirmacion = TextField(default='')
|
||||||
uso_cfdi = TextField(default='')
|
uso_cfdi = TextField(default='')
|
||||||
total_retenciones = DecimalField(
|
total_retenciones = DecimalField(
|
||||||
decimal_places=6, auto_round=True, null=True)
|
max_digits=20, decimal_places=6, auto_round=True, null=True)
|
||||||
total_trasladados = DecimalField(
|
total_trasladados = DecimalField(
|
||||||
decimal_places=6, auto_round=True, null=True)
|
max_digits=20, decimal_places=6, auto_round=True, null=True)
|
||||||
xml = TextField(default='')
|
xml = TextField(default='')
|
||||||
uuid = UUIDField(null=True)
|
uuid = UUIDField(null=True)
|
||||||
estatus = TextField(default='Guardada')
|
estatus = TextField(default='Guardada')
|
||||||
|
estatus_sat = TextField(default='Vigente')
|
||||||
regimen_fiscal = TextField(default='')
|
regimen_fiscal = TextField(default='')
|
||||||
notas = TextField(default='')
|
notas = TextField(default='')
|
||||||
pagada = BooleanField(default=False)
|
pagada = BooleanField(default=False)
|
||||||
cancelada = BooleanField(default=False)
|
cancelada = BooleanField(default=False)
|
||||||
|
fecha_cancelacion = DateTimeField(null=True)
|
||||||
|
acuse = TextField(default='')
|
||||||
donativo = BooleanField(default=False)
|
donativo = BooleanField(default=False)
|
||||||
tipo_relacion = TextField(default='')
|
tipo_relacion = TextField(default='')
|
||||||
error = TextField(default='')
|
error = TextField(default='')
|
||||||
|
@ -1011,6 +1035,38 @@ class Facturas(BaseModel):
|
||||||
class Meta:
|
class Meta:
|
||||||
order_by = ('fecha',)
|
order_by = ('fecha',)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def cancel(cls, id):
|
||||||
|
msg = 'Factura cancelada correctamente'
|
||||||
|
auth = Emisor.get_auth()
|
||||||
|
certificado = Certificado.select()[0]
|
||||||
|
obj = Facturas.get(Facturas.id==id)
|
||||||
|
data, result = util.cancel_cfdi(
|
||||||
|
obj.uuid, certificado.p12, certificado.rfc, auth)
|
||||||
|
if data['ok']:
|
||||||
|
obj.estatus = 'Cancelada'
|
||||||
|
obj.error = ''
|
||||||
|
obj.cancelada = True
|
||||||
|
obj.fecha_cancelacion = result['Fecha']
|
||||||
|
obj.acuse = result['Acuse']
|
||||||
|
else:
|
||||||
|
obj.error = data['msg']
|
||||||
|
obj.save()
|
||||||
|
return data
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def filter_years(cls):
|
||||||
|
data = [{'id': -1, 'value': 'Todos'}]
|
||||||
|
rows = (Facturas
|
||||||
|
.select(Facturas.fecha.year)
|
||||||
|
.group_by(Facturas.fecha.year)
|
||||||
|
.order_by(Facturas.fecha.year)
|
||||||
|
.scalar(as_tuple=True)
|
||||||
|
)
|
||||||
|
if not rows is None:
|
||||||
|
data += [{'id': int(row), 'value': int(row)} for row in rows]
|
||||||
|
return tuple(data)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_xml(cls, id):
|
def get_xml(cls, id):
|
||||||
obj = Facturas.get(Facturas.id==id)
|
obj = Facturas.get(Facturas.id==id)
|
||||||
|
@ -1171,10 +1227,27 @@ class Facturas(BaseModel):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_(cls, values):
|
def get_(cls, values):
|
||||||
|
if 'start' in values:
|
||||||
|
filters = Facturas.fecha.between(
|
||||||
|
util.get_date(values['start']),
|
||||||
|
util.get_date(values['end'], True)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if values['year'] == '-1':
|
||||||
|
fy = (Facturas.fecha.year > 0)
|
||||||
|
else:
|
||||||
|
fy = (Facturas.fecha.year == int(values['year']))
|
||||||
|
if values['month'] == '-1':
|
||||||
|
fm = (Facturas.fecha.month > 0)
|
||||||
|
else:
|
||||||
|
fm = (Facturas.fecha.month == int(values['month']))
|
||||||
|
filters = (fy & fm)
|
||||||
|
|
||||||
rows = tuple(Facturas
|
rows = tuple(Facturas
|
||||||
.select(Facturas.id, Facturas.serie, Facturas.folio, Facturas.uuid,
|
.select(Facturas.id, Facturas.serie, Facturas.folio, Facturas.uuid,
|
||||||
Facturas.fecha, Facturas.tipo_comprobante, Facturas.estatus,
|
Facturas.fecha, Facturas.tipo_comprobante, Facturas.estatus,
|
||||||
Facturas.total_mn, Socios.nombre.alias('cliente'))
|
Facturas.total_mn, Socios.nombre.alias('cliente'))
|
||||||
|
.where(filters)
|
||||||
.join(Socios)
|
.join(Socios)
|
||||||
.switch(Facturas).dicts()
|
.switch(Facturas).dicts()
|
||||||
)
|
)
|
||||||
|
@ -1190,6 +1263,8 @@ class Facturas(BaseModel):
|
||||||
q.execute()
|
q.execute()
|
||||||
q = FacturasImpuestos.delete().where(FacturasImpuestos.factura==obj)
|
q = FacturasImpuestos.delete().where(FacturasImpuestos.factura==obj)
|
||||||
q.execute()
|
q.execute()
|
||||||
|
q = FacturasRelacionadas.delete().where(FacturasRelacionadas.factura==obj)
|
||||||
|
q.execute()
|
||||||
return bool(obj.delete_instance())
|
return bool(obj.delete_instance())
|
||||||
|
|
||||||
def _get_folio(self, serie):
|
def _get_folio(self, serie):
|
||||||
|
@ -1198,6 +1273,8 @@ class Facturas(BaseModel):
|
||||||
inicio = (Facturas
|
inicio = (Facturas
|
||||||
.select(fn.Max(Facturas.folio))
|
.select(fn.Max(Facturas.folio))
|
||||||
.where(Facturas.serie==serie)
|
.where(Facturas.serie==serie)
|
||||||
|
.group_by(Facturas.folio)
|
||||||
|
.order_by(Facturas.folio)
|
||||||
.scalar())
|
.scalar())
|
||||||
if inicio is None:
|
if inicio is None:
|
||||||
inicio = inicio_serie
|
inicio = inicio_serie
|
||||||
|
@ -1437,6 +1514,13 @@ class Facturas(BaseModel):
|
||||||
}
|
}
|
||||||
return util.make_xml(data, certificado)
|
return util.make_xml(data, certificado)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_status_sat(cls, id):
|
||||||
|
obj = Facturas.get(Facturas.id == id)
|
||||||
|
obj.estatus_sat = util.get_sat(obj.xml)
|
||||||
|
obj.save()
|
||||||
|
return obj.estatus_sat
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def timbrar(cls, id):
|
def timbrar(cls, id):
|
||||||
obj = Facturas.get(Facturas.id == id)
|
obj = Facturas.get(Facturas.id == id)
|
||||||
|
@ -1446,7 +1530,7 @@ class Facturas(BaseModel):
|
||||||
|
|
||||||
auth = Emisor.get_auth()
|
auth = Emisor.get_auth()
|
||||||
|
|
||||||
error = False
|
#~ error = False
|
||||||
msg = 'Factura timbrada correctamente'
|
msg = 'Factura timbrada correctamente'
|
||||||
result = util.timbra_xml(obj.xml, auth)
|
result = util.timbra_xml(obj.xml, auth)
|
||||||
if result['ok']:
|
if result['ok']:
|
||||||
|
@ -1455,10 +1539,10 @@ class Facturas(BaseModel):
|
||||||
obj.fecha_timbrado = result['fecha']
|
obj.fecha_timbrado = result['fecha']
|
||||||
obj.estatus = 'Timbrada'
|
obj.estatus = 'Timbrada'
|
||||||
obj.error = ''
|
obj.error = ''
|
||||||
obj.save()
|
#~ obj.save()
|
||||||
row = {'uuid': obj.uuid, 'estatus': 'Timbrada'}
|
row = {'uuid': obj.uuid, 'estatus': 'Timbrada'}
|
||||||
else:
|
else:
|
||||||
error = True
|
#~ error = True
|
||||||
msg = result['error']
|
msg = result['error']
|
||||||
obj.estatus = 'Error'
|
obj.estatus = 'Error'
|
||||||
obj.error = msg
|
obj.error = msg
|
||||||
|
@ -1467,25 +1551,69 @@ class Facturas(BaseModel):
|
||||||
return {'ok': result['ok'], 'msg': msg, 'row': row}
|
return {'ok': result['ok'], 'msg': msg, 'row': row}
|
||||||
|
|
||||||
|
|
||||||
|
class PreFacturas(BaseModel):
|
||||||
|
cliente = ForeignKeyField(Socios)
|
||||||
|
serie = TextField(default='PRE')
|
||||||
|
folio = IntegerField(default=0)
|
||||||
|
fecha = DateTimeField(default=util.now, formats=['%Y-%m-%d %H:%M:%S'])
|
||||||
|
forma_pago = TextField(default='')
|
||||||
|
condiciones_pago = TextField(default='')
|
||||||
|
subtotal = DecimalField(default=0.0, max_digits=20, decimal_places=6,
|
||||||
|
auto_round=True)
|
||||||
|
descuento = DecimalField(default=0.0, max_digits=20, decimal_places=6,
|
||||||
|
auto_round=True)
|
||||||
|
moneda = TextField(default='MXN')
|
||||||
|
tipo_cambio = DecimalField(default=1.0, decimal_places=6, auto_round=True)
|
||||||
|
total = DecimalField(default=0.0, max_digits=20, decimal_places=6,
|
||||||
|
auto_round=True)
|
||||||
|
total_mn = DecimalField(default=0.0, max_digits=20, decimal_places=6,
|
||||||
|
auto_round=True)
|
||||||
|
tipo_comprobante = TextField(default='I')
|
||||||
|
metodo_pago = TextField(default='PUE')
|
||||||
|
lugar_expedicion = TextField(default='')
|
||||||
|
uso_cfdi = TextField(default='')
|
||||||
|
total_retenciones = DecimalField(
|
||||||
|
max_digits=20, decimal_places=6, auto_round=True, null=True)
|
||||||
|
total_trasladados = DecimalField(
|
||||||
|
max_digits=20, decimal_places=6, auto_round=True, null=True)
|
||||||
|
estatus = TextField(default='Generada')
|
||||||
|
regimen_fiscal = TextField(default='')
|
||||||
|
notas = TextField(default='')
|
||||||
|
donativo = BooleanField(default=False)
|
||||||
|
tipo_relacion = TextField(default='')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
order_by = ('fecha',)
|
||||||
|
|
||||||
|
|
||||||
class FacturasRelacionadas(BaseModel):
|
class FacturasRelacionadas(BaseModel):
|
||||||
factura = ForeignKeyField(Facturas, related_name='original')
|
factura = ForeignKeyField(Facturas, related_name='original')
|
||||||
factura_origen = ForeignKeyField(Facturas, related_name='relacion')
|
factura_origen = ForeignKeyField(Facturas, related_name='relacion')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
order_by = ('factura',)
|
order_by = ('factura',)
|
||||||
indexes = (
|
|
||||||
(('factura', 'factura_origen'), True),
|
class PreFacturasRelacionadas(BaseModel):
|
||||||
)
|
factura = ForeignKeyField(PreFacturas, related_name='original')
|
||||||
|
factura_origen = ForeignKeyField(PreFacturas, related_name='relacion')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
order_by = ('factura',)
|
||||||
|
|
||||||
|
|
||||||
class FacturasDetalle(BaseModel):
|
class FacturasDetalle(BaseModel):
|
||||||
factura = ForeignKeyField(Facturas)
|
factura = ForeignKeyField(Facturas)
|
||||||
producto = ForeignKeyField(Productos, null=True)
|
producto = ForeignKeyField(Productos, null=True)
|
||||||
cantidad = DecimalField(default=0.0, decimal_places=6, auto_round=True)
|
cantidad = DecimalField(default=0.0, max_digits=18, decimal_places=6,
|
||||||
valor_unitario = DecimalField(default=0.0, decimal_places=6, auto_round=True)
|
auto_round=True)
|
||||||
descuento = DecimalField(default=0.0, decimal_places=6, auto_round=True)
|
valor_unitario = DecimalField(default=0.0, max_digits=18, decimal_places=6,
|
||||||
precio_final = DecimalField(default=0.0, decimal_places=6, auto_round=True)
|
auto_round=True)
|
||||||
importe = DecimalField(default=0.0, decimal_places=6, auto_round=True)
|
descuento = DecimalField(default=0.0, max_digits=18, decimal_places=6,
|
||||||
|
auto_round=True)
|
||||||
|
precio_final = DecimalField(default=0.0, max_digits=18, decimal_places=6,
|
||||||
|
auto_round=True)
|
||||||
|
importe = DecimalField(default=0.0, max_digits=20, decimal_places=6,
|
||||||
|
auto_round=True)
|
||||||
descripcion = TextField(default='')
|
descripcion = TextField(default='')
|
||||||
unidad = TextField(default='')
|
unidad = TextField(default='')
|
||||||
clave = TextField(default='')
|
clave = TextField(default='')
|
||||||
|
@ -1504,11 +1632,54 @@ class FacturasDetalle(BaseModel):
|
||||||
order_by = ('factura',)
|
order_by = ('factura',)
|
||||||
|
|
||||||
|
|
||||||
|
class PreFacturasDetalle(BaseModel):
|
||||||
|
factura = ForeignKeyField(PreFacturas)
|
||||||
|
producto = ForeignKeyField(Productos, null=True)
|
||||||
|
cantidad = DecimalField(default=0.0, max_digits=18, decimal_places=6,
|
||||||
|
auto_round=True)
|
||||||
|
valor_unitario = DecimalField(default=0.0, max_digits=18, decimal_places=6,
|
||||||
|
auto_round=True)
|
||||||
|
descuento = DecimalField(default=0.0, max_digits=18, decimal_places=6,
|
||||||
|
auto_round=True)
|
||||||
|
precio_final = DecimalField(default=0.0, max_digits=18, decimal_places=6,
|
||||||
|
auto_round=True)
|
||||||
|
importe = DecimalField(default=0.0, max_digits=20, decimal_places=6,
|
||||||
|
auto_round=True)
|
||||||
|
aduana = TextField(default='')
|
||||||
|
pedimento = TextField(default='')
|
||||||
|
fecha_pedimento = DateField(null=True)
|
||||||
|
alumno = TextField(default='')
|
||||||
|
curp = TextField(default='')
|
||||||
|
nivel = TextField(default='')
|
||||||
|
autorizacion = TextField(default='')
|
||||||
|
cuenta_predial = TextField(default='')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
order_by = ('factura',)
|
||||||
|
|
||||||
|
|
||||||
class FacturasImpuestos(BaseModel):
|
class FacturasImpuestos(BaseModel):
|
||||||
factura = ForeignKeyField(Facturas)
|
factura = ForeignKeyField(Facturas)
|
||||||
impuesto = ForeignKeyField(SATImpuestos)
|
impuesto = ForeignKeyField(SATImpuestos)
|
||||||
base = DecimalField(default=0.0, decimal_places=6, auto_round=True)
|
base = DecimalField(default=0.0, max_digits=20, decimal_places=6,
|
||||||
importe = DecimalField(default=0.0, decimal_places=6, auto_round=True)
|
auto_round=True)
|
||||||
|
importe = DecimalField(default=0.0, max_digits=18, decimal_places=6,
|
||||||
|
auto_round=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
order_by = ('factura',)
|
||||||
|
indexes = (
|
||||||
|
(('factura', 'impuesto'), True),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class PreFacturasImpuestos(BaseModel):
|
||||||
|
factura = ForeignKeyField(PreFacturas)
|
||||||
|
impuesto = ForeignKeyField(SATImpuestos)
|
||||||
|
base = DecimalField(default=0.0, max_digits=20, decimal_places=6,
|
||||||
|
auto_round=True)
|
||||||
|
importe = DecimalField(default=0.0, max_digits=18, decimal_places=6,
|
||||||
|
auto_round=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
order_by = ('factura',)
|
order_by = ('factura',)
|
||||||
|
@ -1596,12 +1767,14 @@ def test_correo(values):
|
||||||
return util.send_mail(data)
|
return util.send_mail(data)
|
||||||
|
|
||||||
|
|
||||||
def _init_values():
|
def _init_values(rfc):
|
||||||
data = (
|
data = (
|
||||||
{'clave': 'version', 'valor': VERSION},
|
{'clave': 'version', 'valor': VERSION},
|
||||||
{'clave': 'rfc_publico', 'valor': 'XAXX010101000'},
|
{'clave': 'rfc_publico', 'valor': 'XAXX010101000'},
|
||||||
{'clave': 'rfc_extranjero', 'valor': 'XEXX010101000'},
|
{'clave': 'rfc_extranjero', 'valor': 'XEXX010101000'},
|
||||||
{'clave': 'decimales', 'valor': '2'},
|
{'clave': 'decimales', 'valor': '2'},
|
||||||
|
{'clave': 'path_key', 'valor': ''},
|
||||||
|
{'clave': 'path_cer', 'valor': ''},
|
||||||
)
|
)
|
||||||
for row in data:
|
for row in data:
|
||||||
try:
|
try:
|
||||||
|
@ -1609,6 +1782,10 @@ def _init_values():
|
||||||
Configuracion.create(**row)
|
Configuracion.create(**row)
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
if not Certificado.select().count():
|
||||||
|
Certificado.create(rfc=rfc)
|
||||||
|
|
||||||
log.info('Valores iniciales insertados...')
|
log.info('Valores iniciales insertados...')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -1617,6 +1794,8 @@ def _crear_tablas(rfc):
|
||||||
tablas = [Addendas, Categorias, Certificado, CondicionesPago, Configuracion,
|
tablas = [Addendas, Categorias, Certificado, CondicionesPago, Configuracion,
|
||||||
Emisor, Facturas, FacturasDetalle, FacturasImpuestos, Folios,
|
Emisor, Facturas, FacturasDetalle, FacturasImpuestos, Folios,
|
||||||
FacturasRelacionadas, Productos,
|
FacturasRelacionadas, Productos,
|
||||||
|
PreFacturas, PreFacturasDetalle, PreFacturasImpuestos,
|
||||||
|
PreFacturasRelacionadas,
|
||||||
SATAduanas, SATFormaPago, SATImpuestos, SATMonedas, SATRegimenes,
|
SATAduanas, SATFormaPago, SATImpuestos, SATMonedas, SATRegimenes,
|
||||||
SATTipoRelacion, SATUnidades, SATUsoCfdi,
|
SATTipoRelacion, SATUnidades, SATUsoCfdi,
|
||||||
Socios, Tags, Usuarios,
|
Socios, Tags, Usuarios,
|
||||||
|
@ -1639,7 +1818,7 @@ def _crear_tablas(rfc):
|
||||||
msg = 'El usuario ya existe'
|
msg = 'El usuario ya existe'
|
||||||
log.error(msg)
|
log.error(msg)
|
||||||
|
|
||||||
_init_values()
|
_init_values(rfc)
|
||||||
_importar_valores('', rfc)
|
_importar_valores('', rfc)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -11,7 +11,7 @@ from conf import DEBUG
|
||||||
|
|
||||||
|
|
||||||
DEBUG = DEBUG
|
DEBUG = DEBUG
|
||||||
VERSION = '0.1.0'
|
VERSION = '0.2.0'
|
||||||
EMAIL_SUPPORT = ('soporte@empresalibre.net',)
|
EMAIL_SUPPORT = ('soporte@empresalibre.net',)
|
||||||
|
|
||||||
BASE_DIR = os.path.abspath(os.path.dirname(__file__))
|
BASE_DIR = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
@ -25,6 +25,8 @@ DB_SAT = os.path.abspath(os.path.join(BASE_DIR, '..', 'db', 'sat.db'))
|
||||||
|
|
||||||
IV = 'valores_iniciales.json'
|
IV = 'valores_iniciales.json'
|
||||||
INIT_VALUES = os.path.abspath(os.path.join(BASE_DIR, '..', 'db', IV))
|
INIT_VALUES = os.path.abspath(os.path.join(BASE_DIR, '..', 'db', IV))
|
||||||
|
CT = 'cancel_template.xml'
|
||||||
|
TEMPLATE_CANCEL = os.path.abspath(os.path.join(PATH_TEMPLATES, CT))
|
||||||
|
|
||||||
PATH_XSLT = os.path.abspath(os.path.join(BASE_DIR, '..', 'xslt'))
|
PATH_XSLT = os.path.abspath(os.path.join(BASE_DIR, '..', 'xslt'))
|
||||||
PATH_BIN = os.path.abspath(os.path.join(BASE_DIR, '..', 'bin'))
|
PATH_BIN = os.path.abspath(os.path.join(BASE_DIR, '..', 'bin'))
|
||||||
|
@ -50,6 +52,7 @@ if DEBUG:
|
||||||
level=LOG_LEVEL,
|
level=LOG_LEVEL,
|
||||||
format_string=format_string).push_application()
|
format_string=format_string).push_application()
|
||||||
else:
|
else:
|
||||||
|
#~ Establece una ruta con acceso para nginx o el servidor web que uses
|
||||||
LOG_PATH = '/srv/log/empresalibre.log'
|
LOG_PATH = '/srv/log/empresalibre.log'
|
||||||
RotatingFileHandler(
|
RotatingFileHandler(
|
||||||
LOG_PATH,
|
LOG_PATH,
|
||||||
|
@ -69,9 +72,11 @@ log = Logger(LOG_NAME)
|
||||||
|
|
||||||
PATH_XSLTPROC = 'xsltproc'
|
PATH_XSLTPROC = 'xsltproc'
|
||||||
PATH_OPENSSL = 'openssl'
|
PATH_OPENSSL = 'openssl'
|
||||||
|
PATH_XMLSEC = 'xmlsec1'
|
||||||
if 'win' in sys.platform:
|
if 'win' in sys.platform:
|
||||||
PATH_XSLTPROC = os.path.join(PATH_BIN, 'xsltproc.exe')
|
PATH_XSLTPROC = os.path.join(PATH_BIN, 'xsltproc.exe')
|
||||||
PATH_OPENSSL = os.path.join(PATH_BIN, 'openssl.exe')
|
PATH_OPENSSL = os.path.join(PATH_BIN, 'openssl.exe')
|
||||||
|
PATH_XMLSEC = os.path.join(PATH_BIN, 'xmlsec.exe')
|
||||||
|
|
||||||
|
|
||||||
PRE = {
|
PRE = {
|
||||||
|
|
|
@ -27,9 +27,7 @@ function get_series(){
|
||||||
function get_forma_pago(){
|
function get_forma_pago(){
|
||||||
webix.ajax().get('/values/formapago', {key: true}, function(text, data){
|
webix.ajax().get('/values/formapago', {key: true}, function(text, data){
|
||||||
var values = data.json()
|
var values = data.json()
|
||||||
//~ pre = values[0]
|
|
||||||
$$('lst_forma_pago').getList().parse(values)
|
$$('lst_forma_pago').getList().parse(values)
|
||||||
//~ $$('lst_forma_pago').setValue(pre.id)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -697,7 +695,19 @@ function grid_invoices_click(id, e, node){
|
||||||
|
|
||||||
|
|
||||||
function send_cancel(id){
|
function send_cancel(id){
|
||||||
show(id)
|
webix.ajax().get('/values/cancelinvoice', {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 Cancelar',
|
||||||
|
text: values.msg,
|
||||||
|
type: 'alert-error'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function cmd_invoice_cancelar_click(){
|
function cmd_invoice_cancelar_click(){
|
||||||
|
@ -717,6 +727,11 @@ function cmd_invoice_cancelar_click(){
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(row.estatus == 'Cancelada'){
|
||||||
|
msg_error('La factura ya esta cancelada')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
msg = '¿Estás seguro de enviar a cancelar esta factura?<BR><BR> \
|
msg = '¿Estás seguro de enviar a cancelar esta factura?<BR><BR> \
|
||||||
ESTA ACCIÓN NO SE PUEDE DESHACER'
|
ESTA ACCIÓN NO SE PUEDE DESHACER'
|
||||||
webix.confirm({
|
webix.confirm({
|
||||||
|
@ -732,3 +747,87 @@ function cmd_invoice_cancelar_click(){
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function get_invoices(rango){
|
||||||
|
if(rango == undefined){
|
||||||
|
var fy = $$('filter_year')
|
||||||
|
var fm = $$('filter_month')
|
||||||
|
|
||||||
|
var y = fy.getValue()
|
||||||
|
var m = fm.getValue()
|
||||||
|
rango = {'year': y, 'month': m}
|
||||||
|
}
|
||||||
|
|
||||||
|
var grid = $$('grid_invoices')
|
||||||
|
webix.ajax().get('/invoices', rango, {
|
||||||
|
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 filter_year_change(nv, ov){
|
||||||
|
get_invoices()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function filter_month_change(nv, ov){
|
||||||
|
get_invoices()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function filter_dates_change(range){
|
||||||
|
if(range.start != null && range.end != null){
|
||||||
|
get_invoices(range)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function cmd_invoice_sat_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 no esta timbrada, solo es posible consultar \
|
||||||
|
el estatus en el SAT de facturas timbradas')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
webix.ajax().get('/values/statussat', {id: row.id}, function(text, data){
|
||||||
|
var values = data.json()
|
||||||
|
show(values)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function cmd_prefactura_click(){
|
||||||
|
var form = this.getFormView()
|
||||||
|
|
||||||
|
if(!form.validate()) {
|
||||||
|
msg_error('Valores inválidos')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var values = form.getValues()
|
||||||
|
if(!validate_invoice(values)){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
show('PreFactura')
|
||||||
|
}
|
||||||
|
|
|
@ -44,8 +44,13 @@ var controllers = {
|
||||||
$$('grid_details').attachEvent('onBeforeEditStart', grid_details_before_edit_start)
|
$$('grid_details').attachEvent('onBeforeEditStart', grid_details_before_edit_start)
|
||||||
$$('grid_details').attachEvent('onBeforeEditStop', grid_details_before_edit_stop)
|
$$('grid_details').attachEvent('onBeforeEditStop', grid_details_before_edit_stop)
|
||||||
$$('cmd_invoice_timbrar').attachEvent('onItemClick', cmd_invoice_timbrar_click)
|
$$('cmd_invoice_timbrar').attachEvent('onItemClick', cmd_invoice_timbrar_click)
|
||||||
|
$$('cmd_invoice_sat').attachEvent('onItemClick', cmd_invoice_sat_click)
|
||||||
$$('cmd_invoice_cancelar').attachEvent('onItemClick', cmd_invoice_cancelar_click)
|
$$('cmd_invoice_cancelar').attachEvent('onItemClick', cmd_invoice_cancelar_click)
|
||||||
$$('grid_invoices').attachEvent('onItemClick', grid_invoices_click)
|
$$('grid_invoices').attachEvent('onItemClick', grid_invoices_click)
|
||||||
|
$$('filter_year').attachEvent('onChange', filter_year_change)
|
||||||
|
$$('filter_month').attachEvent('onChange', filter_month_change)
|
||||||
|
$$('filter_dates').attachEvent('onChange', filter_dates_change)
|
||||||
|
$$('cmd_prefactura').attachEvent('onItemClick', cmd_prefactura_click)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,23 +97,6 @@ function get_products(){
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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){
|
function menu_user_click(id, e, node){
|
||||||
if (id == 1){
|
if (id == 1){
|
||||||
window.location = '/logout';
|
window.location = '/logout';
|
||||||
|
@ -117,6 +105,26 @@ function menu_user_click(id, e, node){
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function current_dates(){
|
||||||
|
var fy = $$('filter_year')
|
||||||
|
var fm = $$('filter_month')
|
||||||
|
var d = new Date()
|
||||||
|
|
||||||
|
fy.blockEvent()
|
||||||
|
fm.blockEvent()
|
||||||
|
|
||||||
|
fm.setValue(d.getMonth() + 1)
|
||||||
|
webix.ajax().sync().get('/values/filteryears', function(text, data){
|
||||||
|
var values = data.json()
|
||||||
|
fy.getList().parse(values)
|
||||||
|
fy.setValue(d.getFullYear())
|
||||||
|
})
|
||||||
|
|
||||||
|
fy.unblockEvent()
|
||||||
|
fm.unblockEvent()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function multi_change(prevID, nextID){
|
function multi_change(prevID, nextID){
|
||||||
//~ webix.message(nextID)
|
//~ webix.message(nextID)
|
||||||
if(nextID == 'app_partners'){
|
if(nextID == 'app_partners'){
|
||||||
|
@ -138,6 +146,7 @@ function multi_change(prevID, nextID){
|
||||||
if(nextID == 'app_invoices'){
|
if(nextID == 'app_invoices'){
|
||||||
active = $$('multi_invoices').getActiveId()
|
active = $$('multi_invoices').getActiveId()
|
||||||
if(active == 'invoices_home'){
|
if(active == 'invoices_home'){
|
||||||
|
current_dates()
|
||||||
get_invoices()
|
get_invoices()
|
||||||
}
|
}
|
||||||
gi = $$('grid_invoices')
|
gi = $$('grid_invoices')
|
||||||
|
|
|
@ -14,12 +14,41 @@ var toolbar_invoices = [
|
||||||
var toolbar_invoices_util = [
|
var toolbar_invoices_util = [
|
||||||
{view: 'button', id: 'cmd_invoice_timbrar', label: 'Timbrar',
|
{view: 'button', id: 'cmd_invoice_timbrar', label: 'Timbrar',
|
||||||
type: 'iconButton', autowidth: true, icon: 'ticket'},
|
type: 'iconButton', autowidth: true, icon: 'ticket'},
|
||||||
|
{view: 'button', id: 'cmd_invoice_sat', label: 'SAT',
|
||||||
|
type: 'iconButton', autowidth: true, icon: 'check-circle'},
|
||||||
{},
|
{},
|
||||||
{view: 'button', id: 'cmd_invoice_cancelar', label: 'Cancelar',
|
{view: 'button', id: 'cmd_invoice_cancelar', label: 'Cancelar',
|
||||||
type: 'iconButton', autowidth: true, icon: 'ban'},
|
type: 'iconButton', autowidth: true, icon: 'ban'},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
var months = [
|
||||||
|
{id: -1, value: 'Todos'},
|
||||||
|
{id: 1, value: 'Enero'},
|
||||||
|
{id: 2, value: 'Febrero'},
|
||||||
|
{id: 3, value: 'Marzo'},
|
||||||
|
{id: 4, value: 'Abril'},
|
||||||
|
{id: 5, value: 'Mayo'},
|
||||||
|
{id: 6, value: 'Junio'},
|
||||||
|
{id: 7, value: 'Julio'},
|
||||||
|
{id: 8, value: 'Agosto'},
|
||||||
|
{id: 9, value: 'Septiembre'},
|
||||||
|
{id: 10, value: 'Octubre'},
|
||||||
|
{id: 11, value: 'Noviembre'},
|
||||||
|
{id: 12, value: 'Diciembre'},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
var toolbar_invoices_filter = [
|
||||||
|
{view: 'richselect', id: 'filter_year', label: 'Año', labelAlign: 'right',
|
||||||
|
labelWidth: 50, width: 150, options: []},
|
||||||
|
{view: 'richselect', id: 'filter_month', label: 'Mes', labelAlign: 'right',
|
||||||
|
labelWidth: 50, width: 200, options: months},
|
||||||
|
{view: 'daterangepicker', id: 'filter_dates', label: 'Fechas',
|
||||||
|
labelAlign: 'right', width: 300},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
function get_icon(tipo){
|
function get_icon(tipo){
|
||||||
var node = "<img src='/static/img/file-" + tipo + ".png' height='20' width='17' style='margin: 5px 0px'/>"
|
var node = "<img src='/static/img/file-" + tipo + ".png' height='20' width='17' style='margin: 5px 0px'/>"
|
||||||
return node
|
return node
|
||||||
|
@ -34,7 +63,8 @@ var grid_invoices_cols = [
|
||||||
sort:"int", css: "cell_right"},
|
sort:"int", css: "cell_right"},
|
||||||
{id: "uuid", header: ["UUID", {content: "textFilter"}], adjust: "data",
|
{id: "uuid", header: ["UUID", {content: "textFilter"}], adjust: "data",
|
||||||
sort:"string", hidden:true},
|
sort:"string", hidden:true},
|
||||||
{id: "fecha", header: ["Fecha y Hora"], adjust: "data", sort:"string"},
|
{id: "fecha", header: ["Fecha y Hora"],
|
||||||
|
adjust: "data", sort: "date"},
|
||||||
{id: "tipo_comprobante", header: ["Tipo", {content: "selectFilter"}],
|
{id: "tipo_comprobante", header: ["Tipo", {content: "selectFilter"}],
|
||||||
adjust: 'header', sort: 'string'},
|
adjust: 'header', sort: 'string'},
|
||||||
{id: "estatus", header: ["Estatus", {content: "selectFilter"}],
|
{id: "estatus", header: ["Estatus", {content: "selectFilter"}],
|
||||||
|
@ -228,6 +258,7 @@ var body_regimen_fiscal = {
|
||||||
|
|
||||||
|
|
||||||
var controls_generate = [
|
var controls_generate = [
|
||||||
|
{minHeight: 15, maxHeight: 15},
|
||||||
{cols: [ {rows:[
|
{cols: [ {rows:[
|
||||||
{view: 'fieldset', label: 'Buscar Cliente', body: {rows: [
|
{view: 'fieldset', label: 'Buscar Cliente', body: {rows: [
|
||||||
{cols: [
|
{cols: [
|
||||||
|
@ -272,26 +303,86 @@ var controls_generate = [
|
||||||
{view: 'label', label: 'Detalle', height: 30, align: 'left'},
|
{view: 'label', label: 'Detalle', height: 30, align: 'left'},
|
||||||
grid_details,
|
grid_details,
|
||||||
{minHeight: 15, maxHeight: 15},
|
{minHeight: 15, maxHeight: 15},
|
||||||
{cols: [{}, grid_totals]}
|
{cols: [{}, grid_totals]},
|
||||||
|
{minHeight: 15, maxHeight: 15},
|
||||||
|
{margin: 20, cols: [{},
|
||||||
|
{view: "button", id: "cmd_timbrar", label: "Timbrar",
|
||||||
|
type: "form", autowidth: true, align:"center"},
|
||||||
|
{view: "button", id: 'cmd_prefactura', label: "PreFactura",
|
||||||
|
type: "form", autowidth: true, align:"center"},
|
||||||
|
{}]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
var toolbar_preinvoices = [
|
||||||
|
{view: 'button', id: 'cmd_facturar_preinvoice', label: 'Facturar',
|
||||||
|
type: 'iconButton', autowidth: true, icon: 'pencil'},
|
||||||
|
{},
|
||||||
|
{view: "button", id: "cmd_delete_preinvoice", label: "Eliminar",
|
||||||
|
type: "iconButton", autowidth: true, icon: "minus"},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
var toolbar_prefilter = [
|
||||||
|
{view: 'richselect', id: 'prefilter_year', label: 'Año', labelAlign: 'right',
|
||||||
|
labelWidth: 50, width: 150, options: []},
|
||||||
|
{view: 'richselect', id: 'prefilter_month', label: 'Mes', labelAlign: 'right',
|
||||||
|
labelWidth: 50, width: 200, options: months},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
var grid_preinvoices_cols = [
|
||||||
|
{id: "id", header:"ID", hidden:true},
|
||||||
|
{id: "folio", header: ["Folio", {content: "numberFilter"}], adjust: "data",
|
||||||
|
sort:"int", css: "cell_right"},
|
||||||
|
{id: "fecha", header: ["Fecha y Hora"],
|
||||||
|
adjust: "data", sort: "date"},
|
||||||
|
{id: "tipo_comprobante", header: ["Tipo", {content: "selectFilter"}],
|
||||||
|
adjust: 'header', sort: 'string'},
|
||||||
|
{id: 'total_mn', header: ['Total M.N.'], width: 150,
|
||||||
|
sort: 'int', format: webix.i18n.priceFormat, css: 'right'},
|
||||||
|
{id: "cliente", header: ["Razón Social", {content: "selectFilter"}],
|
||||||
|
fillspace:true, sort:"string"},
|
||||||
|
{id: 'pdf', header: 'PDF', adjust: 'data', template: get_icon('pdf')},
|
||||||
|
{id: 'email', header: '', adjust: 'data', template: get_icon('email')}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
var grid_preinvoices = {
|
||||||
|
view: 'datatable',
|
||||||
|
id: 'grid_preinvoices',
|
||||||
|
select: 'row',
|
||||||
|
adjust: true,
|
||||||
|
footer: true,
|
||||||
|
resizeColumn: true,
|
||||||
|
headermenu: true,
|
||||||
|
columns: grid_preinvoices_cols,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var controls_prefactura = [
|
||||||
|
{view: 'toolbar', elements: toolbar_preinvoices},
|
||||||
|
{view: 'toolbar', elements: toolbar_prefilter},
|
||||||
|
grid_preinvoices,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
var controls_invoices = [
|
var controls_invoices = [
|
||||||
{
|
{
|
||||||
view: "tabview",
|
view: 'tabview',
|
||||||
tabbar: {options: ["Generar"]}, animate: true,
|
tabbar: {options: ['Generar', 'PreFacturas']}, animate: true,
|
||||||
cells: [
|
cells: [
|
||||||
{id: "Generar", rows: controls_generate},
|
{id: 'Generar', rows: controls_generate},
|
||||||
|
{id: 'PreFacturas', rows: controls_prefactura},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{rows: [
|
{rows: [
|
||||||
{template:"", type: "section" },
|
{template:"", type: "section" },
|
||||||
{margin: 10, cols: [{},
|
{margin: 10, cols: [{},
|
||||||
{view: "button", id: "cmd_timbrar", label: "Timbrar",
|
|
||||||
type: "form", autowidth: true, align:"center"},
|
|
||||||
{view: 'button', id: 'cmd_close_invoice', label: 'Cancelar',
|
{view: 'button', id: 'cmd_close_invoice', label: 'Cancelar',
|
||||||
type: 'danger', autowidth: true, align: 'center'},
|
type: 'danger', autowidth: true, align: 'center'}
|
||||||
{}]
|
]
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
]
|
]
|
||||||
|
@ -316,6 +407,7 @@ var multi_invoices = {
|
||||||
{id: 'invoices_home', rows:[
|
{id: 'invoices_home', rows:[
|
||||||
{view: 'toolbar', elements: toolbar_invoices},
|
{view: 'toolbar', elements: toolbar_invoices},
|
||||||
{view: 'toolbar', elements: toolbar_invoices_util},
|
{view: 'toolbar', elements: toolbar_invoices_util},
|
||||||
|
{view: 'toolbar', elements: toolbar_invoices_filter},
|
||||||
grid_invoices,
|
grid_invoices,
|
||||||
]},
|
]},
|
||||||
{id: 'invoices_new', rows:[form_invoice, {}]}
|
{id: 'invoices_new', rows:[form_invoice, {}]}
|
||||||
|
|
|
@ -62,7 +62,8 @@ var ui_main = {
|
||||||
{view: 'label', label: 'Empresa Libre'},
|
{view: 'label', label: 'Empresa Libre'},
|
||||||
{},
|
{},
|
||||||
menu_user,
|
menu_user,
|
||||||
{view: 'button', type: 'icon', width: 45, css: 'app_button', icon: 'bell-o', badge: 1}
|
{view: 'button', type: 'icon', width: 45, css: 'app_button',
|
||||||
|
icon: 'bell-o', badge: 0}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<Cancelacion RfcEmisor="{rfc}" Fecha="{fecha}" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://cancelacfd.sat.gob.mx">
|
||||||
|
<Folios>
|
||||||
|
<UUID>{uuid}</UUID>
|
||||||
|
</Folios>
|
||||||
|
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
|
||||||
|
<SignedInfo>
|
||||||
|
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
|
||||||
|
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
|
||||||
|
<Reference URI="">
|
||||||
|
<Transforms>
|
||||||
|
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
|
||||||
|
</Transforms>
|
||||||
|
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
|
||||||
|
<DigestValue />
|
||||||
|
</Reference>
|
||||||
|
</SignedInfo>
|
||||||
|
<SignatureValue />
|
||||||
|
<KeyInfo>
|
||||||
|
<X509Data>
|
||||||
|
<X509SubjectName />
|
||||||
|
<X509IssuerSerial />
|
||||||
|
<X509Certificate />
|
||||||
|
</X509Data>
|
||||||
|
<KeyValue />
|
||||||
|
</KeyInfo>
|
||||||
|
</Signature>
|
||||||
|
</Cancelacion>
|
Loading…
Reference in New Issue