From c0d1c41401d96e3601bc2b435ec6611d75101bdd Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Sun, 26 Nov 2017 13:39:32 -0600 Subject: [PATCH 1/8] SAT formas de pago en Admin --- source/app/models/db.py | 6 +++ source/app/models/main.py | 26 +++++++++++++ source/static/js/controller/admin.js | 29 ++++++++++++++ source/static/js/ui/admin.js | 56 +++++++++++++++++++++++----- 4 files changed, 107 insertions(+), 10 deletions(-) diff --git a/source/app/models/db.py b/source/app/models/db.py index 4b8c81d..c1c7eb0 100644 --- a/source/app/models/db.py +++ b/source/app/models/db.py @@ -112,6 +112,9 @@ class StorageEngine(object): def _get_allunidades(self, values): return main.SATUnidades.get_() + def _get_allformasdepago(self, values): + return main.SATFormaPago.get_() + def _get_taxupdate(self, values): return main.SATImpuestos.actualizar(values) @@ -124,6 +127,9 @@ class StorageEngine(object): def _get_unidadupdate(self, values): return main.SATUnidades.actualizar(values) + def _get_formasdepagoupdate(self, values): + return main.SATFormaPago.actualizar(values) + def _get_emisorcuentasbanco(self, values): return main.CuentasBanco.emisor() diff --git a/source/app/models/main.py b/source/app/models/main.py index 427db72..ceb4c5e 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -695,6 +695,11 @@ class SATFormaPago(BaseModel): def __str__(self): return 'Forma de pago: ({}) {}'.format(self.key, self.name) + @classmethod + def get_(self): + rows = SATFormaPago.select().dicts() + return tuple(rows) + @classmethod def get_by_key(cls, key): return SATFormaPago.get(SATFormaPago.key==key) @@ -711,6 +716,27 @@ class SATFormaPago(BaseModel): ) return tuple(rows) + @classmethod + def actualizar(self, values): + id = int(values['id']) + if values['field'] == 'activo': + v = {'0': False, '1': True} + q = (SATFormaPago + .update(**{'activo': v[values['value']]}) + .where(SATFormaPago.id==id)) + result = bool(q.execute()) + elif values['field'] == 'default': + q = SATFormaPago.update(**{'default': False}) + q.execute() + + v = {'false': False, 'true': True} + q = (SATFormaPago + .update(**{'default': v[values['value']]}) + .where(SATFormaPago.id==id)) + result = bool(q.execute()) + + return {'ok': result} + class SATAduanas(BaseModel): key = TextField(unique=True, index=True) diff --git a/source/static/js/controller/admin.js b/source/static/js/controller/admin.js index 4330737..e1d850c 100644 --- a/source/static/js/controller/admin.js +++ b/source/static/js/controller/admin.js @@ -32,6 +32,7 @@ var controllers = { $$('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_admin_formasdepago').attachEvent('onCheck', grid_admin_formasdepago_on_check) $$('grid_unidad_found').attachEvent('onValueSuggest', grid_unidad_found_click) $$('cmd_agregar_impuesto').attachEvent('onItemClick', cmd_agregar_impuesto_click) //~ Opciones @@ -259,6 +260,7 @@ function get_admin_impuestos(){ $$('grid_admin_taxes').clearAll() $$('grid_admin_taxes').parse(values, 'json') }) + $$('tab_sat').setValue('Impuestos') } @@ -289,6 +291,15 @@ function get_admin_unidades(){ } +function get_admin_formasdepago(){ + webix.ajax().sync().get('/values/allformasdepago', function(text, data){ + var values = data.json() + $$('grid_admin_formasdepago').clearAll() + $$('grid_admin_formasdepago').parse(values, 'json') + }) +} + + function get_config_values(opt){ if(opt == undefined){ return @@ -906,6 +917,8 @@ function tab_sat_change(nv, ov){ get_admin_bancos() }else if(nv == 'Unidades'){ get_admin_unidades() + }else if(nv == 'Formas de Pago'){ + get_admin_formasdepago() } } @@ -974,6 +987,22 @@ function grid_admin_unidades_on_check(row, column, state){ } +function grid_admin_formasdepago_on_check(row, column, state){ + + var values = { + id: row, + field: column, + value: state, + } + webix.ajax().get('/values/formasdepagoupdate', values, { + error: function(text, data, xhr) { + }, + success: function(text, data, xhr) { + } + }) +} + + function emisor_cuenta_saldo_inicial_change(new_value, old_value){ if(!isFinite(new_value)){ this.config.value = old_value diff --git a/source/static/js/ui/admin.js b/source/static/js/ui/admin.js index 1178aaf..0ba0be3 100644 --- a/source/static/js/ui/admin.js +++ b/source/static/js/ui/admin.js @@ -515,7 +515,7 @@ var grid_admin_taxes = { view: 'datatable', id: 'grid_admin_taxes', select: 'cell', - multiselect: true, + //~ multiselect: true, adjust: true, autoheight: true, autowidth: true, @@ -528,7 +528,7 @@ var grid_admin_monedas = { view: 'datatable', id: 'grid_admin_monedas', select: 'cell', - multiselect: true, + //~ multiselect: true, adjust: true, autoheight: true, autowidth: true, @@ -541,7 +541,7 @@ var grid_admin_bancos = { view: 'datatable', id: 'grid_admin_bancos', select: 'cell', - multiselect: true, + //~ multiselect: true, adjust: true, autowidth: true, headermenu: true, @@ -554,7 +554,7 @@ var grid_admin_unidades = { view: 'datatable', id: 'grid_admin_unidades', select: 'cell', - multiselect: true, + //~ multiselect: true, adjust: true, autowidth: true, autoheight: true, @@ -564,6 +564,31 @@ var grid_admin_unidades = { } +var grid_admin_formasdepago_cols = [ + {id: 'id', header: 'ID', hidden: true}, + {id: 'key', header: 'Clave'}, + {id: 'name', header: 'Nombre', adjust: 'data'}, + {id: 'activo', header: 'Activa', template: '{common.checkbox()}', + editor: 'checkbox'}, + {id: 'default', header: 'Predeterminada', template: '{common.radio()}', + adjust: 'header'}, +] + + +var grid_admin_formasdepago = { + view: 'datatable', + id: 'grid_admin_formasdepago', + select: 'cell', + //~ multiselect: true, + adjust: true, + autowidth: true, + //~ autoheight: true, + headermenu: true, + footer: true, + columns: grid_admin_formasdepago_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'], @@ -653,22 +678,33 @@ var sat_unidades = [ ] +var msg_formasdepago = 'Activar las formas de pago.' +var sat_formasdepago = [ + {maxHeight: 20}, + {cols: [{maxWidth: 15}, {view: 'label', label: msg_formasdepago}, {}]}, + {maxHeight: 20}, + {cols: [{maxWidth: 15}, grid_admin_formasdepago, {}]}, + {maxHeight: 20}, +] + + var tab_sat = { view: 'tabview', id: 'tab_sat', multiview: true, - tabbar: {options: [ - 'Impuestos', - 'Monedas', - 'Bancos', - 'Unidades' - ]}, + //~ tabbar: {options: [ + //~ 'Impuestos', + //~ 'Monedas', + //~ 'Bancos', + //~ 'Unidades' + //~ ]}, animate: true, cells: [ {id: 'Impuestos', rows: sat_impuestos}, {id: 'Monedas', rows: sat_monedas}, {id: 'Bancos', rows: sat_bancos}, {id: 'Unidades', rows: sat_unidades}, + {id: 'Formas de Pago', rows: sat_formasdepago}, ], } From e16ee337a3190b03001cb041eaf5ec3bda2d82e2 Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Sun, 26 Nov 2017 13:56:10 -0600 Subject: [PATCH 2/8] Donativo en especien --- source/app/models/main.py | 3 +++ source/static/js/controller/invoices.js | 13 +++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/source/app/models/main.py b/source/app/models/main.py index ceb4c5e..afb6941 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -1996,6 +1996,9 @@ class Facturas(BaseModel): if invoice.tipo_comprobante == 'T': return + if invoice.donativo and invoice.forma_pago == '12': + return + importe = invoice.total_mn if invoice.tipo_comprobante == 'E': importe *= -1 diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js index cd96780..4c19d51 100644 --- a/source/static/js/controller/invoices.js +++ b/source/static/js/controller/invoices.js @@ -4,6 +4,7 @@ var msg = '' var result = false var tipo_relacion = '' var anticipo = false +var donativo = false function get_condicion_pago(){ @@ -498,7 +499,7 @@ function guardar_y_timbrar(values){ data['relacionados'] = ids data['tipo_relacion'] = tipo_relacion data['anticipo'] = anticipo - data['donativo'] = $$('chk_cfdi_donativo').getValue() + data['donativo'] = donativo var usar_ine = $$('chk_cfdi_usar_ine').getValue() if(usar_ine){ @@ -518,6 +519,7 @@ function guardar_y_timbrar(values){ tipo_relacion = '' anticipo = false $$('chk_cfdi_anticipo').setValue(0) + $$('chk_cfdi_donativo').setValue(0) $$('chk_cfdi_usar_ine').setValue(0) $$('form_invoice').setValues({id_partner: 0, lbl_partner: 'Ninguno'}) @@ -545,7 +547,14 @@ function cmd_timbrar_click(id, e, node){ msg += 'La factura tiene CFDI relacionados

' } if(anticipo){ - msg += 'La factura es Anticipo

' + msg += 'La factura es un Anticipo

' + } + if(donativo){ + msg += 'La factura es un Donativo' + if($$('lst_forma_pago').getValue()=='12'){ + msg += ' en Especie' + } + msg += '

' } usar_ine = $$('chk_cfdi_usar_ine').getValue() if(usar_ine){ From d06a5b83611fff0141e4373ab9916acc41809f3b Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Sun, 26 Nov 2017 23:40:14 -0600 Subject: [PATCH 3/8] Mostrar nombre de empresa --- source/app/models/db.py | 3 +++ source/app/models/main.py | 16 ++++++++++++++++ source/static/js/controller/main.js | 4 ++++ source/static/js/ui/main.js | 2 +- source/static/js/ui/partners.js | 2 +- 5 files changed, 25 insertions(+), 2 deletions(-) diff --git a/source/app/models/db.py b/source/app/models/db.py index c1c7eb0..90d1f4d 100644 --- a/source/app/models/db.py +++ b/source/app/models/db.py @@ -14,6 +14,9 @@ class StorageEngine(object): def get_values(self, table, values=None): return getattr(self, '_get_{}'.format(table))(values) + def _get_main(self, values): + return main.config_main() + def _get_configtimbrar(self, values): return main.config_timbrar() diff --git a/source/app/models/main.py b/source/app/models/main.py index afb6941..fb6720c 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -102,6 +102,22 @@ def validar_timbrar(): return {'ok': True, 'msg': msg} +def config_main(): + try: + obj = Emisor.select()[0] + except IndexError: + obj = None + + data = { + 'empresa': 'Empresa Libre', + } + if not obj is None: + titulo = 'Empresa Libre - {}' + data['empresa'] = titulo.format(obj.nombre) + + return data + + def config_timbrar(): try: obj = Emisor.select()[0] diff --git a/source/static/js/controller/main.js b/source/static/js/controller/main.js index d74004a..6fbef20 100644 --- a/source/static/js/controller/main.js +++ b/source/static/js/controller/main.js @@ -6,6 +6,10 @@ function configuracion_inicial(){ var values = data.json() $$('cmd_ir_al_admin').show(values) }) + webix.ajax().get('/values/main', function(text, data){ + var values = data.json() + $$('lbl_title_main').setValue(values.empresa) + }) } diff --git a/source/static/js/ui/main.js b/source/static/js/ui/main.js index 5034328..d004c18 100644 --- a/source/static/js/ui/main.js +++ b/source/static/js/ui/main.js @@ -63,7 +63,7 @@ var ui_main = { $$('$sidebar1').toggle() } }, - {view: 'label', label: 'Empresa Libre'}, + {view: 'label', id: 'lbl_title_main', label: 'Empresa Libre'}, {}, menu_user, {view: 'button', type: 'icon', width: 45, css: 'app_button', diff --git a/source/static/js/ui/partners.js b/source/static/js/ui/partners.js index 3c399c3..4b763fe 100644 --- a/source/static/js/ui/partners.js +++ b/source/static/js/ui/partners.js @@ -14,7 +14,7 @@ var grid_partners_cols = [ {id: 'index', header:'#', css: 'right', footer: {content: 'rowCount', colspan: 2, css: 'right'}}, {id: 'id', header: 'Clave', sort: 'int', css: 'right'}, - {id: 'rfc', header: ['RFC', {content: 'textFilter'}], + {id: 'rfc', header: ['RFC', {content: 'textFilter'}], adjust: 'data', sort: 'string', footer: {text: 'Clientes y Proveedores', colspan: 2}}, {id: 'nombre', header: ['Razón Social', {content: 'textFilter'}], fillspace:true, sort: 'string'}, From f1d47d1119f73fa1e323139d207bffb438c61fda Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Wed, 29 Nov 2017 15:21:51 -0600 Subject: [PATCH 4/8] Agregar deposito con facturas relacionadas --- source/app/models/main.py | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/source/app/models/main.py b/source/app/models/main.py index fb6720c..74ec1e5 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -3220,6 +3220,7 @@ class FacturasPagos(BaseModel): auto_round=True) saldo = DecimalField(default=0.0, max_digits=18, decimal_places=6, auto_round=True) + cancelado = BooleanField(default=False) class Meta: order_by = ('factura',) @@ -3227,10 +3228,46 @@ class FacturasPagos(BaseModel): (('factura', 'numero'), True), ) + def _movimiento_anterior(self, mov, id): + try: + query = (FacturasPagos + .select() + .where( + (FacturasPagos.movimiento==mov) & + (FacturasPagos.factura==id) & + (FacturasPagos.cancelado==False))[-1] + ) + return query + except IndexError: + return None + + def _actualizar_saldo_cliente(self, cliente, importe): + q = (Socios + .update(saldo_cliente=Socios.saldo_cliente - importe) + .where(Socios.id==cliente.id) + ) + return bool(q.execute()) + + @classmethod def add(cls, mov, ids): - print (mov) print (ids) + for i, importe in ids.items(): + fac = Facturas.get(Facturas.id==int(i)) + mov_ant = cls._movimiento_anterior(cls, mov, fac) + nuevo = {'movimiento': mov, 'factura': fac, 'importe': importe} + if mov_ant is None: + nuevo['saldo_anterior'] = float(fac.saldo) + else: + nuevo['numero'] = mov_ant.numero + 1 + nuevo['saldo_anterior'] = float(mov_ant.saldo) + nuevo['saldo'] = nuevo['saldo_anterior'] - importe + FacturasPagos.create(**nuevo) + fac.saldo = nuevo['saldo'] + if nuevo['saldo'] == 0: + fac.pagada = True + fac.save() + cls._actualizar_saldo_cliente(cls, fac.cliente, importe) return From 5cda707d59e694f9a1aa278d69c9d0f7dd4c9185 Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Wed, 29 Nov 2017 19:41:03 -0600 Subject: [PATCH 5/8] Agregar indice para las cancelaciones en depositos de facturas --- source/app/models/main.py | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/source/app/models/main.py b/source/app/models/main.py index 74ec1e5..c1010bd 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -3225,25 +3225,23 @@ class FacturasPagos(BaseModel): class Meta: order_by = ('factura',) indexes = ( - (('factura', 'numero'), True), + (('movimiento', 'factura', 'numero'), True), ) def _movimiento_anterior(self, mov, id): - try: - query = (FacturasPagos - .select() - .where( - (FacturasPagos.movimiento==mov) & - (FacturasPagos.factura==id) & - (FacturasPagos.cancelado==False))[-1] - ) - return query - except IndexError: - return None + query = (FacturasPagos + .select() + .where(FacturasPagos.factura==id, + FacturasPagos.cancelado==False) + ) + if len(query): + return query[-1], len(query) + 1 + else: + return None, 1 def _actualizar_saldo_cliente(self, cliente, importe): q = (Socios - .update(saldo_cliente=Socios.saldo_cliente - importe) + .update(saldo_cliente=Socios.saldo_cliente + importe) .where(Socios.id==cliente.id) ) return bool(q.execute()) @@ -3251,15 +3249,18 @@ class FacturasPagos(BaseModel): @classmethod def add(cls, mov, ids): - print (ids) for i, importe in ids.items(): fac = Facturas.get(Facturas.id==int(i)) - mov_ant = cls._movimiento_anterior(cls, mov, fac) - nuevo = {'movimiento': mov, 'factura': fac, 'importe': importe} + mov_ant, numero = cls._movimiento_anterior(cls, mov, fac) + nuevo = { + 'movimiento': mov, + 'factura': fac, + 'numero': numero, + 'importe': importe, + } if mov_ant is None: nuevo['saldo_anterior'] = float(fac.saldo) else: - nuevo['numero'] = mov_ant.numero + 1 nuevo['saldo_anterior'] = float(mov_ant.saldo) nuevo['saldo'] = nuevo['saldo_anterior'] - importe FacturasPagos.create(**nuevo) @@ -3267,7 +3268,7 @@ class FacturasPagos(BaseModel): if nuevo['saldo'] == 0: fac.pagada = True fac.save() - cls._actualizar_saldo_cliente(cls, fac.cliente, importe) + cls._actualizar_saldo_cliente(cls, fac.cliente, importe * -1) return From c5c79eace88e71b52b7bbb932d66c70b54856a35 Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Wed, 29 Nov 2017 21:39:19 -0600 Subject: [PATCH 6/8] Cancelar depositos --- source/app/models/main.py | 42 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/source/app/models/main.py b/source/app/models/main.py index c1010bd..e804340 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -1269,6 +1269,7 @@ class MovimientosBanco(BaseModel): with database_proxy.transaction(): obj.cancelado = True obj.save() + FacturasPagos.cancelar(obj) obj = cls._movimiento_anterior(cls, obj.cuenta, obj.fecha) cls._actualizar_saldos(cls, obj) @@ -3220,7 +3221,7 @@ class FacturasPagos(BaseModel): auto_round=True) saldo = DecimalField(default=0.0, max_digits=18, decimal_places=6, auto_round=True) - cancelado = BooleanField(default=False) + # ~ cancelado = BooleanField(default=False) class Meta: order_by = ('factura',) @@ -3231,8 +3232,7 @@ class FacturasPagos(BaseModel): def _movimiento_anterior(self, mov, id): query = (FacturasPagos .select() - .where(FacturasPagos.factura==id, - FacturasPagos.cancelado==False) + .where(FacturasPagos.factura==id) ) if len(query): return query[-1], len(query) + 1 @@ -3246,6 +3246,42 @@ class FacturasPagos(BaseModel): ) return bool(q.execute()) + def _actualizar_saldos(self, factura, saldo_anterior): + query = (FacturasPagos + .select() + .where(FacturasPagos.factura==factura) + ) + saldo = saldo_anterior + for i, row in enumerate(query): + if not saldo_anterior: + saldo_anterior = row.saldo_anterior + row.numero = i + 1 + row.saldo_anterior = saldo_anterior + row.saldo = saldo_anterior - row.importe + row.save() + saldo_anterior = row.saldo + saldo = row.saldo + + factura.saldo = saldo + factura.pagada = False + factura.save() + return + + @classmethod + def cancelar(cls, mov): + query = (FacturasPagos + .select() + .where(FacturasPagos.movimiento==mov) + ) + for row in query: + cls._actualizar_saldo_cliente(cls, row.factura.cliente, row.importe) + factura = row.factura + saldo_anterior = 0 + if row.numero == 1: + saldo_anterior = row.saldo_anterior + row.delete_instance() + cls._actualizar_saldos(cls, factura, saldo_anterior) + return @classmethod def add(cls, mov, ids): From cb18e3c3d272daf62e6c07788285bd46a864c1f9 Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Wed, 29 Nov 2017 23:57:31 -0600 Subject: [PATCH 7/8] Subir base de datos de Factura Libre --- source/app/controllers/main.py | 2 + source/app/controllers/util.py | 50 +++++++++-------- source/app/models/db.py | 3 +- source/app/models/main.py | 37 +++++++++++-- source/app/settings.py | 1 + source/static/js/controller/admin.js | 81 ++++++++++++++++++++++++++++ source/static/js/ui/admin.js | 38 +++++++++++++ source/templates/base.html | 2 +- source/xslt/donat11.xslt | 20 +++---- source/xslt/implocal.xslt | 74 ++++++++++++------------- source/xslt/ine11.xslt | 2 +- 11 files changed, 235 insertions(+), 75 deletions(-) diff --git a/source/app/controllers/main.py b/source/app/controllers/main.py index bc3ac45..ab7323a 100644 --- a/source/app/controllers/main.py +++ b/source/app/controllers/main.py @@ -98,6 +98,8 @@ class AppValues(object): req.context['result'] = self._db.add_unidad(values) elif table == 'addimpuesto': req.context['result'] = self._db.add_impuesto(values) + elif table == 'bdfl': + req.context['result'] = self._db.importar_bdfl() 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 a8997a0..3512985 100644 --- a/source/app/controllers/util.py +++ b/source/app/controllers/util.py @@ -1079,24 +1079,22 @@ def _totales(doc, cfdi, version): title = 'Retención {} {}'.format(tmp['impuesto'], '') retenciones.append((title, float(tmp['importe']))) - #~ com = xml.find('%sComplemento' % PRE) - #~ if com is not None: - #~ otros = com.find('%sImpuestosLocales' % IMP_LOCAL) - #~ if otros is not None: - #~ for otro in list(otros): - #~ if otro.tag == '%sRetencionesLocales' % IMP_LOCAL: - #~ name = 'ImpLocRetenido' - #~ tasa = 'TasadeRetencion' - #~ else: - #~ name = 'ImpLocTrasladado' - #~ tasa = 'TasadeTraslado' - #~ title = '%s %s %%' % (otro.attrib[name], otro.attrib[tasa]) - #~ value = otro.attrib['Importe'] - #~ self._copy_cell(cell_title) - #~ self._copy_cell(cell_value) - #~ cell_title = self._set_cell(v=title, cell=cell_title) - #~ cell_value = self._set_cell(v=value, cell=cell_value, value=True) - #~ cell_value.CellStyle = currency + node = doc.find('{}Complemento/{}ImpuestosLocales'.format( + PRE[version], PRE['LOCALES'])) + if node is not None: + for otro in list(node): + if otro.tag == '{}RetencionesLocales'.format(PRE['LOCALES']): + tipo = 'Retención ' + name = 'ImpLocRetenido' + tasa = 'TasadeRetencion' + else: + tipo = 'Traslado ' + name = 'ImpLocTrasladado' + tasa = 'TasadeTraslado' + title = '{} {} {}%'.format( + tipo, otro.attrib[name], otro.attrib[tasa]) + importe = float(otro.attrib['Importe']) + taxlocales.append((title, importe)) data['traslados'] = traslados data['retenciones'] = retenciones @@ -1271,6 +1269,15 @@ def upload_file(rfc, opt, file_obj): name = '{}_3.3_donativo.ods'.format(rfc.lower()) path = _join(PATH_MEDIA, 'templates', name) + elif opt == 'bdfl': + tmp = file_obj.filename.split('.') + ext = tmp[-1].lower() + if ext != 'sqlite': + msg = 'Extensión de archivo incorrecta, selecciona un archivo SQLite' + return {'status': 'server', 'name': msg, 'ok': False} + + name = '{}.sqlite'.format(rfc.lower()) + path = _join('/tmp', name) if save_file(path, file_obj.file.read()): return {'status': 'server', 'name': file_obj.filename, 'ok': True} @@ -1371,9 +1378,10 @@ class ImportFacturaLibre(object): self._error = 'No se encontró al emisor: {}'.format(self._rfc) return False - if obj['rfc'] != self._rfc: - self._error = 'Los datos no corresponden al RFC: {}'.format(self._rfc) - return False + if not DEBUG: + if obj['rfc'] != self._rfc: + self._error = 'Los datos no corresponden al RFC: {}'.format(self._rfc) + return False return True diff --git a/source/app/models/db.py b/source/app/models/db.py index 90d1f4d..f7fa23a 100644 --- a/source/app/models/db.py +++ b/source/app/models/db.py @@ -32,7 +32,6 @@ class StorageEngine(object): def upload_file(self, session, table, file_obj): if not 'rfc' in session: return {'status': 'error'} - return main.upload_file(session['rfc'], table, file_obj) def get_config(self, values): @@ -273,3 +272,5 @@ class StorageEngine(object): def get_movimientosbanco(self, values): return main.MovimientosBanco.get_(values) + def importar_bdfl(self): + return main.importar_bdfl() diff --git a/source/app/models/main.py b/source/app/models/main.py index e804340..80503f2 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -64,7 +64,8 @@ def desconectar(): def upload_file(rfc, opt, file_obj): result = util.upload_file(rfc, opt, file_obj) if result['ok']: - Configuracion.add({opt: file_obj.filename}) + if opt != 'bdfl': + Configuracion.add({opt: file_obj.filename}) return result @@ -2557,11 +2558,11 @@ class Facturas(BaseModel): impuestos['traslados'] = traslados impuestos['retenciones'] = retenciones - impuestos['total_locales_trasladados'] = '' + impuestos['total_locales_trasladados'] = '0.00' if total_locales_trasladados: impuestos['total_locales_trasladados'] = \ FORMAT.format(total_locales_trasladados) - impuestos['total_locales_retenciones'] = '' + impuestos['total_locales_retenciones'] = '0.00' if total_locales_retenciones: impuestos['total_locales_retenciones'] = \ FORMAT.format(total_locales_retenciones) @@ -3753,7 +3754,6 @@ def _importar_facturas(rows): } FacturasImpuestos.create(**new) except IntegrityError as e: - #~ print (e) msg = '\tFactura: id: {}'.format(row['serie'] + str(row['folio'])) log.error(msg) log.info('\tFacturas importadas...') @@ -3875,6 +3875,35 @@ def _generar_archivo_productos(archivo): return +def importar_bdfl(): + try: + emisor = Emisor.select()[0] + except IndexError: + msg = 'Configura primero al emisor' + return {'ok': False, 'msg': msg} + + name = '{}.sqlite'.format(emisor.rfc.lower()) + path = util._join('/tmp', name) + + log.info('Importando datos...') + app = util.ImportFacturaLibre(path, emisor.rfc) + if not app.is_connect: + msg = app._error + log.error('\t{}'.format(msg)) + return {'ok': False, 'msg': msg} + + data = app.import_data() + + _importar_socios(data['Socios']) + _importar_facturas(data['Facturas']) + _importar_categorias(data['Categorias']) + + msg = 'Importación terminada...' + log.info(msg) + + return {'ok': True, 'msg': msg} + + def _importar_factura_libre(archivo): rfc = input('Introduce el RFC: ').strip().upper() if not rfc: diff --git a/source/app/settings.py b/source/app/settings.py index 58007ca..89029af 100644 --- a/source/app/settings.py +++ b/source/app/settings.py @@ -86,6 +86,7 @@ PRE = { 'TIMBRE': '{http://www.sat.gob.mx/TimbreFiscalDigital}', 'DONATARIA': '{http://www.sat.gob.mx/donat}', 'INE': '{http://www.sat.gob.mx/ine}', + 'LOCALES': '{http://www.sat.gob.mx/implocal}', 'NOMINA': { '1.1': '{http://www.sat.gob.mx/nomina}', '1.2': '{http://www.sat.gob.mx/nomina12}', diff --git a/source/static/js/controller/admin.js b/source/static/js/controller/admin.js index e1d850c..fb08555 100644 --- a/source/static/js/controller/admin.js +++ b/source/static/js/controller/admin.js @@ -44,6 +44,9 @@ var controllers = { $$('txt_plantilla_donataria').attachEvent('onItemClick', txt_plantilla_donataria_click) $$('chk_config_anticipo').attachEvent('onItemClick', chk_config_item_click) $$('chk_config_ine').attachEvent('onItemClick', chk_config_item_click) + + $$('cmd_subir_bdfl').attachEvent('onItemClick', cmd_subir_bdfl_click) + $$('up_bdfl').attachEvent('onUploadComplete', up_bdfl_upload_complete) } } @@ -1320,3 +1323,81 @@ function chk_config_item_click(id, e){ }) } + + +function cmd_subir_bdfl_click(){ + var form = $$('form_upload_bdfl') + + if (!form.validate()){ + msg = 'Valores inválidos' + msg_error(msg) + return + } + + var values = form.getValues() + + if($$('lst_bdfl').count() < 1){ + msg = 'Selecciona la base de datos SQLite de Factura Libre' + msg_error(msg) + return + } + + if($$('lst_bdfl').count() > 1){ + msg = 'Selecciona solo un archivo' + msg_error(msg) + return + } + + var bdfl = $$('up_bdfl').files.getItem($$('up_bdfl').files.getFirstId()) + + var ext = [] + if(bdfl.type.toLowerCase() != 'sqlite'){ + msg = 'Archivo inválido, se requiere un archivo SQLITE' + msg_error(msg) + return + } + + msg = '¿Estás seguro de subir este archivo?' + webix.confirm({ + title: 'Base de datos de Factura Libre', + ok: 'Si', + cancel: 'No', + type: 'confirm-error', + text: msg, + callback:function(result){ + if(result){ + $$('up_bdfl').send() + } + } + }) +} + + +function up_bdfl_upload_complete(response){ + if(response.status != 'server'){ + msg = 'Ocurrio un error al subir los archivos' + msg_error(msg) + return + } + + msg = 'Archivo subido correctamente' + msg_ok(msg) + + $$('form_upload_bdfl').setValues({}) + $$('up_bdfl').files.data.clearAll() + + //~ webix.ajax().post('/values/bdfl', {}, { + //~ 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_ok(values.msg) + //~ }else{ + //~ msg_error(values.msg) + //~ } + //~ } + //~ }) +} \ No newline at end of file diff --git a/source/static/js/ui/admin.js b/source/static/js/ui/admin.js index 0ba0be3..be017fb 100644 --- a/source/static/js/ui/admin.js +++ b/source/static/js/ui/admin.js @@ -467,6 +467,33 @@ var tab_options = { } +var utilidades_archivos = [ + {maxHeight: 15}, + {template: 'Cargar Base de Datos de Factura Libre', type: 'section'}, + {view: 'form', id: 'form_upload_bdfl', rows: [ + {cols: [{}, + {view: 'uploader', id: 'up_bdfl', autosend: false, link: 'lst_bdfl', + value: 'Seleccionar base de datos', upload: '/files/bdfl'}, {}]}, + {cols: [{}, + {view: 'list', id: 'lst_bdfl', name: 'bdfl', + type: 'uploader', autoheight: true, borderless: true}, {}]}, + {cols: [{}, {view: 'button', id: 'cmd_subir_bdfl', + label: 'Subir base de datos de Factura Libre'}, {}]}, + ]}, +{}] + + +var tab_utilidades = { + view: 'tabview', + id: 'tab_utilidades', + multiview: true, + animate: true, + cells: [ + {id: 'Utilidades', rows: utilidades_archivos}, + ], +} + + var grid_admin_taxes_cols = [ {id: 'id', header: 'ID', hidden: true}, {id: 'delete', header: '', width: 30, css: 'delete'}, @@ -768,6 +795,16 @@ var app_options = { } +var app_utilidades = { + id: 'app_utilidades', + rows:[ + {view: 'template', id: 'th_utilidades', type: 'header', + template: 'Herramientas'}, + tab_utilidades, + ], +} + + var multi_admin = { id: 'multi_admin', animate: true, @@ -782,6 +819,7 @@ var multi_admin = { app_correo, app_sat, app_options, + app_utilidades, ], } diff --git a/source/templates/base.html b/source/templates/base.html index 253cdcc..dd5df6e 100644 --- a/source/templates/base.html +++ b/source/templates/base.html @@ -7,7 +7,7 @@ - + <%block name="media"/> diff --git a/source/xslt/donat11.xslt b/source/xslt/donat11.xslt index 1f0d7a4..24d4363 100644 --- a/source/xslt/donat11.xslt +++ b/source/xslt/donat11.xslt @@ -1,13 +1,13 @@ - + - - - - - - - - - + + + + + + + + + diff --git a/source/xslt/implocal.xslt b/source/xslt/implocal.xslt index dd95023..80b8d23 100644 --- a/source/xslt/implocal.xslt +++ b/source/xslt/implocal.xslt @@ -1,39 +1,39 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/xslt/ine11.xslt b/source/xslt/ine11.xslt index 06def84..05c1e56 100644 --- a/source/xslt/ine11.xslt +++ b/source/xslt/ine11.xslt @@ -1,5 +1,5 @@  - + From b3c2e168412e71b3ff4451eb7d158cf4e7d4b03c Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Thu, 30 Nov 2017 00:37:13 -0600 Subject: [PATCH 8/8] Agregar emisor UI --- source/app/conf.py.example | 3 ++- source/app/controllers/main.py | 16 ++++++++++++++ source/app/main.py | 3 ++- source/app/middleware.py | 9 ++++++-- source/app/settings.py | 2 +- source/static/js/ui/empresas.js | 35 +++++++++++++++++++++++++++++ source/templates/empresas.html | 39 +++++++++++++++++++++++++++++++++ 7 files changed, 102 insertions(+), 5 deletions(-) create mode 100644 source/static/js/ui/empresas.js create mode 100644 source/templates/empresas.html diff --git a/source/app/conf.py.example b/source/app/conf.py.example index 2cdf172..8610043 100644 --- a/source/app/conf.py.example +++ b/source/app/conf.py.example @@ -1,7 +1,8 @@ #!/usr/bin/env python -DEBUG = True +DEBUG = False +MV = True #~ Establece una ruta accesible para el servidor web LOG_PATH = '/srv/empresa/logs/empresalibre.log' diff --git a/source/app/controllers/main.py b/source/app/controllers/main.py index ab7323a..eab4209 100644 --- a/source/app/controllers/main.py +++ b/source/app/controllers/main.py @@ -4,6 +4,22 @@ import falcon from middleware import get_template +class AppEmpresas(object): + template = 'empresas.html' + + def __init__(self, db): + self._db = db + + @falcon.after(get_template) + def on_get(self, req, resp): + resp.status = falcon.HTTP_200 + + def on_post(self, req, resp): + values = req.params + req.context['result'] = self._db.empresas(values) + resp.status = falcon.HTTP_200 + + class AppLogin(object): template = 'login.html' diff --git a/source/app/main.py b/source/app/main.py index 2db8add..e30da00 100644 --- a/source/app/main.py +++ b/source/app/main.py @@ -12,7 +12,7 @@ from middleware import ( handle_404 ) from models.db import StorageEngine -from controllers.main import ( +from controllers.main import (AppEmpresas, AppLogin, AppLogout, AppAdmin, AppEmisor, AppConfig, AppMain, AppValues, AppPartners, AppProducts, AppInvoices, AppFolios, AppDocumentos, AppFiles, AppPreInvoices, AppCuentasBanco, @@ -32,6 +32,7 @@ api = falcon.API(middleware=[ api.req_options.auto_parse_form_urlencoded = True api.add_sink(handle_404, '') +api.add_route('/empresas', AppEmpresas(db)) api.add_route('/', AppLogin(db)) api.add_route('/logout', AppLogout(db)) api.add_route('/admin', AppAdmin(db)) diff --git a/source/app/middleware.py b/source/app/middleware.py index fc2d00f..ac2e2d7 100644 --- a/source/app/middleware.py +++ b/source/app/middleware.py @@ -3,7 +3,7 @@ import falcon from controllers import util from models import main -from settings import PATH_STATIC +from settings import MV, PATH_STATIC def handle_404(req, resp): @@ -34,7 +34,12 @@ class AuthMiddleware(object): def process_resource(self, req, resp, resource, params): id_session = req.cookies.get('beaker.session.id', '') - if not id_session and req.path != '/': + if req.path == '/empresas': + if MV: + pass + else: + raise falcon.HTTPTemporaryRedirect('/') + elif not id_session and req.path != '/': raise falcon.HTTPTemporaryRedirect('/') diff --git a/source/app/settings.py b/source/app/settings.py index 89029af..f11028c 100644 --- a/source/app/settings.py +++ b/source/app/settings.py @@ -7,7 +7,7 @@ from mako.lookup import TemplateLookup from logbook import Logger, StreamHandler, RotatingFileHandler logbook.set_datetime_format('local') -from conf import DEBUG, LOG_PATH +from conf import DEBUG, MV, LOG_PATH DEBUG = DEBUG diff --git a/source/static/js/ui/empresas.js b/source/static/js/ui/empresas.js new file mode 100644 index 0000000..750febc --- /dev/null +++ b/source/static/js/ui/empresas.js @@ -0,0 +1,35 @@ + +var msg_rfc = 'El RFC es requerido' + +var form_controls_empresa = [ + {view: 'text', label: 'RFC', id: 'txt_alta_rfc', name: 'alta_rfc', + labelPosition: 'top', required: true, invalidMessage: msg_rfc}, + {margin: 10, cols:[{}, {view: 'button', value: 'Agregar RFC', + click: 'validate_nuevo_rfc', hotkey: 'enter'}, {}]} +] + + +var msg_header = 'Bienvenido a Empresa Libre' + +var ui_empresas = { + rows: [ + {maxHeight: 50}, + {view: 'template', template: msg_header, maxHeight: 50, css: 'login_header'}, + {maxHeight: 50}, + {cols: [{}, {type: 'space', padding: 5, + rows: [ + {view: 'template', template: 'Alta de nuevo emisor', type: 'header'}, + { + container: 'form_empresas', + view: 'form', + id: 'form_empresas', + width: 400, + elements: form_controls_empresa, + rules:{ + alta_rfc:function(value){ return value.trim() != '';}, + } + }, + ]}, {}, ] + }, + ] +} diff --git a/source/templates/empresas.html b/source/templates/empresas.html new file mode 100644 index 0000000..a557531 --- /dev/null +++ b/source/templates/empresas.html @@ -0,0 +1,39 @@ +<%inherit file="base.html"/> + +<%block name="media"> + + + +<%block name="content"> + +
+ + + +