diff --git a/source/app/controllers/main.py b/source/app/controllers/main.py
index 6cca2f6..d863ba9 100644
--- a/source/app/controllers/main.py
+++ b/source/app/controllers/main.py
@@ -269,6 +269,22 @@ class AppPreInvoices(object):
resp.status = falcon.HTTP_204
+class AppTickets(object):
+
+ def __init__(self, db):
+ self._db = db
+
+ def on_get(self, req, resp):
+ values = req.params
+ req.context['result'] = self._db.get_tickets(values)
+ resp.status = falcon.HTTP_200
+
+ def on_post(self, req, resp):
+ values = req.params
+ req.context['result'] = self._db.tickets(values)
+ resp.status = falcon.HTTP_200
+
+
class AppEmisor(object):
def __init__(self, db):
diff --git a/source/app/controllers/util.py b/source/app/controllers/util.py
index 6077607..fec29c8 100644
--- a/source/app/controllers/util.py
+++ b/source/app/controllers/util.py
@@ -235,6 +235,10 @@ def now():
return datetime.datetime.now().replace(microsecond=0)
+def today():
+ return datetime.date.today()
+
+
def get_token():
return _get_hash(uuid.uuid4().hex)
diff --git a/source/app/main.py b/source/app/main.py
index 7398382..db2f193 100644
--- a/source/app/main.py
+++ b/source/app/main.py
@@ -16,7 +16,7 @@ from controllers.main import (AppEmpresas,
AppLogin, AppLogout, AppAdmin, AppEmisor, AppConfig,
AppMain, AppValues, AppPartners, AppProducts, AppInvoices, AppFolios,
AppDocumentos, AppFiles, AppPreInvoices, AppCuentasBanco,
- AppMovimientosBanco
+ AppMovimientosBanco, AppTickets
)
@@ -46,11 +46,12 @@ 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('/tickets', AppTickets(db))
api.add_route('/cuentasbanco', AppCuentasBanco(db))
api.add_route('/movbanco', AppMovimientosBanco(db))
-# ~ Activa si usas waitress
+# ~ Activa si usas waitress y NO estas usando servidor web
# ~ api.add_sink(static, '/static')
diff --git a/source/app/models/db.py b/source/app/models/db.py
index 303ca61..7a48236 100644
--- a/source/app/models/db.py
+++ b/source/app/models/db.py
@@ -77,6 +77,9 @@ class StorageEngine(object):
years2 = main.PreFacturas.filter_years()
return [years1, years2]
+ def _get_filteryearsticket(self, values):
+ return main.Tickets.filter_years()
+
def _get_cuentayears(self, values):
return main.CuentasBanco.get_years()
@@ -221,6 +224,9 @@ class StorageEngine(object):
def _get_product(self, values):
return main.Productos.get_by(values)
+ def _get_productokey(self, values):
+ return main.Productos.get_by_key(values)
+
def get_partners(self, values):
return main.Socios.get_(values)
@@ -251,6 +257,18 @@ class StorageEngine(object):
#~ return main.PreFacturas.actualizar(values, id)
return main.PreFacturas.add(values)
+ def tickets(self, values):
+ opt = values.pop('opt')
+ if opt == 'add':
+ return main.Tickets.add(values)
+ if opt == 'cancel':
+ return main.Tickets.cancel(values)
+ if opt == 'invoice':
+ return main.Tickets.invoice(values)
+
+ def get_tickets(self, values):
+ return main.Tickets.get_by(values)
+
def get_invoices(self, values):
return main.Facturas.get_(values)
diff --git a/source/app/models/main.py b/source/app/models/main.py
index a2b89a4..46a1683 100644
--- a/source/app/models/main.py
+++ b/source/app/models/main.py
@@ -110,8 +110,11 @@ def config_main():
except IndexError:
obj = None
+ punto_de_venta = util.get_bool(Configuracion.get_('chk_usar_punto_de_venta'))
data = {
'empresa': 'Empresa Libre',
+ 'punto_de_venta': punto_de_venta
+
}
if not obj is None:
titulo = 'Empresa Libre - {}'
@@ -126,10 +129,15 @@ def config_timbrar():
except IndexError:
return {'cfdi_donativo': False}
+ mp = not util.get_bool(Configuracion.get_('chk_config_ocultar_metodo_pago'))
+ cp = not util.get_bool(
+ Configuracion.get_('chk_config_ocultar_condiciones_pago'))
conf = {
- 'cfdi_anticipo': Configuracion.get_('chk_config_anticipo'),
'cfdi_donativo': obj.es_ong,
+ 'cfdi_anticipo': Configuracion.get_('chk_config_anticipo'),
'cfdi_ine': Configuracion.get_('chk_config_ine'),
+ 'cfdi_metodo_pago': mp,
+ 'cfdi_condicion_pago': cp,
}
return conf
@@ -153,6 +161,21 @@ class Configuracion(BaseModel):
return data[0].valor
return ''
+ if keys['fields'] == 'productos':
+ fields = (
+ 'chk_config_cuenta_predial',
+ 'chk_config_codigo_barras',
+ 'chk_config_precio_con_impuestos',
+ )
+ data = (Configuracion
+ .select()
+ .where(Configuracion.clave.in_(fields))
+ )
+ values = {r.clave: r.valor for r in data}
+ values['default_tax'] = SATImpuestos.select()[0].id
+ values['default_unidad'] = SATUnidades.get_default()
+ return values
+
if keys['fields'] == 'correo':
fields = ('correo_servidor', 'correo_puerto', 'correo_ssl',
'correo_usuario', 'correo_contra', 'correo_copia',
@@ -180,17 +203,25 @@ class Configuracion(BaseModel):
)
elif keys['fields'] == 'configotros':
fields = (
+ 'chk_config_ocultar_metodo_pago',
+ 'chk_config_ocultar_condiciones_pago',
'chk_config_anticipo',
'chk_config_cuenta_predial',
+ 'chk_config_codigo_barras',
+ 'chk_config_precio_con_impuestos',
'chk_config_ine',
+ 'chk_usar_punto_de_venta',
)
data = (Configuracion
.select()
.where(Configuracion.clave.in_(fields))
)
- elif keys['fields'] == 'productos':
+ elif keys['fields'] == 'timbrar':
fields = (
- 'chk_config_cuenta_predial',
+ 'chk_config_ocultar_metodo_pago',
+ 'chk_config_ocultar_condiciones_pago',
+ 'chk_config_anticipo',
+ 'chk_config_ine',
)
data = (Configuracion
.select()
@@ -844,6 +875,13 @@ class SATUnidades(BaseModel):
return {'ok': result}
+ @classmethod
+ def get_default(cls):
+ obj = SATUnidades.select()[0]
+ if obj.default:
+ return obj.id
+ return 0
+
@classmethod
def get_activos(cls):
rows = (SATUnidades
@@ -1752,7 +1790,7 @@ class Socios(BaseModel):
fields = cls._clean(cls, values)
try:
obj = Socios.create(**fields)
- except IntegrityError:
+ except IntegrityError as e:
msg = 'Ya existe el RFC y Razón Social'
data = {'ok': False, 'row': {}, 'new': True, 'msg': msg}
return data
@@ -1973,6 +2011,34 @@ class Productos(BaseModel):
value += 1
return {'value': value}
+ @classmethod
+ def get_by_key(cls, values):
+ clave = values.get('key', '')
+ row = (Productos
+ .select(
+ Productos.id,
+ Productos.clave,
+ Productos.clave_sat,
+ Productos.descripcion,
+ SATUnidades.name.alias('unidad'),
+ Productos.valor_unitario,
+ Productos.descuento)
+ .join(SATUnidades).switch(Productos)
+ .where((Productos.clave==clave) | (Productos.codigo_barras==clave))
+ .dicts()
+ )
+ if len(row):
+ id = row[0]['id']
+ model_pt = Productos.impuestos.get_through_model()
+ taxes = tuple(model_pt
+ .select(
+ model_pt.productos_id.alias('product'),
+ model_pt.satimpuestos_id.alias('tax'))
+ .where(model_pt.productos_id==id).dicts())
+ return {'ok': True, 'row': row[0], 'taxes': taxes}
+
+ return {'ok': False}
+
@classmethod
def get_by(cls, values):
clave = values.get('id', '')
@@ -2723,17 +2789,9 @@ class Facturas(BaseModel):
totals_tax[tax.id] = tax
for tax in totals_tax.values():
- # ~ 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
invoice_tax = {
'factura': invoice.id,
@@ -2743,26 +2801,6 @@ class Facturas(BaseModel):
}
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
-
- # ~ 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) \
+ locales_traslados - locales_retenciones
@@ -3834,6 +3872,339 @@ class Tickets(BaseModel):
class Meta:
order_by = ('fecha',)
+ def _get_folio(self, serie):
+ inicio = (Tickets
+ .select(fn.Max(Tickets.folio).alias('mf'))
+ .where(Tickets.serie==serie)
+ .order_by(SQL('mf'))
+ .scalar())
+
+ if inicio is None:
+ inicio = 1
+ else:
+ inicio += 1
+
+ return inicio
+
+ def _calcular_totales(self, ticket, productos):
+ subtotal = 0
+ descuento_cfdi = 0
+ totals_tax = {}
+ total_trasladados = None
+
+ for producto in productos:
+ id_producto = producto.pop('id')
+ p = Productos.get(Productos.id==id_producto)
+ producto['descripcion'] = p.descripcion
+ producto['ticket'] = ticket.id
+ producto['producto'] = id_producto
+
+ cantidad = float(producto['cantidad'])
+ valor_unitario = float(p.valor_unitario)
+ descuento = float(producto['descuento'])
+ precio_final = valor_unitario - descuento
+ importe = round(cantidad * precio_final, DECIMALES)
+
+ producto['cantidad'] = cantidad
+ producto['valor_unitario'] = valor_unitario
+ producto['descuento'] = descuento
+ producto['precio_final'] = precio_final
+ producto['importe'] = importe
+
+ descuento_cfdi += descuento
+ subtotal += importe
+
+ TicketsDetalle.create(**producto)
+
+ base = producto['importe']
+ 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
+
+ if tax.id in totals_tax:
+ totals_tax[tax.id].base += base
+ totals_tax[tax.id].importe += impuesto_producto
+ else:
+ tax.base = base
+ tax.importe = impuesto_producto
+ totals_tax[tax.id] = tax
+
+
+ for tax in totals_tax.values():
+ if tax.tipo == 'E':
+ continue
+
+ ticket_tax = {
+ 'ticket': ticket.id,
+ 'impuesto': tax.id,
+ 'base': tax.base,
+ 'importe': tax.importe,
+ }
+ TicketsImpuestos.create(**ticket_tax)
+
+ total = subtotal + (total_trasladados or 0)
+ data = {
+ 'subtotal': subtotal + descuento,
+ 'descuento': descuento_cfdi,
+ 'total': total,
+ 'total_trasladados': total_trasladados,
+ }
+ return data
+
+ @classmethod
+ def add(cls, values):
+ productos = util.loads(values.pop('productos'))
+
+ values['serie'] = 'T'
+ values['folio'] = cls._get_folio(cls, values['serie'])
+
+ with database_proxy.atomic() as txn:
+ obj = Tickets.create(**values)
+ totals = cls._calcular_totales(cls, obj, productos)
+ obj.subtotal = totals['subtotal']
+ obj.descuento = totals['descuento']
+ obj.total_trasladados = totals['total_trasladados']
+ obj.total = totals['total']
+ obj.save()
+
+ row = {
+ 'id': obj.id,
+ 'folio': obj.folio,
+ 'fecha': obj.fecha,
+ 'estatus': obj.estatus,
+ 'total': obj.total,
+ }
+ data = {'ok': True, 'row': row}
+ return data
+
+ def _get_folio_invoice(self, serie):
+ inicio = (Facturas
+ .select(fn.Max(Facturas.folio).alias('mf'))
+ .where(Facturas.serie==serie)
+ .order_by(SQL('mf'))
+ .scalar())
+
+ if inicio is None:
+ inicio = 1
+ else:
+ inicio += 1
+
+ return inicio
+
+ def _cancel_tickets(self, invoice, tickets):
+ query = (Tickets
+ .update(estatus='Facturado', cancelado=True, factura=invoice)
+ .where(Tickets.id.in_(tickets))
+ )
+ result = query.execute()
+ print (result)
+ return
+
+ def _calculate_totals_invoice(self, invoice, tickets):
+ subtotal = 0
+ descuento_cfdi = 0
+ totals_tax = {}
+ total_trasladados = None
+ total_retenciones = None
+
+ details = TicketsDetalle.select().where(TicketsDetalle.ticket.in_(tickets))
+
+ for detail in details:
+ product = {}
+ p = detail.producto
+ product['unidad'] = p.unidad.key
+ product['clave'] = p.clave
+ product['clave_sat'] = p.clave_sat
+
+ product['factura'] = invoice.id
+ product['producto'] = p.id
+ product['descripcion'] = detail.descripcion
+
+ cantidad = float(detail.cantidad)
+ valor_unitario = float(detail.valor_unitario)
+ descuento = float(detail.descuento)
+ precio_final = valor_unitario - descuento
+ importe = round(cantidad * precio_final, DECIMALES)
+
+ product['cantidad'] = cantidad
+ product['valor_unitario'] = valor_unitario
+ product['descuento'] = round(descuento * cantidad, DECIMALES)
+ product['precio_final'] = precio_final
+ product['importe'] = round(cantidad * valor_unitario, DECIMALES)
+
+ descuento_cfdi += product['descuento']
+ subtotal += product['importe']
+
+ 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].base += base
+ totals_tax[tax.id].suma_impuestos += impuesto_producto
+ else:
+ tax.base = base
+ tax.suma_impuestos = impuesto_producto
+ totals_tax[tax.id] = tax
+
+ for tax in totals_tax.values():
+ if tax.tipo == 'E':
+ continue
+
+
+ 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)
+ total_mn = round(total * invoice.tipo_cambio, DECIMALES)
+ data = {
+ 'subtotal': subtotal,
+ 'descuento': descuento_cfdi,
+ 'total': total,
+ 'total_mn': total_mn,
+ 'total_trasladados': total_trasladados,
+ 'total_retenciones': total_retenciones,
+ }
+ return data
+
+ @classmethod
+ def invoice(cls, values):
+ is_invoice_day = util.get_bool(values['is_invoice_day'])
+ id_client = int(values['client'])
+ tickets = util.loads(values['tickets'])
+
+ if is_invoice_day:
+ filters = (
+ Socios.rfc == 'XAXX010101000' and
+ Socios.slug == 'publico_en_general')
+ try:
+ client = Socios.get(filters)
+ except Socios.DoesNotExist:
+ msg = 'No existe el cliente Público en General. Agregalo primero.'
+ data = {'ok': False, 'msg': msg}
+ return data
+ else:
+ client = Socios.get(Socios.id==id_client)
+
+ emisor = Emisor.select()[0]
+ data = {}
+ data['cliente'] = client
+ data['serie'] = 'T'
+ data['folio'] = cls._get_folio_invoice(cls, data['serie'])
+ data['forma_pago'] = client.forma_pago.key
+ data['tipo_cambio'] = 1.00
+ data['lugar_expedicion'] = emisor.cp_expedicion or emisor.codigo_postal
+ if client.uso_cfdi is None:
+ data['uso_cfdi'] = 'P01'
+ else:
+ data['uso_cfdi'] = client.uso_cfdi.key
+ data['regimen_fiscal'] = emisor.regimenes[0].key
+
+ with database_proxy.atomic() as txn:
+ obj = Facturas.create(**data)
+ totals = cls._calculate_totals_invoice(cls, obj, tickets)
+ obj.subtotal = totals['subtotal']
+ obj.descuento = totals['descuento']
+ obj.total_trasladados = totals['total_trasladados']
+ obj.total_retenciones = totals['total_retenciones']
+ obj.total = totals['total']
+ obj.saldo = totals['total']
+ obj.total_mn = totals['total_mn']
+ obj.save()
+ cls._cancel_tickets(cls, obj, tickets)
+
+ msg = 'Factura generada correctamente.
Enviando a timbrar'
+ data = {'ok': True, 'msg': msg, 'id': obj.id}
+ return data
+
+ @classmethod
+ def cancel(cls, values):
+ id = int(values['id'])
+ msg = 'Ticket cancelado correctamente'
+ u = {'cancelado': True, 'estatus': 'Cancelado'}
+ obj = Tickets.update(**u).where(Tickets.id==id)
+ result = bool(obj.execute())
+ row = {'estatus': 'Cancelado'}
+ return {'ok': result, 'row': row, 'msg': msg}
+
+ def _get_filters(self, values):
+ opt = values.get('opt', '')
+ if not opt:
+ return
+
+ if opt == 'active':
+ filters = (Tickets.cancelado==False)
+ return filters
+
+ if opt == 'today':
+ t = util.today()
+ filters = (
+ (Tickets.fecha.day == t.day) &
+ (Tickets.fecha.month == t.month) &
+ (Tickets.fecha.year == t.year)
+ )
+ return filters
+
+ if opt == 'yearmonth':
+ if values['year'] == '-1':
+ fy = (Tickets.fecha.year > 0)
+ else:
+ fy = (Tickets.fecha.year == int(values['year']))
+ if values['month'] == '-1':
+ fm = (Tickets.fecha.month > 0)
+ else:
+ fm = (Tickets.fecha.month == int(values['month']))
+ filters = (fy & fm)
+ return filters
+
+ return
+
+ @classmethod
+ def get_by(cls, values):
+ filters = cls._get_filters(cls, values)
+
+ rows = tuple(Tickets
+ .select(
+ Tickets.id,
+ Tickets.serie,
+ Tickets.folio,
+ Tickets.fecha,
+ Tickets.estatus,
+ Tickets.total)
+ .where(filters)
+ .dicts()
+ )
+ return {'ok': True, 'rows': rows}
+
+ @classmethod
+ def filter_years(cls):
+ data = [{'id': -1, 'value': 'Todos'}]
+ rows = (Tickets
+ .select(Tickets.fecha.year.alias('year'))
+ .group_by(Tickets.fecha.year)
+ .order_by(Tickets.fecha.year)
+ )
+ if not rows is None:
+ data += [{'id': int(r.year), 'value': int(r.year)} for r in rows]
+ return tuple(data)
+
class TicketsDetalle(BaseModel):
ticket = ForeignKeyField(Tickets)
diff --git a/source/static/css/app.css b/source/static/css/app.css
index 1634313..0bd1d39 100644
--- a/source/static/css/app.css
+++ b/source/static/css/app.css
@@ -24,6 +24,23 @@
font-size: 125%;
color: DarkRed;
}
+.right_footer2 {
+ text-align: right;
+ font-weight: bold;
+ font-size: 150%;
+ color: DarkRed;
+}
+.right_footer3 {
+ text-align: right;
+ font-weight: bold;
+ font-size: 120%;
+ color: DarkBlue;
+}
+.footer3 {
+ font-weight: bold;
+ font-size: 120%;
+ color: DarkBlue;
+}
.lbl_partner {
font-weight: bold;
@@ -37,6 +54,10 @@
color: red;
}
+.cancel {
+ color: red;
+}
+
.cmd_close_partner div button {
background-color: red !important;
diff --git a/source/static/js/controller/admin.js b/source/static/js/controller/admin.js
index 52bd83a..4e63fde 100644
--- a/source/static/js/controller/admin.js
+++ b/source/static/js/controller/admin.js
@@ -50,9 +50,14 @@ var controllers = {
$$('txt_plantilla_factura_33').attachEvent('onItemClick', txt_plantilla_factura_33_click)
$$('txt_plantilla_factura_33j').attachEvent('onItemClick', txt_plantilla_factura_33j_click)
$$('txt_plantilla_donataria').attachEvent('onItemClick', txt_plantilla_donataria_click)
+ $$('chk_config_ocultar_metodo_pago').attachEvent('onItemClick', chk_config_item_click)
+ $$('chk_config_ocultar_condiciones_pago').attachEvent('onItemClick', chk_config_item_click)
$$('chk_config_anticipo').attachEvent('onItemClick', chk_config_item_click)
$$('chk_config_ine').attachEvent('onItemClick', chk_config_item_click)
$$('chk_config_cuenta_predial').attachEvent('onItemClick', chk_config_item_click)
+ $$('chk_config_codigo_barras').attachEvent('onItemClick', chk_config_item_click)
+ $$('chk_config_precio_con_impuestos').attachEvent('onItemClick', chk_config_item_click)
+ $$('chk_usar_punto_de_venta').attachEvent('onItemClick', chk_config_item_click)
$$('cmd_subir_bdfl').attachEvent('onItemClick', cmd_subir_bdfl_click)
$$('up_bdfl').attachEvent('onUploadComplete', up_bdfl_upload_complete)
diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js
index 2e03ff9..0de74b5 100644
--- a/source/static/js/controller/invoices.js
+++ b/source/static/js/controller/invoices.js
@@ -41,14 +41,6 @@ function get_series(){
}
-function get_forma_pago(){
- webix.ajax().get('/values/formapago', {key: true}, function(text, data){
- var values = data.json()
- $$('lst_forma_pago').getList().parse(values)
- })
-}
-
-
function get_monedas(){
webix.ajax().get('/values/monedas', function(text, data){
var values = data.json()
@@ -105,7 +97,7 @@ function default_config(){
table_taxes.insert(values)
})
get_series()
- get_forma_pago()
+ get_forma_pago('lst_forma_pago')
get_monedas()
get_uso_cfdi()
get_regimen_fiscal()
@@ -116,8 +108,11 @@ function default_config(){
webix.ajax().sync().get('/values/configtimbrar', function(text, data){
var values = data.json()
+ //~ showvar(values)
show('chk_cfdi_anticipo', values.cfdi_anticipo)
show('chk_cfdi_donativo', values.cfdi_donativo)
+ show('lst_metodo_pago', values.cfdi_metodo_pago)
+ show('txt_condicion_pago', values.cfdi_condicion_pago)
if(!values.cfdi_ine || values.cfdi_ine == '0'){
$$('tv_invoice').getTabbar().hideOption('INE')
}else{
diff --git a/source/static/js/controller/main.js b/source/static/js/controller/main.js
index 1dfb30f..4d76373 100644
--- a/source/static/js/controller/main.js
+++ b/source/static/js/controller/main.js
@@ -9,7 +9,16 @@ function configuracion_inicial(){
webix.ajax().get('/values/main', function(text, data){
var values = data.json()
$$('lbl_title_main').setValue(values.empresa)
+ //~ showvar()
+ if(values.punto_de_venta){
+ var node = {
+ id: 'app_tickets',
+ icon: 'money',
+ value: 'Punto de venta'}
+ $$('main_sidebar').add(node, 4)
+ }
})
+
}
@@ -40,15 +49,7 @@ var controllers = {
$$("es_proveedor").attachEvent( "onChange", is_supplier_change)
$$("rfc").attachEvent( "onBlur", rfc_lost_focus)
$$('multi').attachEvent('onViewChange', multi_change)
- //~ Products
- $$("cmd_new_product").attachEvent("onItemClick", cmd_new_product_click)
- $$("cmd_edit_product").attachEvent("onItemClick", cmd_edit_product_click)
- $$("cmd_delete_product").attachEvent("onItemClick", cmd_delete_product_click)
- $$("cmd_save_product").attachEvent("onItemClick", cmd_save_product_click)
- $$("cmd_cancel_product").attachEvent("onItemClick", cmd_cancel_product_click)
- $$("chk_automatica").attachEvent("onChange", chk_automatica_change)
- $$("valor_unitario").attachEvent("onChange", valor_unitario_change)
- $$("clave_sat").attachEvent('onSearchIconClick', clave_sat_icon_click)
+
//~ Invoices
$$('cmd_new_invoice').attachEvent("onItemClick", cmd_new_invoice_click)
$$('cmd_refacturar').attachEvent("onItemClick", cmd_refacturar_click)
@@ -86,7 +87,9 @@ var controllers = {
webix.extend($$('grid_invoices'), webix.ProgressBar)
+ products_controllers.init()
bancos_controllers.init()
+ tickets_controllers.init()
}
}
@@ -195,6 +198,14 @@ function multi_change(prevID, nextID){
return
}
+ if(nextID == 'app_tickets'){
+ active = $$('multi_tickets').getActiveId()
+ if(active == 'tickets_home'){
+ configuracion_inicial_ticket()
+ }
+ return
+ }
+
if(nextID == 'app_invoices'){
active = $$('multi_invoices').getActiveId()
if(active == 'invoices_home'){
diff --git a/source/static/js/controller/partners.js b/source/static/js/controller/partners.js
index 93b4346..74185e4 100644
--- a/source/static/js/controller/partners.js
+++ b/source/static/js/controller/partners.js
@@ -155,7 +155,7 @@ function cmd_save_partner_click(id, e, node){
if (values.ok) {
update_grid_partner(values)
} else {
- msg_error(msg)
+ msg_error(values.msg)
}
}
})
diff --git a/source/static/js/controller/products.js b/source/static/js/controller/products.js
index 0db84e9..62a30e5 100644
--- a/source/static/js/controller/products.js
+++ b/source/static/js/controller/products.js
@@ -1,5 +1,21 @@
+var products_controllers = {
+ init: function(){
+ $$('cmd_new_product').attachEvent('onItemClick', cmd_new_product_click)
+ $$("cmd_edit_product").attachEvent("onItemClick", cmd_edit_product_click)
+ $$("cmd_delete_product").attachEvent("onItemClick", cmd_delete_product_click)
+ $$("cmd_save_product").attachEvent("onItemClick", cmd_save_product_click)
+ $$("cmd_cancel_product").attachEvent("onItemClick", cmd_cancel_product_click)
+ $$("chk_automatica").attachEvent("onChange", chk_automatica_change)
+ $$("valor_unitario").attachEvent("onChange", valor_unitario_change)
+ //~ $$("clave_sat").attachEvent('onSearchIconClick', clave_sat_icon_click)
+ $$('precio_con_impuestos').attachEvent('onChange', precio_con_impuestos_change)
+ $$('precio_con_impuestos').attachEvent('onTimedKeyPress', precio_con_impuestos_key_up);
+ }
+}
+
+
function configurar_productos(){
webix.ajax().get('/config', {'fields': 'productos'}, {
error: function(text, data, xhr) {
@@ -10,6 +26,10 @@ function configurar_productos(){
var values = data.json()
//~ showvar(values)
show('cuenta_predial', values.chk_config_cuenta_predial)
+ show('codigo_barras', values.chk_config_codigo_barras)
+ show('precio_con_impuestos', values.chk_config_precio_con_impuestos)
+ $$('unidad').setValue(values.default_unidad)
+ $$('grid_product_taxes').select(values.default_tax)
}
})
}
@@ -228,6 +248,49 @@ function valor_unitario_change(new_value, old_value){
}
-function clave_sat_icon_click(){
- show('Buscar SAT')
+function precio_con_impuestos_change(new_value, old_value){
+ if(!isFinite(new_value)){
+ this.config.value = old_value
+ this.refresh()
+ }
}
+
+
+//~ function clave_sat_icon_click(){
+ //~ show('Buscar SAT')
+//~ }
+
+function calcular_sin_impuestos(value, taxes){
+ var vu = $$('valor_unitario')
+ var precio = value
+
+ taxes.forEach(function(tax){
+ var tasa = 1.00 + tax.tasa.to_float()
+ if(tax.tipo == 'T' && tax.name == 'IVA'){
+ precio = (value / tasa).round(DECIMALES)
+ }
+ })
+ vu.setValue(precio)
+}
+
+function precio_con_impuestos_key_up(){
+ var value = this.getValue()
+
+ if(!value){
+ return
+ }
+
+ var taxes = $$('grid_product_taxes').getSelectedItem(true)
+ if (taxes.length == 0){
+ msg = 'Selecciona al menos un impuesto'
+ msg_error(msg)
+ return
+ }
+
+ if(!isFinite(value)){
+ msg = 'Captura un valor válido'
+ msg_error(msg)
+ return
+ }
+ calcular_sin_impuestos(parseFloat(value), taxes)
+}
\ No newline at end of file
diff --git a/source/static/js/controller/tickets.js b/source/static/js/controller/tickets.js
new file mode 100644
index 0000000..741b5f1
--- /dev/null
+++ b/source/static/js/controller/tickets.js
@@ -0,0 +1,617 @@
+var query = []
+var msg = ''
+
+
+var tickets_controllers = {
+ init: function(){
+ $$('cmd_nuevo_ticket').attachEvent('onItemClick', cmd_nuevo_ticket_click)
+ $$('cmd_ticket_to_invoice').attachEvent('onItemClick', cmd_ticket_to_invoice_click)
+ $$('cmd_generar_ticket').attachEvent('onItemClick', cmd_generar_ticket_click)
+ $$('cmd_cerrar_ticket').attachEvent('onItemClick', cmd_cerrar_ticket_click)
+ $$('cmd_new_invoice_from_ticket').attachEvent('onItemClick', cmd_new_invoice_from_ticket_click)
+ $$('cmd_close_ticket_invoice').attachEvent('onItemClick', cmd_cerrar_ticket_click)
+ $$('cmd_cancelar_ticket').attachEvent('onItemClick', cmd_cancelar_ticket_click)
+ $$('cmd_move_tickets_right').attachEvent('onItemClick', cmd_move_tickets_right_click)
+ $$('cmd_move_tickets_left').attachEvent('onItemClick', cmd_move_tickets_left_click)
+ $$('tsearch_product_key').attachEvent('onKeyPress', tsearch_product_key_press)
+ $$('grid_tdetails').attachEvent('onItemClick', grid_ticket_details_click)
+ $$('grid_tdetails').attachEvent('onBeforeEditStop', grid_tickets_details_before_edit_stop)
+ $$('gt_productos_found').attachEvent('onValueSuggest', gt_productos_found_click)
+ $$('filter_year_ticket').attachEvent('onChange', filter_year_ticket_change)
+ $$('filter_month_ticket').attachEvent('onChange', filter_month_ticket_change)
+ $$('chk_is_invoice_day').attachEvent('onChange', chk_is_invoice_day_change)
+ $$('grid_tickets_active').attachEvent('onItemDblClick', grid_tickets_active_double_click)
+ $$('grid_tickets_invoice').attachEvent('onItemDblClick', grid_tickets_invoice_double_click)
+ $$('tsearch_client_key').attachEvent('onKeyPress', tsearch_client_key_press)
+ $$('grid_ticket_clients_found').attachEvent('onValueSuggest', grid_ticket_clients_found_click)
+
+ webix.extend($$('grid_tickets'), webix.ProgressBar)
+ webix.extend($$('grid_tickets_active'), webix.ProgressBar)
+ }
+}
+
+
+function current_dates_tickets(){
+ var fy = $$('filter_year_ticket')
+ var fm = $$('filter_month_ticket')
+ var d = new Date()
+
+ fy.blockEvent()
+ fm.blockEvent()
+
+ fm.setValue(d.getMonth() + 1)
+ webix.ajax().sync().get('/values/filteryearsticket', function(text, data){
+ var values = data.json()
+ fy.getList().parse(values)
+ fy.setValue(d.getFullYear())
+ })
+
+ fy.unblockEvent()
+ fm.unblockEvent()
+}
+
+
+function get_tickets(filters){
+ if(filters == undefined){
+ filters = {'opt': 'today'}
+ }
+
+ var grid = $$('grid_tickets')
+ grid.showProgress({type: 'icon'})
+
+ webix.ajax().get('/tickets', filters, {
+ error: function(text, data, xhr) {
+ msg_error('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_ticket_change(nv, ov){
+ var fm = $$('filter_month_ticket')
+ filters = {'opt': 'yearmonth','year': nv, 'month': fm.getValue()}
+ get_tickets(filters)
+}
+
+
+function filter_month_ticket_change(nv, ov){
+ var fy = $$('filter_year_ticket')
+ filters = {'opt': 'yearmonth','year': fy.getValue(), 'month': nv}
+ get_tickets(filters)
+}
+
+
+function configuracion_inicial_ticket(){
+ current_dates_tickets()
+ get_tickets()
+}
+
+
+function get_active_tickets(grid){
+ filters = {'opt': 'active'}
+ grid.showProgress({type: 'icon'})
+
+ webix.ajax().get('/tickets', filters, {
+ error: function(text, data, xhr) {
+ msg_error('Error al consultar')
+ },
+ success: function(text, data, xhr) {
+ var values = data.json();
+ grid.clearAll();
+ if (values.ok){
+ grid.parse(values.rows, 'json')
+ }
+ }
+ })
+}
+
+
+function configuracion_inicial_ticket_to_invoice(){
+ var grid = $$('grid_tickets_active')
+ var gridt = $$('grid_tickets_invoice')
+ var form = $$('form_ticket_invoice')
+
+ get_active_tickets(grid)
+ form.setValues({id_partner: 0, lbl_tclient: 'Ninguno'})
+ gridt.attachEvent('onAfterAdd', function(id, index){
+ gridt.adjustColumn('index')
+ gridt.adjustColumn('folio', 'all')
+ gridt.adjustColumn('fecha', 'all')
+ });
+ gridt.clearAll()
+}
+
+
+function configuracion_inicial_nuevo_ticket(){
+ var grid = $$('grid_tdetails')
+
+ webix.ajax().sync().get('/values/taxes', function(text, data){
+ var values = data.json()
+ table_taxes.clear()
+ table_taxes.insert(values)
+ })
+ get_forma_pago('lst_ticket_forma_pago')
+ grid.clearAll()
+ table_pt.clear()
+ table_totals.clear()
+}
+
+
+function cmd_nuevo_ticket_click(){
+ configuracion_inicial_nuevo_ticket()
+ $$('multi_tickets').setValue('tickets_new')
+}
+
+
+function cmd_ticket_to_invoice_click(){
+ configuracion_inicial_ticket_to_invoice()
+ $$('multi_tickets').setValue('tickets_invoice')
+}
+
+
+function validar_ticket(){
+ var grid = $$('grid_tdetails')
+
+ if(!grid.count()){
+ webix.UIManager.setFocus('tsearch_product_key')
+ msg = 'Agrega al menos un producto'
+ msg_error(msg)
+ return false
+ }
+
+ return true
+}
+
+
+function guardar_ticket(values){
+ var gd = $$('grid_tdetails')
+ var grid = $$('grid_tickets')
+
+ var rows = gd.data.getRange()
+ for (i = 0; i < rows.length; i++) {
+ delete rows[i]['delete']
+ delete rows[i]['clave']
+ delete rows[i]['clave_sat']
+ 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()
+ data['opt'] = 'add'
+ data['productos'] = rows
+ data['forma_pago'] = values.forma_pago
+
+ webix.ajax().sync().post('tickets', data, {
+ error:function(text, data, XmlHttpRequest){
+ msg = 'Ocurrio un error, consulta a soporte técnico'
+ msg_error(msg)
+ },
+ success:function(text, data, XmlHttpRequest){
+ values = data.json();
+ if(values.ok){
+ msg_ok('Ticket generado correctamente')
+ $$('form_new_ticket').setValues({})
+ gd.clearAll()
+ grid.add(values.row)
+ $$('multi_tickets').setValue('tickets_home')
+ }else{
+ msg_error(values.msg)
+ }
+ }
+ })
+
+}
+
+
+function cmd_generar_ticket_click(){
+ var form = this.getFormView();
+
+ if(!form.validate()) {
+ msg_error('Valores inválidos')
+ return
+ }
+
+ var values = form.getValues()
+ if(!validar_ticket()){
+ return
+ }
+
+ msg = '¿Todos los datos son correctos?
'
+ msg += '¿Estás seguro de generar este Ticket?'
+
+ webix.confirm({
+ title: 'Generar Ticket',
+ ok: 'Si',
+ cancel: 'No',
+ type: 'confirm-error',
+ text: msg,
+ callback:function(result){
+ if(result){
+ guardar_ticket(values)
+ }
+ }
+ })
+}
+
+
+function cmd_cerrar_ticket_click(){
+ $$('multi_tickets').setValue('tickets_home')
+}
+
+
+function calcular_precio_con_impuestos(precio, taxes){
+ var precio_final = precio
+
+ for(var tax of taxes){
+ impuesto = table_taxes.findOne({'id': tax.tax})
+ if(impuesto.tipo == 'E'){
+ continue
+ }
+ var base = precio
+ if(impuesto.tipo == 'R'){
+ base = (precio * -1).round(DECIMALES)
+ }
+ precio_final += (impuesto.tasa * base).round(DECIMALES)
+ }
+
+ return precio_final
+}
+
+
+function agregar_producto(values){
+ var taxes = values.taxes
+ var producto = values.row
+ var form = $$('form_new_ticket')
+ var grid = $$('grid_tdetails')
+ var row = grid.getItem(producto.id)
+ var precio_final = 0.0
+
+ if(row == undefined){
+ producto['cantidad'] = 1
+ producto['valor_unitario'] = calcular_precio_con_impuestos(
+ parseFloat(producto['valor_unitario']), taxes)
+ producto['importe'] = producto['valor_unitario']
+ grid.add(producto)
+ }else{
+ producto['cantidad'] = parseFloat(row.cantidad) + 1
+ producto['descuento'] = parseFloat(row.descuento)
+ producto['valor_unitario'] = parseFloat(row.valor_unitario)
+ precio_final = producto['valor_unitario'] - producto['descuento']
+ producto['importe'] = (precio_final * producto['cantidad']).round(DECIMALES)
+ grid.updateItem(row.id, producto)
+ }
+ form.setValues({tsearch_product_key: '', tsearch_product_name: ''}, true)
+}
+
+
+function buscar_producto_key(key){
+ webix.ajax().get('/values/productokey', {'key': key}, {
+ error: function(text, data, xhr) {
+ msg_error('Error al consultar')
+ },
+ success: function(text, data, xhr){
+ var values = data.json()
+ if (values.ok){
+ agregar_producto(values)
+ } else {
+ msg = 'No se encontró la clave
' + key
+ msg_error(msg)
+ }
+ }
+ })
+
+}
+
+
+function tsearch_product_key_press(code, e){
+ var value = this.getValue()
+ if(code == 13 && value.trim().length > 0){
+ buscar_producto_key(value.trim())
+ }
+}
+
+
+
+function grid_ticket_details_click(id, e, node){
+ if(id.column != 'delete'){
+ return
+ }
+ var grid = $$('grid_tdetails')
+ grid.remove(id.row)
+}
+
+
+function grid_tickets_details_before_edit_stop(state, editor){
+ var grid = $$('grid_tdetails')
+ var row = grid.getItem(editor.row)
+
+ if(editor.column == 'cantidad'){
+ var cantidad = parseFloat(state.value)
+ if(isNaN(cantidad)){
+ msg = 'La cantidad debe ser un número'
+ msg_error(msg)
+ grid.blockEvent()
+ state.value = state.old
+ grid.editCancel()
+ grid.unblockEvent()
+ return true
+ }
+ var valor_unitario = parseFloat(row['valor_unitario'])
+ var descuento = parseFloat(row['descuento'])
+ }
+
+ if(editor.column == 'valor_unitario'){
+ var valor_unitario = parseFloat(state.value)
+ if(isNaN(valor_unitario)){
+ msg = 'El valor unitario debe ser un número'
+ msg_error(msg)
+ grid.blockEvent()
+ state.value = state.old
+ grid.editCancel()
+ grid.unblockEvent()
+ return true
+ }
+ var cantidad = parseFloat(row['cantidad'])
+ var descuento = parseFloat(row['descuento'])
+ }
+
+ if(editor.column == 'descuento'){
+ var descuento = parseFloat(state.value)
+ if(isNaN(descuento)){
+ msg = 'El descuento debe ser un número'
+ msg_error(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()
+}
+
+
+function gt_productos_found_click(obj){
+ buscar_producto_key(obj.clave)
+}
+
+
+function cancel_ticket(id){
+ var grid = $$('grid_tickets')
+ var data = new Object()
+ data['opt'] = 'cancel'
+ data['id'] = id
+
+ webix.ajax().sync().post('tickets', data, {
+ error:function(text, data, XmlHttpRequest){
+ msg = 'Ocurrio un error, consulta a soporte técnico'
+ msg_error(msg)
+ },
+ success:function(text, data, XmlHttpRequest){
+ values = data.json();
+ if(values.ok){
+ grid.updateItem(id, values.row)
+ msg_ok(values.msg)
+ }else{
+ msg_error(values.msg)
+ }
+ }
+ })
+}
+
+
+function cmd_cancelar_ticket_click(){
+ var grid = $$('grid_tickets')
+
+ if(grid.count() == 0){
+ return
+ }
+
+ var row = grid.getSelectedItem()
+ if (row == undefined){
+ msg_error('Selecciona un ticket')
+ return
+ }
+
+ msg = '¿Estás seguro de cancelar el siguiente Ticket?
'
+ msg += 'Folio: ' + row['folio']
+ msg += '
ESTA ACCIÓN NO SE PUEDE DESHACER'
+ webix.confirm({
+ title:'Cancelar Ticket',
+ ok:'Si',
+ cancel:'No',
+ type:'confirm-error',
+ text:msg,
+ callback:function(result){
+ if (result){
+ cancel_ticket(row['id'])
+ }
+ }
+ })
+
+}
+
+
+function chk_is_invoice_day_change(new_value, old_value){
+ var value = Boolean(new_value)
+ show('fs_ticket_search_client', !value)
+}
+
+
+function send_timbrar_invoice(id){
+ webix.ajax().get('/values/timbrar', {id: id}, function(text, data){
+ var values = data.json()
+ if(values.ok){
+ msg_ok(values.msg)
+ }else{
+ webix.alert({
+ title: 'Error al Timbrar',
+ text: values.msg,
+ type: 'alert-error'
+ })
+ }
+ })
+
+}
+
+
+function save_ticket_to_invoice(data){
+ webix.ajax().sync().post('tickets', data, {
+ error:function(text, data, XmlHttpRequest){
+ msg = 'Ocurrio un error, consulta a soporte técnico'
+ msg_error(msg)
+ },
+ success:function(text, data, XmlHttpRequest){
+ values = data.json();
+ if(values.ok){
+ msg_ok(values.msg)
+ send_timbrar_invoice(values.id)
+ $$('multi_tickets').setValue('tickets_home')
+ }else{
+ msg_error(values.msg)
+ }
+ }
+ })
+}
+
+
+function cmd_new_invoice_from_ticket_click(){
+ var form = this.getFormView();
+ var chk = $$('chk_is_invoice_day')
+ var grid = $$('grid_tickets_invoice')
+ var values = form.getValues()
+ var tickets = []
+
+ if(!chk.getValue()){
+ if(values.id_partner == 0){
+ webix.UIManager.setFocus('tsearch_client_name')
+ msg = 'Selecciona un cliente'
+ msg_error(msg)
+ return false
+ }
+ }
+
+ if(!grid.count()){
+ msg = 'Agrega al menos un ticket a facturar'
+ msg_error(msg)
+ return false
+ }
+
+ grid.eachRow(function(row){
+ tickets.push(row)
+ })
+
+ var data = new Object()
+ data['client'] = values.id_partner
+ data['tickets'] = tickets
+ data['is_invoice_day'] = chk.getValue()
+ data['opt'] = 'invoice'
+
+ msg = 'Todos los datos son correctos.
¿Estás seguro de generar esta factura?'
+ webix.confirm({
+ title: 'Generar Factura',
+ ok: 'Si',
+ cancel: 'No',
+ type: 'confirm-error',
+ text: msg,
+ callback:function(result){
+ if(result){
+ save_ticket_to_invoice(data)
+ }
+ }
+ })
+}
+
+
+function grid_tickets_active_double_click(id, e, node){
+ this.move(id.row, -1, $$('grid_tickets_invoice'))
+}
+
+
+function grid_tickets_invoice_double_click(id, e, node){
+ this.move(id.row, -1, $$('grid_tickets_active'))
+}
+
+
+function cmd_move_tickets_right_click(){
+ $$('grid_tickets_active').eachRow(
+ function(row){
+ this.copy(row, -1, $$('grid_tickets_invoice'))
+ }
+ )
+ $$('grid_tickets_active').clearAll()
+}
+
+
+function cmd_move_tickets_left_click(){
+ $$('grid_tickets_invoice').eachRow(
+ function(row){
+ this.copy(row, -1, $$('grid_tickets_active'))
+ }
+ )
+ $$('grid_tickets_invoice').clearAll()
+}
+
+
+function ticket_set_client(row){
+ var form = $$('form_ticket_invoice')
+ var html = ''
+ form.setValues({
+ id_partner: row.id,
+ tsearch_client_key: '',
+ tsearch_client_name: ''}, true)
+ html += row.nombre + ' (' + row.rfc + ')'
+ $$('lbl_tclient').setValue(html)
+}
+
+
+function ticket_search_client_by_id(id){
+ webix.ajax().get('/values/client', {'id': id}, {
+ error: function(text, data, xhr) {
+ msg_error('Error al consultar')
+ },
+ success: function(text, data, xhr){
+ var values = data.json()
+ if (values.ok){
+ ticket_set_client(values.row)
+ }else{
+ msg = 'No se encontró un cliente con la clave: ' + id
+ msg_error(msg)
+ }
+ }
+ })
+
+}
+
+
+function tsearch_client_key_press(code, e){
+ var value = this.getValue()
+ if(code == 13 && value.length > 0){
+ var id = parseInt(value, 10)
+ if (isNaN(id)){
+ msg_error('Captura una clave válida')
+ }else{
+ ticket_search_client_by_id(id)
+ }
+ }
+}
+
+
+function grid_ticket_clients_found_click(obj){
+ ticket_set_client(obj)
+}
\ No newline at end of file
diff --git a/source/static/js/controller/util.js b/source/static/js/controller/util.js
index 8e8c4f1..0beb469 100644
--- a/source/static/js/controller/util.js
+++ b/source/static/js/controller/util.js
@@ -33,6 +33,18 @@ var months = [
]
+function get_icon(tipo){
+ icons = {
+ xml: 'fa-file-code-o',
+ pdf: 'fa-file-pdf-o',
+ zip: 'fa-file-zip-o',
+ email: 'fa-envelope-o',
+ print: 'fa-print',
+ }
+ return ""
+}
+
+
function focus(name){
webix.UIManager.setFocus(name)
}
@@ -43,6 +55,11 @@ function showvar(values){
}
+function showtype(values){
+ webix.message(typeof(values))
+}
+
+
function show(nombre, value){
if(value == '0'){
value = false
@@ -84,7 +101,7 @@ String.prototype.to_float = function(){
function get_float(value){
- return parseFloat(value.replace('$', '').replace(',', '').trim()).round(2)
+ return parseFloat(value.replace('$', '').replace(',', '').trim()).round(DECIMALES)
}
@@ -112,12 +129,17 @@ webix.protoUI({
}, webix.ui.text)
-webix.ui.datafilter.rowCount = webix.extend({
- refresh:function(master, node, value){
- node.firstChild.innerHTML = master.count();
- }
-}, webix.ui.datafilter.summColumn)
+//~ webix.ui.datafilter.rowCount = webix.extend({
+ //~ refresh:function(master, node, value){
+ //~ node.firstChild.innerHTML = master.count();
+ //~ }
+//~ }, webix.ui.datafilter.summColumn)
+webix.ui.datafilter.countRows = webix.extend({
+ refresh:function(master, node, value){
+ node.firstChild.innerHTML = master.count();
+ }
+}, webix.ui.datafilter.summColumn);
function validate_rfc(value){
rfc = value.trim().toUpperCase();
@@ -279,3 +301,11 @@ webix.DataDriver.plainjs = webix.extend({
return this.hash2tree(hash, 0);
}
}, webix.DataDriver.json)
+
+
+function get_forma_pago(control){
+ webix.ajax().get('/values/formapago', {key: true}, function(text, data){
+ var values = data.json()
+ $$(control).getList().parse(values)
+ })
+}
\ No newline at end of file
diff --git a/source/static/js/ui/admin.js b/source/static/js/ui/admin.js
index 3cd09a1..d60e456 100644
--- a/source/static/js/ui/admin.js
+++ b/source/static/js/ui/admin.js
@@ -426,7 +426,7 @@ var controls_correo = [
var form_folios = {
type: 'space',
- responsive: true,
+ //~ responsive: true,
cols: [{
view: 'form',
id: 'form_folios',
@@ -451,12 +451,13 @@ var form_correo = {
view: 'form',
id: 'form_correo',
complexData: true,
+ scroll: true,
elements: controls_correo,
elementsConfig: {
labelWidth: 150,
labelAlign: 'right'
},
- autoheight: true
+ //~ autoheight: true
}],
}
@@ -486,6 +487,14 @@ var options_templates = [
var options_admin_otros = [
{maxHeight: 15},
+ {template: 'Facturación', type: 'section'},
+ {cols: [{maxWidth: 15},
+ {view: 'checkbox', id: 'chk_config_ocultar_metodo_pago', labelWidth: 0,
+ labelRight: 'Ocultar método de pago'},
+ {view: 'checkbox', id: 'chk_config_ocultar_condiciones_pago', labelWidth: 0,
+ labelRight: 'Ocultar condiciones de pago'},
+ {}]},
+ {maxHeight: 20},
{template: 'Ayudas varias', type: 'section'},
{cols: [{maxWidth: 15},
{view: 'checkbox', id: 'chk_config_anticipo', labelWidth: 0,
@@ -496,7 +505,11 @@ var options_admin_otros = [
{cols: [{maxWidth: 15},
{view: 'checkbox', id: 'chk_config_cuenta_predial', labelWidth: 0,
labelRight: 'Mostrar cuenta predial'},
- {}]},
+ {view: 'checkbox', id: 'chk_config_codigo_barras', labelWidth: 0,
+ labelRight: 'Mostrar código de barras'},
+ {view: 'checkbox', id: 'chk_config_precio_con_impuestos', labelWidth: 0,
+ labelRight: 'Mostrar precio con impuestos'},
+ ]},
{maxHeight: 20},
{template: 'Complementos', type: 'section'},
{cols: [{maxWidth: 15},
@@ -504,13 +517,24 @@ var options_admin_otros = [
labelRight: 'Mostrar el complemento INE al facturar'},
{}]},
{maxHeight: 20},
+ {template: 'Punto de venta', type: 'section'},
+ {cols: [{maxWidth: 15},
+ {view: 'checkbox', id: 'chk_usar_punto_de_venta', labelWidth: 0,
+ labelRight: 'Usar punto de venta'},
+ {}]},
+
+ {maxHeight: 20},
{}]
+
+var body_admin_otros = {
+ view: 'scrollview', body: {rows: options_admin_otros},
+}
+
var tab_options = {
view: 'tabview',
id: 'tab_options',
- multiview: true,
animate: true,
cells: [
{id: 'Plantillas', rows: options_templates},
@@ -696,8 +720,8 @@ var suggest_sat_moneda = {
name: 'grid_moneda_found',
body: {
autoConfig: false,
- scroll:true,
- autoheight:false,
+ scroll: true,
+ autoheight: false,
header: true,
yCount: 10,
columns: [
@@ -935,7 +959,6 @@ var app_correo = {
{view: 'template', id: 'th_correo', type: 'header',
template: 'Configuración de correo'},
form_correo,
- {},
],
}
diff --git a/source/static/js/ui/invoices.js b/source/static/js/ui/invoices.js
index 532fe7f..7d1327a 100644
--- a/source/static/js/ui/invoices.js
+++ b/source/static/js/ui/invoices.js
@@ -2,7 +2,7 @@
var grid_cfdi_cliente_cols = [
{id: 'index', header: '#', adjust: 'data', css: 'right',
- footer: {content: 'rowCount', colspan: 3, css: 'right'}},
+ footer: {content: 'countRows', colspan: 3, css: 'right'}},
{id: "id", header:"ID", hidden:true},
{id: "serie", header: ["Serie", {content: "selectFilter"}], adjust: "header",
sort:"string"},
@@ -180,20 +180,9 @@ var toolbar_invoices_filter = [
]
-function get_icon(tipo){
- icons = {
- xml: 'fa-file-code-o',
- pdf: 'fa-file-pdf-o',
- zip: 'fa-file-zip-o',
- email: 'fa-envelope-o',
- }
- return ""
-}
-
-
var grid_invoices_cols = [
{id: 'index', header: '#', adjust: 'data', css: 'right',
- footer: {content: 'rowCount', colspan: 3, css: 'right'}},
+ footer: {content: 'countRows', colspan: 3, css: 'right'}},
{id: "id", header:"ID", hidden:true},
{id: "serie", header: ["Serie", {content: "selectFilter"}], adjust: "data",
sort:"string"},
@@ -227,6 +216,13 @@ var grid_invoices = {
resizeColumn: true,
headermenu: true,
columns: grid_invoices_cols,
+ scheme:{
+ $change:function(item){
+ if (item.estatus == 'Cancelada'){
+ item.$css = 'cancel'
+ }
+ }
+ },
on:{
'data->onStoreUpdated':function(){
this.data.each(function(obj, i){
@@ -317,6 +313,7 @@ var suggest_partners = {
}
}
+
var suggest_products = {
view: 'gridsuggest',
id: 'grid_products_found',
diff --git a/source/static/js/ui/main.js b/source/static/js/ui/main.js
index d004c18..cd6bdaf 100644
--- a/source/static/js/ui/main.js
+++ b/source/static/js/ui/main.js
@@ -5,12 +5,13 @@ var menu_data = [
{id: 'app_partners', icon: 'users', value: 'Clientes y Proveedores'},
{id: 'app_products', icon: 'server', value: 'Productos y Servicios'},
{id: 'app_bancos', icon: 'university', value: 'Bancos'},
- {id: 'app_invoices', icon: 'cart-plus', value: 'Facturas'},
+ {id: 'app_invoices', icon: 'file-code-o', value: 'Facturas'},
]
var sidebar = {
view: 'sidebar',
+ id: 'main_sidebar',
data: menu_data,
ready: function(){
this.select('app_home');
@@ -36,6 +37,7 @@ var multi_main = {
app_partners,
app_products,
app_bancos,
+ app_tickets,
app_invoices,
],
}
@@ -60,7 +62,7 @@ var ui_main = {
{view: 'toolbar', padding: 3, elements: [
{view: 'button', type: 'icon', icon: 'bars',
width: 37, align: 'left', css: 'app_button', click: function(){
- $$('$sidebar1').toggle()
+ $$('main_sidebar').toggle()
}
},
{view: 'label', id: 'lbl_title_main', label: 'Empresa Libre'},
diff --git a/source/static/js/ui/partners.js b/source/static/js/ui/partners.js
index 4b763fe..bc082c7 100644
--- a/source/static/js/ui/partners.js
+++ b/source/static/js/ui/partners.js
@@ -12,7 +12,7 @@ var toolbar_partners = [
var grid_partners_cols = [
{id: 'index', header:'#', css: 'right',
- footer: {content: 'rowCount', colspan: 2, css: 'right'}},
+ footer: {content: 'countRows', colspan: 2, css: 'right'}},
{id: 'id', header: 'Clave', sort: 'int', css: 'right'},
{id: 'rfc', header: ['RFC', {content: 'textFilter'}], adjust: 'data',
sort: 'string', footer: {text: 'Clientes y Proveedores', colspan: 2}},
@@ -143,7 +143,7 @@ var toolbar_contacts = [
var grid_contacts_cols = [
{id: 'index', header: '#', adjust:'data', css:'right',
- footer: {content: 'rowCount'}},
+ footer: {content: 'countRows'}},
{id: 'id', header: '', hidden: true},
{id: 'title', header: 'Título', adjust:'data', sort: 'string',
footer: 'Contactos'},
diff --git a/source/static/js/ui/products.js b/source/static/js/ui/products.js
index d5d87d4..2296f7c 100644
--- a/source/static/js/ui/products.js
+++ b/source/static/js/ui/products.js
@@ -13,7 +13,7 @@ var toolbar_products = [
var grid_products_cols = [
{ id: "id", header: "ID", width: 75, hidden: true},
{ id: "clave", header: ["Clave", {content: "textFilter"}], width: 100,
- sort: 'string', footer: {content: 'rowCount', css: 'right'}},
+ sort: 'string', footer: {content: 'countRows', css: 'right'}},
{ id: "descripcion", header: ["Descripción", {content: "textFilter"}],
fillspace:true, sort: 'string', footer: 'Productos y Servicios'},
{ id: "unidad", header: ["Unidad", {content: "selectFilter"}], width: 150,
@@ -123,10 +123,16 @@ var controls_generals = [
labelAlign: 'right', label: 'Etiquetas',
placeholder: 'Separadas por comas'}
]},
- {cols: [{view: "currency", type: "text", id: "valor_unitario",
- name: "valor_unitario", label: "Valor Unitario", width: 300,
- labelWidth: 130, labelAlign: "right", required: true,
- invalidMessage: "Captura un valor númerico", inputAlign: "right" },{}]},
+ {cols: [
+ {view: "currency", type: "text", id: "valor_unitario",
+ name: "valor_unitario", label: "Valor Unitario", width: 300,
+ labelWidth: 130, labelAlign: "right", required: true,
+ invalidMessage: "Captura un valor númerico", inputAlign: "right"},
+ {view: 'currency', type: 'text', id: 'precio_con_impuestos',
+ name: 'precio_con_impuestos', label: 'Con Impuestos', width: 300,
+ labelWidth: 115, labelAlign: 'right', required: false,
+ invalidMessage: 'Captura un valor númerico', inputAlign: 'right'},
+ {},]},
{cols: [
{view: 'checkbox', id: 'inventario', name: 'inventario', hidden: true,
label: 'Inventario', labelAlign: 'right', labelWidth: 130},
diff --git a/source/static/js/ui/tickets.js b/source/static/js/ui/tickets.js
new file mode 100644
index 0000000..609a847
--- /dev/null
+++ b/source/static/js/ui/tickets.js
@@ -0,0 +1,421 @@
+
+
+var toolbar_tickets = [
+ {view: 'button', id: 'cmd_nuevo_ticket', label: 'Nuevo', type: 'iconButton',
+ autowidth: true, icon: 'plus'},
+ {view: 'button', id: 'cmd_ticket_to_invoice', label: 'Facturar',
+ type: 'iconButton', autowidth: true, icon: 'file-code-o'},
+ {},
+ {view: 'button', id: 'cmd_cancelar_ticket', label: 'Cancelar',
+ type: 'iconButton', autowidth: true, icon: 'ban'},
+]
+
+
+var toolbar_tickets_filter = [
+ {view: 'richselect', id: 'filter_year_ticket', label: 'Año',
+ labelAlign: 'right', labelWidth: 50, width: 150, options: []},
+ {view: 'richselect', id: 'filter_month_ticket', label: 'Mes',
+ labelAlign: 'right', labelWidth: 50, width: 200, options: months},
+ {view: 'daterangepicker', id: 'filter_dates_ticket', label: 'Fechas',
+ labelAlign: 'right', width: 300},
+]
+
+
+var grid_tickets_cols = [
+ {id: 'index', header: '#', adjust: 'data', css: 'right',
+ footer: {content: 'countRows', colspan: 3, css: 'right'}},
+ {id: "id", header:"ID", hidden:true},
+ {id: "serie", header: ["Serie", {content: "selectFilter"}], adjust: "data",
+ sort:"string", hidden: true},
+ {id: 'folio', header: ['Folio', {content: 'numberFilter'}], adjust: 'header',
+ sort: 'int', css: 'right', footer: {text: 'Tickets', colspan: 3}},
+ {id: "fecha", header: ["Fecha y Hora"],
+ adjust: "data", sort: "date"},
+ {id: "estatus", header: ["Estatus", {content: "selectFilter"}],
+ adjust: "data", sort:"string"},
+ {id: 'total', header: ['Total', {content: 'numberFilter'}], width: 150,
+ sort: 'int', format: webix.i18n.priceFormat, css: 'right'},
+ {id: "cliente", header: ["Razón Social", {content: "selectFilter"}],
+ fillspace:true, sort:"string", hidden: true},
+ {id: 'pdf', header: 'PDF', adjust: 'data', template: get_icon('pdf')},
+ {id: 'print', header: 'I', adjust: 'data', template: get_icon('print')},
+]
+
+
+var grid_tickets = {
+ view: 'datatable',
+ id: 'grid_tickets',
+ select: 'row',
+ adjust: true,
+ footer: true,
+ resizeColumn: true,
+ headermenu: true,
+ columns: grid_tickets_cols,
+ scheme:{
+ $change:function(item){
+ if (item.estatus == 'Cancelado'){
+ item.$css = 'cancel'
+ }
+ }
+ },
+ on:{
+ 'data->onStoreUpdated':function(){
+ this.data.each(function(obj, i){
+ obj.index = i + 1
+ })
+ }
+ },
+}
+
+
+var rows_tickets_home = [
+ {view: 'toolbar', elements: toolbar_tickets},
+ {view: 'toolbar', elements: toolbar_tickets_filter},
+ grid_tickets,
+]
+
+
+var ticket_suggest_products = {
+ view: 'gridsuggest',
+ id: 'gt_productos_found',
+ name: 'gt_productos_found',
+ body: {
+ autoConfig: false,
+ header: true,
+ columns: [
+ {id: 'id', hidden: true},
+ {id: 'clave', header: 'Clave', adjust: 'data'},
+ {id: 'descripcion', header: 'Descripción', adjust: 'data'},
+ {id: 'unidad', header: 'Unidad', adjust: 'data'},
+ {id: 'valor_unitario', header: 'Valor Unitario', adjust: 'data',
+ format: webix.i18n.priceFormat}
+ ],
+ dataFeed:function(text){
+ if (text.length > 2){
+ this.load('/values/product?name=' + text)
+ }else{
+ this.hide()
+ }
+ }
+ },
+}
+
+
+var tbody_buscar_producto = {rows: [
+ {cols: [
+ {view: 'search', id: 'tsearch_product_key', name: 'tsearch_product_key',
+ label: 'por Clave', labelPosition:'top', maxWidth: 250,
+ placeholder: 'Presiona ENTER para buscar'},
+ {view: 'search', id: 'tsearch_product_name', name: 'tsearch_product_name',
+ label: 'por Descripción', labelPosition:'top',
+ suggest: ticket_suggest_products, placeholder: 'Captura al menos tres letras'},
+ ]},
+]}
+
+
+var grid_tdetails_cols = [
+ {id: "id", header:"ID", hidden: true},
+ {id: 'delete', header: '', width: 30, css: 'delete'},
+ {id: "clave", header:{text: 'Clave', css: 'center'}, width: 100,
+ footer: {text: 'Artículos', css: 'right_footer3'}},
+ {id: "clave_sat", hidden: true},
+ {id: "descripcion", header:{text: 'Descripción', css: 'center'},
+ fillspace: true, footer: {content: 'countRows', css: 'footer3'}},
+ {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',
+ footer: {content: 'summColumn', css: 'right_footer3'}},
+ {id: "valor_unitario", header:{text: 'Valor Unitario', css: 'center'},
+ width: 100, format: webix.i18n.priceFormat, css: 'right',
+ footer: {text: 'Total ', css: 'right_footer2'}},
+ {id: 'descuento', header: {text: 'Descuento', css: 'center'}, hidden: true,
+ 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',
+ footer: {content: 'summColumn', css: 'right_footer2'}},
+]
+
+
+var grid_tdetails = {
+ view: 'datatable',
+ id: 'grid_tdetails',
+ select: 'row',
+ adjust: true,
+ autoheight: true,
+ editable: true,
+ footer: true,
+ columns: grid_tdetails_cols,
+ on:{
+ 'data->onStoreUpdated':function(){
+ this.data.each(function(obj, i){
+ obj.delete = '-'
+ })
+ }
+ },
+ data: [],
+}
+
+
+var body_ticket_informacion = {rows: [
+ {view: 'richselect', id: 'lst_ticket_forma_pago', name: 'forma_pago',
+ label: 'Forma de Pago', labelPosition: 'top', required: true,
+ options: []},
+],}
+
+
+var controls_generate_ticket = [
+ {minHeight: 10, maxHeight: 10},
+ {cols: [{rows: [
+ {view: 'fieldset', label: 'Buscar Producto', body: tbody_buscar_producto},
+ ]},
+ {maxWidth: 10},
+ {maxWidth: 300, rows: [
+ {view: 'fieldset', label: 'Información', body: body_ticket_informacion},
+ ]},
+ ]},
+ {view: 'label', label: 'Detalle', height: 30, align: 'left'},
+ grid_tdetails,
+ {minHeight: 20, maxHeight: 20},
+ {margin: 20, cols: [{},
+ {view: 'button', id: 'cmd_generar_ticket', label: 'Generar',
+ icon: 'ticket', type: 'iconButton', autowidth: true, align: 'center'},
+ {}]
+ },
+ {rows: [
+ {template: '', type: 'section'},
+ {margin: 10, cols: [{},
+ {view: 'button', id: 'cmd_cerrar_ticket', label: 'Cerrar',
+ type: 'danger', autowidth: true, align: 'center'}
+ ]
+ },
+ ]}
+]
+
+
+var toolbar_ticket_invoice = {view: 'toolbar', elements: [{},
+ {view: 'checkbox', id: 'chk_is_invoice_day', labelWidth: 0, width: 150,
+ labelRight: 'Es factura del día'},
+{}]}
+
+
+var ticket_suggest_partners = {
+ view: 'gridsuggest',
+ id: 'grid_ticket_clients_found',
+ name: 'grid_ticket_clients_found',
+ body: {
+ autoConfig: false,
+ header: false,
+ columns: [
+ {id: 'id', hidden: true},
+ {id: 'nombre', adjust: 'data'},
+ {id: 'rfc', adjust: 'data'},
+ {id: 'forma_pago', hidden: true},
+ {id: 'uso_cfdi', hidden: true},
+ ],
+ dataFeed:function(text){
+ if (text.length > 2){
+ this.load('/values/client?name=' + text)
+ }else{
+ this.hide()
+ }
+ }
+ }
+}
+
+
+var ticket_search_client = {cols: [{rows: [
+ {view: 'fieldset', id: 'fs_ticket_search_client', label: 'Buscar Cliente', body: {rows: [
+ {cols: [
+ {view: 'search', id: 'tsearch_client_key', name: 'tsearch_client_key',
+ label: 'por Clave', labelPosition: 'top', maxWidth: 250,
+ placeholder:'Presiona ENTER para buscar'},
+ {view: 'search', id: 'tsearch_client_name',
+ name: 'tsearch_client_name', label: 'por Nombre o RFC',
+ labelPosition: 'top', suggest: ticket_suggest_partners,
+ placeholder: 'Captura al menos tres letras'},
+ ]},
+ {cols: [
+ {view: 'label', id: 'lbl_tclient_title', autowidth:true,
+ name: "lbl_tclient_title", label: 'Seleccionado: ' },
+ {view: 'label', id: 'lbl_tclient', name: 'lbl_tclient',
+ label: 'Ninguno'},
+ ]}
+ ]}},
+]},]}
+
+
+var grid_tickets_active_cols = [
+ {id: 'index', header: '#', adjust: 'data', css: 'right'},
+ {id: "id", header:"ID", hidden:true},
+ {id: "serie", header: ["Serie", {content: "selectFilter"}], adjust: "data",
+ sort: "string", hidden: true},
+ {id: 'folio', header: ['Folio', {content: 'numberFilter'}], adjust: 'header',
+ sort: 'int', css: 'right', footer: {content: 'countRows', css: 'right'}},
+ {id: "fecha", header: ["Fecha y Hora"], adjust: "data", sort: "string",
+ footer: 'Tickets'},
+ {id: 'total', header: 'Total', width: 150,sort: 'int', css: 'right',
+ format: webix.i18n.priceFormat, footer: {content: 'summColumn',
+ css: 'right'}},
+]
+
+
+var grid_tickets_active = {
+ view: 'datatable',
+ id: 'grid_tickets_active',
+ select: 'row',
+ adjust: true,
+ footer: true,
+ drag: true,
+ resizeColumn: true,
+ headermenu: true,
+ columns: grid_tickets_active_cols,
+ on:{
+ 'data->onStoreUpdated':function(){
+ this.data.each(function(obj, i){
+ obj.index = i + 1
+ })
+ }
+ },
+}
+
+
+var grid_tickets_invoice_cols = [
+ {id: 'index', header: '#', adjust: 'data', css: 'right'},
+ {id: "id", header:"ID", hidden:true},
+ {id: "serie", header: ["Serie", {content: "selectFilter"}], adjust: "data",
+ sort: "string", hidden: true},
+ {id: 'folio', header: ['Folio', {content: 'numberFilter'}], adjust: 'header',
+ sort: 'int', css: 'right', footer: {content: 'countRows', css: 'right'}},
+ {id: "fecha", header: ["Fecha y Hora"], adjust: "data", sort: "string",
+ footer: 'Tickets'},
+ {id: 'total', header: 'Total', width: 150,sort: 'int', css: 'right',
+ format: webix.i18n.priceFormat, footer: {content: 'summColumn',
+ css: 'right'}},
+]
+
+
+var grid_tickets_invoice = {
+ view: 'datatable',
+ id: 'grid_tickets_invoice',
+ select: 'row',
+ adjust: true,
+ footer: true,
+ drag: true,
+ resizeColumn: true,
+ headermenu: true,
+ columns: grid_tickets_invoice_cols,
+ on:{
+ 'data->onStoreUpdated':function(){
+ this.data.each(function(obj, i){
+ obj.index = i + 1
+ })
+ }
+ },
+}
+
+
+var controls_ticket_to_invoice = [
+ {minHeight: 10, maxHeight: 10},
+ toolbar_ticket_invoice,
+ {minHeight: 10, maxHeight: 10},
+ ticket_search_client,
+ {minHeight: 5, maxHeight: 5},
+ {cols:[
+ {rows: [{view: 'label', label: 'Tickets sin facturar', height: 30,
+ align: 'left'},
+ grid_tickets_active]},
+ {rows:[{},
+ {view: 'button', id: 'cmd_move_tickets_right', label: '->', autowidth: true},
+ {view: 'button', id: 'cmd_move_tickets_left', label: '<-', autowidth: true},
+ {}]},
+ {rows: [{view: 'label', label: 'Tickets a facturar', height: 30, align: 'left'}, grid_tickets_invoice]},
+ ]},
+ {minHeight: 20, maxHeight: 20},
+ {margin: 20, cols: [{},
+ {view: 'button', id: 'cmd_new_invoice_from_ticket', label: 'Facturar',
+ icon: 'ticket', type: 'iconButton', autowidth: true, align: 'center'},
+ {}]
+ },
+ {rows: [
+ {template: '', type: 'section'},
+ {margin: 10, cols: [{},
+ {view: 'button', id: 'cmd_close_ticket_invoice', label: 'Cerrar',
+ type: 'danger', autowidth: true, align: 'center'}
+ ]
+ },
+ ]}
+]
+
+
+var controls_new_ticket = [
+ {
+ view: 'tabview',
+ id: 'tv_new_ticket',
+ animate: true,
+ cells: [
+ {id: 'Generar', rows: controls_generate_ticket},
+ ]
+ },
+]
+
+
+var controls_ticket_invoice = [
+ {
+ view: 'tabview',
+ id: 'tv_ticket_invoice',
+ animate: true,
+ cells: [
+ {id: 'Facturar Tickets', rows: controls_ticket_to_invoice},
+ ]
+ },
+]
+
+
+var form_new_ticket = {
+ type: 'space',
+ responsive: true,
+ cols: [{
+ view: 'form',
+ id: 'form_new_ticket',
+ complexData: true,
+ scroll: true,
+ elements: controls_new_ticket,
+ }],
+}
+
+
+var form_ticket_invoice = {
+ type: 'space',
+ responsive: true,
+ cols: [{
+ view: 'form',
+ id: 'form_ticket_invoice',
+ complexData: true,
+ scroll: true,
+ elements: controls_ticket_invoice,
+ }],
+}
+
+
+var multi_tickets = {
+ id: 'multi_tickets',
+ view: 'multiview',
+ animate: true,
+ cells:[
+ {id: 'tickets_home', rows: rows_tickets_home},
+ {id: 'tickets_new', rows:[form_new_ticket]},
+ {id: 'tickets_invoice', rows:[form_ticket_invoice]}
+ ],
+}
+
+
+var app_tickets = {
+ id: 'app_tickets',
+ rows:[
+ {view: 'template', id: 'th_ticckets', type: 'header',
+ template: 'Punto de venta - Tickets'},
+ multi_tickets
+ ],
+}
diff --git a/source/templates/main.html b/source/templates/main.html
index 8c95768..d5395ca 100644
--- a/source/templates/main.html
+++ b/source/templates/main.html
@@ -8,12 +8,14 @@
+
+