From a728a35b8f4d7d063224482f5ec9c690c53ebab5 Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Mon, 2 Oct 2017 20:34:30 -0500 Subject: [PATCH] Guardar, editar y eliminar socios de negocio --- source/app/controllers/main.py | 10 +-- source/app/controllers/util.py | 23 +++++-- source/app/main.py | 6 +- source/app/models/db.py | 21 ++++--- source/app/models/main.py | 84 ++++++++++++++++--------- source/static/js/controller/main.js | 12 ++++ source/static/js/controller/partners.js | 74 +++++++++++++++------- source/static/js/ui/partners.js | 25 ++++---- 8 files changed, 165 insertions(+), 90 deletions(-) diff --git a/source/app/controllers/main.py b/source/app/controllers/main.py index 8ec2e8f..06ba734 100644 --- a/source/app/controllers/main.py +++ b/source/app/controllers/main.py @@ -83,17 +83,13 @@ class AppPartners(object): def on_post(self, req, resp): values = req.params - print ('VALUES', values) - if values['id'] == '0': - del values['id'] - req.context['result'] = self._db.new_partner(values) - else: - req.context['result'] = self._db.update_partner(values) + #~ print ('VALUES', values) + req.context['result'] = self._db.partner(values) resp.status = falcon.HTTP_200 def on_delete(self, req, resp): values = req.params - if self._db.delete_partner(values['id']): + if self._db.delete('partner', values['id']): resp.status = falcon.HTTP_200 else: resp.status = falcon.HTTP_204 diff --git a/source/app/controllers/util.py b/source/app/controllers/util.py index 398c048..33e841e 100644 --- a/source/app/controllers/util.py +++ b/source/app/controllers/util.py @@ -5,20 +5,22 @@ import getpass import json import mimetypes import os +import re import sqlite3 +import unicodedata import uuid -import bcrypt +#~ import bcrypt from settings import log, template_lookup, COMPANIES -def _get_hash(password): - return bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode() +#~ def _get_hash(password): + #~ return bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode() -def validate_password(hashed, password): - return bcrypt.hashpw(password.encode(), hashed.encode()) == hashed.encode() +#~ def validate_password(hashed, password): + #~ return bcrypt.hashpw(password.encode(), hashed.encode()) == hashed.encode() def get_pass(): @@ -151,3 +153,14 @@ def parse_con(values): return con except IndexError: return {} + + +def spaces(value): + return ' '.join(value.split()) + + +def to_slug(string): + value = (unicodedata.normalize('NFKD', string) + .encode('ascii', 'ignore') + .decode('ascii').lower()) + return value diff --git a/source/app/main.py b/source/app/main.py index 65fd108..75fbf11 100644 --- a/source/app/main.py +++ b/source/app/main.py @@ -12,13 +12,12 @@ from middleware import ( ) from models.db import StorageEngine from controllers.main import ( - AppLogin, AppLogout, AppAdmin, AppMain, AppValues + AppLogin, AppLogout, AppAdmin, AppMain, AppValues, AppPartners ) from settings import DEBUG db = StorageEngine() -#~ partners = AppPartners(db) api = falcon.API( middleware=[AuthMiddleware(), JSONTranslator(), ConnectionMiddleware()]) @@ -30,8 +29,7 @@ api.add_route('/logout', AppLogout(db)) api.add_route('/admin', AppAdmin(db)) api.add_route('/main', AppMain(db)) api.add_route('/values/{table}', AppValues(db)) - -#~ api.add_route('/partners', partners) +api.add_route('/partners', AppPartners(db)) if DEBUG: diff --git a/source/app/models/db.py b/source/app/models/db.py index cae1d5a..eddcf3b 100644 --- a/source/app/models/db.py +++ b/source/app/models/db.py @@ -20,11 +20,20 @@ class StorageEngine(object): def _get_formapago(self, values): return main.SATFormaPago.get_activos() - def get_partners(self, values): - return main.get_partners(values) + def delete(self, table, id): + if table == 'partner': + return main.Socios.remove(id) + return False - def new_partner(self, values): - return main.new_partner(values) + def get_partners(self, values): + return main.Socios.get(values) + + def partner(self, values): + id = int(values['id']) + del values['id'] + if id: + return main.Socios.update(values, id) + return main.Socios.add(values) def update_partner(self, values): id = int(values['id']) @@ -42,10 +51,6 @@ class StorageEngine(object): data = {'ok': True, 'row': row, 'new': False} return data - def delete_partner(self, id): - q = Clients.delete().where(Clients.id==id) - return bool(q.execute()) - diff --git a/source/app/models/main.py b/source/app/models/main.py index ecac5f2..fd73192 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -216,7 +216,7 @@ class SATFormaPago(BaseModel): def get_activos(cls): rows = (SATFormaPago .select( - SATFormaPago.key.alias('id'), + SATFormaPago.id, SATFormaPago.name.alias('value')) .where(SATFormaPago.activo==True) .dicts() @@ -290,9 +290,11 @@ class Addendas(BaseModel): class Socios(BaseModel): + tipo_persona = IntegerField(default=1) rfc = TextField(index=True) nombre = TextField(index=True) - nombre_comercial = TextField(index=True) + slug = TextField(default='') + nombre_comercial = TextField(index=True, default='') calle = TextField(default='') no_exterior = TextField(default='') no_interior = TextField(default='') @@ -304,8 +306,6 @@ class Socios(BaseModel): notas = TextField(default='') telefonos = TextField(default='') es_activo = BooleanField(default=True) - es_moral = BooleanField(default=False) - es_extranjero = BooleanField(default=False) es_ong = BooleanField(default=False) fecha_alta = DateField(default=util.now) dias_pago = IntegerField(default=0) @@ -328,9 +328,59 @@ class Socios(BaseModel): class Meta: order_by = ('nombre',) indexes = ( - (('rfc', 'nombre'), True), + (('rfc', 'slug'), True), ) + @classmethod + def get(cls, values): + if values: + id = int(values['id']) + row = Socios.select().where(Socios.id==id).dicts()[0] + return row + + rows = Socios.select(Socios.id, Socios.rfc, Socios.nombre).dicts() + return {'ok': True, 'rows': tuple(rows)} + + @classmethod + def add(cls, values): + fields = util.clean(values) + fields['rfc'] = fields['rfc'].upper() + fields['nombre'] = util.spaces(fields['nombre']) + fields['slug'] = util.to_slug(fields['nombre']) + fb = ('dias_habiles', 'es_activo', 'es_cliente', + 'es_proveedor', 'es_ong') + for name in fb: + fields[name] = bool(fields[name].replace('0', '')) + + try: + obj = Socios.create(**fields) + except IntegrityError: + msg = 'Ya existe el RFC y Razón Social' + data = {'ok': False, 'row': {}, 'new': True, 'msg': msg} + return data + + #~ ToDo Agregar Condicion de pago y tags + + row = { + 'id': obj.id, + 'rfc': obj.rfc, + 'nombre': obj.nombre, + } + data = {'ok': True, 'row': row, 'new': True} + return data + + @classmethod + def remove(cls, id): + count = (Facturas + .select(fn.COUNT(Facturas.id)).join(Socios) + .where(Socios.id==id) + .count()) + if count: + return False + + q = Socios.delete().where(Socios.id==id) + return bool(q.execute()) + class Productos(BaseModel): categoria = ForeignKeyField(Categorias, null=True) @@ -453,30 +503,6 @@ def authenticate(args): return respuesta -def get_partners(values): - if values: - id = int(values['id']) - row = Clients.select().where(Clients.id==id).dicts()[0] - return row - - rows = Clients.select(Clients.id, Clients.rfc, Clients.name).dicts() - return {'ok': True, 'rows': tuple(rows)} - - -def new_partner(values): - fields = util.clean(values) - fields['rfc'] = fields['rfc'].upper() - obj = Clients.create(**fields) - row = { - 'id': obj.id, - 'cia': obj.cia, - 'rfc': obj.rfc, - 'name': obj.name, - } - data = {'ok': True, 'row': row, 'new': True} - return data - - def get_cp(cp): con = sqlite3.connect(PATH_CP) cursor = con.cursor() diff --git a/source/static/js/controller/main.js b/source/static/js/controller/main.js index 63d2946..f726fd2 100644 --- a/source/static/js/controller/main.js +++ b/source/static/js/controller/main.js @@ -23,6 +23,7 @@ var controllers = { $$("es_cliente").attachEvent( "onChange", is_client_change) $$("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); @@ -61,3 +62,14 @@ function menu_user_click(id, e, node){ return } } + + +function multi_change(prevID, nextID){ + //~ webix.message(prevID) + if(nextID == 'app_partners'){ + active = $$('multi_partners').getActiveId() + if(active == 'partners_home'){ + get_partners() + } + } +} diff --git a/source/static/js/controller/partners.js b/source/static/js/controller/partners.js index 390d8c1..892619d 100644 --- a/source/static/js/controller/partners.js +++ b/source/static/js/controller/partners.js @@ -6,6 +6,7 @@ function cmd_new_partner_click(id, e, node){ $$('forma_pago').getList().load('/values/formapago') $$('grid_partners').clearSelection() $$('multi_partners').setValue('partners_new') + $$('tab_partner').setValue('Datos Fiscales') }; @@ -16,36 +17,46 @@ function cmd_new_contact_click(id, e, node){ function cmd_edit_partner_click(id, e, node){ - var row = $$('grid_partners').getSelectedItem(); + var msg = '' + var row = $$('grid_partners').getSelectedItem() + if (row == undefined){ - webix.message({ type:'error', text:'Selecciona un Cliente' }); + msg = 'Selecciona un Socio de Negocio' + webix.message({type:'error', text: msg}) return - }; + } webix.ajax().get("/partners", {id:row['id']}, { error: function(text, data, xhr) { - webix.message({ type:"error", text: "Error al consultar"}); + webix.message({type:"error", text: "Error al consultar"}) }, success: function(text, data, xhr){ var values = data.json(); - $$('form_partner').setValues(values); + $$('form_partner').setValues(values) + $$('forma_pago').getList().load('/values/formapago') } - }); + }) - $$("multi_partners").setValue("partners_new") + $$('multi_partners').setValue('partners_new') + $$('tab_partner').setValue('Datos Fiscales') }; function cmd_delete_partner_click(id, e, node){ - var row = $$('grid_partners').getSelectedItem(); - if (row == undefined){ - webix.message({ type:'error', text:'Selecciona un Cliente' }); - return - }; + var msg = '' + var row = $$('grid_partners').getSelectedItem() - var msg = '¿Estás seguro de eliminar al cliente?

' - msg += row['name'] + ' (' + row['rfc'] + ')' - msg += '

ESTA ACCIÓN NO SE PUEDE DESHACER' + if (row == undefined){ + msg = 'Selecciona un Cliente o Proveedor' + webix.message({type:'error', text: msg}) + return + } + + msg = '¿Estás seguro de eliminar al cliente?

' + msg += row['nombre'] + ' (' + row['rfc'] + ')' + msg += '

ESTA ACCIÓN NO SE PUEDE DESHACER

' + msg += 'Solo se pueden eliminar clientes o proveedores sin documentos ' + msg += 'relacionados. Se recomienda solo desactivar en vez de eliminar' webix.confirm({ title:'Eliminar Cliente', ok:'Si', @@ -62,28 +73,37 @@ function cmd_delete_partner_click(id, e, node){ function delete_partner(id){ - webix.ajax().del('/partners', {id:id}, function(text, xml, xhr){ - var msg = 'Cliente eliminado correctamente' + webix.ajax().del('/partners', {id: id}, function(text, xml, xhr){ + var msg = 'Socio eliminado correctamente' if (xhr.status == 200){ $$('grid_partners').remove(id); - webix.message({type:'success', text:msg}); + webix.message({type: 'success', text: msg}) } else { - msg = 'No se pudo eliminar' - webix.message({type:'error', text:msg}); + msg = 'No se pudo eliminar. Asegurate de que no tenga documentos relacionados' + webix.message({type: 'error', text: msg}) } - }); + }) } function cmd_save_partner_click(id, e, node){ + var msg = 'Valores inválidos' var form = this.getFormView(); if (!form.validate()) { - webix.message({ type:'error', text:'Valores inválidos'}) + webix.message({type: 'error', text: msg}) return } var values = form.getValues(); + + if (!values.es_cliente && !values.es_proveedor){ + msg = 'Selecciona si es cliente, proveedor o ambos' + webix.message({type: 'error', text: msg}) + $$('tab_partner').setValue('Otros Datos') + return + } + webix.ajax().post('/partners', values, { error:function(text, data, XmlHttpRequest){ msg = 'Ocurrio un error, consulta a soporte técnico'; @@ -97,9 +117,9 @@ function cmd_save_partner_click(id, e, node){ webix.message({type:'error', text:values.msg}); } } - }); + }) -}; +} function update_grid_partner(values){ @@ -263,3 +283,9 @@ function rfc_lost_focus(prev_view){ //~ } //~ }) } + + +function multi_partners_change(prevID, nextID){ + webix.message(prevID) + webix.message(nextID) +} diff --git a/source/static/js/ui/partners.js b/source/static/js/ui/partners.js index a424be0..358f7d1 100644 --- a/source/static/js/ui/partners.js +++ b/source/static/js/ui/partners.js @@ -11,13 +11,12 @@ var toolbar_partners = [ var grid_partners_cols = [ - {id: 'index', header:'#', adjust:'data', css:'right', - footer: {content: 'rowCount'}}, - {id: 'id', header: 'Clave', adjust:'data', sort: 'int', css: 'right', - footer: 'Clientes'}, + {id: 'index', header:'#', adjust:'data', css: 'right', + footer: {content: 'rowCount', colspan: 2, css: 'right'}}, + {id: 'id', header: 'Clave', adjust:'data', sort: 'int', css: 'right'}, {id: 'rfc', header: ['RFC', {content: 'textFilter'}], adjust:'data', - sort: 'string'}, - {id: 'name', header: ['Razón Social', {content: 'textFilter'}], + sort: 'string', footer: {text: 'Clientes y Proveedores', colspan: 2}}, + {id: 'nombre', header: ['Razón Social', {content: 'textFilter'}], fillspace:true, sort: 'string'}, ] @@ -38,9 +37,6 @@ var grid_partners = { }) } }, - //~ onClick:{ - //~ cmd_edit_parter: cmd_edit_partner_click, - //~ }, } @@ -83,10 +79,12 @@ var controls_fiscales = [ label: 'Condiciones de Pago: '}, ]}, {cols: [ - {view: 'counter', name: 'dias_pago', label: 'Días de pago', step:5, - value: 0, min: 0, max: 365, tooltip: 'Permite calcular las fechas de pago'}, - {view: 'checkbox', id: 'chk_business_days', name: 'dias_habiles', - label: 'Hábiles: ', value: false}, + {view: 'counter', id: 'dias_pago', name: 'dias_pago', + label: 'Días de pago', step: 5, value: 0, min: 0, max: 365, + tooltip: 'Permite calcular las fechas de pago', width: 250}, + {view: 'checkbox', id: 'dias_habiles', name: 'dias_habiles', + label: 'Hábiles: ', value: false, width: 180}, + {}, ]} ] @@ -256,6 +254,7 @@ var multi_contacts = { var controls_partner = [ { view: 'tabview', + id: 'tab_partner', tabbar: {options: ['Datos Fiscales', 'Otros Datos', 'Contactos']}, animate: true, cells: [ {id: 'Datos Fiscales', rows: controls_fiscales},