diff --git a/source/app/controllers/main.py b/source/app/controllers/main.py
index 721a503..bc7a47a 100644
--- a/source/app/controllers/main.py
+++ b/source/app/controllers/main.py
@@ -238,6 +238,23 @@ class AppEmisor(object):
resp.status = falcon.HTTP_204
+class AppCuentasBanco(object):
+
+ def __init__(self, db):
+ self._db = db
+
+ def on_get(self, req, resp):
+ values = req.params
+ session = req.env['beaker.session']
+ #~ req.context['result'] = self._db.get_emisor(session['rfc'])
+ resp.status = falcon.HTTP_200
+
+ def on_post(self, req, resp):
+ values = req.params
+ req.context['result'] = self._db.cuentasbanco(values)
+ resp.status = falcon.HTTP_200
+
+
class AppFolios(object):
def __init__(self, db):
diff --git a/source/app/controllers/util.py b/source/app/controllers/util.py
index a73090d..a82ca4a 100644
--- a/source/app/controllers/util.py
+++ b/source/app/controllers/util.py
@@ -729,7 +729,9 @@ class LIBO(object):
#~ Si no se encuentra, copia las celdas hacia abajo de
#~ {subtotal.titulo} y {subtotal}
+ print (data['descuento'])
if 'descuento' in data:
+
self._copy_cell(cell_title)
self._copy_cell(cell_value)
cell_title = self._set_cell(v='Descuento', cell=cell_title)
diff --git a/source/app/main.py b/source/app/main.py
index e6a6710..ed80b25 100644
--- a/source/app/main.py
+++ b/source/app/main.py
@@ -15,7 +15,7 @@ from models.db import StorageEngine
from controllers.main import (
AppLogin, AppLogout, AppAdmin, AppEmisor, AppConfig,
AppMain, AppValues, AppPartners, AppProducts, AppInvoices, AppFolios,
- AppDocumentos, AppFiles, AppPreInvoices
+ AppDocumentos, AppFiles, AppPreInvoices, AppCuentasBanco
)
from settings import DEBUG
@@ -45,6 +45,7 @@ api.add_route('/partners', AppPartners(db))
api.add_route('/products', AppProducts(db))
api.add_route('/invoices', AppInvoices(db))
api.add_route('/preinvoices', AppPreInvoices(db))
+api.add_route('/cuentasbanco', AppCuentasBanco(db))
if DEBUG:
diff --git a/source/app/models/db.py b/source/app/models/db.py
index b7db743..d3fab7e 100644
--- a/source/app/models/db.py
+++ b/source/app/models/db.py
@@ -91,12 +91,27 @@ class StorageEngine(object):
def _get_allcurrencies(self, values):
return main.SATMonedas.get_()
+ def _get_allbancos(self, values):
+ return main.SATBancos.get_()
+
+ def _get_allunidades(self, values):
+ return main.SATUnidades.get_()
+
def _get_taxupdate(self, values):
return main.SATImpuestos.actualizar(values)
def _get_currencyupdate(self, values):
return main.SATMonedas.actualizar(values)
+ def _get_bancoupdate(self, values):
+ return main.SATBancos.actualizar(values)
+
+ def _get_unidadupdate(self, values):
+ return main.SATUnidades.actualizar(values)
+
+ def _get_emisorcuentasbanco(self, values):
+ return main.CuentasBanco.emisor()
+
def _get_satkey(self, values):
return main.get_sat_key(values['key'])
@@ -106,11 +121,17 @@ class StorageEngine(object):
def _get_monedas(self, values):
return main.SATMonedas.get_activos()
+ def _get_monedasid(self, values):
+ return main.SATMonedas.get_activos_by_id()
+
+ def _get_bancosid(self, values):
+ return main.SATBancos.get_activos_by_id()
+
def _get_regimenes(self, values):
return main.Emisor.get_regimenes()
def _get_usocfdi(self, values):
- return main.SATUsoCfdi.get_activos(values)
+ return main.SATUsoCfdi.get_activos()
def delete(self, table, id):
if table == 'partner':
@@ -170,12 +191,18 @@ class StorageEngine(object):
def _get_timbrar(self, values):
return main.Facturas.timbrar(int(values['id']))
+ def _get_anticipoegreso(self, values):
+ return main.Facturas.anticipo_egreso(int(values['id']))
+
def get_emisor(self, rfc):
return main.Emisor.get_(rfc)
def emisor(self, values):
return main.Emisor.add(values)
+ def cuentasbanco(self, values):
+ return main.CuentasBanco.add(values)
+
def get_folios(self):
return main.Folios.get_()
diff --git a/source/app/models/main.py b/source/app/models/main.py
index 2a353a1..db6c618 100644
--- a/source/app/models/main.py
+++ b/source/app/models/main.py
@@ -15,7 +15,7 @@ if __name__ == '__main__':
from controllers import util
from settings import log, VERSION, PATH_CP, COMPANIES, PRE, CURRENT_CFDI, \
- INIT_VALUES
+ INIT_VALUES, DEFAULT_PASSWORD
FORMAT = '{0:.2f}'
@@ -574,6 +574,32 @@ class SATUnidades(BaseModel):
(('key', 'name'), True),
)
+ @classmethod
+ def get_(self):
+ rows = SATUnidades.select().dicts()
+ return tuple(rows)
+
+ @classmethod
+ def actualizar(self, values):
+ id = int(values['id'])
+ if values['field'] == 'activo':
+ v = {'0': False, '1': True}
+ q = (SATUnidades
+ .update(**{'activo': v[values['value']]})
+ .where(SATUnidades.id==id))
+ result = bool(q.execute())
+ elif values['field'] == 'default':
+ q = SATUnidades.update(**{'default': False})
+ q.execute()
+
+ v = {'false': False, 'true': True}
+ q = (SATUnidades
+ .update(**{'default': v[values['value']]})
+ .where(SATUnidades.id==id))
+ result = bool(q.execute())
+
+ return {'ok': result}
+
@classmethod
def get_activos(cls):
rows = (SATUnidades
@@ -658,6 +684,17 @@ class SATMonedas(BaseModel):
)
return tuple(rows)
+ @classmethod
+ def get_activos_by_id(cls):
+ rows = (SATMonedas
+ .select(
+ SATMonedas.id,
+ SATMonedas.name.alias('value'))
+ .where(SATMonedas.activo==True)
+ .dicts()
+ )
+ return tuple(rows)
+
@classmethod
def actualizar(self, values):
id = int(values['id'])
@@ -756,6 +793,127 @@ class SATTipoRelacion(BaseModel):
+class SATBancos(BaseModel):
+ key = TextField(index=True, unique=True)
+ name = TextField(index=True)
+ razon_social = TextField(default='')
+ rfc = TextField(default='')
+ activo = BooleanField(default=False)
+
+ class Meta:
+ order_by = ('-activo', 'name')
+ indexes = (
+ (('key', 'name'), True),
+ )
+
+ def __str__(self):
+ return 'Banco: {} ({})'.format(self.name, self.key)
+
+ @classmethod
+ def get_(cls):
+ rows = SATBancos.select().dicts()
+ return tuple(rows)
+
+ @classmethod
+ def get_activos_by_id(cls):
+ rows = (SATBancos
+ .select(
+ SATBancos.id,
+ SATBancos.name.alias('value'))
+ .where(SATBancos.activo==True)
+ .dicts()
+ )
+ return tuple(rows)
+
+ @classmethod
+ def actualizar(cls, values):
+ id = int(values['id'])
+ if values['field'] == 'activo':
+ v = {'0': False, '1': True}
+ q = (SATBancos
+ .update(**{'activo': v[values['value']]})
+ .where(SATBancos.id==id))
+ result = bool(q.execute())
+ return {'ok': result}
+
+
+class CuentasBanco(BaseModel):
+ de_emisor = BooleanField(default=False)
+ activa = BooleanField(default=True)
+ nombre = TextField()
+ banco = ForeignKeyField(SATBancos)
+ fecha_apertura = DateField(null=True)
+ cuenta = TextField(default='')
+ clabe = TextField(default='')
+ moneda = ForeignKeyField(SATMonedas)
+ saldo_inicial = DecimalField(default=0.0, max_digits=20, decimal_places=6,
+ auto_round=True)
+ saldo = DecimalField(default=0.0, max_digits=20, decimal_places=6,
+ auto_round=True)
+
+ class Meta:
+ order_by = ('nombre',)
+ indexes = (
+ (('banco', 'cuenta'), True),
+ )
+
+ def __str__(self):
+ return '{} ({})'.format(self.banco.name, self.cuenta[-4:])
+
+ @classmethod
+ def emisor(cls):
+ rows = (CuentasBanco
+ .select(
+ CuentasBanco.id,
+ CuentasBanco.activa,
+ CuentasBanco.nombre,
+ SATBancos.name.alias('banco'),
+ CuentasBanco.fecha_apertura,
+ CuentasBanco.cuenta,
+ CuentasBanco.clabe,
+ SATMonedas.name.alias('moneda'),
+ CuentasBanco.saldo
+ )
+ .join(SATBancos).switch(CuentasBanco)
+ .join(SATMonedas).switch(CuentasBanco)
+ .where(CuentasBanco.de_emisor==True).dicts()
+ )
+ return tuple(rows)
+
+ @classmethod
+ def add(cls, values):
+ w = '37137137137137137'
+ dv = str(
+ 10 -
+ sum([(int(v) * int(values['clabe'][i])) % 10 for i, v in enumerate(w)])
+ % 10)
+ if dv != values['clabe'][-1]:
+ msg = 'Digito de control de la CLABE es incorrecto'
+ return {'ok': False, 'msg': msg}
+
+ with database_proxy.transaction():
+ obj = CuentasBanco.create(**values)
+
+ rows = (CuentasBanco
+ .select(
+ CuentasBanco.id,
+ CuentasBanco.activa,
+ CuentasBanco.nombre,
+ SATBancos.name.alias('banco'),
+ CuentasBanco.fecha_apertura,
+ CuentasBanco.cuenta,
+ CuentasBanco.clabe,
+ SATMonedas.name.alias('moneda'),
+ CuentasBanco.saldo
+ )
+ .join(SATBancos).switch(CuentasBanco)
+ .join(SATMonedas).switch(CuentasBanco)
+ .where(CuentasBanco.id==obj.id).dicts()
+ )
+ data = {'ok': True, 'row': rows[0]}
+ return data
+
+
class SATUsoCfdi(BaseModel):
key = TextField(index=True, unique=True)
name = TextField(default='', index=True)
@@ -774,13 +932,16 @@ class SATUsoCfdi(BaseModel):
return 'Uso del CFDI: {} ({})'.format(self.name, self.key)
@classmethod
- def get_activos(cls, values):
- field = SATUsoCfdi.id
- if values:
- field = SATUsoCfdi.key.alias('id')
+ def get_id(self, key):
+ if key is None:
+ return
+ return SATUsoCfdi.get(SATUsoCfdi.key==key).id
+
+ @classmethod
+ def get_activos(cls):
rows = (SATUsoCfdi
.select(
- field,
+ SATUsoCfdi.key.alias('id'),
SATUsoCfdi.name.alias('value'),
SATUsoCfdi.fisica,
SATUsoCfdi.moral,
@@ -791,6 +952,15 @@ class SATUsoCfdi(BaseModel):
return tuple(rows)
+class TipoCambio(BaseModel):
+ dia = DateField(default=util.now)
+ moneda = ForeignKeyField(SATMonedas)
+ tipo_cambio = DecimalField(max_digits=15, decimal_places=6, auto_round=True)
+
+ class Meta:
+ order_by = ('-dia',)
+
+
class Addendas(BaseModel):
nombre = TextField(unique=True)
addenda = TextField()
@@ -851,7 +1021,8 @@ class Socios(BaseModel):
fields['rfc'] = fields['rfc'].upper()
fields['nombre'] = util.spaces(fields['nombre'])
fields['slug'] = util.to_slug(fields['nombre'])
- fields['uso_cfdi'] = fields.pop('uso_cfdi_socio', None)
+ uso_cfdi = fields.pop('uso_cfdi_socio', None)
+ fields['uso_cfdi'] = SATUsoCfdi.get_id(uso_cfdi)
fields['condicion_pago'] = \
CondicionesPago.get_or(fields.get('condicion_pago', None))
@@ -872,7 +1043,14 @@ class Socios(BaseModel):
str(CondicionesPago.get(id=row['condicion_pago']))
return row
- rows = Socios.select(Socios.id, Socios.rfc, Socios.nombre).dicts()
+ rows = (Socios
+ .select(
+ Socios.id,
+ Socios.rfc,
+ Socios.nombre,
+ Socios.saldo_cliente)
+ .dicts()
+ )
return {'ok': True, 'rows': tuple(rows)}
@classmethod
@@ -889,7 +1067,6 @@ class Socios(BaseModel):
.where((Socios.id==id) & (Socios.es_cliente==True))
.dicts()
)
- #~ print (id, row)
if len(row):
return {'ok': True, 'row': row[0]}
return {'ok': False}
@@ -919,12 +1096,13 @@ class Socios(BaseModel):
data = {'ok': False, 'row': {}, 'new': True, 'msg': msg}
return data
- #~ ToDo Agregar Condicion de pago y tags
+ #~ ToDo Agregar tags
row = {
'id': obj.id,
'rfc': obj.rfc,
'nombre': obj.nombre,
+ 'saldo_cliente': obj.saldo_cliente,
}
data = {'ok': True, 'row': row, 'new': True}
return data
@@ -933,7 +1111,6 @@ class Socios(BaseModel):
def actualizar(cls, values, id):
fields = cls._clean(cls, values)
try:
- #~ print (fields)
q = Socios.update(**fields).where(Socios.id==id)
q.execute()
except IntegrityError:
@@ -1005,8 +1182,13 @@ class Productos(BaseModel):
id = int(values.get('id', 0))
if id:
row = (Productos
- .select(Productos.id, Productos.clave, Productos.descripcion,
- SATUnidades.name.alias('unidad'), Productos.valor_unitario)
+ .select(
+ Productos.id,
+ Productos.clave,
+ Productos.descripcion,
+ SATUnidades.name.alias('unidad'),
+ Productos.valor_unitario,
+ Productos.descuento)
.join(SATUnidades).switch(Productos)
.where(Productos.id==id).dicts())
if len(row):
@@ -1162,7 +1344,8 @@ class Facturas(BaseModel):
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)
+ tipo_cambio = DecimalField(default=1.0, max_digits=15, 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,
@@ -1187,6 +1370,8 @@ class Facturas(BaseModel):
fecha_cancelacion = DateTimeField(null=True)
acuse = TextField(default='')
donativo = BooleanField(default=False)
+ anticipo = BooleanField(default=False)
+ egreso_anticipo = BooleanField(default=False)
tipo_relacion = TextField(default='')
error = TextField(default='')
@@ -1313,8 +1498,6 @@ class Facturas(BaseModel):
obj = SATTipoRelacion.get(SATTipoRelacion.key==invoice.tipo_relacion)
values['tiporelacion'] = str(obj)
- print ('\nTR', invoice.tipo_relacion)
-
return values
@classmethod
@@ -1352,6 +1535,21 @@ class Facturas(BaseModel):
def _send(self, id, rfc):
return Facturas.send(id, rfc)
+ @util.run_in_thread
+ def _actualizar_saldo_cliente(self, invoice):
+ if invoice.tipo_comprobante == 'T':
+ return
+
+ importe = invoice.total_mn
+ if invoice.tipo_comprobante == 'E':
+ importe *= -1
+
+ q = (Socios
+ .update(saldo_cliente=Socios.saldo_cliente + importe)
+ .where(Socios.id==invoice.cliente.id)
+ )
+ return bool(q.execute())
+
@classmethod
def send(cls, id, rfc):
values = Configuracion.get_({'fields': 'correo'})
@@ -1440,12 +1638,14 @@ class Facturas(BaseModel):
else:
f_ids = (Facturas.id > 0)
+ filters = (fy & fm & cliente & f_ids)
if folios:
- filters = (fy & fm & folios & cliente & f_ids)
+ filters = filters & folios
elif uuid:
- filters = (fy & fm & f_uuid & cliente & f_ids)
- else:
- filters = (fy & fm & cliente & f_ids)
+ filters = filters & f_uuid
+
+ if values['anticipo'] == '1':
+ filters = filters & (Facturas.anticipo == True)
rows = tuple(Facturas
.select(Facturas.id, Facturas.serie, Facturas.folio,
@@ -1521,6 +1721,7 @@ class Facturas(BaseModel):
def _calculate_totals(self, invoice, products):
subtotal = 0
+ descuento_cfdi = 0
totals_tax = {}
total_trasladados = None
total_retenciones = None
@@ -1529,29 +1730,44 @@ class Facturas(BaseModel):
for product in products:
id_product = product.pop('id')
p = Productos.get(Productos.id==id_product)
- #~ product['descripcion'] = p.descripcion
+
product['unidad'] = p.unidad.key
product['clave'] = p.clave
product['clave_sat'] = p.clave_sat
product['factura'] = invoice.id
product['producto'] = id_product
- product['importe'] = round(
- float(product['cantidad']) * float(product['valor_unitario']), 2)
- subtotal += product['importe']
+
+ cantidad = float(product['cantidad'])
+ valor_unitario = float(product['valor_unitario'])
+ descuento = float(product['descuento'])
+ precio_final = valor_unitario - descuento
+ importe = round(cantidad * precio_final, 2)
+
+ product['cantidad'] = cantidad
+ product['valor_unitario'] = valor_unitario
+ product['descuento'] = descuento
+ product['precio_final'] = precio_final
+ product['importe'] = round(cantidad * valor_unitario, 2)
+
+ descuento_cfdi += descuento
+ subtotal += importe
FacturasDetalle.create(**product)
+
for tax in p.impuestos:
if tax.id in totals_tax:
- totals_tax[tax.id].importe += product['importe']
+ #~ totals_tax[tax.id].importe += product['importe']
+ totals_tax[tax.id].importe += importe
else:
- tax.importe = product['importe']
+ #~ tax.importe = product['importe']
+ tax.importe = importe
totals_tax[tax.id] = tax
- #~ totals_tax[tax.id]['importe'] = product['importe']
for tax in totals_tax.values():
if tax.tipo == 'E' or tax.tipo == 'R':
continue
+
import_tax = round(float(tax.tasa) * tax.importe, 2)
total_trasladados = (total_trasladados or 0) + import_tax
if tax.name == 'IVA':
@@ -1585,7 +1801,8 @@ class Facturas(BaseModel):
total = subtotal + (total_trasladados or 0) - (total_retenciones or 0)
total_mn = round(total * invoice.tipo_cambio, 2)
data = {
- 'subtotal': subtotal,
+ 'subtotal': subtotal + descuento,
+ 'descuento': descuento_cfdi,
'total': total,
'total_mn': total_mn,
'total_trasladados': total_trasladados,
@@ -1611,12 +1828,14 @@ class Facturas(BaseModel):
values['folio'] = cls._get_folio(cls, values['serie'])
values['tipo_cambio'] = float(values['tipo_cambio'])
values['lugar_expedicion'] = emisor.cp_expedicion or emisor.codigo_postal
+ values['anticipo'] = util.get_bool(values['anticipo'])
with database_proxy.atomic() as txn:
obj = Facturas.create(**values)
totals = cls._calculate_totals(cls, obj, productos)
cls._guardar_relacionados(cls, obj, relacionados)
obj.subtotal = totals['subtotal']
+ obj.descuento = totals['descuento']
obj.total_trasladados = totals['total_trasladados']
obj.total_retenciones = totals['total_retenciones']
obj.total = totals['total']
@@ -1664,6 +1883,9 @@ class Facturas(BaseModel):
comprobante['TipoDeComprobante'] = invoice.tipo_comprobante
comprobante['MetodoPago'] = invoice.metodo_pago
comprobante['LugarExpedicion'] = invoice.lugar_expedicion
+ if invoice.descuento:
+ comprobante['Descuento'] = FORMAT.format(invoice.descuento)
+
if invoice.tipo_relacion:
relacionados = {
'tipo': invoice.tipo_relacion,
@@ -1682,6 +1904,7 @@ class Facturas(BaseModel):
'UsoCFDI': invoice.uso_cfdi,
}
+ #~ descuento = 0
conceptos = []
rows = FacturasDetalle.select().where(FacturasDetalle.factura==invoice)
for row in rows:
@@ -1695,6 +1918,9 @@ class Facturas(BaseModel):
'ValorUnitario': FORMAT.format(row.valor_unitario),
'Importe': FORMAT.format(row.importe),
}
+ if row.descuento:
+ concepto['Descuento'] = FORMAT.format(row.descuento)
+ #~ descuento += row.descuento
taxes = {}
traslados = []
@@ -1703,12 +1929,13 @@ class Facturas(BaseModel):
for impuesto in row.producto.impuestos:
if impuesto.tipo == 'E':
continue
- import_tax = round(impuesto.tasa * row.importe, 2)
+ base = row.importe - row.descuento
+ import_tax = round(impuesto.tasa * base, 2)
tipo_factor = 'Tasa'
if impuesto.factor != 'T':
tipo_factor = 'Cuota'
tax = {
- "Base": FORMAT.format(row.importe),
+ "Base": FORMAT.format(base),
"Impuesto": impuesto.key,
"TipoFactor": tipo_factor,
"TasaOCuota": str(impuesto.tasa),
@@ -1726,6 +1953,9 @@ class Facturas(BaseModel):
concepto['impuestos'] = taxes
conceptos.append(concepto)
+ #~ if descuento:
+ #~ comprobante['Descuento'] = FORMAT.format(descuento)
+
impuestos = {}
traslados = []
retenciones = []
@@ -1778,6 +2008,42 @@ class Facturas(BaseModel):
obj.save()
return obj.estatus_sat
+ @classmethod
+ def anticipo_egreso(cls, id):
+ origen = Facturas.get(Facturas.id == id)
+ relacionadas = (FacturasRelacionadas
+ .select(FacturasRelacionadas.factura_origen)
+ .where(FacturasRelacionadas.factura==origen))
+ conceptos = (FacturasDetalle
+ .select()
+ .where(FacturasDetalle.factura==origen))
+ impuestos = (FacturasImpuestos
+ .select()
+ .where(FacturasImpuestos.factura==origen))
+
+ #~ egreso_anticipo = BooleanField(default=False)
+
+ serie = Folios.get_egreso(origen.serie)
+ nueva = {
+ 'cliente': origen.cliente,
+ 'tipo_comprobante': 'E',
+ 'forma_pago': '30',
+ 'serie': serie,
+ 'folio': cls._get_folio(cls, serie),
+ 'tipo_relacion': '07',
+ 'pagada': True,
+ 'lugar_expedicion': origen.lugar_expedicion,
+ 'uso_cfdi': origen.uso_cfdi,
+ 'moneda': origen.moneda,
+ 'tipo_cambio': origen.tipo_cambio,
+ 'regimen_fiscal': origen.regimen_fiscal,
+ 'subtotal': origen.subtotal,
+ 'total': origen.total,
+ 'total_trasladados': origen.total_trasladados,
+ 'total_retenciones': origen.total_retenciones,
+ }
+ return
+
@classmethod
def timbrar(cls, id):
obj = Facturas.get(Facturas.id == id)
@@ -1788,6 +2054,7 @@ class Facturas(BaseModel):
enviar_correo = util.get_bool(Configuracion.get_('correo_directo'))
auth = Emisor.get_auth()
+ anticipo = False
msg = 'Factura timbrada correctamente'
result = util.timbra_xml(obj.xml, auth)
if result['ok']:
@@ -1800,6 +2067,9 @@ class Facturas(BaseModel):
row = {'uuid': obj.uuid, 'estatus': 'Timbrada'}
if enviar_correo:
cls._send(cls, id, auth['RFC'])
+ if obj.tipo_comprobante == 'I' and obj.tipo_relacion == '07':
+ anticipo = True
+ cls._actualizar_saldo_cliente(cls, obj)
else:
msg = result['error']
obj.estatus = 'Error'
@@ -1807,7 +2077,14 @@ class Facturas(BaseModel):
obj.save()
row = {'estatus': 'Error'}
- return {'ok': result['ok'], 'msg': msg, 'row': row}
+ result = {
+ 'ok': result['ok'],
+ 'msg': msg,
+ 'row': row,
+ 'anticipo': anticipo
+ }
+
+ return result
class PreFacturas(BaseModel):
@@ -1954,6 +2231,9 @@ class PreFacturas(BaseModel):
data['totales']['subtotal'] = str(data['comprobante']['subtotal'])
data['totales']['total'] = str(data['comprobante']['total'])
+ if obj['descuento']:
+ data['totales']['descuento'] = float(obj['descuento'])
+
taxes = PreFacturasImpuestos.get_(id)
data['totales']['traslados'] = taxes['traslados']
data['totales']['retenciones'] = taxes['retenciones']
@@ -2040,6 +2320,7 @@ class PreFacturas(BaseModel):
def _calculate_totals(self, invoice, products):
subtotal = 0
+ descuento_cfdi = 0
totals_tax = {}
total_trasladados = None
total_retenciones = None
@@ -2048,25 +2329,36 @@ class PreFacturas(BaseModel):
for product in products:
id_product = product.pop('id')
p = Productos.get(Productos.id==id_product)
- #~ product['descripcion'] = p.descripcion
+
product['unidad'] = p.unidad.key
product['clave'] = p.clave
product['clave_sat'] = p.clave_sat
product['factura'] = invoice.id
product['producto'] = id_product
- product['importe'] = round(
- float(product['cantidad']) * float(product['valor_unitario']), 2)
- subtotal += product['importe']
+
+ cantidad = float(product['cantidad'])
+ valor_unitario = float(product['valor_unitario'])
+ descuento = float(product['descuento'])
+ precio_final = valor_unitario - descuento
+ importe = round(cantidad * precio_final, 2)
+
+ product['cantidad'] = cantidad
+ product['valor_unitario'] = valor_unitario
+ product['descuento'] = descuento
+ product['precio_final'] = precio_final
+ product['importe'] = round(cantidad * valor_unitario, 2)
+
+ descuento_cfdi += descuento
+ subtotal += importe
PreFacturasDetalle.create(**product)
for tax in p.impuestos:
if tax.id in totals_tax:
- totals_tax[tax.id].importe += product['importe']
+ totals_tax[tax.id].importe += importe
else:
- tax.importe = product['importe']
+ tax.importe = importe
totals_tax[tax.id] = tax
- #~ totals_tax[tax.id]['importe'] = product['importe']
for tax in totals_tax.values():
if tax.tipo == 'E' or tax.tipo == 'R':
@@ -2104,7 +2396,8 @@ class PreFacturas(BaseModel):
total = subtotal + (total_trasladados or 0) - (total_retenciones or 0)
total_mn = round(total * invoice.tipo_cambio, 2)
data = {
- 'subtotal': subtotal,
+ 'subtotal': subtotal + descuento,
+ 'descuento': descuento_cfdi,
'total': total,
'total_mn': total_mn,
'total_trasladados': total_trasladados,
@@ -2114,7 +2407,6 @@ class PreFacturas(BaseModel):
@classmethod
def add(cls, values):
- print ('VALUES', values)
productos = util.loads(values.pop('productos'))
emisor = Emisor.select()[0]
@@ -2127,6 +2419,7 @@ class PreFacturas(BaseModel):
obj = PreFacturas.create(**values)
totals = cls._calculate_totals(cls, obj, productos)
obj.subtotal = totals['subtotal']
+ obj.descuento = totals['descuento']
obj.total_trasladados = totals['total_trasladados']
obj.total_retenciones = totals['total_retenciones']
obj.total = totals['total']
@@ -2450,8 +2743,8 @@ def _crear_tablas(rfc):
PreFacturas, PreFacturasDetalle, PreFacturasImpuestos,
PreFacturasRelacionadas,
SATAduanas, SATFormaPago, SATImpuestos, SATMonedas, SATRegimenes,
- SATTipoRelacion, SATUnidades, SATUsoCfdi,
- Socios, Tags, Usuarios,
+ SATTipoRelacion, SATUnidades, SATUsoCfdi, SATBancos,
+ Socios, Tags, Usuarios, CuentasBanco, TipoCambio,
Emisor.regimenes.get_through_model(),
Socios.tags.get_through_model(),
Productos.impuestos.get_through_model(),
@@ -2461,10 +2754,10 @@ def _crear_tablas(rfc):
database_proxy.create_tables(tablas, True)
log.info('Tablas creadas correctamente...')
- contraseña = 'blades3.3'
usuarios = (
- {'usuario': 'superadmin', 'contraseña': contraseña, 'es_superusuario': True},
- {'usuario': 'admin', 'contraseña': contraseña, 'es_admin': True},
+ {'usuario': 'superadmin', 'contraseña': DEFAULT_PASSWORD,
+ 'es_superusuario': True},
+ {'usuario': 'admin', 'contraseña': DEFAULT_PASSWORD, 'es_admin': True},
)
for usuario in usuarios:
diff --git a/source/app/settings.py b/source/app/settings.py
index bc7eade..7880a4c 100644
--- a/source/app/settings.py
+++ b/source/app/settings.py
@@ -91,3 +91,4 @@ PRE = {
}
CURRENT_CFDI = '3.3'
+DEFAULT_PASSWORD = 'blades3.3'
diff --git a/source/db/valores_iniciales.json b/source/db/valores_iniciales.json
index c66fe65..57ffeba 100644
--- a/source/db/valores_iniciales.json
+++ b/source/db/valores_iniciales.json
@@ -99,6 +99,104 @@
{"key": "615", "name": "Régimen de los ingresos por obtención de premios", "fisica": true, "activo": false}
]
},
+{
+ "tabla": "SATBancos",
+ "datos": [
+ {"key": "002", "name": "BANAMEX", "razon_social": "Banco Nacional de México, S.A., Institución de Banca Múltiple, Grupo Financiero Banamex"},
+ {"key": "006", "name": "BANCOMEXT", "razon_social": "Banco Nacional de Comercio Exterior, Sociedad Nacional de Crédito, Institución de Banca de Desarrollo"},
+ {"key": "009", "name": "BANOBRAS", "razon_social": "Banco Nacional de Obras y Servicios Públicos, Sociedad Nacional de Crédito, Institución de Banca de Desarrollo"},
+ {"key": "012", "name": "BBVABANCOMER", "razon_social": "BBVA Bancomer, S.A., Institución de Banca Múltiple, Grupo Financiero BBVA Bancomer"},
+ {"key": "014", "name": "SANTANDER", "razon_social": "Banco Santander (México), S.A., Institución de Banca Múltiple, Grupo Financiero Santander"},
+ {"key": "019", "name": "BANJERCITO", "razon_social": "Banco Nacional del Ejército, Fuerza Aérea y Armada, Sociedad Nacional de Crédito, Institución de Banca de Desarrollo"},
+ {"key": "021", "name": "HSBC", "razon_social": "HSBC México, S.A., institución De Banca Múltiple, Grupo Financiero HSBC"},
+ {"key": "030", "name": "BAJIO", "razon_social": "Banco del Bajío, S.A., Institución de Banca Múltiple"},
+ {"key": "106", "name": "BAMSA", "razon_social": "Bank of America México, S.A., Institución de Banca Múltiple, Grupo Financiero Bank of America"},
+ {"key": "032", "name": "IXE", "razon_social": "IXE Banco, S.A., Institución de Banca Múltiple, IXE Grupo Financiero"},
+ {"key": "036", "name": "INBURSA", "razon_social": "Banco Inbursa, S.A., Institución de Banca Múltiple, Grupo Financiero Inbursa"},
+ {"key": "037", "name": "INTERACCIONES", "razon_social": "Banco Interacciones, S.A., Institución de Banca Múltiple"},
+ {"key": "042", "name": "MIFEL", "razon_social": "Banca Mifel, S.A., Institución de Banca Múltiple, Grupo Financiero Mifel"},
+ {"key": "044", "name": "SCOTIABANK", "razon_social": "Scotiabank Inverlat, S.A."},
+ {"key": "059", "name": "INVEX", "razon_social": "Banco Invex, S.A., Institución de Banca Múltiple, Invex Grupo Financiero"},
+ {"key": "058", "name": "BANREGIO", "razon_social": "Banco Regional de Monterrey, S.A., Institución de Banca Múltiple, Banregio Grupo Financiero"},
+ {"key": "060", "name": "BANSI", "razon_social": "Bansi, S.A., Institución de Banca Múltiple"},
+ {"key": "062", "name": "AFIRME", "razon_social": "Banca Afirme, S.A., Institución de Banca Múltiple"},
+ {"key": "072", "name": "BANORTE", "razon_social": "Banco Mercantil del Norte, S.A., Institución de Banca Múltiple, Grupo Financiero Banorte"},
+ {"key": "102", "name": "THE ROYAL BANK", "razon_social": "The Royal Bank of Scotland México, S.A., Institución de Banca Múltiple"},
+ {"key": "103", "name": "AMERICAN EXPRESS", "razon_social": "American Express Bank (México), S.A., Institución de Banca Múltiple"},
+ {"key": "108", "name": "TOKYO", "razon_social": "Bank of Tokyo-Mitsubishi UFJ (México), S.A."},
+ {"key": "110", "name": "JP MORGAN", "razon_social": "Banco J.P. Morgan, S.A., Institución de Banca Múltiple, J.P. Morgan Grupo Financiero"},
+ {"key": "112", "name": "BMONEX", "razon_social": "Banco Monex, S.A., Institución de Banca Múltiple"},
+ {"key": "113", "name": "VE POR MAS", "razon_social": "Banco Ve Por Mas, S.A. Institución de Banca Múltiple"},
+ {"key": "116", "name": "ING", "razon_social": "ING Bank (México), S.A., Institución de Banca Múltiple, ING Grupo Financiero"},
+ {"key": "124", "name": "DEUTSCHE", "razon_social": "Deutsche Bank México, S.A., Institución de Banca Múltiple"},
+ {"key": "126", "name": "CREDIT SUISSE", "razon_social": "Banco Credit Suisse (México), S.A. Institución de Banca Múltiple, Grupo Financiero Credit Suisse (México)"},
+ {"key": "127", "name": "AZTECA", "razon_social": "Banco Azteca, S.A. Institución de Banca Múltiple."},
+ {"key": "128", "name": "AUTOFIN", "razon_social": "Banco Autofin México, S.A. Institución de Banca Múltiple"},
+ {"key": "129", "name": "BARCLAYS", "razon_social": "Barclays Bank México, S.A., Institución de Banca Múltiple, Grupo Financiero Barclays México"},
+ {"key": "130", "name": "COMPARTAMOS", "razon_social": "Banco Compartamos, S.A., Institución de Banca Múltiple"},
+ {"key": "131", "name": "BANCO FAMSA", "razon_social": "Banco Ahorro Famsa, S.A., Institución de Banca Múltiple"},
+ {"key": "132", "name": "BMULTIVA", "razon_social": "Banco Multiva, S.A., Institución de Banca Múltiple, Multivalores Grupo Financiero"},
+ {"key": "133", "name": "ACTINVER", "razon_social": "Banco Actinver, S.A. Institución de Banca Múltiple, Grupo Financiero Actinver"},
+ {"key": "134", "name": "WAL-MART", "razon_social": "Banco Wal-Mart de México Adelante, S.A., Institución de Banca Múltiple"},
+ {"key": "135", "name": "NAFIN", "razon_social": "Nacional Financiera, Sociedad Nacional de Crédito, Institución de Banca de Desarrollo"},
+ {"key": "136", "name": "INTERBANCO", "razon_social": "Inter Banco, S.A. Institución de Banca Múltiple"},
+ {"key": "137", "name": "BANCOPPEL", "razon_social": "BanCoppel, S.A., Institución de Banca Múltiple"},
+ {"key": "138", "name": "ABC CAPITAL", "razon_social": "ABC Capital, S.A., Institución de Banca Múltiple"},
+ {"key": "139", "name": "UBS BANK", "razon_social": "UBS Bank México, S.A., Institución de Banca Múltiple, UBS Grupo Financiero"},
+ {"key": "140", "name": "CONSUBANCO", "razon_social": "Consubanco, S.A. Institución de Banca Múltiple"},
+ {"key": "141", "name": "VOLKSWAGEN", "razon_social": "Volkswagen Bank, S.A., Institución de Banca Múltiple"},
+ {"key": "143", "name": "CIBANCO", "razon_social": "CIBanco, S.A."},
+ {"key": "145", "name": "BBASE", "razon_social": "Banco Base, S.A., Institución de Banca Múltiple"},
+ {"key": "166", "name": "BANSEFI", "razon_social": "Banco del Ahorro Nacional y Servicios Financieros, Sociedad Nacional de Crédito, Institución de Banca de Desarrollo"},
+ {"key": "168", "name": "HIPOTECARIA FEDERAL", "razon_social": "Sociedad Hipotecaria Federal, Sociedad Nacional de Crédito, Institución de Banca de Desarrollo"},
+ {"key": "600", "name": "MONEXCB", "razon_social": "Monex Casa de Bolsa, S.A. de C.V. Monex Grupo Financiero"},
+ {"key": "601", "name": "GBM", "razon_social": "GBM Grupo Bursátil Mexicano, S.A. de C.V. Casa de Bolsa"},
+ {"key": "602", "name": "MASARI", "razon_social": "Masari Casa de Bolsa, S.A."},
+ {"key": "605", "name": "VALUE", "razon_social": "Value, S.A. de C.V. Casa de Bolsa"},
+ {"key": "606", "name": "ESTRUCTURADORES", "razon_social": "Estructuradores del Mercado de Valores Casa de Bolsa, S.A. de C.V."},
+ {"key": "607", "name": "TIBER", "razon_social": "Casa de Cambio Tiber, S.A. de C.V."},
+ {"key": "608", "name": "VECTOR", "razon_social": "Vector Casa de Bolsa, S.A. de C.V."},
+ {"key": "610", "name": "B&B", "razon_social": "B y B, Casa de Cambio, S.A. de C.V."},
+ {"key": "614", "name": "ACCIVAL", "razon_social": "Acciones y Valores Banamex, S.A. de C.V., Casa de Bolsa"},
+ {"key": "615", "name": "MERRILL LYNCH", "razon_social": "Merrill Lynch México, S.A. de C.V. Casa de Bolsa"},
+ {"key": "616", "name": "FINAMEX", "razon_social": "Casa de Bolsa Finamex, S.A. de C.V."},
+ {"key": "617", "name": "VALMEX", "razon_social": "Valores Mexicanos Casa de Bolsa, S.A. de C.V."},
+ {"key": "618", "name": "UNICA", "razon_social": "Unica Casa de Cambio, S.A. de C.V."},
+ {"key": "619", "name": "MAPFRE", "razon_social": "MAPFRE Tepeyac, S.A."},
+ {"key": "620", "name": "PROFUTURO", "razon_social": "Profuturo G.N.P., S.A. de C.V., Afore"},
+ {"key": "621", "name": "CB ACTINVER", "razon_social": "Actinver Casa de Bolsa, S.A. de C.V."},
+ {"key": "622", "name": "OACTIN", "razon_social": "OPERADORA ACTINVER, S.A. DE C.V."},
+ {"key": "623", "name": "SKANDIA", "razon_social": "Skandia Vida, S.A. de C.V."},
+ {"key": "626", "name": "CBDEUTSCHE", "razon_social": "Deutsche Securities, S.A. de C.V. CASA DE BOLSA"},
+ {"key": "627", "name": "ZURICH", "razon_social": "Zurich Compañía de Seguros, S.A."},
+ {"key": "628", "name": "ZURICHVI", "razon_social": "Zurich Vida, Compañía de Seguros, S.A."},
+ {"key": "629", "name": "SU CASITA", "razon_social": "Hipotecaria Su Casita, S.A. de C.V. SOFOM ENR"},
+ {"key": "630", "name": "CB INTERCAM", "razon_social": "Intercam Casa de Bolsa, S.A. de C.V."},
+ {"key": "631", "name": "CI BOLSA", "razon_social": "CI Casa de Bolsa, S.A. de C.V."},
+ {"key": "632", "name": "BULLTICK CB", "razon_social": "Bulltick Casa de Bolsa, S.A., de C.V."},
+ {"key": "633", "name": "STERLING", "razon_social": "Sterling Casa de Cambio, S.A. de C.V."},
+ {"key": "634", "name": "FINCOMUN", "razon_social": "Fincomún, Servicios Financieros Comunitarios, S.A. de C.V."},
+ {"key": "636", "name": "HDI SEGUROS", "razon_social": "HDI Seguros, S.A. de C.V."},
+ {"key": "637", "name": "ORDER", "razon_social": "Order Express Casa de Cambio, S.A. de C.V"},
+ {"key": "638", "name": "AKALA", "razon_social": "Akala, S.A. de C.V., Sociedad Financiera Popular"},
+ {"key": "640", "name": "CB JPMORGAN", "razon_social": "J.P. Morgan Casa de Bolsa, S.A. de C.V. J.P. Morgan Grupo Financiero"},
+ {"key": "642", "name": "REFORMA", "razon_social": "Operadora de Recursos Reforma, S.A. de C.V., S.F.P."},
+ {"key": "646", "name": "STP", "razon_social": "Sistema de Transferencias y Pagos STP, S.A. de C.V.SOFOM ENR"},
+ {"key": "647", "name": "TELECOMM", "razon_social": "Telecomunicaciones de México"},
+ {"key": "648", "name": "EVERCORE", "razon_social": "Evercore Casa de Bolsa, S.A. de C.V."},
+ {"key": "649", "name": "SKANDIA", "razon_social": "Skandia Operadora de Fondos, S.A. de C.V."},
+ {"key": "651", "name": "SEGMTY", "razon_social": "Seguros Monterrey New York Life, S.A de C.V"},
+ {"key": "652", "name": "ASEA", "razon_social": "Solución Asea, S.A. de C.V., Sociedad Financiera Popular"},
+ {"key": "653", "name": "KUSPIT", "razon_social": "Kuspit Casa de Bolsa, S.A. de C.V."},
+ {"key": "655", "name": "SOFIEXPRESS", "razon_social": "J.P. SOFIEXPRESS, S.A. de C.V., S.F.P."},
+ {"key": "656", "name": "UNAGRA", "razon_social": "UNAGRA, S.A. de C.V., S.F.P."},
+ {"key": "659", "name": "OPCIONES EMPRESARIALES DEL NOROESTE", "razon_social": "OPCIONES EMPRESARIALES DEL NORESTE, S.A. DE C.V., S.F.P."},
+ {"key": "670", "name": "LIBERTAD", "razon_social": "Libertad Servicios Financieros, S.A. De C.V."},
+ {"key": "901", "name": "CLS", "razon_social": "Cls Bank International"},
+ {"key": "902", "name": "INDEVAL", "razon_social": "SD. Indeval, S.A. de C.V."},
+ {"key": "999", "name": "N/A"}
+ ]
+},
{
"tabla": "SATUsoCfdi",
"datos": [
diff --git a/source/static/js/controller/admin.js b/source/static/js/controller/admin.js
index 408e280..187d6c9 100644
--- a/source/static/js/controller/admin.js
+++ b/source/static/js/controller/admin.js
@@ -21,11 +21,15 @@ var controllers = {
$$('cmd_probar_correo').attachEvent('onItemClick', cmd_probar_correo_click)
$$('cmd_guardar_correo').attachEvent('onItemClick', cmd_guardar_correo_click)
$$('emisor_logo').attachEvent('onItemClick', emisor_logo_click)
+ $$('cmd_emisor_agregar_cuenta').attachEvent('onItemClick', cmd_emisor_agregar_cuenta_click)
+ $$('emisor_cuenta_saldo_inicial').attachEvent('onChange', emisor_cuenta_saldo_inicial_change)
//~ SAT
tb_sat = $$('tab_sat').getTabbar()
tb_sat.attachEvent('onChange', tab_sat_change)
$$('grid_admin_taxes').attachEvent('onCheck', grid_admin_taxes_on_check)
$$('grid_admin_monedas').attachEvent('onCheck', grid_admin_monedas_on_check)
+ $$('grid_admin_bancos').attachEvent('onCheck', grid_admin_bancos_on_check)
+ $$('grid_admin_unidades').attachEvent('onCheck', grid_admin_unidades_on_check)
//~ Opciones
tb_options = $$('tab_options').getTabbar()
tb_options.attachEvent('onChange', tab_options_change)
@@ -177,6 +181,38 @@ function get_certificado(){
}
+function get_cuentas_banco(){
+
+ webix.ajax().get('/values/monedasid', function(text, data){
+ var values = data.json()
+ pre = values[0]
+ $$('lst_emisor_cuenta_moneda').getList().parse(values)
+ $$('lst_emisor_cuenta_moneda').setValue(pre.id)
+ })
+
+ webix.ajax().get('/values/bancosid', function(text, data){
+ var values = data.json()
+ pre = values[0]
+ $$('lst_emisor_banco').getList().parse(values)
+ if(values.length == 1){
+ $$('lst_emisor_banco').setValue(pre.id)
+ }
+ })
+
+ webix.ajax().get('/values/emisorcuentasbanco', {}, {
+ error: function(text, data, xhr) {
+ msg = 'Error al consultar'
+ msg_error(msg)
+ },
+ success: function(text, data, xhr) {
+ var values = data.json()
+ $$('grid_emisor_cuentas_banco').parse(values)
+ }
+ })
+
+}
+
+
function get_table_folios(){
webix.ajax().get("/folios", {}, {
error: function(text, data, xhr) {
@@ -217,6 +253,7 @@ function get_admin_impuestos(){
})
}
+
function get_admin_monedas(){
webix.ajax().sync().get('/values/allcurrencies', function(text, data){
var values = data.json()
@@ -226,6 +263,24 @@ function get_admin_monedas(){
}
+function get_admin_bancos(){
+ webix.ajax().sync().get('/values/allbancos', function(text, data){
+ var values = data.json()
+ $$('grid_admin_bancos').clearAll()
+ $$('grid_admin_bancos').parse(values, 'json')
+ })
+}
+
+
+function get_admin_unidades(){
+ webix.ajax().sync().get('/values/allunidades', function(text, data){
+ var values = data.json()
+ $$('grid_admin_unidades').clearAll()
+ $$('grid_admin_unidades').parse(values, 'json')
+ })
+}
+
+
function get_config_values(opt){
if(opt == undefined){
return
@@ -252,6 +307,7 @@ function multi_admin_change(prevID, nextID){
$$('tab_emisor').setValue('Datos Fiscales')
get_emisor()
get_certificado()
+ get_cuentas_banco()
return
}
@@ -754,10 +810,12 @@ function tab_options_change(nv, ov){
function tab_sat_change(nv, ov){
- //~ show(nv)
- if(nv = 'Monedas'){
+ if(nv == 'Monedas'){
get_admin_monedas()
- return
+ }else if(nv == 'Bancos'){
+ get_admin_bancos()
+ }else if(nv == 'Unidades'){
+ get_admin_unidades()
}
}
@@ -792,3 +850,136 @@ function grid_admin_monedas_on_check(row, column, state){
}
})
}
+
+
+function grid_admin_bancos_on_check(row, column, state){
+
+ var values = {
+ id: row,
+ field: column,
+ value: state,
+ }
+ webix.ajax().get('/values/bancoupdate', values, {
+ error: function(text, data, xhr) {
+ },
+ success: function(text, data, xhr) {
+ }
+ })
+}
+
+
+function grid_admin_unidades_on_check(row, column, state){
+
+ var values = {
+ id: row,
+ field: column,
+ value: state,
+ }
+ webix.ajax().get('/values/unidadupdate', values, {
+ error: function(text, data, xhr) {
+ },
+ success: function(text, data, xhr) {
+ }
+ })
+}
+
+
+function emisor_cuenta_saldo_inicial_change(new_value, old_value){
+ if(!isFinite(new_value)){
+ this.config.value = old_value
+ this.refresh()
+ }
+}
+
+
+function cmd_emisor_agregar_cuenta_click(){
+ var form = $$('form_emisor_cuenta_banco')
+
+ if (!form.validate()){
+ msg = 'Valores inválidos'
+ msg_error(msg)
+ return
+ }
+
+ var values = form.getValues()
+
+ var si = parseFloat(values.emisor_cuenta_saldo_inicial.replace('$', '').replace(',', ''))
+ var cuenta = {
+ de_emisor: true,
+ activa: true,
+ nombre: values.emisor_cuenta_nombre.trim(),
+ banco: values.emisor_banco,
+ fecha_apertura: values.emisor_cuenta_fecha,
+ cuenta: values.emisor_cuenta.trim(),
+ clabe: values.emisor_clabe.trim(),
+ moneda: values.emisor_cuenta_moneda,
+ saldo_inicial: si
+ }
+
+ if(!cuenta.nombre){
+ msg = 'El nombre de la cuenta es requerido'
+ msg_error(msg)
+ return
+ }
+
+ if(!cuenta.cuenta){
+ msg = 'La cuenta es requerida'
+ msg_error(msg)
+ return
+ }
+
+ if(!cuenta.cuenta.is_number()){
+ msg = 'Solo digitos en la cuenta'
+ msg_error(msg)
+ return
+ }
+
+ if(cuenta.cuenta.length < 9){
+ msg = 'Longitud incorrecta de la cuenta'
+ msg_error(msg)
+ return
+ }
+
+ if(!cuenta.clabe){
+ msg = 'La CLABE es requerida'
+ msg_error(msg)
+ return
+ }
+
+ if(cuenta.clabe.length != 18){
+ msg = 'La CLABE debe ser de 18 digitos'
+ msg_error(msg)
+ return
+ }
+
+ if(!cuenta.clabe.is_number()){
+ msg = 'Solo digitos en la CLABE'
+ msg_error(msg)
+ return
+ }
+
+ //~ if(!isFinite(cuenta.saldo_inicial)){
+ if(cuenta.saldo_inicial <= 0){
+ msg = 'El saldo inicial no puede ser negativo o cero'
+ msg_error(msg)
+ return
+ }
+
+ cuenta.saldo = cuenta.saldo_inicial
+
+ webix.ajax().post('/cuentasbanco', cuenta, {
+ error:function(text, data, XmlHttpRequest){
+ msg = 'Ocurrio un error, consulta a soporte técnico'
+ msg_error(msg)
+ },
+ success:function(text, data, XmlHttpRequest){
+ var values = data.json()
+ if(values.ok){
+ $$('grid_emisor_cuentas_banco').add(values.row)
+ }else{
+ msg_error(values.msg)
+ }
+ }
+ })
+
+}
diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js
index f486f06..ddf2a1b 100644
--- a/source/static/js/controller/invoices.js
+++ b/source/static/js/controller/invoices.js
@@ -3,6 +3,7 @@ var grid = null
var msg = ''
var result = false
var tipo_relacion = ''
+var anticipo = false
function get_condicion_pago(){
@@ -244,6 +245,29 @@ function validate_invoice(values){
return false
}
+ anticipo = $$('chk_cfdi_anticipo').getValue()
+ if(anticipo){
+ var mp = $$('lst_metodo_pago').getValue()
+ if(mp != 'PUE'){
+ msg = 'En anticipos, el método de pago debe ser: Pago en una sola exhibición'
+ msg_error(msg)
+ return false
+ }
+
+ if(grid.count() != 1){
+ msg = 'Los anticipos solo llevan un concepto'
+ msg_error(msg)
+ return false
+ }
+
+ query = table_relaciones.chain().data()
+ if(query.length > 0){
+ msg = 'Los anticipos no deben llevar CFDI relacionados'
+ msg_error(msg)
+ return false
+ }
+ }
+
return true
}
@@ -256,12 +280,54 @@ function update_grid_invoices(values){
}
}
+
+function send_anticipo_egreso(id){
+ webix.ajax().get('/values/anticipoegreso', {id: id}, function(text, data){
+ var values = data.json()
+ if(values.ok){
+ msg_sucess(values.msg)
+ gi.add(values.row)
+ }else{
+ webix.alert({
+ title: 'Error al Timbrar',
+ text: values.msg,
+ type: 'alert-error'
+ })
+ }
+ })
+
+}
+
+
+function generar_anticipo_egreso(id){
+ msg = 'La factura tiene un CFDI de anticipo relacionado
'
+ msg += '¿Deseas generar la factura de egreso correspondiente?'
+
+ webix.confirm({
+ title: 'Generar Egreso',
+ ok: 'Si',
+ cancel: 'No',
+ type: 'confirm-error',
+ text: msg,
+ callback:function(result){
+ if(result){
+ send_anticipo_egreso(id)
+ }
+ }
+ })
+}
+
+
function send_timbrar(id){
webix.ajax().get('/values/timbrar', {id: id}, function(text, data){
var values = data.json()
if(values.ok){
msg_sucess(values.msg)
gi.updateItem(id, values.row)
+ if(values.anticipo){
+ //~ generar_anticipo_egreso(id)
+ //~ show('Generar egreso de anticipo')
+ }
}else{
webix.alert({
title: 'Error al Timbrar',
@@ -356,6 +422,7 @@ function guardar_y_timbrar(values){
delete rows[i]['unidad']
delete rows[i]['importe']
rows[i]['valor_unitario'] = parseFloat(rows[i]['valor_unitario'])
+ rows[i]['descuento'] = parseFloat(rows[i]['descuento'])
}
var data = new Object()
@@ -373,6 +440,7 @@ function guardar_y_timbrar(values){
data['regimen_fiscal'] = $$('lst_regimen_fiscal').getValue()
data['relacionados'] = ids
data['tipo_relacion'] = tipo_relacion
+ data['anticipo'] = anticipo
if(!save_invoice(data)){
return
@@ -380,6 +448,9 @@ function guardar_y_timbrar(values){
table_relaciones.clear()
tipo_relacion = ''
+ anticipo = false
+ $$('chk_cfdi_anticipo').setValue(0)
+
$$('form_invoice').setValues({id_partner: 0, lbl_partner: 'Ninguno'})
$$('multi_invoices').setValue('invoices_home')
@@ -404,6 +475,9 @@ function cmd_timbrar_click(id, e, node){
if(query.length > 0){
msg += 'La factura tiene CFDI relacionados
'
}
+ if(anticipo){
+ msg += 'La factura es Anticipo
'
+ }
msg += '¿Estás seguro de timbrar esta factura?'
webix.confirm({
@@ -614,7 +688,7 @@ function search_product_id_key_press(code, e){
function grid_details_before_edit_start(id){
- var columns = ['', 'descripcion', 'cantidad', 'valor_unitario']
+ var columns = ['', 'descripcion', 'cantidad', 'valor_unitario', 'descuento']
if(!columns.indexOf(id.column)){
return !this.getItem(id.row)[id.column]
}
@@ -649,7 +723,8 @@ function grid_details_before_edit_stop(state, editor){
grid.unblockEvent()
return true
}
- var valor_unitario = row['valor_unitario']
+ var valor_unitario = parseFloat(row['valor_unitario'])
+ var descuento = parseFloat(row['descuento'])
}
if(editor.column == 'valor_unitario'){
@@ -663,10 +738,28 @@ function grid_details_before_edit_stop(state, editor){
grid.unblockEvent()
return true
}
- var cantidad = row['cantidad']
+ var cantidad = parseFloat(row['cantidad'])
+ var descuento = parseFloat(row['descuento'])
}
- row['importe'] = (cantidad * valor_unitario).round(DECIMALES)
+ if(editor.column == 'descuento'){
+ var descuento = parseFloat(state.value)
+ if(isNaN(descuento)){
+ msg = 'El descuento debe ser un número'
+ webix.message({type:'error', text: msg})
+ grid.blockEvent()
+ state.value = state.old
+ grid.editCancel()
+ grid.unblockEvent()
+ return true
+ }
+ var cantidad = parseFloat(row['cantidad'])
+ var valor_unitario = parseFloat(row['valor_unitario'])
+ }
+
+ var precio_final = valor_unitario - descuento
+ row['importe'] = (cantidad * precio_final).round(DECIMALES)
+
grid.refresh()
calculate_taxes()
}
@@ -952,6 +1045,7 @@ function cmd_prefactura_click(){
delete rows[i]['unidad']
delete rows[i]['importe']
rows[i]['valor_unitario'] = parseFloat(rows[i]['valor_unitario'])
+ rows[i]['descuento'] = parseFloat(rows[i]['descuento'])
}
var data = new Object()
@@ -1201,6 +1295,7 @@ function get_facturas_por_cliente(){
'month': m,
'id_cliente': id,
'cfdis': ids,
+ 'anticipo': $$('chk_relacionados_anticipo').getValue(),
'folio': $$('filter_cfdi_folio').getValue(),
'uuid': $$('filter_cfdi_uuid').getValue(),
'opt': 'relacionados'
@@ -1321,5 +1416,27 @@ function filter_cfdi_month_change(nv, ov){
}
+function lst_tipo_relacion_change(nv, ov){
+ $$('chk_relacionados_anticipo').setValue(0)
+ $$('chk_relacionados_anticipo').disable()
+ if(nv=='07'){
+ $$('chk_relacionados_anticipo').enable()
+ $$('chk_relacionados_anticipo').setValue(1)
+ cmd_filter_relacionados_click()
+ }
+
+}
+function lst_serie_change(nv, ov){
+ query = table_series.chain().find({id: nv}).data()[0]
+ if(query.usarcon){
+ $$('lst_tipo_comprobante').setValue(query.usarcon)
+ $$('lst_tipo_comprobante').config.readonly = true
+ }else{
+ $$('lst_tipo_comprobante').setValue('I')
+ $$('lst_tipo_comprobante').config.readonly = false
+ }
+ $$('lst_tipo_comprobante').refresh()
+}
+
diff --git a/source/static/js/controller/main.js b/source/static/js/controller/main.js
index e9bcb1c..ab9fcdb 100644
--- a/source/static/js/controller/main.js
+++ b/source/static/js/controller/main.js
@@ -54,6 +54,7 @@ var controllers = {
$$('cmd_prefactura').attachEvent('onItemClick', cmd_prefactura_click)
$$('cmd_cfdi_relacionados').attachEvent('onItemClick', cmd_cfdi_relacionados_click)
$$('lst_metodo_pago').attachEvent('onChange', lst_metodo_pago_change)
+ $$('lst_serie').attachEvent('onChange', lst_serie_change)
var tb_invoice = $$('tv_invoice').getTabbar()
tb_invoice.attachEvent('onChange', tb_invoice_change)
@@ -66,8 +67,8 @@ var controllers = {
}
-function get_uso_cfdi_to_table(args){
- webix.ajax().sync().get('/values/usocfdi', args, function(text, data){
+function get_uso_cfdi_to_table(){
+ webix.ajax().sync().get('/values/usocfdi', function(text, data){
var values = data.json()
table_usocfdi.clear()
table_usocfdi.insert(values)
diff --git a/source/static/js/controller/partners.js b/source/static/js/controller/partners.js
index ae208eb..7b331ae 100644
--- a/source/static/js/controller/partners.js
+++ b/source/static/js/controller/partners.js
@@ -22,7 +22,7 @@ function cmd_new_partner_click(id, e, node){
$$('multi_partners').setValue('partners_new')
$$('tab_partner').setValue('Datos Fiscales')
- get_uso_cfdi_to_table({})
+ get_uso_cfdi_to_table()
query = table_usocfdi.chain().find({fisica: true}).data()
$$('lst_uso_cfdi_socio').getList().parse(query)
}
@@ -54,7 +54,7 @@ function cmd_edit_partner_click(id, e, node){
$$('form_partner').setValues(values)
$$('forma_pago').getList().load('/values/formapago')
- get_uso_cfdi_to_table({})
+ get_uso_cfdi_to_table()
if(values.tipo_persona == 1){
query = table_usocfdi.chain().find({fisica: true}).data()
}else if(values.tipo_persona == 2){
@@ -339,6 +339,6 @@ function rfc_lost_focus(prev_view){
function multi_partners_change(prevID, nextID){
- webix.message(prevID)
- webix.message(nextID)
+ //~ webix.message(prevID)
+ //~ webix.message(nextID)
}
diff --git a/source/static/js/controller/util.js b/source/static/js/controller/util.js
index f945d78..dd26684 100644
--- a/source/static/js/controller/util.js
+++ b/source/static/js/controller/util.js
@@ -35,6 +35,11 @@ Number.prototype.round = function(decimals){
}
+String.prototype.is_number = function(){
+ return /^\d+$/.test(this)
+}
+
+
webix.protoUI({
$cssName: "text",
name: "currency",
diff --git a/source/static/js/ui/admin.js b/source/static/js/ui/admin.js
index 5a81982..00d5a26 100644
--- a/source/static/js/ui/admin.js
+++ b/source/static/js/ui/admin.js
@@ -132,6 +132,78 @@ var emisor_certificado = [
]
+var grid_emisor_cuentas_banco_cols = [
+ {id: 'id', header: 'ID', hidden: true},
+ {id: 'activa', header: 'Activa', template: '{common.checkbox()}',
+ editor: 'checkbox', width: 50},
+ {id: 'nombre', header: 'Nombre', fillspace: 1},
+ {id: 'banco', header: 'Banco', fillspace: 1},
+ {id: 'fecha_apertura', header: 'Fecha de Apertura', fillspace: 1},
+ {id: 'cuenta', header: 'Cuenta', fillspace: 1},
+ {id: 'clabe', header: 'CLABE', fillspace: 1},
+ {id: 'moneda', header: 'Moneda', fillspace: 1},
+ {id: 'saldo', header: 'Saldo', width: 150, format: webix.i18n.priceFormat,
+ css: 'right'},
+]
+
+
+var grid_emisor_cuentas_banco = {
+ view: 'datatable',
+ id: 'grid_emisor_cuentas_banco',
+ select: 'row',
+ adjust: true,
+ autoheight: true,
+ headermenu: true,
+ columns: grid_emisor_cuentas_banco_cols
+}
+
+
+var emisor_cuentas_banco = [
+ {template: 'Agregar cuenta de banco', type: 'section'},
+ {view: 'form', id: 'form_emisor_cuenta_banco', rows: [
+ {cols: [
+ {view: 'text', id: 'emisor_cuenta_nombre', name: 'emisor_cuenta_nombre',
+ label: 'Nombre: ', required: true}, {}]},
+ {cols: [
+ {view: 'richselect', id: 'lst_emisor_banco', name: 'emisor_banco',
+ label: 'Banco: ', required: true, options: []},
+ {view: 'datepicker', id: 'emisor_cuenta_fecha', format: '%d-%M-%Y',
+ name: 'emisor_cuenta_fecha', label: 'Fecha de apertura: ',
+ required: true},
+ ]},
+ {cols: [
+ {view: 'text', id: 'emisor_cuenta', name: 'emisor_cuenta',
+ label: 'Cuenta: ', required: true},
+ {view: 'text', id: 'emisor_clabe', name: 'emisor_clabe',
+ label: 'CLABE: ', required: true},
+ ]},
+ {cols: [
+ {view: 'richselect', id: 'lst_emisor_cuenta_moneda',
+ name: 'emisor_cuenta_moneda', label: 'Moneda: ', required: true,
+ options: []},
+ {view: 'currency', type: 'text', id: 'emisor_cuenta_saldo_inicial',
+ name: 'emisor_cuenta_saldo_inicial', label: 'Saldo inicial',
+ required: true, invalidMessage: 'Captura un valor númerico',
+ inputAlign: 'right', value: ''},
+ ]},
+ {minHeight: 10},
+ {cols: [{}, {view: 'button', id: 'cmd_emisor_agregar_cuenta',
+ label: 'Agregar cuenta'}, {}]},
+
+ ],
+
+ rules: {
+ emisor_cuenta_saldo_inicial: function(value){return value.trim() != "$";},
+ }
+
+ },
+ {minHeight: 20},
+ {template: 'Cuentas de banco existentes', type: 'section'},
+ grid_emisor_cuentas_banco,
+ {minHeight: 50},
+]
+
+
var controls_emisor = [
{
view: 'tabview',
@@ -139,13 +211,14 @@ var controls_emisor = [
tabbar: {options: [
'Datos Fiscales',
'Otros Datos',
- 'Certificado']},
+ 'Certificado',
+ 'Cuentas de Banco']},
animate: true,
cells: [
{id: 'Datos Fiscales', rows: emisor_datos_fiscales},
{id: 'Otros Datos', rows: emisor_otros_datos},
{id: 'Certificado', rows: emisor_certificado},
- {},
+ {id: 'Cuentas de Banco', rows: emisor_cuentas_banco}
]
}
]
@@ -388,6 +461,26 @@ var grid_admin_monedas_cols = [
]
+var grid_admin_bancos_cols = [
+ {id: 'id', header: 'ID', hidden: true},
+ {id: 'key', header: 'Clave', footer: {content: 'rowCount', css: 'right'}},
+ {id: 'name', header: 'Nombre', adjust: 'data', footer: 'Bancos'},
+ {id: 'activo', header: 'Activo', template: '{common.checkbox()}',
+ editor: 'checkbox'},
+]
+
+
+var grid_admin_unidades_cols = [
+ {id: 'id', header: 'ID', hidden: true},
+ {id: 'key', header: 'Clave'},
+ {id: 'name', header: 'Nombre', adjust: 'data'},
+ {id: 'activo', header: 'Activa', template: '{common.checkbox()}',
+ editor: 'checkbox'},
+ {id: 'default', header: 'Predeterminada', template: '{common.radio()}',
+ adjust: 'header'},
+]
+
+
var grid_admin_taxes = {
view: 'datatable',
id: 'grid_admin_taxes',
@@ -414,6 +507,33 @@ var grid_admin_monedas = {
}
+var grid_admin_bancos = {
+ view: 'datatable',
+ id: 'grid_admin_bancos',
+ select: 'cell',
+ multiselect: true,
+ adjust: true,
+ autowidth: true,
+ headermenu: true,
+ footer: true,
+ columns: grid_admin_bancos_cols
+}
+
+
+var grid_admin_unidades = {
+ view: 'datatable',
+ id: 'grid_admin_unidades',
+ select: 'cell',
+ multiselect: true,
+ adjust: true,
+ autowidth: true,
+ autoheight: true,
+ headermenu: true,
+ footer: true,
+ columns: grid_admin_unidades_cols
+}
+
+
var msg_tax = 'Activa los impuestos que uses. El predeterminado se muestra primero'
var sat_impuestos = [
{maxHeight: 20},
@@ -422,6 +542,7 @@ var sat_impuestos = [
{cols: [{maxWidth: 15}, grid_admin_taxes, {}]},
{}]
+
var msg_moneda = 'Activa las monedas que uses. La predeterminada se muestra primero'
var sat_monedas = [
{maxHeight: 20},
@@ -431,6 +552,26 @@ var sat_monedas = [
{}]
+var msg_bancos = 'Activar los bancos necesarios'
+var sat_bancos = [
+ {maxHeight: 20},
+ {cols: [{maxWidth: 15}, {view: 'label', label: msg_bancos}, {}]},
+ {maxHeight: 20},
+ {cols: [{maxWidth: 15}, grid_admin_bancos, {}]},
+ {maxHeight: 20},
+]
+
+
+var msg_unidades = 'Activar las unidades necesarias'
+var sat_unidades = [
+ {maxHeight: 20},
+ {cols: [{maxWidth: 15}, {view: 'label', label: msg_unidades}, {}]},
+ {maxHeight: 20},
+ {cols: [{maxWidth: 15}, grid_admin_unidades, {}]},
+ {},
+]
+
+
var tab_sat = {
view: 'tabview',
id: 'tab_sat',
@@ -438,13 +579,15 @@ var tab_sat = {
tabbar: {options: [
'Impuestos',
'Monedas',
- 'Unidades']},
+ 'Bancos',
+ 'Unidades'
+ ]},
animate: true,
cells: [
{id: 'Impuestos', rows: sat_impuestos},
{id: 'Monedas', rows: sat_monedas},
- {id: 'Unidades', rows: [{}]},
- {},
+ {id: 'Bancos', rows: sat_bancos},
+ {id: 'Unidades', rows: sat_unidades},
]
}
@@ -545,7 +688,6 @@ var ui_admin = {
{view: 'toolbar', padding: 3, elements: [
{view: 'button', type: 'icon', icon: 'bars',
width: 37, align: 'left', css: 'app_button', click: function(){
- //~ $$('$sidebar1').toggle()
$$('sidebar_admin').toggle()
}
},
diff --git a/source/static/js/ui/invoices.js b/source/static/js/ui/invoices.js
index fb2c11f..b99c319 100644
--- a/source/static/js/ui/invoices.js
+++ b/source/static/js/ui/invoices.js
@@ -97,8 +97,11 @@ var body_cfdi_relacionados = {rows: [
{view: 'label', id: 'lbl_cfdi_title', label: 'Cliente: ',
autowidth: true},
{view: 'label', id: 'lbl_cfdi_cliente', label: '', align: 'left'}]},
- {view: 'richselect', id: 'lst_tipo_relacion', label: 'Tipo de Relación',
- labelWidth: 150, required: true, options: []},
+ {cols: [
+ {view: 'richselect', id: 'lst_tipo_relacion', label: 'Tipo de Relación',
+ labelWidth: 140, required: true, options: []},
+ {view: 'checkbox', id: 'chk_relacionados_anticipo',
+ labelRight: 'Solo Anticipos', width: 200, disabled: true}]},
{minHeight: 10, maxHeight: 10},
{cols: [
{view: 'richselect', id: 'filter_cfdi_year', label: 'Año', width: 100,
@@ -145,7 +148,8 @@ var ui_invoice = {
$$('cmd_guardar_relacionados').attachEvent('onItemClick', cmd_guardar_relacionados_click)
$$('cmd_limpiar_relacionados').attachEvent('onItemClick', cmd_limpiar_relacionados_click)
$$('cmd_filter_relacionados').attachEvent('onItemClick', cmd_filter_relacionados_click)
- $$('filter_cfdi_year').attachEvent('onChange', filter_cfdi_year_change)
+ $$('lst_tipo_relacion').attachEvent('onChange', lst_tipo_relacion_change)
+ $$('filter_cfdi_month').attachEvent('onChange', filter_cfdi_month_change)
$$('filter_cfdi_month').attachEvent('onChange', filter_cfdi_month_change)
}}
@@ -175,6 +179,7 @@ var toolbar_invoices_util = [
var toolbar_invoices_generate = {view: 'toolbar', elements: [{},
{view: 'button', id: 'cmd_cfdi_relacionados', label: 'CFDI Relacionados',
type: 'iconButton', autowidth: true, icon: 'file-o'},
+ {view: 'checkbox', id: 'chk_cfdi_anticipo', label: 'Es Anticipo', width: 100},
]}
@@ -249,9 +254,14 @@ var grid_details_cols = [
{id: "unidad", header:{text: 'Unidad', css: 'center'}, width: 100},
{id: 'cantidad', header: {text: 'Cantidad', css: 'center'}, width: 100,
format: webix.i18n.numberFormat, css:'right', editor: 'text'},
- {id: "valor_unitario", header:{text: 'Valor Unitario', css: 'center'}, width: 100,
- format: webix.i18n.priceFormat, css:'right', editor: 'text'},
- {id: "importe", header:{text: 'Importe', css: 'center'}, width: 150, format: webix.i18n.priceFormat, css:'right'},
+ {id: "valor_unitario", header:{text: 'Valor Unitario', css: 'center'},
+ width: 100, format: webix.i18n.priceFormat, css:'right', editor: 'text'},
+ {id: 'descuento', header:{text: 'Descuento', css: 'center'},
+ width: 80, format: webix.i18n.priceFormat, css:'right', editor: 'text'},
+ {id: 'precio_final', hidden: true, header: 'precio_final', width: 80,
+ format: webix.i18n.priceFormat, css:'right'},
+ {id: "importe", header:{text: 'Importe', css: 'center'}, width: 150,
+ format: webix.i18n.priceFormat, css:'right'},
]
diff --git a/source/static/js/ui/partners.js b/source/static/js/ui/partners.js
index e1613e7..96c925e 100644
--- a/source/static/js/ui/partners.js
+++ b/source/static/js/ui/partners.js
@@ -18,6 +18,8 @@ var grid_partners_cols = [
sort: 'string', footer: {text: 'Clientes y Proveedores', colspan: 2}},
{id: 'nombre', header: ['Razón Social', {content: 'textFilter'}],
fillspace:true, sort: 'string'},
+ {id: 'saldo_cliente', header: ['Saldo Cliente', {content: 'numberFilter'}],
+ width: 150, sort: 'int', format: webix.i18n.priceFormat, css: 'right'},
]