diff --git a/source/app/controllers/main.py b/source/app/controllers/main.py index b1b0b1c..946c137 100644 --- a/source/app/controllers/main.py +++ b/source/app/controllers/main.py @@ -424,6 +424,29 @@ class AppEmployees(object): resp.status = falcon.HTTP_204 +class AppNomina(object): + + def __init__(self, db): + self._db = db + + def on_get(self, req, resp): + values = req.params + req.context['result'] = self._db.get_nomina(values) + resp.status = falcon.HTTP_200 + + def on_post(self, req, resp): + values = req.params + req.context['result'] = self._db.nomina(values) + resp.status = falcon.HTTP_200 + + def on_delete(self, req, resp): + values = req.params + if self._db.delete('nomina', values): + resp.status = falcon.HTTP_200 + else: + resp.status = falcon.HTTP_204 + + class AppDocumentos(object): def __init__(self, db): diff --git a/source/app/controllers/util.py b/source/app/controllers/util.py index 56218e0..e80941a 100644 --- a/source/app/controllers/util.py +++ b/source/app/controllers/util.py @@ -1013,6 +1013,27 @@ class LIBO(object): msg = 'Empleados importados correctamente' return rows, msg + def nomina(self, path): + options = {'AsTemplate': True, 'Hidden': True} + doc = self._doc_open(path, options) + if doc is None: + return () + + data, msg = self._get_data(doc, 'Nomina') + doc.close(True) + + if len(data) == 1: + msg = 'Sin datos para importar' + return (), msg + + fields = ( + 'num_empleado', + 'rfc', + ) + rows = tuple([dict(zip(fields, r)) for r in data[1:]]) + msg = 'Nomina importada correctamente' + return rows, msg + def invoice(self, path): options = {'AsTemplate': True, 'Hidden': True} doc = self._doc_open(path, options) @@ -1072,6 +1093,20 @@ def import_employees(rfc): return () +def import_nomina(rfc): + name = '{}_nomina.ods'.format(rfc.lower()) + path = _join(PATH_MEDIA, 'tmp', name) + if not is_file(path): + return () + + if APP_LIBO: + app = LIBO() + if app.is_running: + return app.nomina(path) + + return () + + def parse_xml(xml): return ET.fromstring(xml) @@ -1504,6 +1539,15 @@ def upload_file(rfc, opt, file_obj): name = '{}_employees.ods'.format(rfc.lower()) path = _join(PATH_MEDIA, 'tmp', name) + elif opt == 'nomina': + tmp = file_obj.filename.split('.') + ext = tmp[-1].lower() + if ext != 'ods': + msg = 'Extensión de archivo incorrecta, selecciona un archivo ODS' + return {'status': 'server', 'name': msg, 'ok': False} + + name = '{}_nomina.ods'.format(rfc.lower()) + path = _join(PATH_MEDIA, 'tmp', name) if save_file(path, file_obj.file.read()): return {'status': 'server', 'name': file_obj.filename, 'ok': True} diff --git a/source/app/main.py b/source/app/main.py index a394349..a11dfd4 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, AppTickets, AppEmployees + AppMovimientosBanco, AppTickets, AppEmployees, AppNomina ) @@ -53,6 +53,7 @@ api.add_route('/tickets', AppTickets(db)) api.add_route('/cuentasbanco', AppCuentasBanco(db)) api.add_route('/movbanco', AppMovimientosBanco(db)) api.add_route('/employees', AppEmployees(db)) +api.add_route('/nomina', AppNomina(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 a1557aa..4d9e420 100644 --- a/source/app/models/db.py +++ b/source/app/models/db.py @@ -14,6 +14,9 @@ class StorageEngine(object): def get_employees(self, values): return main.Empleados.get_by(values) + def get_nomina(self, values): + return main.CfdiNomina.get_by(values) + def empresa_agregar(self, values): return main.empresa_agregar(values['alta_rfc'], False) diff --git a/source/app/models/main.py b/source/app/models/main.py index ef14780..3d2a68a 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -67,7 +67,8 @@ def desconectar(): def upload_file(rfc, opt, file_obj): result = util.upload_file(rfc, opt, file_obj) if result['ok']: - if opt != 'bdfl': + names = ('bdfl', 'employees', 'nomina', 'products', 'invoiceods') + if not opt in names: Configuracion.add({opt: file_obj.filename}) return result @@ -197,7 +198,7 @@ def config_main(): nomina = util.get_bool(Configuracion.get_('chk_usar_nomina')) data = { 'empresa': get_title_app(3), - 'punto_de_venta': punto_de_venta + 'punto_de_venta': punto_de_venta, 'nomina': nomina, } if not obj is None: @@ -5526,6 +5527,53 @@ class CfdiNomina(BaseModel): class Meta: order_by = ('fecha',) + def _validate_import(self, row): + sn = {'si': True, 'no': False} + data = row.copy() + return data + + def _import(self): + emisor = Emisor.select()[0] + rows, msg = util.import_nomina(emisor.rfc) + if not rows: + return {'ok': False, 'msg': msg} + + for row in rows: + data = self._validate_import(self, row) + # ~ w = (Nomina.empleado.rfc==row['rfc']) + with database_proxy.transaction(): + pass + # ~ if Empleados.select().where(w).exists(): + # ~ q = Empleados.update(**data).where(w) + # ~ q.execute() + # ~ else: + # ~ obj = Empleados.create(**data) + return {'ok': True, 'msg': msg} + + def _get(self): + rows = (nomina + .select( + Nomina.id, + Nomina.serie, + Nomina.folio, + Nomina.fecha, + Nomina.status, + Nomina.fecha_pago, + Nomina.total, + Nomina.empleado.nombre_completo + ) + .dicts() + ) + return {'ok': True, 'rows': tuple(rows)} + + @classmethod + def get_by(cls, values): + if not 'opt' in values: + return cls._get(cls) + + if values['opt'] == 'import': + return cls._import(cls) + class CfdiNominaDetalle(BaseModel): cfdi = ForeignKeyField(CfdiNomina) diff --git a/source/static/js/controller/nomina.js b/source/static/js/controller/nomina.js index 1b57859..c09668a 100644 --- a/source/static/js/controller/nomina.js +++ b/source/static/js/controller/nomina.js @@ -39,8 +39,82 @@ function current_dates_nomina(){ } +function get_nomina(){ + +} + + function cmd_nomina_import_click(){ - msg_ok('ok') + win_import_nomina.init() + $$('win_import_nomina').show() +} + + +function cmd_import_template_nomina_click(){ + var form = $$('form_upload_nomina') + + var values = form.getValues() + + if(!$$('lst_upload_nomina').count()){ + $$('win_import_nomina').close() + return + } + + if($$('lst_upload_nomina').count() > 1){ + msg = 'Selecciona solo un archivo' + msg_error(msg) + return + } + + var template = $$('up_nomina').files.getItem($$('up_nomina').files.getFirstId()) + + if(template.type.toLowerCase() != 'ods'){ + msg = 'Archivo inválido.\n\nSe requiere un archivo ODS' + msg_error(msg) + return + } + + msg = '¿Estás seguro de importar este archivo?' + webix.confirm({ + title: 'Importar Nómina', + ok: 'Si', + cancel: 'No', + type: 'confirm-error', + text: msg, + callback:function(result){ + if(result){ + $$('up_nomina').send() + } + } + }) +} + + +function up_nomina_upload_complete(response){ + if(response.status != 'server'){ + msg = 'Ocurrio un error al subir el archivo' + msg_error(msg) + return + } + msg = 'Archivo subido correctamente.\n\nComenzando importación.' + msg_ok(msg) + $$('win_import_nomina').close() + + webix.ajax().get('/nomina', {opt: 'import'}, { + error: function(text, data, xhr) { + msg = 'Error al importar' + msg_error(msg) + }, + success: function(text, data, xhr) { + var values = data.json(); + if (values.ok){ + msg_ok(values.msg) + get_nomina() + }else{ + msg_error(values.msg) + } + } + }) } diff --git a/source/static/js/ui/nomina.js b/source/static/js/ui/nomina.js index 1f82cdb..5cbe1a2 100644 --- a/source/static/js/ui/nomina.js +++ b/source/static/js/ui/nomina.js @@ -201,3 +201,35 @@ var win_import_employees = { $$('up_employees').attachEvent('onUploadComplete', up_employees_upload_complete) } } + + +var body_import_nomina = {rows: [ + {view: 'form', id: 'form_upload_nomina', rows: [ + {cols: [{}, + {view: 'uploader', id: 'up_nomina', autosend: false, + link: 'lst_upload_nomina', value: 'Seleccionar Plantilla', + upload: '/files/nomina'}, {}]}, + {cols: [ + {view: 'list', id: 'lst_upload_nomina', name: 'lst_nomina', + type: 'uploader', autoheight: true, borderless: true}]}, + {cols: [{}, {view: 'button', id: 'cmd_import_template_nomina', + label: 'Importar Nómina'}, {}]}, + ]}, +],} + + +var win_import_nomina = { + init: function(){ + webix.ui({ + view: 'window', + id: 'win_import_nomina', + width: 400, + modal: true, + position: 'center', + head: 'Importar Nómina', + body: body_import_nomina, + }) + $$('cmd_import_template_nomina').attachEvent('onItemClick', cmd_import_template_nomina_click) + $$('up_nomina').attachEvent('onUploadComplete', up_nomina_upload_complete) + } +}