Merge branch 'tickets' into develop

Generar tickets
This commit is contained in:
Mauricio Baeza 2017-12-25 23:51:28 -06:00
commit 28146ed47a
20 changed files with 1693 additions and 90 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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 - <b><font color="#610B0B">{}</font></b>'
@ -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.<BR><BR>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)

View File

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

View File

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

View File

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

View File

@ -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'){

View File

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

View File

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

View File

@ -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?<BR><BR>'
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<BR><BR>' + 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?<BR><BR>'
msg += 'Folio: ' + row['folio']
msg += '<BR><BR>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.<BR><BR>¿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 = '<span class="webix_icon fa-user"></span><span class="lbl_partner">'
form.setValues({
id_partner: row.id,
tsearch_client_key: '',
tsearch_client_name: ''}, true)
html += row.nombre + ' (' + row.rfc + ')</span>'
$$('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)
}

View File

@ -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 "<span class='webix_icon " + icons[tipo] + "'></span>"
}
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)
})
}

View File

@ -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,
{},
],
}

View File

@ -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 "<span class='webix_icon " + icons[tipo] + "'></span>"
}
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',

View File

@ -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: '<b>Empresa Libre</b>'},

View File

@ -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'},

View File

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

View File

@ -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
],
}

View File

@ -8,12 +8,14 @@
<script src="/static/js/ui/partners.js" type="text/javascript" ></script>
<script src="/static/js/ui/products.js" type="text/javascript" ></script>
<script src="/static/js/ui/bancos.js" type="text/javascript" ></script>
<script src="/static/js/ui/tickets.js" type="text/javascript" ></script>
<script src="/static/js/ui/invoices.js" type="text/javascript" ></script>
<script src="/static/js/ui/main.js" type="text/javascript" ></script>
<script src="/static/js/controller/partners.js" type="text/javascript" ></script>
<script src="/static/js/controller/products.js" type="text/javascript" ></script>
<script src="/static/js/controller/bancos.js" type="text/javascript" ></script>
<script src="/static/js/controller/tickets.js" type="text/javascript" ></script>
<script src="/static/js/controller/invoices.js" type="text/javascript" ></script>
<script src="/static/js/controller/main.js" type="text/javascript" ></script>