From c45d95d762787380f5fcd99eb44a1dc739620f16 Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Fri, 16 Feb 2018 13:52:30 -0600 Subject: [PATCH] Fix - Issue #188 --- source/app/controllers/cfdi_xml.py | 2 +- source/app/controllers/util.py | 5 +- source/app/models/main.py | 155 +++++++++++++++++------- source/static/js/controller/invoices.js | 17 ++- source/static/js/controller/partners.js | 1 + 5 files changed, 130 insertions(+), 50 deletions(-) diff --git a/source/app/controllers/cfdi_xml.py b/source/app/controllers/cfdi_xml.py index 8a77554..1692db4 100644 --- a/source/app/controllers/cfdi_xml.py +++ b/source/app/controllers/cfdi_xml.py @@ -265,7 +265,7 @@ class CFDI(object): return def _impuestos(self, datos): - if self._is_nomina: + if self._is_nomina or not datos: return if not datos: diff --git a/source/app/controllers/util.py b/source/app/controllers/util.py index c23ac9b..6100ece 100644 --- a/source/app/controllers/util.py +++ b/source/app/controllers/util.py @@ -1420,7 +1420,8 @@ def _comprobante(doc, options): data['tipodecomprobante'] = tipos.get(data['tipodecomprobante']) data['lugarexpedicion'] = \ 'C.P. de Expedición: {}'.format(data['lugarexpedicion']) - data['metododepago'] = options['metododepago'] + if 'metododepago' in options: + data['metododepago'] = options['metododepago'] data['formadepago'] = options['formadepago'] if 'condicionesdepago' in data: @@ -2199,7 +2200,7 @@ def local_copy(files): args = 'df -P {} | tail -1 | cut -d" " -f 1'.format(path_bk) try: result = _call(args) - log.info(result) + # ~ log.info(result) except: pass # ~ if result != 'empresalibre\n': diff --git a/source/app/models/main.py b/source/app/models/main.py index 744bda5..f74a8c6 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -3235,8 +3235,9 @@ class Facturas(BaseModel): 'PUE': 'Pago en una sola exhibición', 'PPD': 'Pago en parcialidades o diferido', } - values['metododepago'] = 'Método de Pago: ({}) {}'.format( - invoice.metodo_pago, mp[invoice.metodo_pago]) + if invoice.metodo_pago: + values['metododepago'] = 'Método de Pago: ({}) {}'.format( + invoice.metodo_pago, mp[invoice.metodo_pago]) obj = SATFormaPago.get(SATFormaPago.key==invoice.forma_pago) values['formadepago'] = str(obj) @@ -3591,7 +3592,7 @@ class Facturas(BaseModel): return inicio - def _calculate_totals(self, invoice, products): + def _calculate_totals(self, invoice, products, tipo_comprobante): tax_locales = Configuracion.get_bool('chk_config_tax_locales') tax_decimals = Configuracion.get_bool('chk_config_tax_decimals') subtotal = 0 @@ -3618,6 +3619,9 @@ class Facturas(BaseModel): cantidad = float(product['cantidad']) valor_unitario = float(product['valor_unitario']) descuento = float(product['descuento']) + if tipo_comprobante == 'T': + valor_unitario = 0.0 + descuento = 0.0 precio_final = valor_unitario - descuento importe = round(cantidad * precio_final, DECIMALES) @@ -3639,6 +3643,9 @@ class Facturas(BaseModel): FacturasDetalle.create(**product) + if tipo_comprobante == 'T': + continue + base = product['importe'] - product['descuento'] for tax in p.impuestos: if tax_locales and tax.tipo == 'R' and tax.key == '000': @@ -3666,17 +3673,18 @@ class Facturas(BaseModel): tax.suma_impuestos = impuesto_producto totals_tax[tax.id] = tax - for tax in totals_tax.values(): - if tax.tipo == 'E': - continue + if tipo_comprobante != 'T': + for tax in totals_tax.values(): + if tax.tipo == 'E': + continue - invoice_tax = { - 'factura': invoice.id, - 'impuesto': tax.id, - 'base': tax.base, - 'importe': tax.suma_impuestos, - } - FacturasImpuestos.create(**invoice_tax) + invoice_tax = { + 'factura': invoice.id, + 'impuesto': tax.id, + 'base': tax.base, + 'importe': tax.suma_impuestos, + } + FacturasImpuestos.create(**invoice_tax) total = subtotal - descuento_cfdi + \ (total_trasladados or 0) - (total_retenciones or 0) \ @@ -3725,6 +3733,7 @@ class Facturas(BaseModel): productos = util.loads(values.pop('productos')) relacionados = util.loads(values.pop('relacionados')) ine = values.pop('ine', {}) + tipo_comprobante = values['tipo_comprobante'] emisor = Emisor.select()[0] values['serie'] = cls._get_serie(cls, user, values['serie']) @@ -3733,10 +3742,12 @@ class Facturas(BaseModel): values['lugar_expedicion'] = emisor.cp_expedicion or emisor.codigo_postal values['anticipo'] = util.get_bool(values['anticipo']) values['donativo'] = util.get_bool(values['donativo']) + if tipo_comprobante == 'T': + values['metodo_pago'] = '' with database_proxy.atomic() as txn: obj = Facturas.create(**values) - totals = cls._calculate_totals(cls, obj, productos) + totals = cls._calculate_totals(cls, obj, productos, tipo_comprobante) cls._guardar_relacionados(cls, obj, relacionados) cls._guardar_ine(cls, obj, ine) obj.subtotal = totals['subtotal'] @@ -3798,7 +3809,8 @@ class Facturas(BaseModel): comprobante['TipoCambio'] = FORMAT.format(invoice.tipo_cambio) comprobante['Total'] = FORMAT.format(invoice.total) comprobante['TipoDeComprobante'] = invoice.tipo_comprobante - comprobante['MetodoPago'] = invoice.metodo_pago + if invoice.metodo_pago: + comprobante['MetodoPago'] = invoice.metodo_pago comprobante['LugarExpedicion'] = invoice.lugar_expedicion if invoice.descuento: comprobante['Descuento'] = FORMAT.format(invoice.descuento) @@ -3857,38 +3869,45 @@ class Facturas(BaseModel): traslados = [] retenciones = [] - for impuesto in row.producto.impuestos: - if impuesto.tipo == 'E': - continue + if invoice.tipo_comprobante != 'T': + for impuesto in row.producto.impuestos: + base = float(row.importe - row.descuento) + if impuesto.tipo == 'E': + tax = { + 'Base': FORMAT.format(base), + 'Impuesto': '002', + 'TipoFactor': 'Exento', + } + traslados.append(tax) + continue - if impuesto.key == '000': - continue + if impuesto.key == '000': + continue - base = float(row.importe - row.descuento) - tasa = float(impuesto.tasa) + tasa = float(impuesto.tasa) - if tax_decimals: - import_tax = round(tasa * base, DECIMALES_TAX) - tmp += import_tax - xml_importe = FORMAT_TAX.format(import_tax) - else: - import_tax = round(tasa * base, DECIMALES) - xml_importe = FORMAT.format(import_tax) + if tax_decimals: + import_tax = round(tasa * base, DECIMALES_TAX) + tmp += import_tax + xml_importe = FORMAT_TAX.format(import_tax) + else: + import_tax = round(tasa * base, DECIMALES) + xml_importe = FORMAT.format(import_tax) - tipo_factor = 'Tasa' - if impuesto.factor != 'T': - tipo_factor = 'Cuota' - tax = { - "Base": FORMAT.format(base), - "Impuesto": impuesto.key, - "TipoFactor": tipo_factor, - "TasaOCuota": str(impuesto.tasa), - "Importe": xml_importe, - } - if impuesto.tipo == 'T': - traslados.append(tax) - else: - retenciones.append(tax) + tipo_factor = 'Tasa' + if impuesto.factor != 'T': + tipo_factor = 'Cuota' + tax = { + "Base": FORMAT.format(base), + "Impuesto": impuesto.key, + "TipoFactor": tipo_factor, + "TasaOCuota": str(impuesto.tasa), + "Importe": xml_importe, + } + if impuesto.tipo == 'T': + traslados.append(tax) + else: + retenciones.append(tax) if traslados: taxes['traslados'] = traslados @@ -3974,6 +3993,9 @@ class Facturas(BaseModel): impuestos['locales_trasladados'] = locales_trasladados impuestos['locales_retenciones'] = locales_retenciones + if invoice.tipo_comprobante == 'T': + impuestos = {} + data = { 'comprobante': comprobante, 'relacionados': relacionados, @@ -7856,6 +7878,45 @@ def _import_from_folder(path): return +def _exportar_documentos(): + rfc = input('Introduce el RFC: ').strip().upper() + if not rfc: + msg = 'El RFC es requerido' + log.error(msg) + return + + args = util.get_con(rfc) + if not args: + return + + conectar(args) + log.info('Exportar documentos...') + + n = util.now() + year = input('Introduce el año [{}]: '.format(n.year)).strip() + if not year: + year = str(n.year) + month = input('Introduce el mes [{}]: '.format(n.month)).strip() + if not month: + month = str(n.month) + + filters = { + 'year': year, + 'month': month, + } + result = Facturas.get_(filters) + if result['ok']: + t = len(result['rows']) + for i, row in enumerate(result['rows']): + msg = 'Extrayendo factura {} de {}: {}-{}'.format( + i+1, t, row['serie'], row['folio']) + log.info(msg) + Facturas.get_xml(row['id']) + Facturas.get_pdf(row['id'], rfc, True) + log.info('Documentos exportados...') + return + + def _test(): rfc = input('Introduce el RFC: ').strip().upper() if not rfc: @@ -7907,11 +7968,12 @@ help_lr = 'Listar RFCs' @click.option('-r', '--rfc') @click.option('-d', '--detalle', is_flag=True, default=False) @click.option('-id', '--importar-directorio') +@click.option('-ed', '--exportar_documentos', is_flag=True, default=False) def main(iniciar_bd, migrar_bd, nuevo_superusuario, cambiar_contraseña, agregar_rfc, borrar_rfc, listar_rfc, importar_valores, archivo, conexion, factura_libre, factura_libre_gambas, test, generar_archivo_productos, importar_productos, backup_dbs, no_bd, alta, rfc, detalle, - importar_directorio): + importar_directorio, exportar_documentos): opt = locals() @@ -8019,9 +8081,14 @@ def main(iniciar_bd, migrar_bd, nuevo_superusuario, cambiar_contraseña, if opt['importar_directorio']: _import_from_folder(opt['importar_directorio']) + sys.exit(0) if opt['backup_dbs']: util.backup_dbs() + sys.exit(0) + + if opt['exportar_documentos']: + _exportar_documentos() return diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js index 7ec6581..775fb9f 100644 --- a/source/static/js/controller/invoices.js +++ b/source/static/js/controller/invoices.js @@ -406,6 +406,11 @@ function validate_invoice(values){ } + var tipo_comprobante = $$('lst_tipo_comprobante').getValue() + if(tipo_comprobante == 'T'){ + msg_ok('El CFDI es de tipo Traslado') + } + return true } @@ -565,7 +570,6 @@ function guardar_y_timbrar(values){ delete rows[i]['delete'] delete rows[i]['clave'] 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']) @@ -630,7 +634,9 @@ function cmd_timbrar_click(id, e, node){ if(!validate_invoice(values)){ return } - //~ showvar(values) + + var tipo_comprobante = $$('lst_tipo_comprobante').getValue() + query = table_relaciones.chain().data() msg = '¿Todos los datos son correctos?

' if(query.length > 0){ @@ -650,7 +656,12 @@ function cmd_timbrar_click(id, e, node){ if(usar_ine){ msg += 'Estas usando el complemento INE

' } - msg += '¿Estás seguro de timbrar esta factura?' + + if(tipo_comprobante == 'T'){ + msg += 'El Tipo de Comprobante es Traslado, todos los importes serán puesto a 0 (Cero), asegurate de que sea el tipo de comprobante correcto

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

' webix.confirm({ title: 'Timbrar Factura', diff --git a/source/static/js/controller/partners.js b/source/static/js/controller/partners.js index 2aa28ca..155ab7f 100644 --- a/source/static/js/controller/partners.js +++ b/source/static/js/controller/partners.js @@ -337,6 +337,7 @@ function partner_reset_saldo(id){ if(values.ok){ msg = 'Saldo actualizado correctamente' $$('grid_partners').updateItem(id, {saldo_cliente: 0.0}) + $$('cmd_partner_zero').disable() msg_ok(msg) } }