From 589792c05f35e44a9077577ad57851ea60261fed Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Tue, 12 Dec 2017 23:36:22 -0600 Subject: [PATCH 1/8] Fix - Issue 58 --- source/app/models/main.py | 76 ++++++++++++++---------- source/static/js/controller/invoices.js | 78 +++++++++++++++++++++++-- source/static/js/ui/invoices.js | 4 +- 3 files changed, 120 insertions(+), 38 deletions(-) diff --git a/source/app/models/main.py b/source/app/models/main.py index 976b390..6d60aa8 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -2343,7 +2343,7 @@ class Facturas(BaseModel): totals_tax = {} total_trasladados = None total_retenciones = None - total_iva = 0 + # ~ total_iva = 0 locales_traslados = 0 locales_retenciones = 0 @@ -2376,52 +2376,66 @@ class Facturas(BaseModel): FacturasDetalle.create(**product) + base = product['importe'] - product['descuento'] for tax in p.impuestos: + impuesto_producto = round(float(tax.tasa) * base, DECIMALES) + if tax.tipo == 'T' and tax.key != '000': + total_trasladados = (total_trasladados or 0) + impuesto_producto + elif tax.tipo == 'R' and tax.key != '000': + total_retenciones = (total_retenciones or 0) + impuesto_producto + elif tax.tipo == 'T' and tax.key == '000': + locales_traslados += impuesto_producto + elif tax.tipo == 'R' and tax.key == '000': + locales_retenciones += impuesto_producto + if tax.id in totals_tax: - totals_tax[tax.id].importe += importe + totals_tax[tax.id].base += base + totals_tax[tax.id].suma_impuestos += impuesto_producto else: - tax.importe = importe + tax.base = base + tax.suma_impuestos = impuesto_producto totals_tax[tax.id] = tax for tax in totals_tax.values(): - if tax.tipo == 'E' or tax.tipo == 'R': + # ~ if tax.tipo == 'E' or tax.tipo == 'R': + if tax.tipo == 'E': continue - import_tax = round(float(tax.tasa) * tax.importe, DECIMALES) - if tax.key == '000': - locales_traslados += import_tax - else: - total_trasladados = (total_trasladados or 0) + import_tax - if tax.name == 'IVA': - total_iva += import_tax + # ~ import_tax = round(float(tax.tasa) * tax.importe, DECIMALES) + # ~ if tax.key == '000': + # ~ locales_traslados += import_tax + # ~ else: + # ~ total_trasladados = (total_trasladados or 0) + import_tax + # ~ if tax.name == 'IVA': + # ~ total_iva += import_tax invoice_tax = { 'factura': invoice.id, 'impuesto': tax.id, - 'base': tax.importe, - 'importe': import_tax, + 'base': tax.base, + 'importe': tax.suma_impuestos, } FacturasImpuestos.create(**invoice_tax) - for tax in totals_tax.values(): - if tax.tipo == 'E' or tax.tipo == 'T': - continue - if tax.tasa == round(Decimal(2/3), 6): - import_tax = round(float(tax.tasa) * total_iva, DECIMALES) - else: - import_tax = round(float(tax.tasa) * tax.importe, DECIMALES) - if tax.key == '000': - locales_retenciones += import_tax - else: - total_retenciones = (total_retenciones or 0) + import_tax + # ~ for tax in totals_tax.values(): + # ~ if tax.tipo == 'E' or tax.tipo == 'T': + # ~ continue + # ~ if tax.tasa == round(Decimal(2/3), 6): + # ~ import_tax = round(float(tax.tasa) * total_iva, DECIMALES) + # ~ else: + # ~ import_tax = round(float(tax.tasa) * tax.importe, DECIMALES) + # ~ if tax.key == '000': + # ~ locales_retenciones += import_tax + # ~ else: + # ~ total_retenciones = (total_retenciones or 0) + import_tax - invoice_tax = { - 'factura': invoice.id, - 'impuesto': tax.id, - 'base': tax.importe, - 'importe': import_tax, - } - 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) \ diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js index 4c19d51..ed24b7a 100644 --- a/source/static/js/controller/invoices.js +++ b/source/static/js/controller/invoices.js @@ -702,6 +702,69 @@ function calculate_taxes(){ } +function calcular_impuestos(){ + var tmp = null + var subtotal = 0 + var id = 2 + var grid_totals = $$('grid_totals') + var impuesto_producto = 0 + var impuesto = null + + table_totals.clear() + grid_totals.clearAll() + grid_totals.add({id: 1, concepto: 'SubTotal', importe: 0}) + + grid.eachRow(function(row){ + var product = grid.getItem(row) + var importe = parseFloat(product.importe) + subtotal += importe + query = table_pt.chain().find({'product': product.id}).data() + for(var tax of query){ + impuesto = table_taxes.findOne({'id': tax.tax}) + if(impuesto.tipo == 'E'){ + continue + } + + var base = importe + if(impuesto.tipo == 'R'){ + base = (importe * -1).round(DECIMALES) + } + impuesto_producto = (impuesto.tasa * base).round(DECIMALES) + + tmp = table_totals.findOne({'tax': tax.tax}) + if(tmp === null){ + table_totals.insert({'tax': tax.tax, 'importe': impuesto_producto}) + }else{ + tmp.importe += impuesto_producto + table_totals.update(tmp) + } + + } + }) + + var tipo = '' + var concepto = '' + + query = table_totals.chain().data() + for(var t of query){ + tax = table_taxes.findOne({'id': t.tax}) + if(tax.tipo == 'E'){ + continue + } + tipo = 'Traslado ' + if(tax.tipo == 'R'){ + tipo = 'RetenciĆ³n ' + } + concepto = tipo + tax.name + ' (' + tax.tasa + ')' + grid_totals.add({id: id, concepto: concepto, importe: t.importe}) + id += 1 + } + + var row = {importe: subtotal} + grid_totals.updateItem(1, row) +} + + function set_product(values){ var taxes = values.taxes var values = values.row @@ -729,7 +792,8 @@ function set_product(values){ table_pt.insert(v) } } - calculate_taxes() + //~ calculate_taxes() + calcular_impuestos() } @@ -846,7 +910,8 @@ function grid_details_before_edit_stop(state, editor){ row['importe'] = (cantidad * precio_final).round(DECIMALES) grid.refresh() - calculate_taxes() + //~ calculate_taxes() + calcular_impuestos() } @@ -855,7 +920,8 @@ function grid_details_click(id, e, node){ return } grid.remove(id.row) - calculate_taxes() + //~ calculate_taxes() + calcular_impuestos() } @@ -874,7 +940,8 @@ function grid_details_header_click(id){ callback:function(result){ if (result){ grid.clearAll() - calculate_taxes() + //~ calculate_taxes() + calcular_impuestos() } } }) @@ -1288,7 +1355,8 @@ function refacturar_preinvoice(id){ for(var p of values.rows){ agregar_preproducto(p) } - calculate_taxes() + //~ calculate_taxes() + calcular_impuestos() $$('tv_invoice').getTabbar().setValue('Generar') } }) diff --git a/source/static/js/ui/invoices.js b/source/static/js/ui/invoices.js index edda18f..532fe7f 100644 --- a/source/static/js/ui/invoices.js +++ b/source/static/js/ui/invoices.js @@ -10,7 +10,7 @@ var grid_cfdi_cliente_cols = [ css: 'right'}, {id: 'uuid', header: ['UUID', {content: 'textFilter'}], width: 250, sort: 'string'}, - {id: "fecha", header: ["Fecha y Hora"], width: 150, sort: 'date'}, + {id: "fecha", header: ["Fecha y Hora"], width: 150, sort: 'string'}, {id: "tipo_comprobante", header: ["Tipo", {content: "selectFilter"}], adjust: 'header', sort: 'string'}, {id: "estatus", header: ["Estatus", {content: "selectFilter"}], @@ -202,7 +202,7 @@ var grid_invoices_cols = [ {id: "uuid", header: ["UUID", {content: "textFilter"}], adjust: "data", sort:"string", hidden:true}, {id: "fecha", header: ["Fecha y Hora"], - adjust: "data", sort: "date"}, + adjust: "data", sort: "string"}, {id: "tipo_comprobante", header: ["Tipo", {content: "selectFilter"}], adjust: 'header', sort: 'string'}, {id: "estatus", header: ["Estatus", {content: "selectFilter"}], From 9720bece800ad79e9f6703bdd27087b06d82a045 Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Wed, 13 Dec 2017 00:13:34 -0600 Subject: [PATCH 2/8] Fix - Issue 57 --- source/app/models/main.py | 6 ++++- source/static/js/controller/invoices.js | 29 +++++++++++++++---------- source/static/js/controller/main.js | 1 + 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/source/app/models/main.py b/source/app/models/main.py index 6d60aa8..312a24c 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -2057,7 +2057,11 @@ class Facturas(BaseModel): @classmethod def get_pdf(cls, id, rfc): - emisor = Emisor.select()[0] + try: + emisor = Emisor.select()[0] + except IndexError: + return b'', 'sin_datos_de_emisor.pdf' + obj = Facturas.get(Facturas.id==id) name = '{}{}_{}.pdf'.format(obj.serie, obj.folio, obj.cliente.rfc) if obj.uuid is None: diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js index ed24b7a..e99dc7d 100644 --- a/source/static/js/controller/invoices.js +++ b/source/static/js/controller/invoices.js @@ -82,6 +82,22 @@ function get_regimen_fiscal(){ } +function validar_timbrar(){ + webix.ajax().sync().get('/values/validartimbrar', function(text, data){ + var values = data.json() + if(!values.ok){ + msg_error(values.msg) + $$('cmd_timbrar').disable() + }else{ + if(values.msg){ + msg_error(values.msg) + } + $$('cmd_timbrar').enable() + } + }) +} + + function default_config(){ webix.ajax().sync().get('/values/taxes', function(text, data){ var values = data.json() @@ -96,18 +112,7 @@ function default_config(){ table_pt.clear() table_totals.clear() - webix.ajax().sync().get('/values/validartimbrar', function(text, data){ - var values = data.json() - if(!values.ok){ - msg_error(values.msg) - $$('cmd_timbrar').disable() - }else{ - if(values.msg){ - msg_error(values.msg) - } - $$('cmd_timbrar').enable() - } - }) + validar_timbrar() webix.ajax().sync().get('/values/configtimbrar', function(text, data){ var values = data.json() diff --git a/source/static/js/controller/main.js b/source/static/js/controller/main.js index 7676d07..44e0d23 100644 --- a/source/static/js/controller/main.js +++ b/source/static/js/controller/main.js @@ -199,6 +199,7 @@ function multi_change(prevID, nextID){ if(active == 'invoices_home'){ current_dates() get_invoices() + validar_timbrar() } gi = $$('grid_invoices') return From a0c8b2e336698d0c007bc3f633afe8fe9cd0b734 Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Wed, 13 Dec 2017 01:13:48 -0600 Subject: [PATCH 3/8] Copia local en carpeta compartida --- source/app/controllers/util.py | 52 ++++++++++++++++++++++++++++++++-- source/app/models/main.py | 6 ++-- 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/source/app/controllers/util.py b/source/app/controllers/util.py index 2bb3f65..6b70c91 100644 --- a/source/app/controllers/util.py +++ b/source/app/controllers/util.py @@ -19,6 +19,7 @@ import uuid import zipfile from io import BytesIO +from pathlib import Path from smtplib import SMTPException, SMTPAuthenticationError from xml.etree import ElementTree as ET @@ -35,7 +36,7 @@ from dateutil import parser from .helper import CaseInsensitiveDict, NumLet, SendMail, TemplateInvoice, \ SeaFileAPI -from settings import DEBUG, log, template_lookup, COMPANIES, DB_SAT, \ +from settings import DEBUG, MV, log, template_lookup, COMPANIES, DB_SAT, \ PATH_XSLT, PATH_XSLTPROC, PATH_OPENSSL, PATH_TEMPLATES, PATH_MEDIA, PRE, \ PATH_XMLSEC, TEMPLATE_CANCEL, DEFAULT_SAT_PRODUCTO, DECIMALES @@ -1459,16 +1460,63 @@ def backup_dbs(): return +def _validar_directorios(path_bk, target): + path = Path(_join(path_bk, target)) + path.mkdir(parents=True, exist_ok=True) + return str(path) + + +def local_copy(files): + if not MV: + return + + path_bk = _join(str(Path.home()), 'facturas') + if not os.path.isdir(path_bk): + msg = 'No existe la carpeta: facturas' + log.error(msg) + return + + args = 'df -P ~/facturas | tail -1 | cut -d' ' -f 1' + try: + result = _call(args) + if result != 'empresalibre': + log.info(result) + msg = 'Asegurate de que exista la carpeta para sincronizar' + log.error(msg) + return + except subprocess.CalledProcessError: + msg = 'No se pudo obtener la ruta para sincronizar' + log.error(msg) + return + + try: + for obj, name, target in files: + path = _validar_directorios(path_bk, target) + path_file = _join(path, name) + m = 'wb' + if name.endswith('xml'): + m = 'w' + save_file(path_file, obj, m) + except Exception as e: + log.error(e) + + return + + def sync_cfdi(auth, files): + local_copy(files) + if DEBUG: return + if not auth['REPO']: + return + seafile = SeaFileAPI(SEAFILE_SERVER['URL'], auth['USER'], auth['PASS']) if seafile.is_connect: for f in files: seafile.update_file( f, auth['REPO'], 'Facturas/{}/'.format(f[2]), auth['PASS']) - return diff --git a/source/app/models/main.py b/source/app/models/main.py index 312a24c..9ccccf9 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -2163,17 +2163,15 @@ class Facturas(BaseModel): @classmethod def sync(cls, id, auth): - if not auth['REPO']: - return - obj = Facturas.get(Facturas.id==id) if obj.uuid is None: msg = 'La factura no esta timbrada' return + emisor = Emisor.select()[0] pdf, name_pdf = cls.get_pdf(id, auth['RFC']) name_xml = '{}{}_{}.xml'.format(obj.serie, obj.folio, obj.cliente.rfc) - target = str(obj.fecha)[:7].replace('-', '/') + target = emisor.rfc + '/' + str(obj.fecha)[:7].replace('-', '/') files = ( (obj.xml, name_xml, target), (pdf, name_pdf, target), From 4755223c125edc5f883d9f4a1c9389a7c0f99706 Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Wed, 13 Dec 2017 02:11:14 -0600 Subject: [PATCH 4/8] Fix - al obtener carpeta compartida --- source/app/controllers/util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/app/controllers/util.py b/source/app/controllers/util.py index 6b70c91..db7aa25 100644 --- a/source/app/controllers/util.py +++ b/source/app/controllers/util.py @@ -1476,10 +1476,10 @@ def local_copy(files): log.error(msg) return - args = 'df -P ~/facturas | tail -1 | cut -d' ' -f 1' + args = 'df -P ~/facturas | tail -1 | cut -d" " -f 1' try: result = _call(args) - if result != 'empresalibre': + if result != 'empresalibre\n': log.info(result) msg = 'Asegurate de que exista la carpeta para sincronizar' log.error(msg) From 29ecedda82b3b0a1f3e5da8846ed94979f1f80cf Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Wed, 13 Dec 2017 20:34:38 -0600 Subject: [PATCH 5/8] Configuracion para Linux nativa con Uwsgi --- source/app/main_linux.ini | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/app/main_linux.ini b/source/app/main_linux.ini index 119d72c..3b427d6 100644 --- a/source/app/main_linux.ini +++ b/source/app/main_linux.ini @@ -1,8 +1,10 @@ [uwsgi] -http = 127.0.0.1:8000 +http = :8000 wsgi-file = main.py callable = app master = true processes = 4 threads = 4 - +thunder-lock = true +static-map = /static=../static +logger = file:../../../empresalibre-uwsgi.log From 5acb893cf86ad3e3b632e7bf9b5a5fbb6219eb7c Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Wed, 13 Dec 2017 20:38:31 -0600 Subject: [PATCH 6/8] Statics servidos por Uwsgi en pruebas --- source/app/main.py | 6 +++--- source/app/main_debug.ini | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/source/app/main.py b/source/app/main.py index e30da00..6b1580b 100644 --- a/source/app/main.py +++ b/source/app/main.py @@ -18,7 +18,7 @@ from controllers.main import (AppEmpresas, AppDocumentos, AppFiles, AppPreInvoices, AppCuentasBanco, AppMovimientosBanco ) -from settings import DEBUG +# ~ from settings import DEBUG db = StorageEngine() @@ -51,8 +51,8 @@ api.add_route('/cuentasbanco', AppCuentasBanco(db)) api.add_route('/movbanco', AppMovimientosBanco(db)) -if DEBUG: - api.add_sink(static, '/static') +# ~ if DEBUG: + # ~ api.add_sink(static, '/static') session_options = { diff --git a/source/app/main_debug.ini b/source/app/main_debug.ini index 065dcfc..043ea43 100644 --- a/source/app/main_debug.ini +++ b/source/app/main_debug.ini @@ -6,3 +6,5 @@ master = true processes = 4 threads = 4 py-autoreload = 1 +thunder-lock = true +static-map = /static=../static From 53652804c7f62ceb8bc85e8aaaa329bce77943be Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Wed, 13 Dec 2017 22:33:00 -0600 Subject: [PATCH 7/8] Ajustes en los archivos de ejemplo --- source/app/conf.py.example | 3 +-- source/app/controllers/conf.py.example | 2 +- source/app/main_linux.ini | 5 ++++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/source/app/conf.py.example b/source/app/conf.py.example index 0892815..968da07 100644 --- a/source/app/conf.py.example +++ b/source/app/conf.py.example @@ -10,7 +10,7 @@ DEFAULT_PASSWORD = 'blades3.3' #~ Establece una ruta accesible para el servidor web -LOG_PATH = '/srv/empresa/logs/empresalibre.log' +LOG_PATH = '/srv/empresa/logs/empresa-libre.log' # ~ Establece los valores para sincronizar los backups de la base de datos # ~ por ejemplo @@ -21,5 +21,4 @@ LOG_PATH = '/srv/empresa/logs/empresalibre.log' # ~ 'REPO': 'id_repo', # ~ } - SEAFILE_SERVER = {} \ No newline at end of file diff --git a/source/app/controllers/conf.py.example b/source/app/controllers/conf.py.example index d400ea7..5da41e8 100644 --- a/source/app/controllers/conf.py.example +++ b/source/app/controllers/conf.py.example @@ -1,7 +1,7 @@ #!/usr/bin/env python -DEBUG = True +DEBUG = False #~ Ecodex ID_INTEGRADOR = '' diff --git a/source/app/main_linux.ini b/source/app/main_linux.ini index 3b427d6..e028cb8 100644 --- a/source/app/main_linux.ini +++ b/source/app/main_linux.ini @@ -1,5 +1,8 @@ [uwsgi] http = :8000 +uid = user +gid = user +chdir = /home/USER/.opt/empresa-libre/source/app wsgi-file = main.py callable = app master = true @@ -7,4 +10,4 @@ processes = 4 threads = 4 thunder-lock = true static-map = /static=../static -logger = file:../../../empresalibre-uwsgi.log +logger = file:../../../empresa-libre-uwsgi.log From 4d0d387e19d4ac8886582f8c5cc23c0f388b26fe Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Wed, 13 Dec 2017 23:45:06 -0600 Subject: [PATCH 8/8] Fix - Issue 61 --- source/app/controllers/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/app/controllers/util.py b/source/app/controllers/util.py index db7aa25..db47329 100644 --- a/source/app/controllers/util.py +++ b/source/app/controllers/util.py @@ -1719,7 +1719,7 @@ class ImportFacturaLibre(object): data = [] for row in rows: - new = {t: row[s] for s, t in fields} + new = {t: row[s] for s, t in fields if row[s]} data.append(new) return data