From 19c0708d15e5b51c23eaffd89b7c50cd4a9ee465 Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Mon, 4 Dec 2017 22:09:22 -0600 Subject: [PATCH 01/13] UI para administrar tickets --- source/app/models/main.py | 4 ++ source/static/js/controller/admin.js | 1 + source/static/js/controller/main.js | 16 +++++ source/static/js/controller/util.js | 12 ++++ source/static/js/ui/admin.js | 7 +++ source/static/js/ui/invoices.js | 11 ---- source/static/js/ui/main.js | 6 +- source/static/js/ui/tickets.js | 89 ++++++++++++++++++++++++++++ source/templates/main.html | 1 + 9 files changed, 134 insertions(+), 13 deletions(-) create mode 100644 source/static/js/ui/tickets.js diff --git a/source/app/models/main.py b/source/app/models/main.py index a2b89a4..9a2547a 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 - {}' @@ -183,6 +186,7 @@ class Configuracion(BaseModel): 'chk_config_anticipo', 'chk_config_cuenta_predial', 'chk_config_ine', + 'chk_usar_punto_de_venta', ) data = (Configuracion .select() diff --git a/source/static/js/controller/admin.js b/source/static/js/controller/admin.js index 52bd83a..0f779d9 100644 --- a/source/static/js/controller/admin.js +++ b/source/static/js/controller/admin.js @@ -53,6 +53,7 @@ var controllers = { $$('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_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/main.js b/source/static/js/controller/main.js index 1dfb30f..45b4cbf 100644 --- a/source/static/js/controller/main.js +++ b/source/static/js/controller/main.js @@ -9,7 +9,18 @@ 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) + } }) + + + } @@ -195,6 +206,11 @@ function multi_change(prevID, nextID){ return } + if(nextID == 'app_tickets'){ + + return + } + if(nextID == 'app_invoices'){ active = $$('multi_invoices').getActiveId() if(active == 'invoices_home'){ diff --git a/source/static/js/controller/util.js b/source/static/js/controller/util.js index 8e8c4f1..c0a01ab 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: 'print', + } + return "" +} + + function focus(name){ webix.UIManager.setFocus(name) } diff --git a/source/static/js/ui/admin.js b/source/static/js/ui/admin.js index 3cd09a1..8b0f944 100644 --- a/source/static/js/ui/admin.js +++ b/source/static/js/ui/admin.js @@ -504,6 +504,13 @@ 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}, {}] diff --git a/source/static/js/ui/invoices.js b/source/static/js/ui/invoices.js index 532fe7f..49adef3 100644 --- a/source/static/js/ui/invoices.js +++ b/source/static/js/ui/invoices.js @@ -180,17 +180,6 @@ 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'}}, 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/tickets.js b/source/static/js/ui/tickets.js new file mode 100644 index 0000000..7a3f534 --- /dev/null +++ b/source/static/js/ui/tickets.js @@ -0,0 +1,89 @@ + + + + +var toolbar_tickets = [ + {view: 'button', id: 'cmd_nuevo_ticket', label: 'Nuevo', type: 'iconButton', + autowidth: true, icon: 'plus'}, + {}, + {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: 'rowCount', colspan: 3, css: 'right'}}, + {id: "id", header:"ID", hidden:true}, + {id: "serie", header: ["Serie", {content: "selectFilter"}], adjust: "data", + sort:"string"}, + {id: 'folio', header: ['Folio', {content: 'numberFilter'}], adjust: 'data', + 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"}, + {id: 'pdf', header: 'PDF', adjust: 'data', template: get_icon('pdf')}, + {id: 'print', header: 'I', adjust: 'data', template: get_icon('print')}, + {id: 'email', header: '', adjust: 'data', template: get_icon('email')} +] + + +var grid_tickets = { + view: 'datatable', + id: 'grid_tickets', + select: 'row', + adjust: true, + footer: true, + resizeColumn: true, + headermenu: true, + columns: grid_tickets_cols, + 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 multi_tickets = { + id: 'multi_tickets', + view: 'multiview', + animate: true, + cells:[ + {id: 'tickets_home', rows: rows_tickets_home}, + {id: 'tickets_new', rows:[]} + ], +} + + +var app_tickets = { + id: 'app_tickets', + rows:[ + {view: 'template', id: 'th_ticckets', type: 'header', template: 'Punto de venta'}, + multi_tickets + ], +} diff --git a/source/templates/main.html b/source/templates/main.html index 8c95768..2e3ee63 100644 --- a/source/templates/main.html +++ b/source/templates/main.html @@ -8,6 +8,7 @@ + From 50ed11ed7f9ad721fe57357b62e98b238761a34e Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Tue, 19 Dec 2017 00:56:55 -0600 Subject: [PATCH 02/13] UI para nuevo ticket --- source/static/js/controller/main.js | 1 + source/static/js/controller/tickets.js | 26 ++++++ source/static/js/ui/tickets.js | 107 ++++++++++++++++++++++++- source/templates/main.html | 1 + 4 files changed, 131 insertions(+), 4 deletions(-) create mode 100644 source/static/js/controller/tickets.js diff --git a/source/static/js/controller/main.js b/source/static/js/controller/main.js index 45b4cbf..2965a10 100644 --- a/source/static/js/controller/main.js +++ b/source/static/js/controller/main.js @@ -98,6 +98,7 @@ var controllers = { webix.extend($$('grid_invoices'), webix.ProgressBar) bancos_controllers.init() + tickets_controllers.init() } } diff --git a/source/static/js/controller/tickets.js b/source/static/js/controller/tickets.js new file mode 100644 index 0000000..5c3faf5 --- /dev/null +++ b/source/static/js/controller/tickets.js @@ -0,0 +1,26 @@ +var query = [] +var msg = '' + + +var tickets_controllers = { + init: function(){ + $$('cmd_nuevo_ticket').attachEvent('onItemClick', cmd_nuevo_ticket_click) + $$('cmd_generar_ticket').attachEvent('onItemClick', cmd_generar_ticket_click) + $$('cmd_cerrar_ticket').attachEvent('onItemClick', cmd_cerrar_ticket_click) + } +} + + +function cmd_nuevo_ticket_click(){ + $$('multi_tickets').setValue('tickets_new') +} + + +function cmd_generar_ticket_click(){ + showvar('ok') +} + + +function cmd_cerrar_ticket_click(){ + $$('multi_tickets').setValue('tickets_home') +} \ No newline at end of file diff --git a/source/static/js/ui/tickets.js b/source/static/js/ui/tickets.js index 7a3f534..a0d728a 100644 --- a/source/static/js/ui/tickets.js +++ b/source/static/js/ui/tickets.js @@ -1,7 +1,5 @@ - - var toolbar_tickets = [ {view: 'button', id: 'cmd_nuevo_ticket', label: 'Nuevo', type: 'iconButton', autowidth: true, icon: 'plus'}, @@ -69,13 +67,113 @@ var rows_tickets_home = [ ] +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: 'search_product_name', + label: 'por Descripción', labelPosition:'top', suggest: [], + 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}, + {id: "clave_sat", hidden: true}, + {id: "descripcion", header:{text: 'Descripción', css: 'center'}, + fillspace: true, editor: 'text'}, + {id: "unidad", header:{text: 'Unidad', css: 'center'}, width: 100}, + {id: 'cantidad', header: {text: 'Cantidad', css: 'center'}, width: 100, + format: webix.i18n.numberFormat, css: 'right', editor: 'text'}, + {id: "valor_unitario", header:{text: 'Valor Unitario', css: 'center'}, + width: 100, format: webix.i18n.priceFormat, css: 'right', editor: 'text'}, + {id: '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'}, +] + + +var grid_tdetails = { + view: 'datatable', + id: 'grid_tdetails', + select: 'row', + adjust: true, + autoheight: true, + editable: true, + columns: grid_tdetails_cols, + data: [], +} + + +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: {}}, + ]}, + ]}, + {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 controls_new_ticket = [ + { + view: 'tabview', + id: 'tv_new_ticket', + animate: true, + cells: [ + {id: 'Generar', rows: controls_generate_ticket}, + ] + }, +] + + +var form_new_ticket = { + type: 'space', + responsive: true, + cols: [{ + view: 'form', + id: 'form_new_ticket', + complexData: true, + scroll: true, + elements: controls_new_ticket, + }] +} + + var multi_tickets = { id: 'multi_tickets', view: 'multiview', animate: true, cells:[ {id: 'tickets_home', rows: rows_tickets_home}, - {id: 'tickets_new', rows:[]} + {id: 'tickets_new', rows:[form_new_ticket]} ], } @@ -83,7 +181,8 @@ var multi_tickets = { var app_tickets = { id: 'app_tickets', rows:[ - {view: 'template', id: 'th_ticckets', type: 'header', template: 'Punto de venta'}, + {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 2e3ee63..d5395ca 100644 --- a/source/templates/main.html +++ b/source/templates/main.html @@ -15,6 +15,7 @@ + From cab7878288d469ad58b92147f000754628a3ef58 Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Mon, 4 Dec 2017 22:09:22 -0600 Subject: [PATCH 03/13] UI para administrar tickets --- source/static/js/ui/tickets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/static/js/ui/tickets.js b/source/static/js/ui/tickets.js index a0d728a..f9e1b85 100644 --- a/source/static/js/ui/tickets.js +++ b/source/static/js/ui/tickets.js @@ -172,8 +172,8 @@ var multi_tickets = { view: 'multiview', animate: true, cells:[ - {id: 'tickets_home', rows: rows_tickets_home}, {id: 'tickets_new', rows:[form_new_ticket]} + {id: 'tickets_new', rows:[]} ], } From 3ef40892cc363ef87084a6e34e6075a7e5f276af Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Tue, 19 Dec 2017 00:56:55 -0600 Subject: [PATCH 04/13] UI para nuevo ticket --- source/static/js/ui/tickets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/static/js/ui/tickets.js b/source/static/js/ui/tickets.js index f9e1b85..a0d728a 100644 --- a/source/static/js/ui/tickets.js +++ b/source/static/js/ui/tickets.js @@ -172,8 +172,8 @@ var multi_tickets = { view: 'multiview', animate: true, cells:[ + {id: 'tickets_home', rows: rows_tickets_home}, {id: 'tickets_new', rows:[form_new_ticket]} - {id: 'tickets_new', rows:[]} ], } From ccdfacb476b4fe311700ff2eb7b5e15d1a22defa Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Tue, 19 Dec 2017 11:54:59 -0600 Subject: [PATCH 05/13] Cambio de expiracion --- source/app/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/app/main.py b/source/app/main.py index 7398382..4946258 100644 --- a/source/app/main.py +++ b/source/app/main.py @@ -50,7 +50,7 @@ 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') From 9b4d0bffd76c3588b11ca18dae25c8ba8ba447dd Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Wed, 20 Dec 2017 01:15:48 -0600 Subject: [PATCH 06/13] Generar ticket --- source/app/controllers/main.py | 16 ++ source/app/main.py | 3 +- source/app/models/db.py | 8 + source/app/models/main.py | 134 +++++++++++++ source/static/css/app.css | 17 ++ source/static/js/controller/invoices.js | 10 +- source/static/js/controller/main.js | 2 - source/static/js/controller/tickets.js | 244 +++++++++++++++++++++++- source/static/js/controller/util.js | 30 ++- source/static/js/ui/invoices.js | 5 +- source/static/js/ui/products.js | 2 +- source/static/js/ui/tickets.js | 70 +++++-- 12 files changed, 505 insertions(+), 36 deletions(-) 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/main.py b/source/app/main.py index 4946258..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,6 +46,7 @@ api.add_route('/partners', AppPartners(db)) api.add_route('/products', AppProducts(db)) api.add_route('/invoices', AppInvoices(db)) api.add_route('/preinvoices', AppPreInvoices(db)) +api.add_route('/tickets', AppTickets(db)) api.add_route('/cuentasbanco', AppCuentasBanco(db)) api.add_route('/movbanco', AppMovimientosBanco(db)) diff --git a/source/app/models/db.py b/source/app/models/db.py index 303ca61..693e05f 100644 --- a/source/app/models/db.py +++ b/source/app/models/db.py @@ -221,6 +221,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 +254,11 @@ 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) + 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 9a2547a..c0a689b 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -1977,6 +1977,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', '') @@ -3838,6 +3866,112 @@ 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 + class TicketsDetalle(BaseModel): ticket = ForeignKeyField(Tickets) diff --git a/source/static/css/app.css b/source/static/css/app.css index 1634313..3ddaa5a 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; diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js index 2e03ff9..2b17e20 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() diff --git a/source/static/js/controller/main.js b/source/static/js/controller/main.js index 2965a10..1d707cb 100644 --- a/source/static/js/controller/main.js +++ b/source/static/js/controller/main.js @@ -19,8 +19,6 @@ function configuracion_inicial(){ } }) - - } diff --git a/source/static/js/controller/tickets.js b/source/static/js/controller/tickets.js index 5c3faf5..c1bfc63 100644 --- a/source/static/js/controller/tickets.js +++ b/source/static/js/controller/tickets.js @@ -7,20 +7,260 @@ var tickets_controllers = { $$('cmd_nuevo_ticket').attachEvent('onItemClick', cmd_nuevo_ticket_click) $$('cmd_generar_ticket').attachEvent('onItemClick', cmd_generar_ticket_click) $$('cmd_cerrar_ticket').attachEvent('onItemClick', cmd_cerrar_ticket_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) } } +function configuracion_inicial_ticket(){ + 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') + table_pt.clear() + table_totals.clear() +} + + function cmd_nuevo_ticket_click(){ + configuracion_inicial_ticket() $$('multi_tickets').setValue('tickets_new') } +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 grid = $$('grid_tdetails') + //~ showvar(values) + + var rows = grid.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({}) + $$('multi_tickets').setValue('tickets_home') + }else{ + msg_error(values.msg) + } + } + }) + +} + + function cmd_generar_ticket_click(){ - showvar('ok') + 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') -} \ No newline at end of file +} + +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) +} + diff --git a/source/static/js/controller/util.js b/source/static/js/controller/util.js index c0a01ab..fc8e90d 100644 --- a/source/static/js/controller/util.js +++ b/source/static/js/controller/util.js @@ -55,6 +55,11 @@ function showvar(values){ } +function showtype(values){ + webix.message(typeof(values)) +} + + function show(nombre, value){ if(value == '0'){ value = false @@ -96,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) } @@ -124,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(); @@ -291,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/invoices.js b/source/static/js/ui/invoices.js index 49adef3..c183c19 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"}, @@ -182,7 +182,7 @@ var toolbar_invoices_filter = [ 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"}, @@ -306,6 +306,7 @@ var suggest_partners = { } } + var suggest_products = { view: 'gridsuggest', id: 'grid_products_found', diff --git a/source/static/js/ui/products.js b/source/static/js/ui/products.js index d5d87d4..8085984 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, diff --git a/source/static/js/ui/tickets.js b/source/static/js/ui/tickets.js index a0d728a..7087603 100644 --- a/source/static/js/ui/tickets.js +++ b/source/static/js/ui/tickets.js @@ -21,7 +21,7 @@ var toolbar_tickets_filter = [ var grid_tickets_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"}, @@ -34,7 +34,7 @@ var grid_tickets_cols = [ {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"}, + 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')}, {id: 'email', header: '', adjust: 'data', template: get_icon('email')} @@ -67,37 +67,66 @@ var rows_tickets_home = [ ] +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: 'search_product_name', - label: 'por Descripción', labelPosition:'top', suggest: [], - placeholder: 'Captura al menos tres letras'}, + {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}, + {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, editor: 'text'}, + 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'}, + 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', editor: 'text'}, - {id: 'descuento', header:{text: 'Descuento', css: 'center'}, hidden: true, + 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'}, + format: webix.i18n.priceFormat, css: 'right', + footer: {content: 'summColumn', css: 'right_footer2'}}, ] @@ -108,11 +137,26 @@ var grid_tdetails = { 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: [ @@ -120,7 +164,7 @@ var controls_generate_ticket = [ ]}, {maxWidth: 10}, {maxWidth: 300, rows: [ - {view: 'fieldset', label: 'Información', body: {}}, + {view: 'fieldset', label: 'Información', body: body_ticket_informacion}, ]}, ]}, {view: 'label', label: 'Detalle', height: 30, align: 'left'}, From f4309477087fd22cd33f67440a5b3133ab21226c Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Thu, 21 Dec 2017 01:13:28 -0600 Subject: [PATCH 07/13] Filtrar tickets. Cancelar tickets --- source/app/controllers/util.py | 4 + source/app/models/db.py | 8 ++ source/app/models/main.py | 67 ++++++++++++ source/static/css/app.css | 4 + source/static/js/controller/main.js | 5 +- source/static/js/controller/tickets.js | 136 ++++++++++++++++++++++++- source/static/js/controller/util.js | 2 +- source/static/js/ui/invoices.js | 7 ++ source/static/js/ui/partners.js | 4 +- source/static/js/ui/tickets.js | 12 ++- 10 files changed, 238 insertions(+), 11 deletions(-) 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/models/db.py b/source/app/models/db.py index 693e05f..89df7f6 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() @@ -258,6 +261,11 @@ class StorageEngine(object): opt = values.pop('opt') if opt == 'add': return main.Tickets.add(values) + if opt == 'cancel': + return main.Tickets.cancel(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 c0a689b..bd5d43f 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -3972,6 +3972,73 @@ class Tickets(BaseModel): data = {'ok': True, 'row': row} 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 == '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 3ddaa5a..0bd1d39 100644 --- a/source/static/css/app.css +++ b/source/static/css/app.css @@ -54,6 +54,10 @@ color: red; } +.cancel { + color: red; +} + .cmd_close_partner div button { background-color: red !important; diff --git a/source/static/js/controller/main.js b/source/static/js/controller/main.js index 1d707cb..f7be4f1 100644 --- a/source/static/js/controller/main.js +++ b/source/static/js/controller/main.js @@ -206,7 +206,10 @@ function multi_change(prevID, nextID){ } if(nextID == 'app_tickets'){ - + active = $$('multi_tickets').getActiveId() + if(active == 'tickets_home'){ + configuracion_inicial_ticket() + } return } diff --git a/source/static/js/controller/tickets.js b/source/static/js/controller/tickets.js index c1bfc63..6adcffa 100644 --- a/source/static/js/controller/tickets.js +++ b/source/static/js/controller/tickets.js @@ -7,28 +7,99 @@ var tickets_controllers = { $$('cmd_nuevo_ticket').attachEvent('onItemClick', cmd_nuevo_ticket_click) $$('cmd_generar_ticket').attachEvent('onItemClick', cmd_generar_ticket_click) $$('cmd_cerrar_ticket').attachEvent('onItemClick', cmd_cerrar_ticket_click) + $$('cmd_cancelar_ticket').attachEvent('onItemClick', cmd_cancelar_ticket_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) + + webix.extend($$('grid_tickets'), 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 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_ticket() + configuracion_inicial_nuevo_ticket() $$('multi_tickets').setValue('tickets_new') } @@ -48,10 +119,10 @@ function validar_ticket(){ function guardar_ticket(values){ - var grid = $$('grid_tdetails') - //~ showvar(values) + var gd = $$('grid_tdetails') + var grid = $$('grid_tickets') - var rows = grid.data.getRange() + var rows = gd.data.getRange() for (i = 0; i < rows.length; i++) { delete rows[i]['delete'] delete rows[i]['clave'] @@ -77,6 +148,8 @@ function guardar_ticket(values){ 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) @@ -264,3 +337,58 @@ 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']) + } + } + }) + +} \ No newline at end of file diff --git a/source/static/js/controller/util.js b/source/static/js/controller/util.js index fc8e90d..0beb469 100644 --- a/source/static/js/controller/util.js +++ b/source/static/js/controller/util.js @@ -39,7 +39,7 @@ function get_icon(tipo){ pdf: 'fa-file-pdf-o', zip: 'fa-file-zip-o', email: 'fa-envelope-o', - print: 'print', + print: 'fa-print', } return "" } diff --git a/source/static/js/ui/invoices.js b/source/static/js/ui/invoices.js index c183c19..7d1327a 100644 --- a/source/static/js/ui/invoices.js +++ b/source/static/js/ui/invoices.js @@ -216,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){ 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/tickets.js b/source/static/js/ui/tickets.js index 7087603..a60051c 100644 --- a/source/static/js/ui/tickets.js +++ b/source/static/js/ui/tickets.js @@ -24,8 +24,8 @@ var grid_tickets_cols = [ footer: {content: 'countRows', colspan: 3, css: 'right'}}, {id: "id", header:"ID", hidden:true}, {id: "serie", header: ["Serie", {content: "selectFilter"}], adjust: "data", - sort:"string"}, - {id: 'folio', header: ['Folio', {content: 'numberFilter'}], 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"}, @@ -37,7 +37,6 @@ var grid_tickets_cols = [ 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')}, - {id: 'email', header: '', adjust: 'data', template: get_icon('email')} ] @@ -50,6 +49,13 @@ var grid_tickets = { 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){ From c47b242520008f024c82b0409d6987c4ca6338dc Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Sat, 23 Dec 2017 22:41:44 -0600 Subject: [PATCH 08/13] =?UTF-8?q?Mostrar=20c=C3=B3digo=20de=20barras=20en?= =?UTF-8?q?=20productos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/app/models/main.py | 2 ++ source/static/js/controller/admin.js | 1 + source/static/js/controller/products.js | 1 + source/static/js/ui/admin.js | 2 ++ 4 files changed, 6 insertions(+) diff --git a/source/app/models/main.py b/source/app/models/main.py index bd5d43f..0a10b8f 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -185,6 +185,7 @@ class Configuracion(BaseModel): fields = ( 'chk_config_anticipo', 'chk_config_cuenta_predial', + 'chk_config_codigo_barras', 'chk_config_ine', 'chk_usar_punto_de_venta', ) @@ -195,6 +196,7 @@ class Configuracion(BaseModel): elif keys['fields'] == 'productos': fields = ( 'chk_config_cuenta_predial', + 'chk_config_codigo_barras', ) data = (Configuracion .select() diff --git a/source/static/js/controller/admin.js b/source/static/js/controller/admin.js index 0f779d9..6645739 100644 --- a/source/static/js/controller/admin.js +++ b/source/static/js/controller/admin.js @@ -53,6 +53,7 @@ var controllers = { $$('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_usar_punto_de_venta').attachEvent('onItemClick', chk_config_item_click) $$('cmd_subir_bdfl').attachEvent('onItemClick', cmd_subir_bdfl_click) diff --git a/source/static/js/controller/products.js b/source/static/js/controller/products.js index 0db84e9..c0b0481 100644 --- a/source/static/js/controller/products.js +++ b/source/static/js/controller/products.js @@ -10,6 +10,7 @@ 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) } }) } diff --git a/source/static/js/ui/admin.js b/source/static/js/ui/admin.js index 8b0f944..00f59e5 100644 --- a/source/static/js/ui/admin.js +++ b/source/static/js/ui/admin.js @@ -496,6 +496,8 @@ 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'}, {}]}, {maxHeight: 20}, {template: 'Complementos', type: 'section'}, From d5027988ddca57882aacbfadd14754d0f486afde Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Sat, 23 Dec 2017 22:58:23 -0600 Subject: [PATCH 09/13] Unidad predeterminada --- source/app/models/main.py | 30 +++++++++++++++++-------- source/static/js/controller/products.js | 2 ++ 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/source/app/models/main.py b/source/app/models/main.py index 0a10b8f..7a72a2e 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -156,6 +156,20 @@ class Configuracion(BaseModel): return data[0].valor return '' + if keys['fields'] == 'productos': + fields = ( + 'chk_config_cuenta_predial', + 'chk_config_codigo_barras', + ) + 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', @@ -193,15 +207,6 @@ class Configuracion(BaseModel): .select() .where(Configuracion.clave.in_(fields)) ) - elif keys['fields'] == 'productos': - fields = ( - 'chk_config_cuenta_predial', - 'chk_config_codigo_barras', - ) - data = (Configuracion - .select() - .where(Configuracion.clave.in_(fields)) - ) values = {r.clave: r.valor for r in data} return values @@ -850,6 +855,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 diff --git a/source/static/js/controller/products.js b/source/static/js/controller/products.js index c0b0481..3fa5b72 100644 --- a/source/static/js/controller/products.js +++ b/source/static/js/controller/products.js @@ -11,6 +11,8 @@ function configurar_productos(){ //~ showvar(values) show('cuenta_predial', values.chk_config_cuenta_predial) show('codigo_barras', values.chk_config_codigo_barras) + $$('unidad').setValue(values.default_unidad) + $$('grid_product_taxes').select(values.default_tax) } }) } From ff0a2f27b1350f3fbfd19e5191ed5ba71dce4a3d Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Sat, 23 Dec 2017 23:54:47 -0600 Subject: [PATCH 10/13] Calcular precio sin impuestos --- source/app/models/main.py | 2 + source/static/js/controller/admin.js | 1 + source/static/js/controller/main.js | 11 +---- source/static/js/controller/products.js | 64 ++++++++++++++++++++++++- source/static/js/ui/admin.js | 4 +- source/static/js/ui/products.js | 14 ++++-- 6 files changed, 80 insertions(+), 16 deletions(-) diff --git a/source/app/models/main.py b/source/app/models/main.py index 7a72a2e..9f39829 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -160,6 +160,7 @@ class Configuracion(BaseModel): fields = ( 'chk_config_cuenta_predial', 'chk_config_codigo_barras', + 'chk_config_precio_con_impuestos', ) data = (Configuracion .select() @@ -200,6 +201,7 @@ class Configuracion(BaseModel): 'chk_config_anticipo', 'chk_config_cuenta_predial', 'chk_config_codigo_barras', + 'chk_config_precio_con_impuestos', 'chk_config_ine', 'chk_usar_punto_de_venta', ) diff --git a/source/static/js/controller/admin.js b/source/static/js/controller/admin.js index 6645739..638dc6c 100644 --- a/source/static/js/controller/admin.js +++ b/source/static/js/controller/admin.js @@ -54,6 +54,7 @@ var controllers = { $$('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) diff --git a/source/static/js/controller/main.js b/source/static/js/controller/main.js index f7be4f1..4d76373 100644 --- a/source/static/js/controller/main.js +++ b/source/static/js/controller/main.js @@ -49,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) @@ -95,6 +87,7 @@ var controllers = { webix.extend($$('grid_invoices'), webix.ProgressBar) + products_controllers.init() bancos_controllers.init() tickets_controllers.init() } diff --git a/source/static/js/controller/products.js b/source/static/js/controller/products.js index 3fa5b72..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) { @@ -11,6 +27,7 @@ function configurar_productos(){ //~ 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) } @@ -231,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/ui/admin.js b/source/static/js/ui/admin.js index 00f59e5..c97a031 100644 --- a/source/static/js/ui/admin.js +++ b/source/static/js/ui/admin.js @@ -498,7 +498,9 @@ var options_admin_otros = [ 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}, diff --git a/source/static/js/ui/products.js b/source/static/js/ui/products.js index 8085984..2296f7c 100644 --- a/source/static/js/ui/products.js +++ b/source/static/js/ui/products.js @@ -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}, From c7bd84f9bf065f6ffa14b391a1a4200e83222f08 Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Sun, 24 Dec 2017 22:09:21 -0600 Subject: [PATCH 11/13] =?UTF-8?q?Ocultar=20m=C3=A9todo=20y=20forma=20de=20?= =?UTF-8?q?pago?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/app/models/main.py | 20 +++++++++++++++++++- source/static/js/controller/admin.js | 2 ++ source/static/js/controller/invoices.js | 3 +++ source/static/js/ui/admin.js | 24 ++++++++++++++++++------ 4 files changed, 42 insertions(+), 7 deletions(-) diff --git a/source/app/models/main.py b/source/app/models/main.py index 9f39829..803427f 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -129,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 @@ -198,6 +203,8 @@ 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', @@ -209,6 +216,17 @@ class Configuracion(BaseModel): .select() .where(Configuracion.clave.in_(fields)) ) + elif keys['fields'] == 'timbrar': + fields = ( + 'chk_config_ocultar_metodo_pago', + 'chk_config_ocultar_condiciones_pago', + 'chk_config_anticipo', + 'chk_config_ine', + ) + data = (Configuracion + .select() + .where(Configuracion.clave.in_(fields)) + ) values = {r.clave: r.valor for r in data} return values diff --git a/source/static/js/controller/admin.js b/source/static/js/controller/admin.js index 638dc6c..4e63fde 100644 --- a/source/static/js/controller/admin.js +++ b/source/static/js/controller/admin.js @@ -50,6 +50,8 @@ 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) diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js index 2b17e20..0de74b5 100644 --- a/source/static/js/controller/invoices.js +++ b/source/static/js/controller/invoices.js @@ -108,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/ui/admin.js b/source/static/js/ui/admin.js index c97a031..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, @@ -518,10 +527,14 @@ var options_admin_otros = [ {}] + +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}, @@ -707,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: [ @@ -946,7 +959,6 @@ var app_correo = { {view: 'template', id: 'th_correo', type: 'header', template: 'Configuración de correo'}, form_correo, - {}, ], } From 937329bd9c743079463e2c96a42007b8e2db4289 Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Sun, 24 Dec 2017 23:54:19 -0600 Subject: [PATCH 12/13] UI Ticket a factura --- source/static/js/controller/tickets.js | 27 ++++ source/static/js/ui/tickets.js | 182 ++++++++++++++++++++++++- 2 files changed, 206 insertions(+), 3 deletions(-) diff --git a/source/static/js/controller/tickets.js b/source/static/js/controller/tickets.js index 6adcffa..74060ca 100644 --- a/source/static/js/controller/tickets.js +++ b/source/static/js/controller/tickets.js @@ -5,8 +5,11 @@ 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) $$('tsearch_product_key').attachEvent('onKeyPress', tsearch_product_key_press) $$('grid_tdetails').attachEvent('onItemClick', grid_ticket_details_click) @@ -14,6 +17,7 @@ var tickets_controllers = { $$('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) webix.extend($$('grid_tickets'), webix.ProgressBar) } @@ -83,6 +87,11 @@ function configuracion_inicial_ticket(){ } +function configuracion_inicial_ticket_to_invoice(){ + //~ get_active_tickets() +} + + function configuracion_inicial_nuevo_ticket(){ var grid = $$('grid_tdetails') @@ -104,6 +113,12 @@ function cmd_nuevo_ticket_click(){ } +function cmd_ticket_to_invoice_click(){ + configuracion_inicial_ticket_to_invoice() + $$('multi_tickets').setValue('tickets_invoice') +} + + function validar_ticket(){ var grid = $$('grid_tdetails') @@ -195,6 +210,7 @@ function cmd_cerrar_ticket_click(){ $$('multi_tickets').setValue('tickets_home') } + function calcular_precio_con_impuestos(precio, taxes){ var precio_final = precio @@ -391,4 +407,15 @@ function cmd_cancelar_ticket_click(){ } }) +} + + +function chk_is_invoice_day_change(new_value, old_value){ + var value = Boolean(new_value) + show('fs_ticket_search_client', !value) +} + + +function cmd_new_invoice_from_ticket_click(){ + showvar('ok') } \ No newline at end of file diff --git a/source/static/js/ui/tickets.js b/source/static/js/ui/tickets.js index a60051c..5d2df46 100644 --- a/source/static/js/ui/tickets.js +++ b/source/static/js/ui/tickets.js @@ -3,6 +3,8 @@ 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'}, @@ -95,7 +97,7 @@ var ticket_suggest_products = { this.hide() } } - } + }, } @@ -192,6 +194,154 @@ var controls_generate_ticket = [ ] +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 tsuggest_partners = { + view: 'gridsuggest', + id: 'grid_tclients_found', + name: 'grid_tclients_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: tsuggest_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', + 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: "string"}, + {id: 'total', header: 'Total', width: 150,sort: 'int', + format: webix.i18n.priceFormat, css: 'right'}, +] + + +var grid_tickets_active = { + view: 'datatable', + id: 'grid_tickets_active', + select: 'row', + adjust: true, + footer: 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', + 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', adjust: 'header', sort: 'int', + css: 'right', footer: {text: 'Tickets', colspan: 3}}, + {id: "fecha", header: ["Fecha y Hora"], + adjust: "data", sort: "string"}, + {id: 'total', header: 'Total', width: 150,sort: 'int', + format: webix.i18n.priceFormat, css: 'right'}, +] + + +var grid_tickets_invoice = { + view: 'datatable', + id: 'grid_tickets_invoice', + select: 'row', + adjust: true, + footer: 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]}, + {minWidth: 10, maxWidth: 10}, + {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', @@ -204,6 +354,18 @@ var controls_new_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, @@ -213,7 +375,20 @@ var 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, + }], } @@ -223,7 +398,8 @@ var multi_tickets = { animate: true, cells:[ {id: 'tickets_home', rows: rows_tickets_home}, - {id: 'tickets_new', rows:[form_new_ticket]} + {id: 'tickets_new', rows:[form_new_ticket]}, + {id: 'tickets_invoice', rows:[form_ticket_invoice]} ], } From b777e1774fdbf426470dcf650a500ef2959cadbd Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Mon, 25 Dec 2017 23:30:34 -0600 Subject: [PATCH 13/13] Generar factura de ticket --- source/app/models/db.py | 2 + source/app/models/main.py | 190 ++++++++++++++++++---- source/static/js/controller/partners.js | 2 +- source/static/js/controller/tickets.js | 200 +++++++++++++++++++++++- source/static/js/ui/tickets.js | 53 ++++--- 5 files changed, 392 insertions(+), 55 deletions(-) diff --git a/source/app/models/db.py b/source/app/models/db.py index 89df7f6..7a48236 100644 --- a/source/app/models/db.py +++ b/source/app/models/db.py @@ -263,6 +263,8 @@ class StorageEngine(object): 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) diff --git a/source/app/models/main.py b/source/app/models/main.py index 803427f..46a1683 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -1790,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 @@ -2789,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, @@ -2809,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 @@ -4006,6 +3978,162 @@ class Tickets(BaseModel): 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']) @@ -4021,6 +4149,10 @@ class Tickets(BaseModel): if not opt: return + if opt == 'active': + filters = (Tickets.cancelado==False) + return filters + if opt == 'today': t = util.today() filters = ( 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/tickets.js b/source/static/js/controller/tickets.js index 74060ca..741b5f1 100644 --- a/source/static/js/controller/tickets.js +++ b/source/static/js/controller/tickets.js @@ -11,6 +11,8 @@ var tickets_controllers = { $$('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) @@ -18,8 +20,13 @@ var tickets_controllers = { $$('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) } } @@ -87,8 +94,38 @@ function configuracion_inicial_ticket(){ } +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(){ - //~ get_active_tickets() + 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() } @@ -416,6 +453,165 @@ function chk_is_invoice_day_change(new_value, old_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(){ - showvar('ok') + 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/ui/tickets.js b/source/static/js/ui/tickets.js index 5d2df46..609a847 100644 --- a/source/static/js/ui/tickets.js +++ b/source/static/js/ui/tickets.js @@ -200,10 +200,10 @@ var toolbar_ticket_invoice = {view: 'toolbar', elements: [{}, {}]} -var tsuggest_partners = { +var ticket_suggest_partners = { view: 'gridsuggest', - id: 'grid_tclients_found', - name: 'grid_tclients_found', + id: 'grid_ticket_clients_found', + name: 'grid_ticket_clients_found', body: { autoConfig: false, header: false, @@ -233,7 +233,7 @@ var ticket_search_client = {cols: [{rows: [ placeholder:'Presiona ENTER para buscar'}, {view: 'search', id: 'tsearch_client_name', name: 'tsearch_client_name', label: 'por Nombre o RFC', - labelPosition: 'top', suggest: tsuggest_partners, + labelPosition: 'top', suggest: ticket_suggest_partners, placeholder: 'Captura al menos tres letras'}, ]}, {cols: [ @@ -247,17 +247,17 @@ var ticket_search_client = {cols: [{rows: [ var grid_tickets_active_cols = [ - {id: 'index', header: '#', adjust: 'data', css: 'right', - footer: {content: 'countRows', colspan: 3, css: 'right'}}, + {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}, + 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: "string"}, - {id: 'total', header: 'Total', width: 150,sort: 'int', - format: webix.i18n.priceFormat, css: 'right'}, + 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'}}, ] @@ -267,6 +267,7 @@ var grid_tickets_active = { select: 'row', adjust: true, footer: true, + drag: true, resizeColumn: true, headermenu: true, columns: grid_tickets_active_cols, @@ -281,17 +282,17 @@ var grid_tickets_active = { var grid_tickets_invoice_cols = [ - {id: 'index', header: '#', adjust: 'data', css: 'right', - footer: {content: 'countRows', colspan: 3, css: 'right'}}, + {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', adjust: 'header', sort: 'int', - css: 'right', footer: {text: 'Tickets', colspan: 3}}, - {id: "fecha", header: ["Fecha y Hora"], - adjust: "data", sort: "string"}, - {id: 'total', header: 'Total', width: 150,sort: 'int', - format: webix.i18n.priceFormat, css: 'right'}, + 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'}}, ] @@ -301,6 +302,7 @@ var grid_tickets_invoice = { select: 'row', adjust: true, footer: true, + drag: true, resizeColumn: true, headermenu: true, columns: grid_tickets_invoice_cols, @@ -321,8 +323,13 @@ var controls_ticket_to_invoice = [ ticket_search_client, {minHeight: 5, maxHeight: 5}, {cols:[ - {rows: [{view: 'label', label: 'Tickets sin facturar', height: 30, align: 'left'}, grid_tickets_active]}, - {minWidth: 10, maxWidth: 10}, + {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},