forked from elmau/empresa-libre
Merge branch 'develop'
Agregar tablas para Alumnos, Tickets y Rangos de precios
This commit is contained in:
commit
48670592f3
|
@ -10,7 +10,7 @@ DEFAULT_PASSWORD = 'blades3.3'
|
|||
|
||||
|
||||
#~ Establece una ruta accesible para el servidor web
|
||||
LOG_PATH = '/srv/empresa/logs/empresalibre.log'
|
||||
LOG_PATH = '/srv/empresa/logs/empresa-libre.log'
|
||||
|
||||
# ~ Establece los valores para sincronizar los backups de la base de datos
|
||||
# ~ por ejemplo
|
||||
|
@ -21,5 +21,4 @@ LOG_PATH = '/srv/empresa/logs/empresalibre.log'
|
|||
# ~ 'REPO': 'id_repo',
|
||||
# ~ }
|
||||
|
||||
|
||||
SEAFILE_SERVER = {}
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
|
||||
DEBUG = True
|
||||
DEBUG = False
|
||||
|
||||
#~ Ecodex
|
||||
ID_INTEGRADOR = ''
|
||||
|
|
|
@ -19,6 +19,7 @@ import uuid
|
|||
import zipfile
|
||||
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
from smtplib import SMTPException, SMTPAuthenticationError
|
||||
from xml.etree import ElementTree as ET
|
||||
|
||||
|
@ -26,6 +27,7 @@ try:
|
|||
import uno
|
||||
from com.sun.star.beans import PropertyValue
|
||||
from com.sun.star.awt import Size
|
||||
from com.sun.star.view.PaperFormat import LETTER
|
||||
APP_LIBO = True
|
||||
except:
|
||||
APP_LIBO = False
|
||||
|
@ -35,9 +37,9 @@ from dateutil import parser
|
|||
|
||||
from .helper import CaseInsensitiveDict, NumLet, SendMail, TemplateInvoice, \
|
||||
SeaFileAPI
|
||||
from settings import DEBUG, log, template_lookup, COMPANIES, DB_SAT, \
|
||||
from settings import DEBUG, MV, log, template_lookup, COMPANIES, DB_SAT, \
|
||||
PATH_XSLT, PATH_XSLTPROC, PATH_OPENSSL, PATH_TEMPLATES, PATH_MEDIA, PRE, \
|
||||
PATH_XMLSEC, TEMPLATE_CANCEL, DEFAULT_SAT_PRODUCTO, DECIMALES
|
||||
PATH_XMLSEC, TEMPLATE_CANCEL, DEFAULT_SAT_PRODUCTO, DECIMALES, DIR_FACTURAS
|
||||
|
||||
from settings import SEAFILE_SERVER
|
||||
|
||||
|
@ -843,6 +845,7 @@ class LIBO(object):
|
|||
if self._template is None:
|
||||
return b''
|
||||
|
||||
self._template.setPrinter(self._set_properties({'PaperFormat': LETTER}))
|
||||
self._render(data)
|
||||
|
||||
path = '{}.ods'.format(tempfile.mkstemp()[1])
|
||||
|
@ -1459,16 +1462,66 @@ def backup_dbs():
|
|||
return
|
||||
|
||||
|
||||
def _validar_directorios(path_bk, target):
|
||||
path = Path(_join(path_bk, target))
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
return str(path)
|
||||
|
||||
|
||||
def local_copy(files):
|
||||
if not MV:
|
||||
return
|
||||
|
||||
path_bk = _join(str(Path.home()), DIR_FACTURAS)
|
||||
if not os.path.isdir(path_bk):
|
||||
msg = 'No existe la carpeta: facturas'
|
||||
log.error(msg)
|
||||
return
|
||||
|
||||
args = 'df -P {} | tail -1 | cut -d" " -f 1'.format(path_bk)
|
||||
try:
|
||||
result = _call(args)
|
||||
log.info(result)
|
||||
except:
|
||||
pass
|
||||
# ~ if result != 'empresalibre\n':
|
||||
# ~ log.info(result)
|
||||
# ~ msg = 'Asegurate de que exista la carpeta para sincronizar'
|
||||
# ~ log.error(msg)
|
||||
# ~ return
|
||||
# ~ except subprocess.CalledProcessError:
|
||||
# ~ msg = 'No se pudo obtener la ruta para sincronizar'
|
||||
# ~ log.error(msg)
|
||||
# ~ return
|
||||
|
||||
try:
|
||||
for obj, name, target in files:
|
||||
path = _validar_directorios(path_bk, target)
|
||||
path_file = _join(path, name)
|
||||
m = 'wb'
|
||||
if name.endswith('xml'):
|
||||
m = 'w'
|
||||
save_file(path_file, obj, m)
|
||||
except Exception as e:
|
||||
log.error(e)
|
||||
|
||||
return
|
||||
|
||||
|
||||
def sync_cfdi(auth, files):
|
||||
local_copy(files)
|
||||
|
||||
if DEBUG:
|
||||
return
|
||||
|
||||
if not auth['REPO']:
|
||||
return
|
||||
|
||||
seafile = SeaFileAPI(SEAFILE_SERVER['URL'], auth['USER'], auth['PASS'])
|
||||
if seafile.is_connect:
|
||||
for f in files:
|
||||
seafile.update_file(
|
||||
f, auth['REPO'], 'Facturas/{}/'.format(f[2]), auth['PASS'])
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
@ -1671,7 +1724,7 @@ class ImportFacturaLibre(object):
|
|||
|
||||
data = []
|
||||
for row in rows:
|
||||
new = {t: row[s] for s, t in fields}
|
||||
new = {t: row[s] for s, t in fields if row[s]}
|
||||
data.append(new)
|
||||
|
||||
return data
|
||||
|
|
|
@ -18,7 +18,6 @@ from controllers.main import (AppEmpresas,
|
|||
AppDocumentos, AppFiles, AppPreInvoices, AppCuentasBanco,
|
||||
AppMovimientosBanco
|
||||
)
|
||||
from settings import DEBUG
|
||||
|
||||
|
||||
db = StorageEngine()
|
||||
|
@ -51,13 +50,13 @@ api.add_route('/cuentasbanco', AppCuentasBanco(db))
|
|||
api.add_route('/movbanco', AppMovimientosBanco(db))
|
||||
|
||||
|
||||
if DEBUG:
|
||||
api.add_sink(static, '/static')
|
||||
# ~ Activa si usas waitress
|
||||
# ~ api.add_sink(static, '/static')
|
||||
|
||||
|
||||
session_options = {
|
||||
'session.type': 'file',
|
||||
'session.cookie_expires': True,
|
||||
'session.cookie_expires': 3600,
|
||||
'session.data_dir': '/tmp/cache/data',
|
||||
'session.lock_dir': '/tmp/cache/lock',
|
||||
}
|
||||
|
|
|
@ -6,3 +6,5 @@ master = true
|
|||
processes = 4
|
||||
threads = 4
|
||||
py-autoreload = 1
|
||||
thunder-lock = true
|
||||
static-map = /static=../static
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
[uwsgi]
|
||||
http = 127.0.0.1:8000
|
||||
http = :8000
|
||||
uid = user
|
||||
gid = user
|
||||
chdir = /home/USER/.opt/empresa-libre/source/app
|
||||
wsgi-file = main.py
|
||||
callable = app
|
||||
master = true
|
||||
processes = 4
|
||||
threads = 4
|
||||
|
||||
thunder-lock = true
|
||||
static-map = /static=../static
|
||||
logger = file:../../../empresa-libre-uwsgi.log
|
||||
|
|
|
@ -226,6 +226,86 @@ class Tags(BaseModel):
|
|||
order_by = ('tag',)
|
||||
|
||||
|
||||
class TipoDireccion(BaseModel):
|
||||
nombre = TextField(unique=True)
|
||||
|
||||
class Meta:
|
||||
order_by = ('nombre',)
|
||||
|
||||
def __str__(self):
|
||||
return self.nombre
|
||||
|
||||
|
||||
class TipoTitulo(BaseModel):
|
||||
nombre = TextField(unique=True)
|
||||
|
||||
class Meta:
|
||||
order_by = ('nombre',)
|
||||
|
||||
def __str__(self):
|
||||
return self.nombre
|
||||
|
||||
|
||||
class TipoTelefono(BaseModel):
|
||||
nombre = TextField(unique=True)
|
||||
|
||||
class Meta:
|
||||
order_by = ('nombre',)
|
||||
|
||||
def __str__(self):
|
||||
return self.nombre
|
||||
|
||||
|
||||
class TipoCorreo(BaseModel):
|
||||
nombre = TextField(unique=True)
|
||||
|
||||
class Meta:
|
||||
order_by = ('nombre',)
|
||||
|
||||
def __str__(self):
|
||||
return self.nombre
|
||||
|
||||
|
||||
class TipoPariente(BaseModel):
|
||||
nombre = TextField(unique=True)
|
||||
|
||||
class Meta:
|
||||
order_by = ('nombre',)
|
||||
|
||||
def __str__(self):
|
||||
return self.nombre
|
||||
|
||||
|
||||
class TipoResponsable(BaseModel):
|
||||
nombre = TextField(unique=True)
|
||||
|
||||
class Meta:
|
||||
order_by = ('nombre',)
|
||||
|
||||
def __str__(self):
|
||||
return self.nombre
|
||||
|
||||
|
||||
class TipoMovimientoAlumno(BaseModel):
|
||||
nombre = TextField(unique=True)
|
||||
|
||||
class Meta:
|
||||
order_by = ('nombre',)
|
||||
|
||||
def __str__(self):
|
||||
return self.nombre
|
||||
|
||||
|
||||
class TipoMovimientoAlmacen(BaseModel):
|
||||
nombre = TextField(unique=True)
|
||||
|
||||
class Meta:
|
||||
order_by = ('nombre',)
|
||||
|
||||
def __str__(self):
|
||||
return self.nombre
|
||||
|
||||
|
||||
class Usuarios(BaseModel):
|
||||
usuario = TextField(unique=True)
|
||||
nombre = TextField(default='')
|
||||
|
@ -1074,6 +1154,45 @@ class SATBancos(BaseModel):
|
|||
return {'ok': result}
|
||||
|
||||
|
||||
class SATNivelesEducativos(BaseModel):
|
||||
name = TextField(index=True)
|
||||
|
||||
class Meta:
|
||||
order_by = ('name',)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class NivelesEducativos(BaseModel):
|
||||
nombre = TextField()
|
||||
autorizacion = TextField(default='')
|
||||
|
||||
class Meta:
|
||||
order_by = ('nombre',)
|
||||
indexes = (
|
||||
(('nombre', 'autorizacion'), True),
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return '{} ({})'.format(self.nombre, self.autorizacion)
|
||||
|
||||
|
||||
class Grupos(BaseModel):
|
||||
nivel = ForeignKeyField(NivelesEducativos)
|
||||
grado = TextField(default='')
|
||||
nombre = TextField(default='')
|
||||
|
||||
class Meta:
|
||||
order_by = ('nivel', 'grado', 'nombre')
|
||||
indexes = (
|
||||
(('nivel', 'grado', 'nombre'), True),
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return '{} {} {}'.format(self.nivel.nombre, self.grado, self.nombre)
|
||||
|
||||
|
||||
class CuentasBanco(BaseModel):
|
||||
de_emisor = BooleanField(default=False)
|
||||
activa = BooleanField(default=True)
|
||||
|
@ -1671,6 +1790,132 @@ class Socios(BaseModel):
|
|||
return bool(q.execute())
|
||||
|
||||
|
||||
class Contactos(BaseModel):
|
||||
socio = ForeignKeyField(Socios)
|
||||
titulo = ForeignKeyField(TipoTitulo)
|
||||
foto = TextField(default='')
|
||||
nombre = TextField(index=True)
|
||||
paterno = TextField(index=True)
|
||||
materno = TextField(default='')
|
||||
fecha_nacimiento = DateField(null=True)
|
||||
notas = TextField(default='')
|
||||
|
||||
class Meta:
|
||||
order_by = ('socio', 'nombre')
|
||||
indexes = (
|
||||
(('socio', 'nombre', 'paterno', 'materno'), True),
|
||||
)
|
||||
|
||||
|
||||
class ContactoDirecciones(BaseModel):
|
||||
contacto = ForeignKeyField(Contactos)
|
||||
tipo = ForeignKeyField(TipoDireccion)
|
||||
direccion = TextField()
|
||||
|
||||
class Meta:
|
||||
order_by = ('contacto',)
|
||||
indexes = (
|
||||
(('contacto', 'tipo', 'direccion'), True),
|
||||
)
|
||||
|
||||
|
||||
class ContactoTelefonos(BaseModel):
|
||||
contacto = ForeignKeyField(Contactos)
|
||||
tipo = ForeignKeyField(TipoTelefono)
|
||||
telefono = TextField()
|
||||
|
||||
class Meta:
|
||||
order_by = ('contacto',)
|
||||
indexes = (
|
||||
(('contacto', 'tipo', 'telefono'), True),
|
||||
)
|
||||
|
||||
|
||||
class ContactoCorreos(BaseModel):
|
||||
contacto = ForeignKeyField(Contactos)
|
||||
tipo = ForeignKeyField(TipoCorreo)
|
||||
correo = TextField()
|
||||
|
||||
class Meta:
|
||||
order_by = ('contacto',)
|
||||
indexes = (
|
||||
(('contacto', 'tipo', 'correo'), True),
|
||||
)
|
||||
|
||||
|
||||
class Alumnos(BaseModel):
|
||||
rfc = TextField(null=True)
|
||||
curp = TextField(index=True, unique=True)
|
||||
foto = TextField(default='')
|
||||
nombre = TextField(index=True)
|
||||
paterno = TextField(index=True)
|
||||
materno = TextField(default='')
|
||||
calle = TextField(default='')
|
||||
no_exterior = TextField(default='')
|
||||
no_interior = TextField(default='')
|
||||
colonia = TextField(default='')
|
||||
municipio = TextField(default='')
|
||||
estado = TextField(default='')
|
||||
pais = TextField(default='')
|
||||
codigo_postal = TextField(default='')
|
||||
notas = TextField(default='')
|
||||
telefonos = TextField(default='')
|
||||
correos = TextField(default='')
|
||||
es_activo = BooleanField(default=True)
|
||||
fecha_alta = DateField(default=util.now)
|
||||
fecha_nacimiento = DateField(null=True)
|
||||
factura = ForeignKeyField(Socios, null=True)
|
||||
grupo = ForeignKeyField(Grupos, null=True)
|
||||
|
||||
def __str__(self):
|
||||
t = '{} {} {}'
|
||||
return t.format(self.nombre, self.paterno, self.materno)
|
||||
|
||||
class Meta:
|
||||
order_by = ('nombre', 'paterno')
|
||||
|
||||
|
||||
class AlumnosParientes(BaseModel):
|
||||
alumno = ForeignKeyField(Alumnos)
|
||||
tipo_pariente = ForeignKeyField(TipoPariente)
|
||||
foto = TextField(default='')
|
||||
nombre = TextField(index=True)
|
||||
paterno = TextField(index=True)
|
||||
materno = TextField(default='')
|
||||
fecha_nacimiento = DateField(null=True)
|
||||
puede_recoger = BooleanField(default=False)
|
||||
|
||||
class Meta:
|
||||
order_by = ('alumno',)
|
||||
|
||||
|
||||
class ParienteDirecciones(BaseModel):
|
||||
pariente = ForeignKeyField(AlumnosParientes)
|
||||
tipo = ForeignKeyField(TipoDireccion)
|
||||
direccion = TextField()
|
||||
|
||||
class Meta:
|
||||
order_by = ('pariente',)
|
||||
|
||||
|
||||
class ParienteTelefonos(BaseModel):
|
||||
pariente = ForeignKeyField(AlumnosParientes)
|
||||
tipo = ForeignKeyField(TipoTelefono)
|
||||
telefono = TextField()
|
||||
|
||||
class Meta:
|
||||
order_by = ('pariente',)
|
||||
|
||||
|
||||
class ParienteCorreos(BaseModel):
|
||||
pariente = ForeignKeyField(AlumnosParientes)
|
||||
tipo = ForeignKeyField(TipoCorreo)
|
||||
correo = TextField()
|
||||
|
||||
class Meta:
|
||||
order_by = ('pariente',)
|
||||
|
||||
|
||||
class Almacenes(BaseModel):
|
||||
nombre = TextField(default='')
|
||||
ubicacion = TextField(default='')
|
||||
|
@ -1882,6 +2127,21 @@ class Productos(BaseModel):
|
|||
return bool(q.execute())
|
||||
|
||||
|
||||
class RangosPrecios(BaseModel):
|
||||
producto = ForeignKeyField(Productos)
|
||||
descripcion = TextField(default='')
|
||||
desde = DecimalField(default=0.0, max_digits=18, decimal_places=6,
|
||||
auto_round=True)
|
||||
hasta = 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 = IntegerField(default=0)
|
||||
|
||||
class Meta:
|
||||
order_by = ('producto',)
|
||||
|
||||
|
||||
class Facturas(BaseModel):
|
||||
cliente = ForeignKeyField(Socios)
|
||||
version = TextField(default=CURRENT_CFDI)
|
||||
|
@ -1967,6 +2227,7 @@ class Facturas(BaseModel):
|
|||
def get_xml(cls, id):
|
||||
obj = Facturas.get(Facturas.id==id)
|
||||
name = '{}{}_{}.xml'.format(obj.serie, obj.folio, obj.cliente.rfc)
|
||||
cls._sync_xml(cls, obj)
|
||||
return obj.xml, name
|
||||
|
||||
#~ Revisar
|
||||
|
@ -2056,8 +2317,12 @@ class Facturas(BaseModel):
|
|||
return values
|
||||
|
||||
@classmethod
|
||||
def get_pdf(cls, id, rfc):
|
||||
def get_pdf(cls, id, rfc, sync=True):
|
||||
try:
|
||||
emisor = Emisor.select()[0]
|
||||
except IndexError:
|
||||
return b'', 'sin_datos_de_emisor.pdf'
|
||||
|
||||
obj = Facturas.get(Facturas.id==id)
|
||||
name = '{}{}_{}.pdf'.format(obj.serie, obj.folio, obj.cliente.rfc)
|
||||
if obj.uuid is None:
|
||||
|
@ -2066,6 +2331,11 @@ class Facturas(BaseModel):
|
|||
values = cls._get_not_in_xml(cls, obj, emisor)
|
||||
data = util.get_data_from_xml(obj, values)
|
||||
doc = util.to_pdf(data, emisor.rfc)
|
||||
|
||||
if sync:
|
||||
target = emisor.rfc + '/' + str(obj.fecha)[:7].replace('-', '/')
|
||||
cls._sync_pdf(cls, doc, name, target)
|
||||
|
||||
return doc, name
|
||||
|
||||
@classmethod
|
||||
|
@ -2095,6 +2365,27 @@ class Facturas(BaseModel):
|
|||
def _sync(self, id, auth):
|
||||
return Facturas.sync(id, auth)
|
||||
|
||||
@util.run_in_thread
|
||||
def _sync_pdf(self, pdf, name_pdf, target):
|
||||
auth = Emisor.get_auth()
|
||||
files = (
|
||||
(pdf, name_pdf, target),
|
||||
)
|
||||
util.sync_cfdi(auth, files)
|
||||
return
|
||||
|
||||
@util.run_in_thread
|
||||
def _sync_xml(self, obj):
|
||||
emisor = Emisor.select()[0]
|
||||
auth = Emisor.get_auth()
|
||||
name_xml = '{}{}_{}.xml'.format(obj.serie, obj.folio, obj.cliente.rfc)
|
||||
target = emisor.rfc + '/' + str(obj.fecha)[:7].replace('-', '/')
|
||||
files = (
|
||||
(obj.xml, name_xml, target),
|
||||
)
|
||||
util.sync_cfdi(auth, files)
|
||||
return
|
||||
|
||||
@util.run_in_thread
|
||||
def _actualizar_saldo_cliente(self, invoice):
|
||||
if invoice.tipo_comprobante == 'T':
|
||||
|
@ -2159,17 +2450,15 @@ class Facturas(BaseModel):
|
|||
|
||||
@classmethod
|
||||
def sync(cls, id, auth):
|
||||
if not auth['REPO']:
|
||||
return
|
||||
|
||||
obj = Facturas.get(Facturas.id==id)
|
||||
if obj.uuid is None:
|
||||
msg = 'La factura no esta timbrada'
|
||||
return
|
||||
|
||||
pdf, name_pdf = cls.get_pdf(id, auth['RFC'])
|
||||
emisor = Emisor.select()[0]
|
||||
pdf, name_pdf = cls.get_pdf(id, auth['RFC'], False)
|
||||
name_xml = '{}{}_{}.xml'.format(obj.serie, obj.folio, obj.cliente.rfc)
|
||||
target = str(obj.fecha)[:7].replace('-', '/')
|
||||
target = emisor.rfc + '/' + str(obj.fecha)[:7].replace('-', '/')
|
||||
files = (
|
||||
(obj.xml, name_xml, target),
|
||||
(pdf, name_pdf, target),
|
||||
|
@ -2343,7 +2632,7 @@ class Facturas(BaseModel):
|
|||
totals_tax = {}
|
||||
total_trasladados = None
|
||||
total_retenciones = None
|
||||
total_iva = 0
|
||||
# ~ total_iva = 0
|
||||
locales_traslados = 0
|
||||
locales_retenciones = 0
|
||||
|
||||
|
@ -2376,52 +2665,66 @@ class Facturas(BaseModel):
|
|||
|
||||
FacturasDetalle.create(**product)
|
||||
|
||||
base = product['importe'] - product['descuento']
|
||||
for tax in p.impuestos:
|
||||
impuesto_producto = round(float(tax.tasa) * base, DECIMALES)
|
||||
if tax.tipo == 'T' and tax.key != '000':
|
||||
total_trasladados = (total_trasladados or 0) + impuesto_producto
|
||||
elif tax.tipo == 'R' and tax.key != '000':
|
||||
total_retenciones = (total_retenciones or 0) + impuesto_producto
|
||||
elif tax.tipo == 'T' and tax.key == '000':
|
||||
locales_traslados += impuesto_producto
|
||||
elif tax.tipo == 'R' and tax.key == '000':
|
||||
locales_retenciones += impuesto_producto
|
||||
|
||||
if tax.id in totals_tax:
|
||||
totals_tax[tax.id].importe += importe
|
||||
totals_tax[tax.id].base += base
|
||||
totals_tax[tax.id].suma_impuestos += impuesto_producto
|
||||
else:
|
||||
tax.importe = importe
|
||||
tax.base = base
|
||||
tax.suma_impuestos = impuesto_producto
|
||||
totals_tax[tax.id] = tax
|
||||
|
||||
for tax in totals_tax.values():
|
||||
if tax.tipo == 'E' or tax.tipo == 'R':
|
||||
# ~ if tax.tipo == 'E' or tax.tipo == 'R':
|
||||
if tax.tipo == 'E':
|
||||
continue
|
||||
|
||||
import_tax = round(float(tax.tasa) * tax.importe, DECIMALES)
|
||||
if tax.key == '000':
|
||||
locales_traslados += import_tax
|
||||
else:
|
||||
total_trasladados = (total_trasladados or 0) + import_tax
|
||||
if tax.name == 'IVA':
|
||||
total_iva += import_tax
|
||||
# ~ import_tax = round(float(tax.tasa) * tax.importe, DECIMALES)
|
||||
# ~ if tax.key == '000':
|
||||
# ~ locales_traslados += import_tax
|
||||
# ~ else:
|
||||
# ~ total_trasladados = (total_trasladados or 0) + import_tax
|
||||
# ~ if tax.name == 'IVA':
|
||||
# ~ total_iva += import_tax
|
||||
|
||||
invoice_tax = {
|
||||
'factura': invoice.id,
|
||||
'impuesto': tax.id,
|
||||
'base': tax.importe,
|
||||
'importe': import_tax,
|
||||
'base': tax.base,
|
||||
'importe': tax.suma_impuestos,
|
||||
}
|
||||
FacturasImpuestos.create(**invoice_tax)
|
||||
|
||||
for tax in totals_tax.values():
|
||||
if tax.tipo == 'E' or tax.tipo == 'T':
|
||||
continue
|
||||
if tax.tasa == round(Decimal(2/3), 6):
|
||||
import_tax = round(float(tax.tasa) * total_iva, DECIMALES)
|
||||
else:
|
||||
import_tax = round(float(tax.tasa) * tax.importe, DECIMALES)
|
||||
if tax.key == '000':
|
||||
locales_retenciones += import_tax
|
||||
else:
|
||||
total_retenciones = (total_retenciones or 0) + import_tax
|
||||
# ~ for tax in totals_tax.values():
|
||||
# ~ if tax.tipo == 'E' or tax.tipo == 'T':
|
||||
# ~ continue
|
||||
# ~ if tax.tasa == round(Decimal(2/3), 6):
|
||||
# ~ import_tax = round(float(tax.tasa) * total_iva, DECIMALES)
|
||||
# ~ else:
|
||||
# ~ import_tax = round(float(tax.tasa) * tax.importe, DECIMALES)
|
||||
# ~ if tax.key == '000':
|
||||
# ~ locales_retenciones += import_tax
|
||||
# ~ else:
|
||||
# ~ total_retenciones = (total_retenciones or 0) + import_tax
|
||||
|
||||
invoice_tax = {
|
||||
'factura': invoice.id,
|
||||
'impuesto': tax.id,
|
||||
'base': tax.importe,
|
||||
'importe': import_tax,
|
||||
}
|
||||
FacturasImpuestos.create(**invoice_tax)
|
||||
# ~ invoice_tax = {
|
||||
# ~ 'factura': invoice.id,
|
||||
# ~ 'impuesto': tax.id,
|
||||
# ~ 'base': tax.base,
|
||||
# ~ 'importe': tax.suma_impuestos,
|
||||
# ~ }
|
||||
# ~ FacturasImpuestos.create(**invoice_tax)
|
||||
|
||||
total = subtotal - descuento_cfdi + \
|
||||
(total_trasladados or 0) - (total_retenciones or 0) \
|
||||
|
@ -3169,24 +3472,6 @@ class FacturasComplementos(BaseModel):
|
|||
return {r.nombre: util.loads(r.valores) for r in query}
|
||||
|
||||
|
||||
# ~ class CfdiPagosFacturas(BaseModel):
|
||||
# ~ pago = ForeignKeyField(CfdiPagos)
|
||||
# ~ factura = ForeignKeyField(Facturas)
|
||||
# ~ numero = IntegerField(default=1)
|
||||
# ~ saldo_anterior = 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)
|
||||
# ~ saldo = DecimalField(default=0.0, max_digits=18, decimal_places=6,
|
||||
# ~ auto_round=True)
|
||||
|
||||
# ~ class Meta:
|
||||
# ~ order_by = ('pago',)
|
||||
# ~ indexes = (
|
||||
# ~ (('pago', 'factura', 'numero'), True),
|
||||
# ~ )
|
||||
|
||||
|
||||
class PreFacturasRelacionadas(BaseModel):
|
||||
factura = ForeignKeyField(PreFacturas, related_name='original')
|
||||
factura_origen = ForeignKeyField(PreFacturas, related_name='relacion')
|
||||
|
@ -3468,6 +3753,94 @@ class PreFacturasImpuestos(BaseModel):
|
|||
return data
|
||||
|
||||
|
||||
class CamposPersonalizados(BaseModel):
|
||||
nombre = TextField()
|
||||
slug = TextField(unique=True)
|
||||
|
||||
class Meta:
|
||||
order_by = ('nombre',)
|
||||
|
||||
|
||||
class FacturasPersonalizados(BaseModel):
|
||||
factura = ForeignKeyField(Facturas)
|
||||
campo = TextField()
|
||||
valor = TextField()
|
||||
|
||||
class Meta:
|
||||
order_by = ('factura',)
|
||||
|
||||
|
||||
class Tickets(BaseModel):
|
||||
cliente = ForeignKeyField(Socios, null=True)
|
||||
serie = TextField(default='')
|
||||
folio = IntegerField(default=0)
|
||||
fecha = DateTimeField(default=util.now, formats=['%Y-%m-%d %H:%M:%S'])
|
||||
forma_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)
|
||||
total = DecimalField(default=0.0, max_digits=20, decimal_places=6,
|
||||
auto_round=True)
|
||||
total_trasladados = DecimalField(
|
||||
max_digits=20, decimal_places=6, auto_round=True, null=True)
|
||||
estatus = TextField(default='Generado')
|
||||
notas = TextField(default='')
|
||||
factura = ForeignKeyField(Facturas, null=True)
|
||||
cancelado = BooleanField(default=False)
|
||||
vendedor = TextField(default='')
|
||||
comision = DecimalField(default=0.0, max_digits=20, decimal_places=6,
|
||||
auto_round=True)
|
||||
cambio = DecimalField(default=0.0, max_digits=20, decimal_places=6,
|
||||
auto_round=True)
|
||||
|
||||
class Meta:
|
||||
order_by = ('fecha',)
|
||||
|
||||
|
||||
class TicketsDetalle(BaseModel):
|
||||
ticket = ForeignKeyField(Tickets)
|
||||
producto = ForeignKeyField(Productos, null=True)
|
||||
descripcion = TextField(default='')
|
||||
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)
|
||||
|
||||
class Meta:
|
||||
order_by = ('ticket',)
|
||||
|
||||
|
||||
class TicketsImpuestos(BaseModel):
|
||||
ticket = ForeignKeyField(Tickets)
|
||||
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:
|
||||
order_by = ('ticket',)
|
||||
indexes = (
|
||||
(('ticket', 'impuesto'), True),
|
||||
)
|
||||
|
||||
|
||||
class SeriesProductos(BaseModel):
|
||||
factura = ForeignKeyField(FacturasDetalle, null=True)
|
||||
ticket = ForeignKeyField(TicketsDetalle, null=True)
|
||||
serie = TextField(default='')
|
||||
|
||||
class Meta:
|
||||
order_by = ('serie',)
|
||||
|
||||
|
||||
def authenticate(args):
|
||||
respuesta = {'login': False, 'msg': 'No Autorizado', 'user': ''}
|
||||
values = util.get_con(args['rfc'])
|
||||
|
@ -3583,15 +3956,22 @@ def _init_values(rfc):
|
|||
|
||||
def _crear_tablas(rfc):
|
||||
tablas = [Addendas, Categorias, Certificado, CondicionesPago, Configuracion,
|
||||
Folios, Registro,
|
||||
Folios, Registro, CamposPersonalizados,
|
||||
Emisor, Facturas, FacturasDetalle, FacturasImpuestos, FacturasPagos,
|
||||
FacturasRelacionadas, FacturasComplementos, Almacenes, Productos,
|
||||
FacturasRelacionadas, FacturasComplementos, FacturasPersonalizados,
|
||||
SeriesProductos, Almacenes, Productos, RangosPrecios,
|
||||
PreFacturas, PreFacturasDetalle, PreFacturasImpuestos,
|
||||
PreFacturasRelacionadas,
|
||||
PreFacturasRelacionadas, Tickets, TicketsDetalle, TicketsImpuestos,
|
||||
SATAduanas, SATFormaPago, SATImpuestos, SATMonedas, SATRegimenes,
|
||||
SATTipoRelacion, SATUnidades, SATUsoCfdi, SATBancos,
|
||||
Socios, Tags, Usuarios, CuentasBanco, TipoCambio, MovimientosBanco,
|
||||
CfdiPagos,
|
||||
SATNivelesEducativos,
|
||||
Socios, Contactos, ContactoCorreos, ContactoDirecciones,
|
||||
ContactoTelefonos,
|
||||
Tags, Usuarios, CuentasBanco, TipoCambio, MovimientosBanco,
|
||||
TipoCorreo, TipoDireccion, TipoPariente, TipoResponsable, TipoTelefono,
|
||||
TipoTitulo, TipoMovimientoAlumno, TipoMovimientoAlmacen,
|
||||
CfdiPagos, NivelesEducativos, Alumnos, AlumnosParientes, Grupos,
|
||||
ParienteDirecciones, ParienteTelefonos, ParienteCorreos,
|
||||
Emisor.regimenes.get_through_model(),
|
||||
Socios.tags.get_through_model(),
|
||||
Productos.impuestos.get_through_model(),
|
||||
|
|
|
@ -21,7 +21,7 @@ except ImportError:
|
|||
|
||||
|
||||
DEBUG = DEBUG
|
||||
VERSION = '0.2.1'
|
||||
VERSION = '1.2.0'
|
||||
EMAIL_SUPPORT = ('soporte@empresalibre.net',)
|
||||
|
||||
BASE_DIR = os.path.abspath(os.path.dirname(__file__))
|
||||
|
@ -115,3 +115,4 @@ IMPUESTOS = {
|
|||
'CEDULAR': '000',
|
||||
}
|
||||
DEFAULT_SAT_PRODUCTO = '01010101'
|
||||
DIR_FACTURAS = 'facturas'
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -27,6 +27,16 @@
|
|||
{"key": "ACT", "name": "Actividad", "activo": false}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tabla": "SATNivelesEducativos",
|
||||
"datos": [
|
||||
{"name": "Preescolar"},
|
||||
{"name": "Primaria"},
|
||||
{"name": "Secundaria"},
|
||||
{"name": "Profesional técnico"},
|
||||
{"name": "Bachillerato o su equivalente"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tabla": "SATTipoRelacion",
|
||||
"datos": [
|
||||
|
|
|
@ -82,6 +82,22 @@ function get_regimen_fiscal(){
|
|||
}
|
||||
|
||||
|
||||
function validar_timbrar(){
|
||||
webix.ajax().sync().get('/values/validartimbrar', function(text, data){
|
||||
var values = data.json()
|
||||
if(!values.ok){
|
||||
msg_error(values.msg)
|
||||
$$('cmd_timbrar').disable()
|
||||
}else{
|
||||
if(values.msg){
|
||||
msg_error(values.msg)
|
||||
}
|
||||
$$('cmd_timbrar').enable()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function default_config(){
|
||||
webix.ajax().sync().get('/values/taxes', function(text, data){
|
||||
var values = data.json()
|
||||
|
@ -96,18 +112,7 @@ function default_config(){
|
|||
table_pt.clear()
|
||||
table_totals.clear()
|
||||
|
||||
webix.ajax().sync().get('/values/validartimbrar', function(text, data){
|
||||
var values = data.json()
|
||||
if(!values.ok){
|
||||
msg_error(values.msg)
|
||||
$$('cmd_timbrar').disable()
|
||||
}else{
|
||||
if(values.msg){
|
||||
msg_error(values.msg)
|
||||
}
|
||||
$$('cmd_timbrar').enable()
|
||||
}
|
||||
})
|
||||
validar_timbrar()
|
||||
|
||||
webix.ajax().sync().get('/values/configtimbrar', function(text, data){
|
||||
var values = data.json()
|
||||
|
@ -702,6 +707,69 @@ function calculate_taxes(){
|
|||
}
|
||||
|
||||
|
||||
function calcular_impuestos(){
|
||||
var tmp = null
|
||||
var subtotal = 0
|
||||
var id = 2
|
||||
var grid_totals = $$('grid_totals')
|
||||
var impuesto_producto = 0
|
||||
var impuesto = null
|
||||
|
||||
table_totals.clear()
|
||||
grid_totals.clearAll()
|
||||
grid_totals.add({id: 1, concepto: 'SubTotal', importe: 0})
|
||||
|
||||
grid.eachRow(function(row){
|
||||
var product = grid.getItem(row)
|
||||
var importe = parseFloat(product.importe)
|
||||
subtotal += importe
|
||||
query = table_pt.chain().find({'product': product.id}).data()
|
||||
for(var tax of query){
|
||||
impuesto = table_taxes.findOne({'id': tax.tax})
|
||||
if(impuesto.tipo == 'E'){
|
||||
continue
|
||||
}
|
||||
|
||||
var base = importe
|
||||
if(impuesto.tipo == 'R'){
|
||||
base = (importe * -1).round(DECIMALES)
|
||||
}
|
||||
impuesto_producto = (impuesto.tasa * base).round(DECIMALES)
|
||||
|
||||
tmp = table_totals.findOne({'tax': tax.tax})
|
||||
if(tmp === null){
|
||||
table_totals.insert({'tax': tax.tax, 'importe': impuesto_producto})
|
||||
}else{
|
||||
tmp.importe += impuesto_producto
|
||||
table_totals.update(tmp)
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
var tipo = ''
|
||||
var concepto = ''
|
||||
|
||||
query = table_totals.chain().data()
|
||||
for(var t of query){
|
||||
tax = table_taxes.findOne({'id': t.tax})
|
||||
if(tax.tipo == 'E'){
|
||||
continue
|
||||
}
|
||||
tipo = 'Traslado '
|
||||
if(tax.tipo == 'R'){
|
||||
tipo = 'Retención '
|
||||
}
|
||||
concepto = tipo + tax.name + ' (' + tax.tasa + ')'
|
||||
grid_totals.add({id: id, concepto: concepto, importe: t.importe})
|
||||
id += 1
|
||||
}
|
||||
|
||||
var row = {importe: subtotal}
|
||||
grid_totals.updateItem(1, row)
|
||||
}
|
||||
|
||||
|
||||
function set_product(values){
|
||||
var taxes = values.taxes
|
||||
var values = values.row
|
||||
|
@ -729,7 +797,8 @@ function set_product(values){
|
|||
table_pt.insert(v)
|
||||
}
|
||||
}
|
||||
calculate_taxes()
|
||||
//~ calculate_taxes()
|
||||
calcular_impuestos()
|
||||
}
|
||||
|
||||
|
||||
|
@ -846,7 +915,8 @@ function grid_details_before_edit_stop(state, editor){
|
|||
row['importe'] = (cantidad * precio_final).round(DECIMALES)
|
||||
|
||||
grid.refresh()
|
||||
calculate_taxes()
|
||||
//~ calculate_taxes()
|
||||
calcular_impuestos()
|
||||
}
|
||||
|
||||
|
||||
|
@ -855,7 +925,8 @@ function grid_details_click(id, e, node){
|
|||
return
|
||||
}
|
||||
grid.remove(id.row)
|
||||
calculate_taxes()
|
||||
//~ calculate_taxes()
|
||||
calcular_impuestos()
|
||||
}
|
||||
|
||||
|
||||
|
@ -874,7 +945,8 @@ function grid_details_header_click(id){
|
|||
callback:function(result){
|
||||
if (result){
|
||||
grid.clearAll()
|
||||
calculate_taxes()
|
||||
//~ calculate_taxes()
|
||||
calcular_impuestos()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -1299,7 +1371,8 @@ function refacturar_preinvoice(id){
|
|||
for(var p of values.rows){
|
||||
agregar_preproducto(p)
|
||||
}
|
||||
calculate_taxes()
|
||||
//~ calculate_taxes()
|
||||
calcular_impuestos()
|
||||
$$('tv_invoice').getTabbar().setValue('Generar')
|
||||
}
|
||||
})
|
||||
|
|
|
@ -200,6 +200,7 @@ function multi_change(prevID, nextID){
|
|||
if(active == 'invoices_home'){
|
||||
current_dates()
|
||||
get_invoices()
|
||||
validar_timbrar()
|
||||
}
|
||||
gi = $$('grid_invoices')
|
||||
return
|
||||
|
|
|
@ -10,7 +10,7 @@ var grid_cfdi_cliente_cols = [
|
|||
css: 'right'},
|
||||
{id: 'uuid', header: ['UUID', {content: 'textFilter'}], width: 250,
|
||||
sort: 'string'},
|
||||
{id: "fecha", header: ["Fecha y Hora"], width: 150, sort: 'date'},
|
||||
{id: "fecha", header: ["Fecha y Hora"], width: 150, sort: 'string'},
|
||||
{id: "tipo_comprobante", header: ["Tipo", {content: "selectFilter"}],
|
||||
adjust: 'header', sort: 'string'},
|
||||
{id: "estatus", header: ["Estatus", {content: "selectFilter"}],
|
||||
|
@ -202,7 +202,7 @@ var grid_invoices_cols = [
|
|||
{id: "uuid", header: ["UUID", {content: "textFilter"}], adjust: "data",
|
||||
sort:"string", hidden:true},
|
||||
{id: "fecha", header: ["Fecha y Hora"],
|
||||
adjust: "data", sort: "date"},
|
||||
adjust: "data", sort: "string"},
|
||||
{id: "tipo_comprobante", header: ["Tipo", {content: "selectFilter"}],
|
||||
adjust: 'header', sort: 'string'},
|
||||
{id: "estatus", header: ["Estatus", {content: "selectFilter"}],
|
||||
|
|
Loading…
Reference in New Issue