diff --git a/README.md b/README.md index fe9bc4f..5149ff2 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Este proyecto está en continuo desarrollo, contratar un esquema de soporte, nos ayuda a continuar su desarrollo. Ponte en contacto con nosotros para -contratar: administracion@empresalibre.net +contratar: administracion ARROBA empresalibre.net ### Requerimientos: diff --git a/source/app/controllers/main.py b/source/app/controllers/main.py index bc7a47a..e3c3924 100644 --- a/source/app/controllers/main.py +++ b/source/app/controllers/main.py @@ -71,6 +71,13 @@ class AppValues(object): req.context['result'] = self._db.get_values(table, values) resp.status = falcon.HTTP_200 + def on_delete(self, req, resp, table): + values = req.params + if self._db.delete(table, values['id']): + resp.status = falcon.HTTP_200 + else: + resp.status = falcon.HTTP_204 + def on_post(self, req, resp, table): file_object = req.get_param('upload') if file_object is None: @@ -82,6 +89,10 @@ class AppValues(object): req.context['result'] = self._db.send_email(values, session) elif table == 'enviarprefac': req.context['result'] = self._db.enviar_prefac(values) + elif table == 'addunidad': + req.context['result'] = self._db.add_unidad(values) + elif table == 'addimpuesto': + req.context['result'] = self._db.add_impuesto(values) else: req.context['result'] = self._db.validate_cert(values, session) else: diff --git a/source/app/controllers/util.py b/source/app/controllers/util.py index a82ca4a..f1fb86d 100644 --- a/source/app/controllers/util.py +++ b/source/app/controllers/util.py @@ -36,7 +36,7 @@ from dateutil import parser from .helper import CaseInsensitiveDict, NumLet, SendMail, TemplateInvoice from settings import DEBUG, log, template_lookup, COMPANIES, DB_SAT, \ PATH_XSLT, PATH_XSLTPROC, PATH_OPENSSL, PATH_TEMPLATES, PATH_MEDIA, PRE, \ - PATH_XMLSEC, TEMPLATE_CANCEL + PATH_XMLSEC, TEMPLATE_CANCEL, IMPUESTOS #~ def _get_hash(password): @@ -166,7 +166,7 @@ def get_con(rfc=''): def get_sat_key(table, key): con = sqlite3.connect(DB_SAT) cursor = con.cursor() - sql = 'SELECT key, description FROM {} WHERE key=?'.format(table) + sql = 'SELECT key, name FROM {} WHERE key=?'.format(table) cursor.execute(sql, (key,)) data = cursor.fetchone() cursor.close() @@ -176,6 +176,44 @@ def get_sat_key(table, key): return {'ok': True, 'text': data[1]} +def get_sat_unidades(key): + con = sqlite3.connect(DB_SAT) + con.row_factory = sqlite3.Row + cursor = con.cursor() + + filtro = '%{}%'.format(key) + sql = "SELECT * FROM unidades WHERE key LIKE ? OR name LIKE ?" + + cursor.execute(sql, [filtro, filtro]) + data = cursor.fetchall() + cursor.close() + con.close() + if data is None: + return () + + data = [dict(r) for r in data] + return tuple(data) + + +def get_sat_productos(key): + con = sqlite3.connect(DB_SAT) + con.row_factory = sqlite3.Row + cursor = con.cursor() + + filtro = '%{}%'.format(key) + sql = "SELECT * FROM productos WHERE key LIKE ? OR name LIKE ?" + + cursor.execute(sql, [filtro, filtro]) + data = cursor.fetchall() + cursor.close() + con.close() + if data is None: + return () + + data = [dict(r) for r in data] + return tuple(data) + + def now(): return datetime.datetime.now().replace(microsecond=0) diff --git a/source/app/models/db.py b/source/app/models/db.py index d3fab7e..c0b498b 100644 --- a/source/app/models/db.py +++ b/source/app/models/db.py @@ -82,6 +82,12 @@ class StorageEngine(object): def _get_unidades(self, values): return main.SATUnidades.get_activos() + def add_unidad(self, values): + return main.SATUnidades.add(values) + + def add_impuesto(self, values): + return main.SATImpuestos.add(values) + def _get_taxes(self, values): return main.SATImpuestos.get_activos() @@ -115,6 +121,12 @@ class StorageEngine(object): def _get_satkey(self, values): return main.get_sat_key(values['key']) + def _get_satunidades(self, values): + return main.get_sat_unidades(values['key']) + + def _get_satproductos(self, values): + return main.get_sat_productos(values['key']) + def _get_series(self, values): return main.Folios.get_all() @@ -144,6 +156,8 @@ class StorageEngine(object): return main.Folios.remove(id) if table == 'preinvoice': return main.PreFacturas.remove(id) + if table == 'satimpuesto': + return main.SATImpuestos.remove(id) return False def _get_client(self, values): diff --git a/source/app/models/main.py b/source/app/models/main.py index db6c618..b304403 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -15,7 +15,7 @@ if __name__ == '__main__': from controllers import util from settings import log, VERSION, PATH_CP, COMPANIES, PRE, CURRENT_CFDI, \ - INIT_VALUES, DEFAULT_PASSWORD + INIT_VALUES, DEFAULT_PASSWORD, DECIMALES, IMPUESTOS FORMAT = '{0:.2f}' @@ -579,6 +579,14 @@ class SATUnidades(BaseModel): rows = SATUnidades.select().dicts() return tuple(rows) + @classmethod + def add(self, values): + try: + SATUnidades.create(**values) + return {'ok': True} + except: + return {'ok': False} + @classmethod def actualizar(self, values): id = int(values['id']) @@ -732,9 +740,43 @@ class SATImpuestos(BaseModel): (('key', 'factor', 'tipo', 'tasa'), True), ) + @classmethod + def add(self, values): + tasa = float(values['tasa']) + tipo = 'T' + if tasa < 0: + tipo: 'R' + + row = { + 'key': IMPUESTOS.get(values['impuesto']), + 'name': values['impuesto'], + 'tipo': tipo, + 'tasa': abs(tasa), + } + try: + obj = SATImpuestos.create(**row) + row['id'] = obj.id + row['delete'] = '-' + return {'ok': True, 'row': row} + except IntegrityError: + return {'ok': False, 'msg': 'El impuesto ya existe'} + + @classmethod + def remove(cls, id): + with database_proxy.transaction(): + q = SATImpuestos.delete().where(SATImpuestos.id==id) + return bool(q.execute()) + @classmethod def get_(self): - rows = SATImpuestos.select().dicts() + rows = (SATImpuestos.select( + SATImpuestos.id, + SQL(" '-' AS delete"), + SATImpuestos.name, + SATImpuestos.tipo, + SATImpuestos.tasa) + .dicts() + ) return tuple(rows) @classmethod @@ -1742,13 +1784,13 @@ class Facturas(BaseModel): valor_unitario = float(product['valor_unitario']) descuento = float(product['descuento']) precio_final = valor_unitario - descuento - importe = round(cantidad * precio_final, 2) + importe = round(cantidad * precio_final, DECIMALES) product['cantidad'] = cantidad product['valor_unitario'] = valor_unitario product['descuento'] = descuento product['precio_final'] = precio_final - product['importe'] = round(cantidad * valor_unitario, 2) + product['importe'] = round(cantidad * valor_unitario, DECIMALES) descuento_cfdi += descuento subtotal += importe @@ -1768,7 +1810,7 @@ class Facturas(BaseModel): if tax.tipo == 'E' or tax.tipo == 'R': continue - import_tax = round(float(tax.tasa) * tax.importe, 2) + import_tax = round(float(tax.tasa) * tax.importe, DECIMALES) total_trasladados = (total_trasladados or 0) + import_tax if tax.name == 'IVA': total_iva += import_tax @@ -1785,9 +1827,9 @@ class Facturas(BaseModel): 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, 2) + import_tax = round(float(tax.tasa) * total_iva, DECIMALES) else: - import_tax = round(float(tax.tasa) * tax.importe, 2) + import_tax = round(float(tax.tasa) * tax.importe, DECIMALES) total_retenciones = (total_retenciones or 0) + import_tax invoice_tax = { @@ -1799,7 +1841,7 @@ class Facturas(BaseModel): FacturasImpuestos.create(**invoice_tax) total = subtotal + (total_trasladados or 0) - (total_retenciones or 0) - total_mn = round(total * invoice.tipo_cambio, 2) + total_mn = round(total * invoice.tipo_cambio, DECIMALES) data = { 'subtotal': subtotal + descuento, 'descuento': descuento_cfdi, @@ -1930,7 +1972,7 @@ class Facturas(BaseModel): if impuesto.tipo == 'E': continue base = row.importe - row.descuento - import_tax = round(impuesto.tasa * base, 2) + import_tax = round(impuesto.tasa * base, DECIMALES) tipo_factor = 'Tasa' if impuesto.factor != 'T': tipo_factor = 'Cuota' @@ -2341,13 +2383,13 @@ class PreFacturas(BaseModel): valor_unitario = float(product['valor_unitario']) descuento = float(product['descuento']) precio_final = valor_unitario - descuento - importe = round(cantidad * precio_final, 2) + importe = round(cantidad * precio_final, DECIMALES) product['cantidad'] = cantidad product['valor_unitario'] = valor_unitario product['descuento'] = descuento product['precio_final'] = precio_final - product['importe'] = round(cantidad * valor_unitario, 2) + product['importe'] = round(cantidad * valor_unitario, DECIMALES) descuento_cfdi += descuento subtotal += importe @@ -2363,7 +2405,7 @@ class PreFacturas(BaseModel): for tax in totals_tax.values(): if tax.tipo == 'E' or tax.tipo == 'R': continue - import_tax = round(float(tax.tasa) * tax.importe, 2) + import_tax = round(float(tax.tasa) * tax.importe, DECIMALES) total_trasladados = (total_trasladados or 0) + import_tax if tax.name == 'IVA': total_iva += import_tax @@ -2380,9 +2422,9 @@ class PreFacturas(BaseModel): 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, 2) + import_tax = round(float(tax.tasa) * total_iva, DECIMALES) else: - import_tax = round(float(tax.tasa) * tax.importe, 2) + import_tax = round(float(tax.tasa) * tax.importe, DECIMALES) total_retenciones = (total_retenciones or 0) + import_tax invoice_tax = { @@ -2394,7 +2436,7 @@ class PreFacturas(BaseModel): PreFacturasImpuestos.create(**invoice_tax) total = subtotal + (total_trasladados or 0) - (total_retenciones or 0) - total_mn = round(total * invoice.tipo_cambio, 2) + total_mn = round(total * invoice.tipo_cambio, DECIMALES) data = { 'subtotal': subtotal + descuento, 'descuento': descuento_cfdi, @@ -2552,7 +2594,9 @@ class PreFacturasDetalle(BaseModel): row['unidad'] = p.producto.unidad.name row['cantidad'] = p.cantidad row['valor_unitario'] = p.valor_unitario - row['importe'] = p.importe + row['descuento'] = p.descuento + pf = p.valor_unitario - p.descuento + row['importe'] = round(pf * p.cantidad, DECIMALES) impuestos = cls._get_impuestos(cls, row['id']) data.append({'row': row, 'taxes': impuestos}) @@ -2687,7 +2731,14 @@ def get_cp(cp): def get_sat_key(key): - return util.get_sat_key('products', key) + return util.get_sat_key('productos', key) + + +def get_sat_unidades(key): + return util.get_sat_unidades(key) + +def get_sat_productos(key): + return util.get_sat_productos(key) def test_correo(values): diff --git a/source/app/settings.py b/source/app/settings.py index 7880a4c..c670fb1 100644 --- a/source/app/settings.py +++ b/source/app/settings.py @@ -92,3 +92,13 @@ PRE = { CURRENT_CFDI = '3.3' DEFAULT_PASSWORD = 'blades3.3' +DECIMALES = 2 +IMPUESTOS = { + 'ISR': '001', + 'IVA': '002', + 'IEPS': '003', + 'ISH': '000', + 'INSPECCION DE OBRA': '000', + 'ICIC': '000', + 'CEDULAR': '000', +} diff --git a/source/db/sat.db b/source/db/sat.db index f86398d..8bc71a8 100644 Binary files a/source/db/sat.db and b/source/db/sat.db differ diff --git a/source/static/js/controller/admin.js b/source/static/js/controller/admin.js index 187d6c9..dfabde5 100644 --- a/source/static/js/controller/admin.js +++ b/source/static/js/controller/admin.js @@ -27,9 +27,12 @@ var controllers = { tb_sat = $$('tab_sat').getTabbar() tb_sat.attachEvent('onChange', tab_sat_change) $$('grid_admin_taxes').attachEvent('onCheck', grid_admin_taxes_on_check) + $$('grid_admin_taxes').attachEvent('onItemClick', grid_admin_taxes_click) $$('grid_admin_monedas').attachEvent('onCheck', grid_admin_monedas_on_check) $$('grid_admin_bancos').attachEvent('onCheck', grid_admin_bancos_on_check) $$('grid_admin_unidades').attachEvent('onCheck', grid_admin_unidades_on_check) + $$('grid_unidad_found').attachEvent('onValueSuggest', grid_unidad_found_click) + $$('cmd_agregar_impuesto').attachEvent('onItemClick', cmd_agregar_impuesto_click) //~ Opciones tb_options = $$('tab_options').getTabbar() tb_options.attachEvent('onChange', tab_options_change) @@ -983,3 +986,142 @@ function cmd_emisor_agregar_cuenta_click(){ }) } + + +function agregar_nueva_unidad(obj){ + var grid = $$('grid_admin_unidades') + var values = {key: obj.key, name: obj.name} + + webix.ajax().post('/values/addunidad', values, { + error: function(text, data, xhr) { + webix.message({type: 'error', text: 'Error al agregar'}) + }, + success: function(text, data, xhr){ + var values = data.json() + if (values.ok){ + grid.add(obj) + } + } + }) +} + + +function grid_unidad_found_click(obj){ + msg = '¿Estás seguro de agregar la siguiente unidad?' + msg += '(' + obj.key + ')
' + msg += obj.name + + webix.confirm({ + title: 'Agregar Unidad', + ok: 'Si', + cancel: 'No', + type: 'confirm-error', + text: msg, + callback:function(result){ + if(result){ + agregar_nueva_unidad(obj) + } + } + }) + $$('buscar_nueva_unidad').setValue('') +} + + +function agregar_impuesto(impuesto, tasa){ + var grid = $$('grid_admin_taxes') + var values = {impuesto: impuesto, tasa: tasa} + + webix.ajax().post('/values/addimpuesto', values, { + error: function(text, data, xhr) { + webix.message({type: 'error', text: 'Error al agregar'}) + }, + success: function(text, data, xhr){ + var values = data.json() + if (values.ok){ + $$('lst_admin_impuestos').setValue('') + $$('txt_admin_tasa').setValue('') + grid.add(values.row) + }else{ + msg_error(values.msg) + } + } + }) +} + + +function cmd_agregar_impuesto_click(){ + var impuesto = $$('lst_admin_impuestos').getValue().trim() + var tasa = $$('txt_admin_tasa').getValue().trim() + + if(!impuesto){ + msg = 'Selecciona un impuesto' + msg_error(msg) + return + } + if(!tasa){ + msg = 'Captura una tasa' + msg_error(msg) + return + } + if(!isFinite(tasa)){ + msg = 'La tasa debe ser un número' + msg_error(msg) + return + } + + tasa = parseFloat(tasa) + if(tasa >= 1){ + msg = 'La tasa debe ser menor a uno' + msg_error(msg) + return + } + + msg = 'Datos correctos.

¿Estás seguro de agregar este impuesto?' + webix.confirm({ + title: 'Agregar impuesto', + ok: 'Si', + cancel: 'No', + type: 'confirm-error', + text: msg, + callback:function(result){ + if(result){ + agregar_impuesto(impuesto, tasa) + } + } + }) +} + + +function borrar_impuesto(row){ + var grid = $$('grid_admin_taxes') + + webix.ajax().del('/values/satimpuesto', {id: row}, function(text, xml, xhr){ + var msg = 'Impuesto eliminado correctamente' + if(xhr.status == 200){ + grid.remove(row) + msg_sucess(msg) + } + }) +} + + +function grid_admin_taxes_click(id, e, node){ + if(id.column != 'delete'){ + return + } + + msg = '¿Estás seguro de borrar el impuesto seleccionado?' + webix.confirm({ + title: 'Borrar impuesto', + ok: 'Si', + cancel: 'No', + type: 'confirm-error', + text: msg, + callback:function(result){ + if(result){ + borrar_impuesto(id.row) + } + } + }) + +} diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js index ddf2a1b..d23b1c1 100644 --- a/source/static/js/controller/invoices.js +++ b/source/static/js/controller/invoices.js @@ -633,7 +633,10 @@ function set_product(values){ grid.add(values) } else { values['cantidad'] = parseFloat(row.cantidad) + 1 - values['importe'] = values['valor_unitario'] * values['cantidad'] + values['descuento'] = parseFloat(row.descuento) + values['valor_unitario'] = parseFloat(row.valor_unitario) + var precio_final = values['valor_unitario'] - values['descuento'] + values['importe'] = (precio_final * values['cantidad']).round(DECIMALES) grid.updateItem(row.id, values) } form.setValues({search_product_id:'', search_product_name:''}, true) @@ -1173,7 +1176,10 @@ function agregar_preproducto(values){ grid.add(values) } else { values['cantidad'] = parseFloat(row.cantidad) + parseFloat(values['cantidad']) - values['importe'] = values['valor_unitario'] * values['cantidad'] + values['valor_unitario'] = parseFloat(row.valor_unitario) + values['descuento'] = parseFloat(row.descuento) + var precio_final = values['valor_unitario'] - values['descuento'] + values['importe'] = (precio_final * values['cantidad']).round(DECIMALES) grid.updateItem(row.id, values) } diff --git a/source/static/js/ui/admin.js b/source/static/js/ui/admin.js index 00d5a26..d4461cd 100644 --- a/source/static/js/ui/admin.js +++ b/source/static/js/ui/admin.js @@ -21,7 +21,7 @@ var sidebar_admin = { onAfterSelect: function(id){ $$('multi_admin').setValue(id) } - } + }, } @@ -154,7 +154,7 @@ var grid_emisor_cuentas_banco = { adjust: true, autoheight: true, headermenu: true, - columns: grid_emisor_cuentas_banco_cols + columns: grid_emisor_cuentas_banco_cols, } @@ -239,7 +239,7 @@ var form_emisor = { rules: { emisor_nombre: function(value){return value.trim() != ''}, } - }] + }], } @@ -266,7 +266,7 @@ var grid_folios = { select: 'row', adjust: true, headermenu: true, - columns: grid_folios_cols + columns: grid_folios_cols, } @@ -384,7 +384,7 @@ var form_folios = { folio_serie: function(value){return value.trim() != ''}, folio_inicio: function(value){return value > 0}, } - }] + }], } @@ -400,7 +400,7 @@ var form_correo = { labelAlign: 'right' }, autoheight: true - }] + }], } @@ -434,13 +434,14 @@ var tab_options = { {id: 'Plantillas', rows: options_templates}, {id: 'Otros', rows: [{}]}, {}, - ] + ], } var grid_admin_taxes_cols = [ {id: 'id', header: 'ID', hidden: true}, - {id: 'name', header: 'Nombre'}, + {id: 'delete', header: '', width: 30, css: 'delete'}, + {id: 'name', header: 'Nombre', adjust: 'data'}, {id: 'tipo', header: 'Tipo'}, {id: 'tasa', header: 'Tasa'}, {id: 'activo', header: 'Activo', template: '{common.checkbox()}', @@ -490,7 +491,7 @@ var grid_admin_taxes = { autoheight: true, autowidth: true, headermenu: true, - columns: grid_admin_taxes_cols + columns: grid_admin_taxes_cols, } @@ -503,7 +504,7 @@ var grid_admin_monedas = { autoheight: true, autowidth: true, headermenu: true, - columns: grid_admin_monedas_cols + columns: grid_admin_monedas_cols, } @@ -516,7 +517,7 @@ var grid_admin_bancos = { autowidth: true, headermenu: true, footer: true, - columns: grid_admin_bancos_cols + columns: grid_admin_bancos_cols, } @@ -530,15 +531,28 @@ var grid_admin_unidades = { autoheight: true, headermenu: true, footer: true, - columns: grid_admin_unidades_cols + columns: grid_admin_unidades_cols, } +var admin_sat_impuestos = {cols: [{maxWidth: 15}, + {view: 'richselect', id: 'lst_admin_impuestos', label: 'Impuesto', options: + ['ISR', 'IVA', 'IEPS', 'ISH', 'INSPECCION DE OBRA', 'ICIC', 'CEDULAR'], + labelAlign: 'right', required: true}, + {view: 'text', id: 'txt_admin_tasa', label: 'Tasa', labelAlign: 'right', + required: true}, + {view: 'button', id: 'cmd_agregar_impuesto', label: 'Agregar', + autowidth: true, type: 'iconButton', icon: 'plus'}, +{}],} + + var msg_tax = 'Activa los impuestos que uses. El predeterminado se muestra primero' var sat_impuestos = [ {maxHeight: 20}, {cols: [{maxWidth: 15}, {view: 'label', label: msg_tax}, {}]}, {maxHeight: 20}, + {cols: [{maxWidth: 15}, admin_sat_impuestos]}, + {maxHeight: 20}, {cols: [{maxWidth: 15}, grid_admin_taxes, {}]}, {}] @@ -562,11 +576,49 @@ var sat_bancos = [ ] +var suggest_sat_unidad = { + view: 'gridsuggest', + id: 'grid_unidad_found', + name: 'grid_unidad_found', + body: { + autoConfig: false, + scroll:true, + autoheight:false, + header: true, + yCount: 10, + columns: [ + {id: 'id', hidden: true}, + {id: 'key', adjust: 'data', header: 'Clave'}, + {id: 'name', adjust: 'data', header: 'Unidad'}, + ], + dataFeed:function(text){ + if (text.length > 2){ + this.load('/values/satunidades?key=' + text) + }else{ + this.hide() + } + } + }, +} + + +var buscar_nueva_unidad = { + view: 'search', + id: 'buscar_nueva_unidad', + label: 'Buscar Unidad en el catálogo del SAT', + labelPosition: 'top', + suggest: suggest_sat_unidad, + placeholder: 'Por clave o descripción. Captura al menos tres letras', +} + + var msg_unidades = 'Activar las unidades necesarias' var sat_unidades = [ {maxHeight: 20}, {cols: [{maxWidth: 15}, {view: 'label', label: msg_unidades}, {}]}, {maxHeight: 20}, + {cols: [{maxWidth: 15}, buscar_nueva_unidad, {}]}, + {maxHeight: 20}, {cols: [{maxWidth: 15}, grid_admin_unidades, {}]}, {}, ] @@ -588,7 +640,7 @@ var tab_sat = { {id: 'Monedas', rows: sat_monedas}, {id: 'Bancos', rows: sat_bancos}, {id: 'Unidades', rows: sat_unidades}, - ] + ], } @@ -605,7 +657,7 @@ var app_emisor = { {}] }, {}, - ] + ], } @@ -616,7 +668,7 @@ var app_folios = { template: 'Folios'}, form_folios, {}, - ] + ], } @@ -627,7 +679,7 @@ var app_correo = { template: 'Configuración de correo'}, form_correo, {}, - ] + ], } @@ -637,7 +689,7 @@ var app_sat = { {view: 'template', id: 'th_sat', type: 'header', template: 'Catálogos del SAT'}, tab_sat, - ] + ], } @@ -647,7 +699,7 @@ var app_options = { {view: 'template', id: 'th_options', type: 'header', template: 'Opciones'}, tab_options, - ] + ], } @@ -665,7 +717,7 @@ var multi_admin = { app_correo, app_sat, app_options, - ] + ], } @@ -679,7 +731,7 @@ var menu_user = { ], type: { subsign: true, - } + }, } @@ -704,7 +756,7 @@ var ui_admin = { ] } ] -}; +} diff --git a/source/static/js/ui/invoices.js b/source/static/js/ui/invoices.js index b99c319..d2c3d82 100644 --- a/source/static/js/ui/invoices.js +++ b/source/static/js/ui/invoices.js @@ -582,7 +582,7 @@ var app_invoices = { rows:[ {view: "template", id: "th_invoices", type: "header", template:"Administración de Facturas" }, multi_invoices - ] + ], } diff --git a/source/static/js/ui/products.js b/source/static/js/ui/products.js index d0596ab..2e639ca 100644 --- a/source/static/js/ui/products.js +++ b/source/static/js/ui/products.js @@ -35,10 +35,10 @@ var grid_products = { } var suggest_categories = { - view: "datasuggest", - type: "tree", + view: 'datasuggest', + type: 'tree', width: 400, - body: { data: [] }, + body: {data: []}, } @@ -63,6 +63,32 @@ var grid_product_taxes = { } +var suggest_sat_producto = { + view: 'gridsuggest', + id: 'grid_producto_found', + name: 'grid_producto_found', + body: { + autoConfig: false, + scroll: true, + autoheight: false, + header: true, + yCount: 10, + columns: [ + {id: 'id', hidden: true}, + {id: 'key', adjust: 'data', header: 'Clave SAT'}, + {id: 'name', header: 'Producto', width: 750}, + ], + dataFeed:function(text){ + if (text.length > 3){ + this.load('/values/satproductos?key=' + text) + }else{ + this.hide() + } + } + } +} + + var controls_generals = [ {view: 'checkbox', id: 'es_activo_producto', name: 'es_activo_producto', label: 'Activo: ', value: true, @@ -75,7 +101,8 @@ var controls_generals = [ {view: 'checkbox', id: 'chk_automatica', label: 'Automática', labelPosition: 'top', value: true, maxWidth: 80}, {view: 'search', id: 'clave_sat', name: 'clave_sat', label: 'Clave SAT', - labelPosition: 'top', required: true, placeholder: 'Buscar clave...'}, + labelPosition: 'top', required: true, suggest: suggest_sat_producto, + placeholder: 'Al menos 4 caracteres...'}, ]}, {cols: [ {view: 'text', id: 'codigo_barras', name: 'codigo_barras',