diff --git a/CHANGELOG.md b/CHANGELOG.md index 172a404..108a56d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ -v 1.20.0 [04-oct-2018] +v 1.20.0 [08-oct-2018] ---------------------- - Error #295 + - Mejora: Cuentas de banco para clientes + +* IMPORTANTE: Es necesario realizar una migración, despues de actualizar la rama principal. v 1.19.1 [03-oct-2018] diff --git a/docs/empresalibre/docs/notas.md b/docs/empresalibre/docs/notas.md index 3380f94..a16e9e0 100644 --- a/docs/empresalibre/docs/notas.md +++ b/docs/empresalibre/docs/notas.md @@ -6,8 +6,11 @@ siempre actualizado.** Solo se da soporte sobre la ultima versión de **Empresa Libre**. -### 1.20.0 [04-oct-2018] +### 1.20.0 [08-oct-2018] - Error [#295](https://gitlab.com/mauriciobaeza/empresa-libre/issues/295) +- Mejora - Cuentas de banco para clientes + +* IMPORTANTE: Es necesario realizar una migración, despues de actualizar la rama principal. ### 1.19.1 [03-oct-2018] diff --git a/source/app/controllers/main.py b/source/app/controllers/main.py index 69ed5e5..41a169c 100644 --- a/source/app/controllers/main.py +++ b/source/app/controllers/main.py @@ -550,13 +550,29 @@ class AppSATBancos(object): def __init__(self, db): self._db = db - # ~ def on_get(self, req, resp): - # ~ values = req.params - # ~ req.context['result'] = self._db.get_sat_bancos(values) - # ~ resp.status = falcon.HTTP_200 + def on_get(self, req, resp): + values = req.params + req.context['result'] = self._db.get_satbancos(values) + resp.status = falcon.HTTP_200 def on_post(self, req, resp): values = req.params req.context['result'] = self._db.satbancos(values) resp.status = falcon.HTTP_200 + +class AppSociosCuentasBanco(object): + + def __init__(self, db): + self._db = db + + def on_get(self, req, resp): + values = req.params + req.context['result'] = self._db.get_partners_accounts_bank(values) + resp.status = falcon.HTTP_200 + + def on_post(self, req, resp): + values = req.params + req.context['result'] = self._db.partners_accounts_bank(values) + resp.status = falcon.HTTP_200 + diff --git a/source/app/main.py b/source/app/main.py index 87352e9..66298f2 100644 --- a/source/app/main.py +++ b/source/app/main.py @@ -17,7 +17,7 @@ from controllers.main import (AppEmpresas, AppMain, AppValues, AppPartners, AppProducts, AppInvoices, AppFolios, AppDocumentos, AppFiles, AppPreInvoices, AppCuentasBanco, AppMovimientosBanco, AppTickets, AppStudents, AppEmployees, AppNomina, - AppInvoicePay, AppCfdiPay, AppSATBancos + AppInvoicePay, AppCfdiPay, AppSATBancos, AppSociosCuentasBanco ) @@ -59,6 +59,7 @@ api.add_route('/nomina', AppNomina(db)) api.add_route('/invoicepay', AppInvoicePay(db)) api.add_route('/cfdipay', AppCfdiPay(db)) api.add_route('/satbancos', AppSATBancos(db)) +api.add_route('/socioscb', AppSociosCuentasBanco(db)) # ~ Activa si usas waitress y NO estas usando servidor web diff --git a/source/app/models/db.py b/source/app/models/db.py index b1ff1f7..45a8108 100644 --- a/source/app/models/db.py +++ b/source/app/models/db.py @@ -436,6 +436,12 @@ class StorageEngine(object): def get_cfdipay(self, values): return main.CfdiPagos.get_values(values) + def get_satbancos(self, values): + return main.SATBancos.get_values(values) + + def get_partners_accounts_bank(self, values): + return main.SociosCuentasBanco.get_values(values) + def cfdipay(self, values): return main.CfdiPagos.post(values) @@ -444,3 +450,6 @@ class StorageEngine(object): def satbancos(self, values): return main.SATBancos.post(values) + + def partners_accounts_bank(self, values): + return main.SociosCuentasBanco.post(values) diff --git a/source/app/models/main.py b/source/app/models/main.py index 0f2daca..7a9153f 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -1536,6 +1536,21 @@ class SATBancos(BaseModel): opt = values.pop('opt') return getattr(cls, '_{}'.format(opt))(cls, values) + @classmethod + def get_values(cls, values): + opt = values.pop('opt') + return getattr(cls, '_get_{}'.format(opt))(cls, values) + + def _get_active(cls, values): + rows = (SATBancos + .select( + SATBancos.id, + SATBancos.name.alias('value')) + .where(SATBancos.activo==True) + .dicts() + ) + return tuple(rows) + @classmethod def get_(cls): rows = SATBancos.select().dicts() @@ -1575,6 +1590,16 @@ class SATBancos(BaseModel): log.error(msg) return + @classmethod + def get_by_name(cls, name): + try: + obj = SATBancos.get(SATBancos.name==name) + return obj + except SATBancos.DoesNotExist: + msg = 'SATBancos no existe: {}'.format(key) + log.error(msg) + return + class SATNivelesEducativos(BaseModel): name = TextField(index=True) @@ -2697,6 +2722,73 @@ class Socios(BaseModel): return {'ok': False} +class SociosCuentasBanco(BaseModel): + socio = ForeignKeyField(Socios) + banco = ForeignKeyField(SATBancos) + cuenta = TextField(default='') + clabe = TextField(default='') + moneda = ForeignKeyField(SATMonedas, null=True) + + class Meta: + order_by = ('socio', 'banco', 'cuenta') + indexes = ( + (('socio', 'banco', 'cuenta'), True), + ) + + def __str__(self): + return '{} ({})'.format(self.banco.name, self.cuenta[-4:]) + + @classmethod + def get_values(cls, values): + opt = values.pop('opt') + return getattr(cls, '_get_{}'.format(opt))(cls, values) + + def _get_by_partner(self, values): + id = int(values['id_partner']) + rows = (SociosCuentasBanco.select( + SociosCuentasBanco.id, + SQL(" '-' AS delete"), + SATBancos.name.alias('banco'), + SociosCuentasBanco.cuenta, + SociosCuentasBanco.clabe) + .join(SATBancos).switch(SociosCuentasBanco) + .where(SociosCuentasBanco.socio==id) + .dicts()) + return tuple(rows) + + @classmethod + def post(cls, values): + opt = values.pop('opt') + return getattr(cls, '_{}'.format(opt))(cls, values) + + def _new(self, values): + values = util.loads(values['values']) + bank = SATBancos.get_by_name(values['banco']) + fields = { + 'socio': values['id_partner'], + 'banco': bank, + 'cuenta': values['cuenta'], + 'clabe': values['clabe'], + } + try: + obj = SociosCuentasBanco.create(**fields) + except IntegrityError as e: + msg = 'Ya existe esta cuenta' + data = {'ok': False, 'msg': msg} + return data + + msg = 'Cuenta de banco agregada correctamente' + data = {'ok': True, 'id': obj.id, 'msg': msg} + return data + + def _delete(self, values): + values = util.loads(values['values']) + id = int(values['id']) + q = SociosCuentasBanco.delete().where(SociosCuentasBanco.id==id) + result = bool(q.execute()) + msg = 'Cuenta borrada correctamente' + return {'ok': result, 'msg': msg} + class Contactos(BaseModel): socio = ForeignKeyField(Socios) @@ -8123,8 +8215,9 @@ def _crear_tablas(rfc): SATOrigenRecurso, SATTipoContrato, SATTipoDeduccion, SATTipoHoras, SATTipoIncapacidad, SATTipoJornada, SATTipoNomina, SATTipoOtroPago, SATTipoPercepcion, SATTipoRegimen, - Socios, Contactos, ContactoCorreos, ContactoDirecciones, Empleados, - ContactoTelefonos, Departamentos, Puestos, + Socios, SociosCuentasBanco, Contactos, ContactoCorreos, + ContactoDirecciones, Empleados, ContactoTelefonos, Departamentos, + Puestos, Tags, Roles, Usuarios, CuentasBanco, TipoCambio, MovimientosBanco, TipoCorreo, TipoDireccion, TipoPariente, TipoResponsable, TipoTelefono, TipoTitulo, TipoMovimientoAlumno, TipoMovimientoAlmacen, @@ -8190,7 +8283,8 @@ def _migrate_tables(rfc=''): CfdiNominaHorasExtra, CfdiNominaIncapacidad, CfdiNominaJubilacion, CfdiNominaOtroPago, CfdiNominaOtros, CfdiNominaPercepciones, CfdiNominaRelacionados, CfdiNominaSeparacion, CfdiNominaSubcontratos, - CfdiNominaTotales, SATNivelesEducativos, Roles, Permisos + CfdiNominaTotales, SATNivelesEducativos, Roles, Permisos, + SociosCuentasBanco ] log.info('Creando tablas nuevas...') database_proxy.create_tables(tablas, True) diff --git a/source/static/js/controller/partners.js b/source/static/js/controller/partners.js index 8376708..611f275 100644 --- a/source/static/js/controller/partners.js +++ b/source/static/js/controller/partners.js @@ -21,12 +21,12 @@ var cfg_partners = new Object() var partners_controllers = { init: function(){ $$('cmd_new_partner').attachEvent('onItemClick', cmd_new_partner_click); - $$('cmd_new_contact').attachEvent('onItemClick', cmd_new_contact_click); + //~ $$('cmd_new_contact').attachEvent('onItemClick', cmd_new_contact_click); $$('cmd_edit_partner').attachEvent('onItemClick', cmd_edit_partner_click); $$('cmd_delete_partner').attachEvent('onItemClick', cmd_delete_partner_click); $$('cmd_save_partner').attachEvent('onItemClick', cmd_save_partner_click); $$('cmd_cancel_partner').attachEvent('onItemClick', cmd_cancel_partner_click); - $$('cmd_cancel_contact').attachEvent('onItemClick', cmd_cancel_contact_click); + //~ $$('cmd_cancel_contact').attachEvent('onItemClick', cmd_cancel_contact_click); //~ $$('cmd_partner_zero').attachEvent('onItemClick', cmd_partner_zero_click); $$('codigo_postal').attachEvent('onKeyPress', postal_code_key_press); $$('codigo_postal').attachEvent('onTimedKeyPress', postal_code_key_up); @@ -40,6 +40,8 @@ var partners_controllers = { //~ $$('grid_partners').attachEvent('onSelectChange', grid_partners_on_select_change) $$('partner_balance').attachEvent('onChange', partner_balance_on_change) + $$('cmd_partner_add_account_bank').attachEvent('onItemClick', cmd_partner_add_account_bank_click) + $$('grid_partner_account_bank').attachEvent('onItemClick', grid_partner_account_bank_click) default_config_partners() } } @@ -89,6 +91,8 @@ function cmd_new_partner_click(id, e, node){ query = table_usocfdi.chain().find({fisica: true}).data() $$('lst_uso_cfdi_socio').getList().parse(query) $$('partner_balance').define('readonly', !cfg_partners['chk_config_change_balance_partner']) + get_partner_banks() + get_partner_accounts_bank(0) } @@ -136,12 +140,14 @@ function cmd_edit_partner_click(){ if(values.es_proveedor){ $$('cuenta_proveedor').enable() } + get_partner_accounts_bank(row['id']) } }) $$('multi_partners').setValue('partners_new') $$('tab_partner').setValue('Datos Fiscales') -}; + get_partner_banks() +} function cmd_delete_partner_click(id, e, node){ @@ -387,57 +393,6 @@ function is_supplier_change(new_value, old_value){ } -//~ function partner_reset_saldo(id){ - //~ webix.ajax().post('/partners', {opt: 'reset', id: id}, { - //~ error:function(text, data, XmlHttpRequest){ - //~ msg = 'Ocurrio un error, consulta a soporte técnico'; - //~ msg_error(msg) - //~ }, - //~ success:function(text, data, XmlHttpRequest){ - //~ var values = data.json(); - //~ if(values.ok){ - //~ msg = 'Saldo actualizado correctamente' - //~ $$('grid_partners').updateItem(id, {saldo_cliente: 0.0}) - //~ $$('cmd_partner_zero').disable() - //~ msg_ok(msg) - //~ } - //~ } - //~ }) -//~ } - - -//~ function cmd_partner_zero_click(){ - //~ var g = $$('grid_partners') - //~ var row = g.getSelectedItem() - //~ var saldo = row.saldo_cliente.to_float() - - //~ if(saldo){ - //~ msg = '¿Estas seguro de poner en cero el saldo del cliente?

' - //~ msg += 'ESTA ACCIÓN NO SE PUEDE DESHACER' - //~ webix.confirm({ - //~ title: 'Saldo Cliente', - //~ ok: 'Si', - //~ cancel: 'No', - //~ type: 'confirm-error', - //~ text: msg, - //~ callback:function(result){ - //~ if (result){ - //~ partner_reset_saldo(row.id) - //~ } - //~ } - //~ }) - //~ }else{ - //~ $$('cmd_partner_zero').disable() - //~ } -//~ } - - -//~ function grid_partners_on_select_change(){ - //~ $$('cmd_partner_zero').enable() -//~ } - - - function rfc_lost_focus(prev_view){ //~ var form = this.getFormView() //~ var values = form.getValues() @@ -493,3 +448,162 @@ function partner_balance_on_change(new_value, old_value){ this.refresh() } } + + +function cmd_partner_add_account_bank_click(){ + var form = $$('form_partner_account_bank') + + if (!form.validate()){ + msg = 'Valores inválidos' + msg_error(msg) + return + } + + var values = form.getValues() + var id_partner = $$('form_partner').getValues().id + + var account = { + id_partner: id_partner, + delete: '-', + banco: $$('lst_partner_bank').getText(), + cuenta: values.partner_account.trim(), + clabe: values.partner_clabe.trim(), + } + + if(!account.cuenta){ + msg = 'La cuenta es requerida' + msg_error(msg) + return + } + + if(!account.cuenta.is_number()){ + msg = 'Solo digitos en la cuenta' + msg_error(msg) + return + } + + if(account.cuenta.length < 9){ + msg = 'Longitud incorrecta de la cuenta' + msg_error(msg) + return + } + + if(!account.clabe){ + msg = 'La CLABE es requerida' + msg_error(msg) + return + } + + if(account.clabe.length != 18){ + msg = 'La CLABE debe ser de 18 digitos' + msg_error(msg) + return + } + + if(!account.clabe.is_number()){ + msg = 'Solo digitos en la CLABE' + msg_error(msg) + return + } + + var grid = $$('grid_partner_account_bank') + + if(id_partner){ + partner_new_account_bank(account, grid) + }else{ + grid.add(account) + } + + form.setValues({}) +} + + +function get_partner_banks(){ + webix.ajax().get('/satbancos', {opt: 'active'}, function(text, data){ + var values = data.json() + $$('lst_partner_bank').getList().parse(values) + }) +} + + +function partner_new_account_bank(account, grid){ + webix.ajax().post('/socioscb', {opt: 'new', values: account}, { + error: function(text, data, xhr) { + msg = 'Error al guardar' + msg_error(msg) + }, + success: function(text, data, xhr) { + var values = data.json() + if(values.ok){ + account['id'] = values.id + grid.add(account) + msg_ok(values.msg) + }else{ + msg_error(values.msg) + } + } + }) +} + + +function get_partner_accounts_bank(id_partner){ + var grid = $$('grid_partner_account_bank') + grid.clearAll() + + if(id_partner){ + var data = {opt: 'by_partner', id_partner: id_partner} + webix.ajax().get('/socioscb', data, { + error: function(text, data, xhr) { + msg = 'Error al consultar' + msg_error(msg) + }, + success: function(text, data, xhr) { + var values = data.json() + grid.parse(values) + } + }) + } +} + + +function grid_partner_account_bank_click(id, e, node){ + if(id.column != 'delete'){ + return + } + + var msg = '¿Estás seguro de eliminar la cuenta de banco seleccionada?

' + msg += 'ESTA ACCION NO SE PUEDE DESHACER' + webix.confirm({ + title: 'Eliminar cuenta de banco', + ok: 'Si', + cancel: 'No', + type: 'confirm-error', + text: msg, + callback:function(result){ + if (result){ + partner_delete_account_bank(id.row) + } + } + }) +} + + +function partner_delete_account_bank(row){ + var grid = $$('grid_partner_account_bank') + + webix.ajax().post('/socioscb', {opt: 'delete', values: {id: row}}, { + error: function(text, data, xhr) { + msg = 'Error al eliminar' + msg_error(msg) + }, + success: function(text, data, xhr) { + var values = data.json() + if(values.ok){ + grid.remove(row) + msg_ok(values.msg) + }else{ + msg_error(values.msg) + } + } + }) +} diff --git a/source/static/js/ui/partners.js b/source/static/js/ui/partners.js index 4a059d7..d7aeab2 100644 --- a/source/static/js/ui/partners.js +++ b/source/static/js/ui/partners.js @@ -165,6 +165,50 @@ var controls_others = [ ] +var grid_partner_account_bank_cols = [ + {id: 'id', header: 'ID', hidden: true}, + {id: 'delete', header: '', width: 30, css: 'delete'}, + {id: 'banco', header: 'Banco', fillspace: 1}, + {id: 'cuenta', header: 'Cuenta', fillspace: 1}, + {id: 'clabe', header: 'CLABE', fillspace: 1}, + //~ {id: 'moneda', header: 'Moneda', fillspace: 1}, +] + + +var grid_partner_account_bank = { + view: 'datatable', + id: 'grid_partner_account_bank', + select: 'row', + adjust: true, + autoheight: true, + columns: grid_partner_account_bank_cols, +} + + +var controls_partner_bank = [ + {template: 'Agregar cuenta de banco', type: 'section'}, + {view: 'form', id: 'form_partner_account_bank', rows: [ + {cols: [ + {view: 'richselect', id: 'lst_partner_bank', name: 'partner_bank', + label: 'Banco: ', required: true, options: []}, + {view: 'text', id: 'partner_account', name: 'partner_account', + label: 'Cuenta: ', required: true}, + {view: 'text', id: 'partner_clabe', name: 'partner_clabe', + label: 'CLABE: ', required: true}, + ]}, + {minHeight: 10}, + {cols: [{}, + {view: 'button', id: 'cmd_partner_add_account_bank', maxWidth: 200, + label: 'Agregar cuenta'}, + {}]}, + ], + }, + {minHeight: 20, maxHeight: 20}, + {template: 'Cuentas de banco existentes', type: 'section'}, + grid_partner_account_bank, + {minHeight: 50}, +] + var toolbar_contacts = [ {view: 'button', id: 'cmd_new_contact', label: 'Nuevo', type: 'iconButton', @@ -302,11 +346,12 @@ var controls_partner = [ { view: 'tabview', id: 'tab_partner', - tabbar: {options: ['Datos Fiscales', 'Otros Datos', 'Contactos']}, animate: true, + tabbar: {ptions: ['Datos Fiscales', 'Otros Datos', 'Cuentas de Banco']}, + animate: true, cells: [ {id: 'Datos Fiscales', rows: controls_fiscales}, {id: 'Otros Datos', rows: controls_others}, - {id: 'Contactos', rows: [multi_contacts]}, + {id: 'Cuentas de Banco', rows: controls_partner_bank} ] }, {rows: [