diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d7f3db..14b9eb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +v 1.37.0 [02-mar-2020] +---------------------- + - Mejora: Soporte para complemento Leyendas Fiscales + +* IMPORTANTE: + +Es necesario hacer una migración, y agregar los campos necesarios para mostrar +las leyendas, mira la carpeta pública con la plantilla de ejemplo. +``` +git pull origin master + +cd source/app/models + +python main.py -bk + +python main.py -m -r RFC +``` + v 1.36.0 [25-feb-2020] ---------------------- diff --git a/VERSION b/VERSION index 39fc130..bf50e91 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.36.0 +1.37.0 diff --git a/requirements.txt b/requirements.txt index 6b2a201..d33642b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,6 @@ falcon-multipart Beaker Mako peewee==2.10.2 -Click logbook bcrypt python-dateutil diff --git a/source/app/controllers/cfdi_xml.py b/source/app/controllers/cfdi_xml.py index 7054bbe..11fea61 100644 --- a/source/app/controllers/cfdi_xml.py +++ b/source/app/controllers/cfdi_xml.py @@ -90,6 +90,12 @@ SAT = { 'xmlns': 'http://www.sat.gob.mx/divisas', 'schema': ' http://www.sat.gob.mx/divisas http://www.sat.gob.mx/sitio_internet/cfd/divisas/divisas.xsd', }, + 'leyendas': { + 'version': '1.0', + 'prefix': 'leyendasFisc', + 'xmlns': 'http://www.sat.gob.mx/leyendasFiscales', + 'schema': ' http://www.sat.gob.mx/leyendasFiscales http://www.sat.gob.mx/sitio_internet/cfd/leyendasFiscales/leyendasFisc.xsd', + }, } @@ -107,6 +113,7 @@ class CFDI(object): self._edu = False self._pagos = False self._is_nomina = False + self._leyendas = False self._divisas = '' self.error = '' @@ -129,8 +136,6 @@ class CFDI(object): if 'nomina' in datos: self._nomina(datos['nomina']) - #~ if 'complementos' in datos: - #~ self._complementos(datos['complementos']) return self._to_pretty_xml(ET.tostring(self._cfdi, encoding='utf-8')) @@ -158,6 +163,7 @@ class CFDI(object): if 'ine' in datos['complementos']: self._ine = True self._pagos = bool(datos['complementos'].get('pagos', False)) + self._leyendas = bool(datos['complementos'].get('leyendas', False)) self._divisas = datos['comprobante'].pop('divisas', '') @@ -217,9 +223,15 @@ class CFDI(object): attributes[name] = SAT['pagos']['xmlns'] schema_pagos = SAT['pagos']['schema'] + schema_leyendas = '' + if self._leyendas: + name = 'xmlns:{}'.format(SAT['leyendas']['prefix']) + attributes[name] = SAT['leyendas']['xmlns'] + schema_leyendas = SAT['leyendas']['schema'] + attributes['xsi:schemaLocation'] = self._sat_cfdi['schema'] + \ schema_locales + schema_donativo + schema_ine + schema_edu + \ - schema_divisas + schema_nomina + schema_pagos + schema_divisas + schema_nomina + schema_pagos + schema_leyendas attributes.update(datos) if not 'Version' in attributes: @@ -464,6 +476,14 @@ class CFDI(object): for row in relacionados: ET.SubElement(node_pago, '{}:DoctoRelacionado'.format(pre), row) + if 'leyendas' in datos: + pre = SAT['leyendas']['prefix'] + attributes = {'version': SAT['leyendas']['version']} + node_leyend = ET.SubElement( + self._complemento, '{}:LeyendasFiscales'.format(pre), attributes) + for leyend in datos['leyendas']: + ET.SubElement(node_leyend, '{}:Leyenda'.format(pre), leyend) + if 'ce' in datos: pre = 'cce11' datos = datos.pop('ce') diff --git a/source/app/controllers/main.py b/source/app/controllers/main.py index 41a8e3e..3efcfc2 100644 --- a/source/app/controllers/main.py +++ b/source/app/controllers/main.py @@ -594,6 +594,29 @@ class AppSATFormaPago(object): resp.status = falcon.HTTP_200 +class AppSATLeyendaFiscales(object): + + def __init__(self, db): + self._db = db + + def on_get(self, req, resp): + values = req.params + req.context['result'] = self._db.sat_leyendas_fiscales_get(values) + resp.status = falcon.HTTP_200 + + def on_post(self, req, resp): + values = req.params + req.context['result'] = self._db.sat_leyendas_fiscales_post(values) + resp.status = falcon.HTTP_200 + + def on_delete(self, req, resp): + values = req.params + if self._db.sat_leyendas_fiscales_delete(values['id']): + resp.status = falcon.HTTP_200 + else: + resp.status = falcon.HTTP_204 + + class AppSociosCuentasBanco(object): def __init__(self, db): diff --git a/source/app/controllers/util.py b/source/app/controllers/util.py index 0080670..7aa5973 100644 --- a/source/app/controllers/util.py +++ b/source/app/controllers/util.py @@ -1087,6 +1087,28 @@ class LIBO(object): self._set_cell(f'{{divisas.{k}}}', v) return + def _leyendas(self, data): + if not data: + return + + first = True + for row in data: + leyenda = row['textoLeyenda'] + norma = row.get('norma', '') + disposicion = row.get('disposicionFiscal', '') + if first: + first = False + cell1 = self._set_cell('{textoLeyenda}', leyenda) + cell2 = self._set_cell('{norma}', norma) + cell3 = self._set_cell('{disposicionFiscal}', disposicion) + else: + row = cell1.CellAddress.Row + 1 + self._sheet.getRows().insertByIndex(row, 1) + cell1 = self._set_cell(v=leyenda, cell=cell1) + cell2 = self._set_cell(v=norma, cell=cell2) + cell3 = self._set_cell(v=disposicion, cell=cell3) + return + def _nomina(self, data): if not data: return @@ -1314,6 +1336,7 @@ class LIBO(object): self._ine(data['ine']) self._divisas(data.get('divisas', {})) + self._leyendas(data['leyendas']) self._cancelado(data['cancelada']) self._clean() diff --git a/source/app/controllers/utils.py b/source/app/controllers/utils.py index ac24815..cceda98 100644 --- a/source/app/controllers/utils.py +++ b/source/app/controllers/utils.py @@ -244,10 +244,13 @@ class CfdiToDict(object): NS = { 'cfdi': 'http://www.sat.gob.mx/cfd/3', 'divisas': 'http://www.sat.gob.mx/divisas', + 'leyendasFisc': 'http://www.sat.gob.mx/leyendasFiscales', } def __init__(self, xml): - self._values = {} + self._values = { + 'leyendas': (), + } self._root = ET.parse(BytesIO(xml.encode())).getroot() self._get_values() @@ -260,13 +263,21 @@ class CfdiToDict(object): return def _complementos(self): - complemento = self._root.xpath('//cfdi:Complemento', namespaces=self.NS)[0] + path = '//cfdi:Complemento' + complemento = self._root.xpath(path, namespaces=self.NS)[0] - divisas = complemento.xpath('//divisas:Divisas', namespaces=self.NS) + path = '//divisas:Divisas' + divisas = complemento.xpath(path, namespaces=self.NS) if divisas: d = CaseInsensitiveDict(divisas[0].attrib) d.pop('version', '') self._values.update({'divisas': d}) + + path = '//leyendasFisc:Leyenda' + node = complemento.xpath(path, namespaces=self.NS) + if node: + leyendas = [CaseInsensitiveDict(n.attrib) for n in node] + self._values['leyendas'] = leyendas return diff --git a/source/app/main.py b/source/app/main.py index e7afd80..c03b858 100644 --- a/source/app/main.py +++ b/source/app/main.py @@ -17,7 +17,7 @@ from controllers.main import (AppEmpresas, AppDocumentos, AppFiles, AppPreInvoices, AppCuentasBanco, AppMovimientosBanco, AppTickets, AppStudents, AppEmployees, AppNomina, AppInvoicePay, AppCfdiPay, AppSATBancos, AppSociosCuentasBanco, - AppSATFormaPago + AppSATFormaPago, AppSATLeyendaFiscales ) @@ -61,6 +61,7 @@ api.add_route('/cfdipay', AppCfdiPay(db)) api.add_route('/satbancos', AppSATBancos(db)) api.add_route('/satformapago', AppSATFormaPago(db)) api.add_route('/socioscb', AppSociosCuentasBanco(db)) +api.add_route('/leyendasfiscales', AppSATLeyendaFiscales(db)) session_options = { diff --git a/source/app/models/db.py b/source/app/models/db.py index cff00e8..7d50926 100644 --- a/source/app/models/db.py +++ b/source/app/models/db.py @@ -462,6 +462,15 @@ class StorageEngine(object): def nomina(self, values, user): return main.CfdiNomina.post(values, user) + def sat_leyendas_fiscales_get(self, values): + return main.SATLeyendasFiscales.get_values(values) + + def sat_leyendas_fiscales_post(self, values): + return main.SATLeyendasFiscales.post(values) + + def sat_leyendas_fiscales_delete(self, values): + return main.SATLeyendasFiscales.remove(values) + # Companies only in MV def _get_empresas(self, values): return main.companies_get() diff --git a/source/app/models/main.py b/source/app/models/main.py index f9e1a77..b8f062c 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -308,6 +308,7 @@ def config_timbrar(): 'cfdi_add_same_product': Configuracion.get_bool('chk_config_add_same_product'), 'cfdi_tax_locales_truncate': Configuracion.get_bool('chk_config_tax_locales_truncate'), 'cfdi_folio_custom': Configuracion.get_bool('chk_folio_custom'), + 'cfdi_leyendasfiscales': Configuracion.get_bool('chk_config_leyendas_fiscales'), } return conf @@ -432,6 +433,7 @@ class Configuracion(BaseModel): 'chk_config_divisas', 'chk_cfg_pays_data_bank', 'chk_usar_nomina', + 'chk_config_leyendas_fiscales', ) data = (Configuracion .select() @@ -2478,6 +2480,83 @@ class SATRiesgoPuesto(BaseModel): return +class SATLeyendasFiscales(BaseModel): + texto_leyenda = TextField(index=True) + norma = TextField(default='') + disposicion_fiscal = TextField(default='') + active = BooleanField(default=True) + default = BooleanField(default=False) + + class Meta: + order_by = ('texto_leyenda',) + indexes = ( + (('texto_leyenda', 'norma', 'disposicion_fiscal'), True), + ) + + def _get_all(self, values): + rows = (SATLeyendasFiscales.select( + SATLeyendasFiscales.id, + SQL(" '-' AS delete"), + SATLeyendasFiscales.texto_leyenda, + SATLeyendasFiscales.norma, + SATLeyendasFiscales.disposicion_fiscal) + .dicts() + ) + return tuple(rows) + + def _get_active(self, values): + rows = (SATLeyendasFiscales.select( + SATLeyendasFiscales.id, + SATLeyendasFiscales.texto_leyenda, + SATLeyendasFiscales.norma, + SATLeyendasFiscales.disposicion_fiscal) + .where(SATLeyendasFiscales.active==True) + .dicts() + ) + return tuple(rows) + + @classmethod + def get_by_id(cls, ids): + rows = (SATLeyendasFiscales.select( + SATLeyendasFiscales.texto_leyenda.alias('textoLeyenda'), + SATLeyendasFiscales.norma, + SATLeyendasFiscales.disposicion_fiscal.alias('disposicionFiscal')) + .where(SATLeyendasFiscales.id.in_(ids)) + .dicts() + ) + for row in rows: + if not row['norma']: + del row['norma'] + if not row['disposicionFiscal']: + del row['disposicionFiscal'] + return tuple(rows) + + @classmethod + def get_values(cls, values): + opt = values.pop('opt') + return getattr(cls, '_get_{}'.format(opt))(cls, values) + + def _new(self, values): + try: + obj = SATLeyendasFiscales.create(**values) + values['id'] = obj.id + values['delete'] = '-' + result = {'ok': True, 'row': values} + except Exception as e: + result = {'ok': False, 'msg': 'La Leyenda Fiscal ya existe'} + return result + + @classmethod + def post(cls, values): + opt = values.pop('opt') + return getattr(cls, '_{}'.format(opt))(cls, values) + + @classmethod + def remove(cls, id): + q = SATLeyendasFiscales.delete().where(SATLeyendasFiscales.id==id) + return bool(q.execute()) + + class TipoCambio(BaseModel): dia = DateField(default=util.now) moneda = ForeignKeyField(SATMonedas) @@ -4641,6 +4720,8 @@ class Facturas(BaseModel): divisas = '' values['divisas'] = divisas + leyendas_fiscales = utils.loads(values.pop('leyendas_fiscales', '[]')) + emisor = Emisor.select()[0] values['serie'] = cls._get_serie(cls, user, values['serie']) if Configuracion.get_bool('chk_folio_custom') and folio_custom: @@ -4665,6 +4746,7 @@ class Facturas(BaseModel): totals = cls._calculate_totals(cls, obj, productos, tipo_comprobante) cls._guardar_relacionados(cls, obj, relacionados) cls._guardar_ine(cls, obj, ine) + cls._save_leyendas_fiscales(cls, obj, leyendas_fiscales) obj.subtotal = totals['subtotal'] obj.descuento = totals['descuento'] obj.total_trasladados = totals['total_trasladados'] @@ -4695,6 +4777,18 @@ class Facturas(BaseModel): data = {'ok': True, 'row': row, 'new': True, 'error': False, 'msg': msg} return data + def _save_leyendas_fiscales(self, invoice, valores): + if not valores: + return + + data = { + 'factura': invoice, + 'nombre': 'leyendas', + 'valores': utils.dumps(valores), + } + FacturasComplementos.create(**data) + return + def _make_xml(self, invoice, auth): tax_decimals = Configuracion.get_bool('chk_config_tax_decimals') decimales_precios = Configuracion.get_bool('chk_config_decimales_precios') @@ -4709,11 +4803,15 @@ class Facturas(BaseModel): comprobante = {} relacionados = {} donativo = {} - complementos = FacturasComplementos.get_(invoice) + complementos = FacturasComplementos.get_by_invoice(invoice) comprobante['divisas'] = invoice.divisas if invoice.divisas: complementos['divisas'] = True + if 'leyendas' in complementos: + ids = complementos['leyendas'] + complementos['leyendas'] = SATLeyendasFiscales.get_by_id(ids) + if invoice.donativo: donativo['noAutorizacion'] = emisor.autorizacion donativo['fechaAutorizacion'] = str(emisor.fecha_autorizacion) @@ -5854,6 +5952,14 @@ class FacturasComplementos(BaseModel): ) return {r.nombre: utils.loads(r.valores) for r in query} + @classmethod + def get_by_invoice(cls, invoice): + query = (FacturasComplementos + .select() + .where(FacturasComplementos.factura==invoice) + ) + return {r.nombre: utils.loads(r.valores) for r in query} + class PreFacturasRelacionadas(BaseModel): factura = ForeignKeyField(PreFacturas, related_name='original') @@ -9081,7 +9187,7 @@ def _crear_tablas(rfc): SATNivelesEducativos, SATEstados, SATRiesgoPuesto, SATPeriodicidadPago, SATOrigenRecurso, SATTipoContrato, SATTipoDeduccion, SATTipoHoras, SATTipoIncapacidad, SATTipoJornada, SATTipoNomina, SATTipoOtroPago, - SATTipoPercepcion, SATTipoRegimen, + SATTipoPercepcion, SATTipoRegimen, SATLeyendasFiscales, Socios, SociosCuentasBanco, Contactos, ContactoCorreos, ContactoDirecciones, Empleados, ContactoTelefonos, Departamentos, Puestos, @@ -9151,7 +9257,7 @@ def _migrate_tables(rfc=''): CfdiNominaOtroPago, CfdiNominaOtros, CfdiNominaPercepciones, CfdiNominaRelacionados, CfdiNominaSeparacion, CfdiNominaSubcontratos, CfdiNominaTotales, SATNivelesEducativos, Roles, Permisos, - SociosCuentasBanco + SociosCuentasBanco, SATLeyendasFiscales ] log.info('Creando tablas nuevas...') database_proxy.create_tables(tablas, True) @@ -10375,6 +10481,5 @@ def main(args): if __name__ == '__main__': args = _process_command_line_arguments() main(args) - # ~ main2() diff --git a/source/app/settings.py b/source/app/settings.py index edcf327..b1f5bc7 100644 --- a/source/app/settings.py +++ b/source/app/settings.py @@ -47,7 +47,7 @@ except ImportError: DEBUG = DEBUG -VERSION = '1.36.0' +VERSION = '1.37.0' EMAIL_SUPPORT = ('soporte@empresalibre.mx',) TITLE_APP = '{} v{}'.format(TITLE_APP, VERSION) diff --git a/source/static/js/controller/admin.js b/source/static/js/controller/admin.js index 83721b4..310da10 100644 --- a/source/static/js/controller/admin.js +++ b/source/static/js/controller/admin.js @@ -92,6 +92,24 @@ var controllers = { $$('chk_llevar_inventario').attachEvent('onItemClick', chk_config_item_click) $$('chk_use_packing').attachEvent('onItemClick', chk_config_item_click) + //~ Complements + $$('chk_usar_nomina').attachEvent('onItemClick', chk_config_item_click) + $$('txt_config_nomina_serie').attachEvent('onKeyPress', txt_config_nomina_serie_press) + $$('txt_config_nomina_folio').attachEvent('onKeyPress', txt_config_nomina_folio_press) + + $$('chk_config_pagos').attachEvent('onItemClick', chk_config_item_click) + $$('chk_cfg_pays_data_bank').attachEvent('onItemClick', chk_config_item_click) + $$('txt_config_cfdipay_serie').attachEvent('onKeyPress', txt_config_cfdipay_serie_press) + $$('txt_config_cfdipay_folio').attachEvent('onKeyPress', txt_config_cfdipay_folio_press) + + $$('chk_config_divisas').attachEvent('onItemClick', chk_config_item_click) + $$('chk_config_ine').attachEvent('onItemClick', chk_config_item_click) + $$('chk_config_edu').attachEvent('onItemClick', chk_config_item_click) + + $$('chk_config_leyendas_fiscales').attachEvent('onItemClick', chk_config_item_click) + $$('cmd_admin_leyendas_fiscales').attachEvent('onItemClick', cmd_admin_leyendas_fiscales_click) + + //~ Others $$('chk_config_ocultar_metodo_pago').attachEvent('onItemClick', chk_config_item_click) $$('chk_config_ocultar_condiciones_pago').attachEvent('onItemClick', chk_config_item_click) $$('chk_config_send_zip').attachEvent('onItemClick', chk_config_item_click) @@ -105,11 +123,7 @@ var controllers = { $$('chk_config_decimales_precios').attachEvent('onItemClick', chk_config_item_click) $$('chk_config_user_show_doc').attachEvent('onItemClick', chk_config_item_click) $$('chk_config_anticipo').attachEvent('onItemClick', chk_config_item_click) - $$('chk_config_ine').attachEvent('onItemClick', chk_config_item_click) - $$('chk_config_edu').attachEvent('onItemClick', chk_config_item_click) - $$('chk_config_pagos').attachEvent('onItemClick', chk_config_item_click) - $$('chk_config_divisas').attachEvent('onItemClick', chk_config_item_click) - $$('chk_cfg_pays_data_bank').attachEvent('onItemClick', chk_config_item_click) + $$('chk_usar_punto_de_venta').attachEvent('onItemClick', chk_config_item_click) $$('chk_ticket_pdf_show').attachEvent('onItemClick', chk_config_item_click) $$('chk_ticket_direct_print').attachEvent('onItemClick', chk_config_item_click) @@ -117,11 +131,6 @@ var controllers = { $$('chk_ticket_total_up').attachEvent('onItemClick', chk_config_item_click) $$('chk_ticket_user_show_doc').attachEvent('onItemClick', chk_config_item_click) $$('txt_ticket_printer').attachEvent('onKeyPress', txt_ticket_printer_key_press) - $$('txt_config_nomina_serie').attachEvent('onKeyPress', txt_config_nomina_serie_press) - $$('txt_config_nomina_folio').attachEvent('onKeyPress', txt_config_nomina_folio_press) - $$('txt_config_cfdipay_serie').attachEvent('onKeyPress', txt_config_cfdipay_serie_press) - $$('txt_config_cfdipay_folio').attachEvent('onKeyPress', txt_config_cfdipay_folio_press) - $$('chk_usar_nomina').attachEvent('onItemClick', chk_config_item_click) $$('lst_pac').attachEvent('onChange', lst_pac_on_change) $$('cmd_subir_bdfl').attachEvent('onItemClick', cmd_subir_bdfl_click) @@ -471,6 +480,9 @@ function get_config_values(opt){ set_value(key, values[key]) }else{ $$(key).setValue(values[key]) + if(key=='chk_config_leyendas_fiscales'){ + admin_config_other_options(key) + } } }) } @@ -1816,6 +1828,8 @@ function chk_config_item_click(id, e){ } }) + admin_config_other_options(id) + } @@ -2570,3 +2584,109 @@ function lst_pac_on_change(nv, ov){ }) } } + + +function admin_config_other_options(id){ + if(id=='chk_config_leyendas_fiscales'){ + var value = Boolean($$(id).getValue()) + if(value){ + $$('cmd_admin_leyendas_fiscales').enable() + }else{ + $$('cmd_admin_leyendas_fiscales').disable() + } + } +} + + +function cmd_admin_leyendas_fiscales_click(){ + admin_ui_leyendas_fiscales.init() + $$('win_leyendas_fiscales').show() + get_leyendas_fiscales() +} + + +function get_leyendas_fiscales(){ + webix.ajax().sync().get('/leyendasfiscales', {'opt': 'all'}, { + 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() + var grid = $$('grid_admin_leyendas_fiscales') + grid.clearAll() + grid.parse(values) + grid.refresh() + } + }) +} + + +function add_admin_leyenda_fiscal_click(){ + var form = $$('form_admin_leyendas_fiscales') + var grid = $$('grid_admin_leyendas_fiscales') + + if (!form.validate()){ + msg = 'Valores inválidos' + msg_error(msg) + return + } + var values = form.getValues() + values['opt'] = 'new' + + var empty = {texto_leyenda: '', norma: '', disposicion_fiscal: ''} + + webix.ajax().post('/leyendasfiscales', values, { + 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('Leyenda guardada correctamente') + form.setValues(empty) + grid.add(values.row) + }else{ + msg_error(values.msg) + } + } + }) +} + + +function grid_admin_leyendas_fiscales_click(id){ + if(id.column != 'delete'){ + return + } + + msg = '¿Estás seguro de eliminar esta Leyenda Fiscal' + webix.confirm({ + title: 'Eliminar', + ok: 'Si', + cancel: 'No', + type: 'confirm-error', + text: msg, + callback:function(result){ + if(result){ + delete_leyenda_fiscal(id.row) + } + } + }) + +} + +function delete_leyenda_fiscal(id){ + var grid = $$('grid_admin_leyendas_fiscales') + + webix.ajax().del('/leyendasfiscales', {id: id}, function(text, xml, xhr){ + msg = 'Leyenda Fiscal eliminada correctamente' + if(xhr.status == 200){ + grid.remove(id) + msg_ok(msg) + }else{ + msg = 'No se pudo eliminar' + msg_error(msg) + } + }) +} diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js index 1983bcd..a5ccf61 100644 --- a/source/static/js/controller/invoices.js +++ b/source/static/js/controller/invoices.js @@ -205,6 +205,13 @@ function default_config(){ }else{ $$('tv_invoice').getTabbar().showOption('INE') } + if(!values.cfdi_leyendasfiscales){ + $$('tv_invoice').getTabbar().hideOption('Leyendas Fiscales') + }else{ + get_leyendas_fiscales() + $$('tv_invoice').getTabbar().showOption('Leyendas Fiscales') + } + cfg_invoice['leyendasfiscales'] = values.cfdi_leyendasfiscales cfg_invoice['edu'] = values.cfdi_edu cfg_invoice['open_pdf'] = values.cfdi_open_pdf cfg_invoice['tax_locales'] = values.cfdi_tax_locales @@ -642,6 +649,8 @@ function guardar_y_timbrar(values){ data['notas'] = values.notas data['folio_custom'] = $$('txt_folio_custom').getValue() data['divisas'] = $$('opt_divisas').getValue() + data['leyendas_fiscales'] = $$('grid_leyendas_fiscales').getSelectedId(true, true) + $$('grid_leyendas_fiscales').unselectAll() var usar_ine = $$('chk_cfdi_usar_ine').getValue() if(usar_ine){ @@ -709,6 +718,17 @@ function cmd_timbrar_click(id, e, node){ msg += 'El Tipo de Comprobante es Traslado, el total será puesto a 0 (Cero), asegurate de que sea el tipo de comprobante correcto

' } + if(cfg_invoice['leyendasfiscales']){ + var rows = $$('grid_leyendas_fiscales').getSelectedId(true, true) + if(rows.length == 0){ + msg += 'No se agregará ninguna leyenda fiscal

' + }else if(rows.length == 1){ + msg += 'Se agregará una leyenda fiscal

' + }else{ + msg += 'Se agregarán ' + rows.length + ' leyendas fiscales

' + } + } + msg += '¿Estás seguro de timbrar esta factura?

' webix.confirm({ @@ -2352,3 +2372,20 @@ function search_by_click(){ search_by(value) } } + + +function get_leyendas_fiscales(){ + webix.ajax().get('/leyendasfiscales', {'opt': 'active'}, { + 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() + var grid = $$('grid_leyendas_fiscales') + grid.clearAll() + grid.parse(values) + grid.refresh() + } + }) +} diff --git a/source/static/js/ui/admin.js b/source/static/js/ui/admin.js index 8ffe3eb..43d6f5b 100644 --- a/source/static/js/ui/admin.js +++ b/source/static/js/ui/admin.js @@ -784,6 +784,15 @@ var options_admin_complements = [ {view: 'checkbox', id: 'chk_config_edu', labelWidth: 0, labelRight: 'Usar el complemento EDU'}, {maxWidth: 15}]}, + {maxHeight: 20}, + {template: 'Complemento Leyendas Fiscales', type: 'section'}, + {cols: [{maxWidth: 15}, + {view: 'checkbox', id: 'chk_config_leyendas_fiscales', labelWidth: 0, + labelRight: 'Usar el complemento Leyendas Fiscales'}, + {view: 'button', id: 'cmd_admin_leyendas_fiscales', label: 'Leyendas Fiscales', + type: 'form', align: 'center', autowidth: true, disabled: true}, + {}, {maxWidth: 15} + ]}, ] @@ -1415,3 +1424,73 @@ var body_win_emisor_logo = [ + +var grid_cols_admin_leyendas_fiscales = [ + {id: 'id', header: 'ID', hidden: true}, + {id: 'delete', header: '', width: 30, css: 'delete'}, + {id: 'texto_leyenda', header: 'Leyenda', fillspace: 1}, + {id: 'norma', header: 'Norma', fillspace: 1}, + {id: 'disposicion_fiscal', header: 'Disposición Fiscal', fillspace: 1}, + //~ {id: 'active', header: 'Activa', template: '{common.checkbox()}', + //~ editor: 'checkbox'}, +] + + +var grid_admin_leyendas_fiscales = { + view: 'datatable', + id: 'grid_admin_leyendas_fiscales', + select: 'row', + adjust: true, + autoheight: true, + headermenu: true, + columns: grid_cols_admin_leyendas_fiscales, + on:{ + 'data->onStoreUpdated':function(){ + this.data.each(function(obj, i){ + obj.delete = '-' + }) + } + }, +} + + +var form_controls_admin_leyendas_fiscales = [ + {cols: [ + {view: 'text', id: 'txt_texto_leyenda', label: 'Leyenda', width: 200, + labelPosition: 'top', name: 'texto_leyenda', required: true}, + {view: "text", id: 'txt_norma_fiscal', label: 'Norma', width: 200, + name: 'norma', labelPosition: 'top'}, + {view: "text", id: 'txt_disposicion_fiscal', label: 'Disposición', + name: 'disposicion_fiscal', labelPosition: 'top', width: 200} + ]}, + {cols: [{}, + {view: 'button', type: 'form', icon: 'plus', label: 'Agregar', autowidth: true, + click: function(){add_admin_leyenda_fiscal_click()} + }, {}]}, + grid_admin_leyendas_fiscales, + {}, + {cols:[{}, + {view: 'button', value: 'Cerrar', click: "$$('win_leyendas_fiscales').close()"}, + {}]} +] + + +var admin_ui_leyendas_fiscales = { + init: function(){ + webix.ui({ + view: 'window', + id: 'win_leyendas_fiscales', + head: 'Leyendas Fiscales', + width: 800, + modal: true, + position: 'center', + body: { + view: 'form', id: 'form_admin_leyendas_fiscales', + elements: form_controls_admin_leyendas_fiscales + } + }) + + $$('grid_admin_leyendas_fiscales').attachEvent('onItemClick', grid_admin_leyendas_fiscales_click) + + }, +} diff --git a/source/static/js/ui/invoices.js b/source/static/js/ui/invoices.js index 9ff88b3..8e52024 100644 --- a/source/static/js/ui/invoices.js +++ b/source/static/js/ui/invoices.js @@ -701,7 +701,7 @@ var opt_tipo_comite = [ ] -var controles_ine = [ +var controls_ine = [ {maxHeight: 15}, {cols: [{maxWidth: 15}, {view: 'checkbox', id: 'chk_cfdi_usar_ine', labelWidth: 0, @@ -727,16 +727,45 @@ var controles_ine = [ ] +var grid_cols_leyendas_fiscales = [ + {id: 'id', header: 'ID', hidden: true}, + {id: 'texto_leyenda', header: 'Leyenda', fillspace: 2}, + {id: 'norma', header: 'Norma', fillspace: 1}, + {id: 'disposicion_fiscal', header: 'Disposición Fiscal', fillspace: 1}, +] + + +var grid_leyendas_fiscales = { + view: 'datatable', + id: 'grid_leyendas_fiscales', + select: 'row', + multiselect: true, + adjust: true, + autoheight: true, + headermenu: true, + columns: grid_cols_leyendas_fiscales, +} + +var controls_leyendas_fiscales = [ + {maxHeight: 15}, + {cols: [{maxWidth: 15}, + {view: 'label', id: 'lbl_title', label: 'Selecciona las Leyendas Fiscales a integrar en esta factura'}, + {}]}, + {maxHeight: 15}, + grid_leyendas_fiscales, +] + + var controls_invoices = [ { view: 'tabview', id: 'tv_invoice', - //~ tabbar: {options: ['Generar', 'PreFacturas', 'INE']}, animate: true, cells: [ {id: 'Generar', rows: controls_generate}, {id: 'PreFacturas', rows: controls_prefactura}, - {id: 'INE', rows: controles_ine}, + {id: 'INE', rows: controls_ine}, + {id: 'Leyendas Fiscales', rows: controls_leyendas_fiscales}, ] }, ]