diff --git a/source/app/controllers/helper.py b/source/app/controllers/helper.py index 122828a..6ba1863 100644 --- a/source/app/controllers/helper.py +++ b/source/app/controllers/helper.py @@ -313,7 +313,10 @@ class SendMail(object): message.attach(MIMEText(options['mensaje'], 'html')) for f in options['files']: part = MIMEBase('application', 'octet-stream') - part.set_payload(f[0]) + if isinstance(f[0], str): + part.set_payload(f[0].encode('utf-8')) + else: + part.set_payload(f[0]) encoders.encode_base64(part) part.add_header( 'Content-Disposition', diff --git a/source/app/controllers/util.py b/source/app/controllers/util.py index f2ebcf9..29fd528 100644 --- a/source/app/controllers/util.py +++ b/source/app/controllers/util.py @@ -1061,24 +1061,23 @@ class LIBO(object): self._template.setPrinter(self._set_properties({'PaperFormat': LETTER})) self._render(data) - # ~ path = '{}.ods'.format(tempfile.mkstemp()[1]) - path = get_path_temp('.ods') - self._template.storeToURL(self._path_url(path), ()) + path_ods = get_path_temp('.ods') + self._template.storeToURL(self._path_url(path_ods), ()) if ods: - data = self._read(path) - _kill(path) + data = self._read(path_ods) + _kill(path_ods) return data - doc = self._doc_open(path, {'Hidden': True}) + doc = self._doc_open(path_ods, {'Hidden': True}) options = {'FilterName': 'calc_pdf_Export'} - # ~ path = tempfile.mkstemp()[1] - path = get_path_temp('.pdf') - doc.storeToURL(self._path_url(path), self._set_properties(options)) + path_pdf = get_path_temp('.pdf') + doc.storeToURL(self._path_url(path_pdf), self._set_properties(options)) doc.close(True) self._template.close(True) - data = self._read(path) - _kill(path) + data = self._read(path_pdf) + _kill(path_ods) + _kill(path_pdf) return data def _get_data(self, doc, name=0): @@ -1674,7 +1673,8 @@ def _nomina(doc, data, values, version_cfdi): info = CaseInsensitiveDict(node_nomina.attrib.copy()) node = node_nomina.find('{}Emisor'.format(PRE['NOMINA'][version])) - data['emisor'].update(CaseInsensitiveDict(node.attrib.copy())) + if not node is None: + data['emisor'].update(CaseInsensitiveDict(node.attrib.copy())) node = node_nomina.find('{}Receptor'.format(PRE['NOMINA'][version])) data['receptor'].update(CaseInsensitiveDict(node.attrib.copy())) diff --git a/source/app/models/main.py b/source/app/models/main.py index 8df1219..a1b5c72 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -136,44 +136,31 @@ def import_invoice(): if not rows: return {'ok': False, 'msg': msg} - # ~ clave, descripcion, precio, cantidad - products = {} + products = [] for row in rows: try: obj = Productos.get(Productos.clave==row[0]) - if obj.id in products: - vu = round(row[2], 2) - descuento = round(row[3], 2) - cant = round(row[4], 2) - pf = products[obj.id]['valor_unitario'] - descuento - products[obj.id]['cantidad'] += cant - products[obj.id]['importe'] = round( - pf * products[obj.id]['cantidad'], DECIMALES) - if vu != products[obj.id]['valor_unitario']: - msg = 'Precio diferente en producto: {}'.format(row[0]) - return {'ok': False, 'msg': msg} - else: - vu = round(row[2], 2) - descuento = round(row[3], 2) - cant = round(row[4], 2) - pf = vu - descuento - p = { - 'id': obj.id, - 'delete': '-', - 'clave': obj.clave, - 'descripcion': obj.descripcion, - 'unidad': obj.unidad.name, - 'cantidad': cant, - 'valor_unitario': vu, - 'descuento': obj.descuento, - 'importe': round(pf * cant, DECIMALES), - 'taxes': _get_taxes_product(obj.id), - } - products[obj.id] = p + vu = round(row[2], 2) + descuento = round(row[3], 2) + cant = round(row[4], 2) + pf = vu - descuento + p = { + 'id_product': obj.id, + 'delete': '-', + 'clave': obj.clave, + 'descripcion': obj.descripcion, + 'unidad': obj.unidad.id, + 'cantidad': cant, + 'valor_unitario': vu, + 'descuento': descuento, + 'importe': round(pf * cant, DECIMALES), + 'taxes': _get_taxes_product(obj.id), + } + products.append(p) except Productos.DoesNotExist: pass log.info('Factura importada...') - return {'ok': True, 'rows': tuple(products.values())} + return {'ok': True, 'rows': tuple(products)} def get_doc(type_doc, id, rfc): @@ -246,6 +233,7 @@ def config_timbrar(): 'cfdi_tax_locales': Configuracion.get_bool('chk_config_tax_locales'), 'cfdi_tax_decimals': Configuracion.get_bool('chk_config_tax_decimals'), 'cfdi_with_taxes': Configuracion.get_bool('chk_config_price_with_taxes_in_invoice'), + 'cfdi_add_same_product': Configuracion.get_bool('chk_config_add_same_product'), } return conf @@ -333,6 +321,7 @@ class Configuracion(BaseModel): 'chk_config_tax_locales', 'chk_config_tax_decimals', 'chk_config_price_with_taxes_in_invoice', + 'chk_config_add_same_product', 'chk_config_anticipo', 'chk_config_cuenta_predial', 'chk_config_codigo_barras', @@ -2748,11 +2737,11 @@ class Productos(BaseModel): clave = values.get('key', '') row = (Productos .select( - Productos.id, + Productos.id.alias('id_product'), Productos.clave, Productos.clave_sat, Productos.descripcion, - SATUnidades.name.alias('unidad'), + SATUnidades.id.alias('unidad'), Productos.valor_unitario, Productos.descuento) .join(SATUnidades).switch(Productos) @@ -2761,7 +2750,7 @@ class Productos(BaseModel): .dicts() ) if len(row): - id = row[0]['id'] + id = row[0]['id_product'] model_pt = Productos.impuestos.get_through_model() taxes = tuple(model_pt .select( @@ -3360,7 +3349,7 @@ class Facturas(BaseModel): } options = { 'para': obj.cliente.correo_facturas, - 'copia': values['correo_copia'], + 'copia': values.get('correo_copia', ''), 'confirmar': util.get_bool(values.get('correo_confirmacion', '0')), 'asunto': util.make_info_mail(values['correo_asunto'], fields), 'mensaje': util.make_info_mail(values['correo_mensaje'], fields), @@ -3567,12 +3556,11 @@ class Facturas(BaseModel): locales_retenciones = 0 for product in products: - # ~ print ('\n', product['descripcion']) - id_product = product.pop('id') + id_product = product.pop('id_product') id_student = product.pop('id_student', 0) p = Productos.get(Productos.id==id_product) - product['unidad'] = p.unidad.key + product['unidad'] = SATUnidades.get(SATUnidades.id==product['unidad']).key product['clave'] = p.clave product['clave_sat'] = p.clave_sat product['cuenta_predial'] = p.cuenta_predial @@ -3793,8 +3781,8 @@ class Facturas(BaseModel): 'ClaveProdServ': row.producto.clave_sat, 'NoIdentificacion': row.producto.clave, 'Cantidad': FORMAT.format(row.cantidad), - 'ClaveUnidad': row.producto.unidad.key, - 'Unidad': row.producto.unidad.name[:20], + 'ClaveUnidad': row.unidad, + 'Unidad': SATUnidades.get(SATUnidades.key==row.unidad).name[:20], 'Descripcion': row.descripcion, 'ValorUnitario': FORMAT.format(row.valor_unitario), 'Importe': FORMAT.format(row.importe), @@ -4467,10 +4455,10 @@ class PreFacturas(BaseModel): locales_retenciones = 0 for product in products: - id_product = product.pop('id') + id_product = product.pop('id_product') p = Productos.get(Productos.id==id_product) - product['unidad'] = p.unidad.key + product['unidad'] = SATUnidades.get(SATUnidades.id==product['unidad']).key product['clave'] = p.clave product['clave_sat'] = p.clave_sat product['cuenta_predial'] = p.cuenta_predial @@ -4717,16 +4705,16 @@ class PreFacturasDetalle(BaseModel): PreFacturasDetalle.factura==id) for p in reversed(productos): - row = {'id': p.producto.id} + row = {'id_product': p.producto.id} row['clave'] = p.producto.clave row['descripcion'] = p.descripcion - row['unidad'] = p.producto.unidad.name + row['unidad'] = p.producto.unidad.id row['cantidad'] = p.cantidad row['valor_unitario'] = p.valor_unitario row['descuento'] = p.descuento pf = p.valor_unitario - p.descuento row['importe'] = round(pf * p.cantidad, DECIMALES) - impuestos = cls._get_impuestos(cls, row['id']) + impuestos = cls._get_impuestos(cls, row['id_product']) data.append({'row': row, 'taxes': impuestos}) return {'rows': data, 'receptor': receptor} diff --git a/source/static/js/controller/admin.js b/source/static/js/controller/admin.js index f1a8270..5e8786f 100644 --- a/source/static/js/controller/admin.js +++ b/source/static/js/controller/admin.js @@ -63,6 +63,7 @@ var controllers = { $$('chk_config_tax_locales').attachEvent('onItemClick', chk_config_item_click) $$('chk_config_tax_decimals').attachEvent('onItemClick', chk_config_item_click) $$('chk_config_price_with_taxes_in_invoice').attachEvent('onItemClick', chk_config_item_click) + $$('chk_config_add_same_product').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) diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js index 5b81709..df0e660 100644 --- a/source/static/js/controller/invoices.js +++ b/source/static/js/controller/invoices.js @@ -167,6 +167,7 @@ function default_config(){ cfg_invoice['tax_locales'] = values.cfdi_tax_locales cfg_invoice['tax_decimals'] = values.cfdi_tax_decimals cfg_invoice['with_taxes'] = values.cfdi_with_taxes + cfg_invoice['add_same_product'] = values.cfdi_add_same_product if(values.cfdi_show_pedimento){ $$('grid_details').showColumn('pedimento') } @@ -558,10 +559,11 @@ function guardar_y_timbrar(values){ var rows = grid.data.getRange() for (i = 0; i < rows.length; i++) { + delete rows[i]['id'] delete rows[i]['delete'] delete rows[i]['clave'] delete rows[i]['clave_sat'] - delete rows[i]['unidad'] + //~ delete rows[i]['unidad'] delete rows[i]['importe'] delete rows[i]['student'] rows[i]['valor_unitario'] = parseFloat(rows[i]['valor_unitario']) @@ -808,7 +810,7 @@ function calcular_impuestos(){ var import2 = (valor_unitario * cantidad).round(DECIMALES) var importe = parseFloat(product.importe) subtotal += importe - query = table_pt.chain().find({'product': product.id}).data() + query = table_pt.chain().find({'product': product.id_product}).data() for(var tax of query){ impuesto = table_taxes.findOne({'id': tax.tax}) if(impuesto.tipo == 'E'){ @@ -867,7 +869,16 @@ function set_product(values){ var taxes = values.taxes var values = values.row var form = $$('form_invoice') - var row = grid.getItem(values.id) + var row = undefined + + if(!cfg_invoice['add_same_product']){ + for(var id in grid.data.pull){ + if(grid.getItem(id).id_product == values.id_product){ + row = grid.getItem(id) + break + } + } + } values['delete'] = '-' if (row == undefined){ @@ -946,7 +957,7 @@ function search_product_id_key_press(code, e){ function grid_details_before_edit_start(id){ - var columns = ['', 'descripcion', 'pedimento','cantidad', 'valor_unitario', 'descuento'] + var columns = ['', 'unidad', 'descripcion', 'pedimento','cantidad', 'valor_unitario', 'descuento'] if(!columns.indexOf(id.column)){ return !this.getItem(id.row)[id.column] } @@ -956,6 +967,10 @@ function grid_details_before_edit_start(id){ function grid_details_before_edit_stop(state, editor){ var row = grid.getItem(editor.row) + if(editor.column == 'unidad'){ + return true + } + if(editor.column == 'descripcion'){ if(!state.value.trim()){ msg = 'La descripción no puede estar vacía' @@ -1358,9 +1373,9 @@ function cmd_prefactura_click(){ var rows = grid.data.getRange() for (i = 0; i < rows.length; i++) { + delete rows[i]['id'] delete rows[i]['delete'] delete rows[i]['clave'] - delete rows[i]['unidad'] delete rows[i]['importe'] rows[i]['valor_unitario'] = parseFloat(rows[i]['valor_unitario']) rows[i]['descuento'] = parseFloat(rows[i]['descuento']) diff --git a/source/static/js/ui/admin.js b/source/static/js/ui/admin.js index 3dbd7f9..e06351d 100644 --- a/source/static/js/ui/admin.js +++ b/source/static/js/ui/admin.js @@ -599,6 +599,8 @@ var options_admin_otros = [ labelRight: 'Calcular impuestos con 4 decimales'}, {view: 'checkbox', id: 'chk_config_price_with_taxes_in_invoice', labelWidth: 0, labelRight: 'Precio incluye impuestos'}, + {view: 'checkbox', id: 'chk_config_add_same_product', labelWidth: 0, + labelRight: 'Permitir agregar el mismo producto'}, ]}, {maxHeight: 20}, {template: 'Ayudas varias', type: 'section'}, diff --git a/source/static/js/ui/invoices.js b/source/static/js/ui/invoices.js index 94252b1..80024cd 100644 --- a/source/static/js/ui/invoices.js +++ b/source/static/js/ui/invoices.js @@ -277,7 +277,7 @@ var grid_invoices = { var grid_details_cols = [ - {id: "id", header:"ID", hidden: true}, + {id: 'id_product', header: 'id_product', hidden: true}, {id: 'delete', header: '', width: 30, css: 'delete'}, {id: "clave", header:{text: 'Clave', css: 'center'}, width: 100, adjust: 'data'}, @@ -287,7 +287,8 @@ var grid_details_cols = [ {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: "unidad", header:{text: 'Unidad', css: 'center'}, width: 100, + editor: 'select', options: 'values/unidades'}, {id: 'cantidad', header: {text: 'Cantidad', css: 'center'}, width: 100, format: webix.i18n.numberFormat, css: 'right', editor: 'text'}, {id: "valor_unitario", header:{text: 'Valor Unitario', css: 'center'}, diff --git a/source/templates/plantilla_factura.ods b/source/templates/plantilla_factura.ods index b07c8b2..c2c7afe 100644 Binary files a/source/templates/plantilla_factura.ods and b/source/templates/plantilla_factura.ods differ