From 74ac8b92956e320ed0ee497ef6fdeba77d437435 Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Mon, 20 Nov 2017 00:47:23 -0600 Subject: [PATCH] Soporte complemento ine --- source/app/controllers/cfdi_xml.py | 37 +++++++++++++++--- source/app/models/main.py | 47 ++++++++++++++++++++--- source/static/js/controller/admin.js | 1 + source/static/js/controller/invoices.js | 44 ++++++++++++++++++++- source/static/js/ui/admin.js | 4 ++ source/static/js/ui/invoices.js | 44 ++++++++++++++++++++- source/xslt/cadena.xslt | 2 +- source/xslt/ine11.xslt | 51 +++++++++++++++++++++++++ 8 files changed, 217 insertions(+), 13 deletions(-) create mode 100644 source/xslt/ine11.xslt diff --git a/source/app/controllers/cfdi_xml.py b/source/app/controllers/cfdi_xml.py index d9589b2..f680f58 100644 --- a/source/app/controllers/cfdi_xml.py +++ b/source/app/controllers/cfdi_xml.py @@ -52,6 +52,12 @@ SAT = { 'schema': ' http://www.sat.gob.mx/donat http://www.sat.gob.mx/sitio_internet/cfd/donat/donat11.xsd', 'leyenda': 'Este comprobante ampara un donativo, el cual será destinado por la donataria a los fines propios de su objeto social. En el caso de que los bienes donados hayan sido deducidos previamente para los efectos del impuesto sobre la renta, este donativo no es deducible. La reproducción no autorizada de este comprobante constituye un delito en los términos de las disposiciones fiscales.', }, + 'ine': { + 'version': '1.1', + 'prefix': 'ine', + 'xmlns': 'http://www.sat.gob.mx/ine', + 'schema': ' http://www.sat.gob.mx/ine http://www.sat.gob.mx/sitio_internet/cfd/ine/ine11.xsd', + }, } @@ -65,6 +71,7 @@ class CFDI(object): self._complemento = None self._impuestos_locales = False self._donativo = False + self._ine = False self.error = '' def _now(self): @@ -82,11 +89,12 @@ class CFDI(object): self._impuestos(datos['impuestos']) self._locales(datos['impuestos']) self._donatarias(datos['donativo']) + self._complementos(datos['complementos']) if 'nomina' in datos: self._nomina(datos['nomina']) - if 'complementos' in datos: - self._complementos(datos['complementos']) + #~ if 'complementos' in datos: + #~ self._complementos(datos['complementos']) return self._to_pretty_xml(ET.tostring(self._cfdi, encoding='utf-8')) @@ -107,6 +115,9 @@ class CFDI(object): if datos['donativo']: self._donativo = True + if 'ine' in datos['complementos']: + self._ine = True + if 'nomina' in datos: return self._validate_nomina(datos) return True @@ -145,8 +156,14 @@ class CFDI(object): attributes[name] = SAT['donativo']['xmlns'] schema_donativo = SAT['donativo']['schema'] + schema_ine = '' + if self._ine: + name = 'xmlns:{}'.format(SAT['ine']['prefix']) + attributes[name] = SAT['ine']['xmlns'] + schema_donativo = SAT['ine']['schema'] + attributes['xsi:schemaLocation'] = self._sat_cfdi['schema'] + \ - schema_locales + schema_donativo + schema_locales + schema_donativo +schema_ine attributes.update(datos) if not 'Version' in attributes: @@ -338,8 +355,18 @@ class CFDI(object): return def _complementos(self, datos): - self._complemento = ET.SubElement( - self._cfdi, '{}:Complemento'.format(self._pre)) + if not datos: + return + + if self._complemento is None: + self._complemento = ET.SubElement( + self._cfdi, '{}:Complemento'.format(self._pre)) + + if 'ine' in datos: + atributos = {'Version': SAT['ine']['version']} + atributos.update(datos['ine']) + ET.SubElement(self._complemento, 'ine:INE', atributos) + if 'ce' in datos: pre = 'cce11' datos = datos.pop('ce') diff --git a/source/app/models/main.py b/source/app/models/main.py index 560d7f2..fff5fb9 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -111,6 +111,7 @@ def config_timbrar(): conf = { 'cfdi_anticipo': Configuracion.get_('chk_config_anticipo'), 'cfdi_donativo': obj.es_ong, + 'cfdi_ine': Configuracion.get_('chk_config_ine'), } return conf @@ -150,16 +151,19 @@ class Configuracion(BaseModel): ) elif keys['fields'] == 'templates': fields = ( - 'txt_plantilla_factura_32', - 'txt_plantilla_factura_33', - 'txt_plantilla_factura_33j') + 'txt_plantilla_factura_32', + 'txt_plantilla_factura_33', + 'txt_plantilla_factura_33j' + ) data = (Configuracion .select() .where(Configuracion.clave.in_(fields)) ) elif keys['fields'] == 'configotros': fields = ( - 'chk_config_anticipo',) + 'chk_config_anticipo', + 'chk_config_ine', + ) data = (Configuracion .select() .where(Configuracion.clave.in_(fields)) @@ -2112,10 +2116,23 @@ class Facturas(BaseModel): FacturasRelacionadas.create(**data) return + def _guardar_ine(self, invoice, valores): + if not valores: + return + + data = { + 'factura': invoice, + 'nombre': 'ine', + 'valores': valores, + } + FacturasComplementos.create(**data) + return + @classmethod def add(cls, values): productos = util.loads(values.pop('productos')) relacionados = util.loads(values.pop('relacionados')) + ine = values.pop('ine', {}) emisor = Emisor.select()[0] values['folio'] = cls._get_folio(cls, values['serie']) @@ -2128,6 +2145,7 @@ class Facturas(BaseModel): obj = Facturas.create(**values) totals = cls._calculate_totals(cls, obj, productos) cls._guardar_relacionados(cls, obj, relacionados) + cls._guardar_ine(cls, obj, ine) obj.subtotal = totals['subtotal'] obj.descuento = totals['descuento'] obj.total_trasladados = totals['total_trasladados'] @@ -2158,6 +2176,7 @@ class Facturas(BaseModel): comprobante = {} relacionados = {} donativo = {} + complementos = FacturasComplementos.get_(invoice) if invoice.donativo: donativo['noAutorizacion'] = emisor.autorizacion @@ -2334,6 +2353,7 @@ class Facturas(BaseModel): 'conceptos': conceptos, 'impuestos': impuestos, 'donativo': donativo, + 'complementos': complementos, } return util.make_xml(data, certificado) @@ -2791,6 +2811,23 @@ class FacturasRelacionadas(BaseModel): return [str(r.factura_origen.uuid) for r in query] +class FacturasComplementos(BaseModel): + factura = ForeignKeyField(Facturas) + nombre = TextField(default='') + valores = TextField(default='') + + class Meta: + order_by = ('factura',) + + @classmethod + def get_(cls, factura): + query = (FacturasComplementos + .select() + .where(FacturasComplementos.factura==factura) + ) + return {r.nombre: util.loads(r.valores) for r in query} + + class CfdiPagosFacturas(BaseModel): pago = ForeignKeyField(CfdiPagos) factura = ForeignKeyField(Facturas) @@ -3113,7 +3150,7 @@ def _crear_tablas(rfc): tablas = [Addendas, Categorias, Certificado, CondicionesPago, Configuracion, Folios, Emisor, Facturas, FacturasDetalle, FacturasImpuestos, FacturasPagos, - FacturasRelacionadas, Productos, + FacturasRelacionadas, FacturasComplementos, Productos, PreFacturas, PreFacturasDetalle, PreFacturasImpuestos, PreFacturasRelacionadas, SATAduanas, SATFormaPago, SATImpuestos, SATMonedas, SATRegimenes, diff --git a/source/static/js/controller/admin.js b/source/static/js/controller/admin.js index cfbdc49..3a75a1a 100644 --- a/source/static/js/controller/admin.js +++ b/source/static/js/controller/admin.js @@ -40,6 +40,7 @@ var controllers = { $$('txt_plantilla_factura_32').attachEvent('onItemClick', txt_plantilla_factura_32_click) $$('txt_plantilla_factura_33').attachEvent('onItemClick', txt_plantilla_factura_33_click) $$('chk_config_anticipo').attachEvent('onItemClick', chk_config_item_click) + $$('chk_config_ine').attachEvent('onItemClick', chk_config_item_click) } } diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js index 49e6402..d6e9c3b 100644 --- a/source/static/js/controller/invoices.js +++ b/source/static/js/controller/invoices.js @@ -112,7 +112,11 @@ function default_config(){ var values = data.json() show('chk_cfdi_anticipo', values.cfdi_anticipo) show('chk_cfdi_donativo', values.cfdi_donativo) - + if(values.cfdi_ine == '0'){ + $$('tv_invoice').getTabbar().hideOption('INE') + }else{ + $$('tv_invoice').getTabbar().showOption('INE') + } }) } @@ -293,6 +297,32 @@ function validate_invoice(values){ } } + usar_ine = $$('chk_cfdi_usar_ine').getValue() + if(usar_ine){ + var id_contabilidad = $$('txt_ine_idcontabilidad').getValue().trim() + if(!id_contabilidad){ + $$('tv_invoice').getTabbar().setValue('INE') + msg = 'El ID de contabilidad es requerido si se usa el complemento INE' + msg_error(msg) + return false + } + + if(!id_contabilidad.is_number()){ + $$('tv_invoice').getTabbar().setValue('INE') + msg = 'El ID de contabilidad deben ser solo digitos' + msg_error(msg) + return False + } + + if(id_contabilidad.length != 6){ + $$('tv_invoice').getTabbar().setValue('INE') + msg = 'El ID de contabilidad deben ser 6 digitos' + msg_error(msg) + return False + } + + } + return true } @@ -379,6 +409,7 @@ function save_invoice(data){ if(values.ok){ msg_sucess('Factura guardada correctamente. Enviando a timbrar') update_grid_invoices(values) + gi.showItem(values.row['id']) send_timbrar(values.row['id']) result = true }else{ @@ -469,6 +500,16 @@ function guardar_y_timbrar(values){ data['anticipo'] = anticipo data['donativo'] = $$('chk_cfdi_donativo').getValue() + var usar_ine = $$('chk_cfdi_usar_ine').getValue() + if(usar_ine){ + var valores = { + TipoProceso: $$('lst_ine_tipo_proceso').getValue(), + TipoComite: $$('lst_ine_tipo_comite').getValue(), + IdContabilidad: $$('txt_ine_idcontabilidad').getValue(), + } + data['ine'] = valores + } + if(!save_invoice(data)){ return } @@ -477,6 +518,7 @@ function guardar_y_timbrar(values){ tipo_relacion = '' anticipo = false $$('chk_cfdi_anticipo').setValue(0) + $$('chk_cfdi_usar_ine').getValue(0) $$('form_invoice').setValues({id_partner: 0, lbl_partner: 'Ninguno'}) $$('multi_invoices').setValue('invoices_home') diff --git a/source/static/js/ui/admin.js b/source/static/js/ui/admin.js index d418e5f..3ec846f 100644 --- a/source/static/js/ui/admin.js +++ b/source/static/js/ui/admin.js @@ -436,6 +436,10 @@ var options_admin_otros = [ {view: 'checkbox', id: 'chk_config_anticipo', labelWidth: 0, labelRight: 'Ayuda para generar anticipos'}, {}]}, + {cols: [{maxWidth: 15}, + {view: 'checkbox', id: 'chk_config_ine', labelWidth: 0, + labelRight: 'Mostrar el complemento INE al facturar'}, + {}]}, {maxHeight: 20}, {}] diff --git a/source/static/js/ui/invoices.js b/source/static/js/ui/invoices.js index 32fa67e..dd70624 100644 --- a/source/static/js/ui/invoices.js +++ b/source/static/js/ui/invoices.js @@ -524,14 +524,56 @@ var controls_prefactura = [ ] +var opt_tipo_proceso = [ + {id: 'Ordinario', value: 'Ordinario'}, + {id: 'Precampaña', value: 'Precampaña'}, + {id: 'Campaña', value: 'Campaña'}, +] + + +var opt_tipo_comite = [ + {id: 'Ejecutivo Nacional', value: 'Ejecutivo Nacional'}, + {id: 'Ejecutivo Estatal', value: 'Ejecutivo Estatal'}, + {id: 'Directivo Estatal', value: 'Directivo Estatal'}, +] + + +var controles_ine = [ + {maxHeight: 15}, + {cols: [{maxWidth: 15}, + {view: 'checkbox', id: 'chk_cfdi_usar_ine', labelWidth: 0, + labelRight: 'Usar el complemento INE'}, + {}]}, + {maxHeight: 10}, + {cols: [{maxWidth: 15}, + {view: 'richselect', id: 'lst_ine_tipo_proceso', labelWidth: 150, + label: 'Tipo de Proceso', options: opt_tipo_proceso, + value: 'Ordinario'}, + {}]}, + {maxHeight: 10}, + {cols: [{maxWidth: 15}, + {view: 'richselect', id: 'lst_ine_tipo_comite', labelWidth: 150, + label: 'Tipo de Comite', options: opt_tipo_comite, + value: 'Ejecutivo Nacional'}, + {}]}, + {maxHeight: 10}, + {cols: [{maxWidth: 15}, + {view: 'text', id: 'txt_ine_idcontabilidad', name: 'ine_idcontabilidad', + label: 'ID de Contabilidad: ', labelWidth: 150}, + {}]}, +] + + var controls_invoices = [ { view: 'tabview', id: 'tv_invoice', - tabbar: {options: ['Generar', 'PreFacturas']}, animate: true, + //~ tabbar: {options: ['Generar', 'PreFacturas', 'INE']}, + animate: true, cells: [ {id: 'Generar', rows: controls_generate}, {id: 'PreFacturas', rows: controls_prefactura}, + {id: 'INE', rows: controles_ine}, ] }, ] diff --git a/source/xslt/cadena.xslt b/source/xslt/cadena.xslt index 42502d3..be01a88 100644 --- a/source/xslt/cadena.xslt +++ b/source/xslt/cadena.xslt @@ -10,6 +10,7 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file