From 7a573b8eda9703a01dcd98a2c812a2c8729c1ece Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Mon, 29 Jan 2018 13:21:06 -0600 Subject: [PATCH] Complemento EDU --- source/app/controllers/cfdi_xml.py | 34 +++++++++++-------- source/app/controllers/util.py | 7 ++++ source/app/models/db.py | 3 ++ source/app/models/main.py | 45 ++++++++++++++++++++++++- source/static/js/controller/invoices.js | 25 ++++++++++++++ source/static/js/ui/invoices.js | 38 ++++++++++++++++++++- 6 files changed, 136 insertions(+), 16 deletions(-) diff --git a/source/app/controllers/cfdi_xml.py b/source/app/controllers/cfdi_xml.py index 8b933d9..445ef34 100644 --- a/source/app/controllers/cfdi_xml.py +++ b/source/app/controllers/cfdi_xml.py @@ -58,6 +58,12 @@ SAT = { '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', }, + 'edu': { + 'version': '1.0', + 'prefix': 'iedu', + 'xmlns': 'http://www.sat.gob.mx/iedu', + 'schema': ' http://www.sat.gob.mx/iedu http://www.sat.gob.mx/sitio_internet/cfd/ine/iedu.xsd', + }, } @@ -72,6 +78,7 @@ class CFDI(object): self._impuestos_locales = False self._donativo = False self._ine = False + self._edu = False self.error = '' def _now(self): @@ -118,6 +125,8 @@ class CFDI(object): if 'ine' in datos['complementos']: self._ine = True + self._edu = datos['edu'] + if 'nomina' in datos: return self._validate_nomina(datos) return True @@ -160,10 +169,16 @@ class CFDI(object): if self._ine: name = 'xmlns:{}'.format(SAT['ine']['prefix']) attributes[name] = SAT['ine']['xmlns'] - schema_donativo = SAT['ine']['schema'] + schema_ine = SAT['ine']['schema'] + + schema_edu = '' + if self._edu: + name = 'xmlns:{}'.format(SAT['edu']['prefix']) + attributes[name] = SAT['edu']['xmlns'] + schema_edu = SAT['edu']['schema'] attributes['xsi:schemaLocation'] = self._sat_cfdi['schema'] + \ - schema_locales + schema_donativo +schema_ine + schema_locales + schema_donativo + schema_ine + schema_edu attributes.update(datos) if not 'Version' in attributes: @@ -213,6 +228,7 @@ class CFDI(object): complemento = row.pop('complemento') cuenta_predial = row.pop('CuentaPredial', '') pedimento = row.pop('Pedimento', '') + student = row.pop('student', '') taxes = {} if 'impuestos' in row: @@ -246,20 +262,10 @@ class CFDI(object): node_name = '{}:CuentaPredial'.format(self._pre) ET.SubElement(concepto, node_name, attributes) - if 'autRVOE' in row: - fields = ( - 'version', - 'nombreAlumno', - 'CURP', - 'nivelEducativo', - 'autRVOE', - ) - for field in fields: - if field in row['autRVOE']: - attributes[field] = row['autRVOE'][field] + if student: node_name = '{}:ComplementoConcepto'.format(self._pre) complemento = ET.SubElement(concepto, node_name) - ET.SubElement(complemento, 'iedu:instEducativas', attributes) + ET.SubElement(complemento, 'iedu:instEducativas', student) return def _impuestos(self, datos): diff --git a/source/app/controllers/util.py b/source/app/controllers/util.py index 38bb216..1364728 100644 --- a/source/app/controllers/util.py +++ b/source/app/controllers/util.py @@ -1145,6 +1145,13 @@ def _conceptos(doc, version): info = '\nNúmero Pedimento: {}'.format(v['numeropedimento']) values['descripcion'] += info + n = c.find('{}ComplementoConcepto'.format(PRE[version])) + if n is not None: + v = CaseInsensitiveDict(n[0].attrib.copy()) + info = '\nAlumno: {} (CURP: {})\nNivel: {}, Autorización: {}'.format( + v['nombreAlumno'], v['CURP'], v['nivelEducativo'], v['autRVOE']) + values['descripcion'] += info + data.append(values) return data diff --git a/source/app/models/db.py b/source/app/models/db.py index 62bc004..84a9b78 100644 --- a/source/app/models/db.py +++ b/source/app/models/db.py @@ -262,6 +262,9 @@ class StorageEngine(object): def _get_client(self, values): return main.Socios.get_by_client(values) + def _get_student(self, values): + return main.Alumnos.get_by_name(values) + def _get_product(self, values): return main.Productos.get_by(values) diff --git a/source/app/models/main.py b/source/app/models/main.py index ff87883..5d234c0 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -216,6 +216,7 @@ def config_timbrar(): 'cfdi_donativo': obj.es_ong, 'cfdi_anticipo': Configuracion.get_('chk_config_anticipo'), 'cfdi_ine': Configuracion.get_bool('chk_config_ine'), + 'cfdi_edu': Configuracion.get_bool('chk_config_edu'), 'cfdi_metodo_pago': Configuracion.get_bool('chk_config_ocultar_metodo_pago'), 'cfdi_condicion_pago': Configuracion.get_bool('chk_config_ocultar_condiciones_pago'), 'cfdi_open_pdf': Configuracion.get_bool('chk_config_open_pdf'), @@ -2189,6 +2190,28 @@ class Alumnos(BaseModel): ) return tuple(rows) + @classmethod + def get_by_name(cls, values): + rows = () + name = values.get('name', '') + if name: + rows = (Alumnos + .select( + Alumnos.id, + Alumnos.nombre, + Alumnos.paterno, + Alumnos.materno, + Alumnos.rfc) + .where((Alumnos.es_activo==True) & + (Alumnos.nombre.contains(name) | + Alumnos.paterno.contains(name) | + Alumnos.materno.contains(name) | + Alumnos.rfc.contains(name))) + .dicts()) + rows = tuple(rows) + + return rows + @classmethod def get_by(cls, values): if 'id' in values: @@ -3208,8 +3231,9 @@ class Facturas(BaseModel): locales_retenciones = 0 for product in products: - # ~ print ('\n', product['descripcion']) + # ~ print ('\n', product) id_product = product.pop('id') + id_student = product.pop('id_student', 0) p = Productos.get(Productos.id==id_product) product['unidad'] = p.unidad.key @@ -3235,6 +3259,13 @@ class Facturas(BaseModel): descuento_cfdi += product['descuento'] subtotal += product['importe'] + if id_student: + student = Alumnos.get(Alumnos.id==id_student) + product['alumno'] = str(student) + product['curp'] = student.curp + product['nivel'] = student.grupo.nivel.nombre + product['autorizacion'] = student.grupo.nivel.autorizacion.strip() + FacturasDetalle.create(**product) base = product['importe'] - product['descuento'] @@ -3365,6 +3396,7 @@ class Facturas(BaseModel): emisor = Emisor.select()[0] certificado = Certificado.select()[0] + is_edu = False comprobante = {} relacionados = {} donativo = {} @@ -3438,6 +3470,16 @@ class Facturas(BaseModel): if row.pedimento: concepto['Pedimento'] = row.pedimento + if row.autorizacion: + is_edu = True + concepto['student'] = { + 'version': '1.0', + 'nombreAlumno': row.alumno, + 'CURP': row.curp, + 'nivelEducativo': row.nivel, + 'autRVOE': row.autorizacion, + } + taxes = {} traslados = [] retenciones = [] @@ -3564,6 +3606,7 @@ class Facturas(BaseModel): 'conceptos': conceptos, 'impuestos': impuestos, 'donativo': donativo, + 'edu': is_edu, 'complementos': complementos, } return util.make_xml(data, certificado, auth) diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js index 2bc88d4..fb26e80 100644 --- a/source/static/js/controller/invoices.js +++ b/source/static/js/controller/invoices.js @@ -22,6 +22,7 @@ var invoices_controllers = { $$('grid_clients_found').attachEvent('onValueSuggest', grid_clients_found_click) $$('search_product_id').attachEvent('onKeyPress', search_product_id_key_press) $$('grid_products_found').attachEvent('onValueSuggest', grid_products_found_click) + $$('grid_students_found').attachEvent('onValueSuggest', grid_students_found_click) $$('grid_details').attachEvent('onItemClick', grid_details_click) $$('grid_details').attachEvent('onHeaderClick', grid_details_header_click) $$('grid_details').attachEvent('onBeforeEditStart', grid_details_before_edit_start) @@ -161,6 +162,7 @@ function default_config(){ }else{ $$('tv_invoice').getTabbar().showOption('INE') } + cfg_invoice['edu'] = values.cfdi_edu cfg_invoice['open_pdf'] = values.cfdi_open_pdf cfg_invoice['tax_locales'] = values.cfdi_tax_locales cfg_invoice['tax_decimals'] = values.cfdi_tax_decimals @@ -168,6 +170,9 @@ function default_config(){ if(values.cfdi_show_pedimento){ $$('grid_details').showColumn('pedimento') } + if(values.cfdi_edu){ + $$('grid_details').showColumn('student') + } }) } @@ -557,6 +562,7 @@ function guardar_y_timbrar(values){ delete rows[i]['clave_sat'] delete rows[i]['unidad'] delete rows[i]['importe'] + delete rows[i]['student'] rows[i]['valor_unitario'] = parseFloat(rows[i]['valor_unitario']) rows[i]['descuento'] = parseFloat(rows[i]['descuento']) } @@ -893,6 +899,25 @@ function grid_products_found_click(obj){ } +function grid_students_found_click(obj){ + var form = $$('form_invoice') + var row = grid.getSelectedItem() + + if (row == undefined){ + msg = 'Selecciona un registro primero' + msg_error(msg) + return + } + + var values = { + id_student: obj.id, + student: obj.nombre + ' ' + obj.paterno + ' ' + obj.materno, + } + grid.updateItem(row.id, values) + form.setValues({search_student: ''}, true) +} + + function search_product_by_key(key){ webix.ajax().get('/values/productokey', {'key': key}, { error: function(text, data, xhr) { diff --git a/source/static/js/ui/invoices.js b/source/static/js/ui/invoices.js index 754bf82..94252b1 100644 --- a/source/static/js/ui/invoices.js +++ b/source/static/js/ui/invoices.js @@ -285,6 +285,8 @@ var grid_details_cols = [ {id: "descripcion", header:{text: 'Descripción', css: 'center'}, fillspace: true, editor: 'popup'}, {id: "pedimento", header: 'Pedimento', editor: 'text', hidden: true}, + {id: "id_student", header: 'ID_Alumno', hidden: true}, + {id: 'student', header: 'Alumno', hidden: true, width: 150}, {id: "unidad", header:{text: 'Unidad', css: 'center'}, width: 100}, {id: 'cantidad', header: {text: 'Cantidad', css: 'center'}, width: 100, format: webix.i18n.numberFormat, css: 'right', editor: 'text'}, @@ -386,6 +388,31 @@ var suggest_products = { } +var suggest_students = { + view: 'gridsuggest', + id: 'grid_students_found', + name: 'grid_students_found', + body: { + autoConfig: false, + header: false, + columns: [ + {id: 'id', hidden: true}, + {id: 'nombre', adjust: 'data'}, + {id: 'paterno', adjust: 'data'}, + {id: 'materno', adjust: 'data'}, + {id: 'rfc', adjust: 'data'}, + ], + dataFeed:function(text){ + if (text.length > 2){ + this.load('/values/student?name=' + text) + }else{ + this.hide() + } + } + } +} + + var body_comprobante = {rows: [{ cols: [ { @@ -450,6 +477,13 @@ var body_regimen_fiscal = { } +var body_students = {rows:[ + {view: 'search', id: 'search_student', + name: "search_student", label: "por Nombre o RFC", + labelPosition:'top', suggest: suggest_students, + placeholder:'Captura al menos tres letras'}, +]} + var controls_generate = [ {minHeight: 10, maxHeight: 10}, toolbar_invoices_generate, @@ -484,7 +518,9 @@ var controls_generate = [ labelPosition:'top', suggest: suggest_products, placeholder:'Captura al menos tres letras'}, ]}, - ]}} + ]}}, + {view: 'fieldset', id: 'fs_students', label: 'Buscar Alumno', + body: body_students}, ]}, {maxWidth: 10}, {maxWidth: 300, rows: [