From 4f40bc3bfaf788d7a37766c79c037f24d7365dd5 Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Wed, 10 Feb 2021 22:01:39 -0600 Subject: [PATCH 1/6] Add option for import clients --- source/app/controllers/utils.py | 1 + source/app/models/main.py | 34 ++++++++++++++++++++++++++++++--- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/source/app/controllers/utils.py b/source/app/controllers/utils.py index 2e35df8..79c8870 100644 --- a/source/app/controllers/utils.py +++ b/source/app/controllers/utils.py @@ -18,6 +18,7 @@ import base64 import collections +import csv import datetime import getpass import json diff --git a/source/app/models/main.py b/source/app/models/main.py index 908b16d..b4b63e7 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -10505,6 +10505,30 @@ def _migrate_cert(rfc): return +def _import_clients(rfc, path): + if not rfc: + msg = 'El RFC es requerido' + log.error(msg) + return + + if not path: + msg = 'El archivo CSV es necesario' + log.error(msg) + return + + args = util.get_con(rfc) + if not args: + return + + conectar(args) + log.info('Importando clientes...') + + + desconectar() + log.info('Proceso terminado correctamente...') + return + + def _test(rfc): if not rfc: rfc = input('Introduce el RFC: ').strip().upper() @@ -10529,8 +10553,6 @@ def _test(rfc): return - - def _process_command_line_arguments(): parser = argparse.ArgumentParser( description='Empresa Libre') @@ -10554,11 +10576,13 @@ def _process_command_line_arguments(): action='store_true', default=False, required=False) parser.add_argument('-ed', '--export-documents', dest='export_documents', action='store_true', default=False, required=False) - + parser.add_argument('-ic', '--import-clients', dest='import_clients', + action='store_true', default=False, required=False) parser.add_argument('-mc' , '--migrate-cert', dest='migrate_cert', action='store_true', default=False, required=False) parser.add_argument('-r', '--rfc', dest='rfc', default='') + parser.add_argument('-f', '--file', dest='file', default='') return parser.parse_args() @@ -10603,6 +10627,10 @@ def main(args): _migrate_cert(args.rfc) return + if args.import_clients: + _import_clients(args.rfc, args.file) + return + # ~ _test(args.rfc) return From aa682255715a2b5b522bb67c5505369dc516480c Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Wed, 10 Feb 2021 22:34:34 -0600 Subject: [PATCH 2/6] Read csv to dict --- source/app/controllers/utils.py | 19 +++++++++++++++++++ source/app/models/main.py | 20 ++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/source/app/controllers/utils.py b/source/app/controllers/utils.py index 79c8870..761effc 100644 --- a/source/app/controllers/utils.py +++ b/source/app/controllers/utils.py @@ -31,6 +31,7 @@ import smtplib import sqlite3 import subprocess import threading +import unicodedata import zipfile from pathlib import Path from xml.sax.saxutils import escape @@ -760,3 +761,21 @@ def get_status_sat(xml): return node.text +def spaces(value): + return '\n'.join([' '.join(l.split()) for l in value.split('\n')]) + + +def to_slug(string): + value = (unicodedata.normalize('NFKD', string) + .encode('ascii', 'ignore') + .decode('ascii').lower()) + return value.replace(' ', '_') + + +def read_csv(path, args={'delimiter': '|'}): + with open(path) as f: + reader = csv.DictReader(f, **args) + # ~ rows = tuple(csv.reader(f, **args)) + rows = [r for r in reader] + return rows + diff --git a/source/app/models/main.py b/source/app/models/main.py index b4b63e7..ec89a05 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -10523,6 +10523,26 @@ def _import_clients(rfc, path): conectar(args) log.info('Importando clientes...') + data = utils.read_csv(path) + for i, row in enumerate(data): + if i == 0: + continue + print(row) + + # ~ w = ((Socios.rfc==fields['rfc']) & (Socios.slug==fields['slug'])) + # ~ if Socios.select().where(w).exists(): + # ~ msg = 'Ya existe el RFC y Razón Social' + # ~ data = {'ok': False, 'row': {}, 'new': True, 'msg': msg} + # ~ return data + + # ~ try: + # ~ obj = Socios.create(**fields) + # ~ except IntegrityError as e: + # ~ msg = 'Ocurrio un error, al dar de alta el emisor' + # ~ data = {'ok': False, 'row': {}, 'new': True, 'msg': msg} + # ~ return data + + break desconectar() log.info('Proceso terminado correctamente...') From 892906c46fd93a7eb251dd154acd048b0fc9e89d Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Wed, 10 Feb 2021 22:47:00 -0600 Subject: [PATCH 3/6] Import clients --- source/app/controllers/utils.py | 1 - source/app/models/main.py | 33 ++++++++++++++++++--------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/source/app/controllers/utils.py b/source/app/controllers/utils.py index 761effc..e8f3996 100644 --- a/source/app/controllers/utils.py +++ b/source/app/controllers/utils.py @@ -775,7 +775,6 @@ def to_slug(string): def read_csv(path, args={'delimiter': '|'}): with open(path) as f: reader = csv.DictReader(f, **args) - # ~ rows = tuple(csv.reader(f, **args)) rows = [r for r in reader] return rows diff --git a/source/app/models/main.py b/source/app/models/main.py index ec89a05..ef82520 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -10524,25 +10524,28 @@ def _import_clients(rfc, path): log.info('Importando clientes...') data = utils.read_csv(path) + t = len(data) for i, row in enumerate(data): - if i == 0: + msg = f'\tImportando cliente {i+1} de {t}' + log.info(msg) + row['rfc'] = row['rfc'].upper() + row['nombre'] = utils.spaces(row['nombre']) + row['slug'] = utils.to_slug(row['nombre']) + + w = ((Socios.rfc==row['rfc']) & (Socios.slug==row['slug'])) + if Socios.select().where(w).exists(): + msg = 'Ya existe el RFC y Razón Social' + log.info(msg) continue - print(row) - # ~ w = ((Socios.rfc==fields['rfc']) & (Socios.slug==fields['slug'])) - # ~ if Socios.select().where(w).exists(): - # ~ msg = 'Ya existe el RFC y Razón Social' - # ~ data = {'ok': False, 'row': {}, 'new': True, 'msg': msg} - # ~ return data + row['es_cliente'] = True + row['forma_pago'] = SATFormaPago.get(SATFormaPago.id==row['forma_pago']) - # ~ try: - # ~ obj = Socios.create(**fields) - # ~ except IntegrityError as e: - # ~ msg = 'Ocurrio un error, al dar de alta el emisor' - # ~ data = {'ok': False, 'row': {}, 'new': True, 'msg': msg} - # ~ return data - - break + try: + obj = Socios.create(**row) + except Exception as e: + log.error(e) + break desconectar() log.info('Proceso terminado correctamente...') From 66d740e8d11adcb5c7689efe28c9b3e482c5fb6b Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Wed, 10 Feb 2021 22:50:05 -0600 Subject: [PATCH 4/6] Report log --- source/app/models/main.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/source/app/models/main.py b/source/app/models/main.py index ef82520..f51541e 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -10525,6 +10525,7 @@ def _import_clients(rfc, path): data = utils.read_csv(path) t = len(data) + ok = 0 for i, row in enumerate(data): msg = f'\tImportando cliente {i+1} de {t}' log.info(msg) @@ -10534,7 +10535,7 @@ def _import_clients(rfc, path): w = ((Socios.rfc==row['rfc']) & (Socios.slug==row['slug'])) if Socios.select().where(w).exists(): - msg = 'Ya existe el RFC y Razón Social' + msg = '\tYa existe el RFC y Razón Social' log.info(msg) continue @@ -10543,11 +10544,16 @@ def _import_clients(rfc, path): try: obj = Socios.create(**row) + ok += 1 except Exception as e: log.error(e) break desconectar() + msg = f'Total de clientes: {t}' + log.info(msg) + msg = f'Clientes nuevos: {ok}' + log.info(msg) log.info('Proceso terminado correctamente...') return From 720c9f0cb18a25e729e5c2ba5d7ff2a6f7e87b9c Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Wed, 10 Feb 2021 22:54:16 -0600 Subject: [PATCH 5/6] Update changelog and version --- CHANGELOG.md | 5 +++++ VERSION | 2 +- source/app/settings.py | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3848322..9491439 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +v 1.41.0 [10-Feb-2021] +---------------------- + - Importar clientes desde archivo CSV + + v 1.40.1 [09-Feb-2021] ---------------------- - Fix #422 diff --git a/VERSION b/VERSION index 53a714f..7d47e59 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.40.1 +1.41.0 diff --git a/source/app/settings.py b/source/app/settings.py index a23ca65..1fd9946 100644 --- a/source/app/settings.py +++ b/source/app/settings.py @@ -42,7 +42,7 @@ except ImportError: DEBUG = DEBUG -VERSION = '1.40.1' +VERSION = '1.41.0' EMAIL_SUPPORT = ('soporte@empresalibre.mx',) TITLE_APP = '{} v{}'.format(TITLE_APP, VERSION) From 4f71dd0de3ce78f83e994cab1f98f79d895491e9 Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Wed, 10 Feb 2021 23:01:30 -0600 Subject: [PATCH 6/6] Clean review methods --- source/app/models/main.py | 409 -------------------------------------- 1 file changed, 409 deletions(-) diff --git a/source/app/models/main.py b/source/app/models/main.py index f51541e..8f82824 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -9797,389 +9797,6 @@ def _importar_valores(archivo='', rfc=''): return -# ~ def _importar_socios(rows): - # ~ log.info('\tImportando Clientes...') - # ~ totals = len(rows) - # ~ for i, row in enumerate(rows): - # ~ msg = '\tGuardando cliente {} de {}'.format(i+1, totals) - # ~ log.info(msg) - # ~ try: - # ~ with database_proxy.atomic() as txn: - # ~ Socios.create(**row) - # ~ except IntegrityError: - # ~ msg = '\tSocio existente: {}'.format(row['nombre']) - # ~ log.info(msg) - # ~ log.info('\tClientes importados...') - # ~ return - - -# ~ def _existe_factura(row): - # ~ filtro = (Facturas.uuid==row['uuid']) - # ~ if row['uuid'] is None: - # ~ filtro = ( - # ~ (Facturas.serie==row['serie']) & - # ~ (Facturas.folio==row['folio']) - # ~ ) - # ~ return Facturas.select().where(filtro).exists() - - -# ~ def _importar_facturas(rows): - # ~ log.info('\tImportando Facturas...') - # ~ totals = len(rows) - # ~ for i, row in enumerate(rows): - # ~ msg = '\tGuardando factura {} de {}'.format(i+1, totals) - # ~ log.info(msg) - - # ~ try: - # ~ detalles = row.pop('detalles') - # ~ impuestos = row.pop('impuestos') - # ~ cliente = row.pop('cliente') - # ~ row['cliente'] = Socios.get(**cliente) - # ~ with database_proxy.atomic() as txn: - # ~ if _existe_factura(row): - # ~ msg = '\tFactura existente: {}{}'.format( - # ~ row['serie'], row['folio']) - # ~ log.info(msg) - # ~ continue - # ~ obj = Facturas.create(**row) - # ~ for detalle in detalles: - # ~ detalle['factura'] = obj - # ~ FacturasDetalle.create(**detalle) - # ~ for impuesto in impuestos: - # ~ imp = SATImpuestos.get(**impuesto['filtro']) - # ~ new = { - # ~ 'factura': obj, - # ~ 'impuesto': imp, - # ~ 'importe': impuesto['importe'], - # ~ } - # ~ try: - # ~ with database_proxy.atomic() as txn: - # ~ FacturasImpuestos.create(**new) - # ~ except IntegrityError as e: - # ~ pass - - # ~ except IntegrityError as e: - # ~ print (e) - # ~ msg = '\tFactura: id: {}'.format(row['serie'] + str(row['folio'])) - # ~ log.error(msg) - # ~ break - - # ~ log.info('\tFacturas importadas...') - # ~ return - - -# ~ def _importar_categorias(rows): - # ~ log.info('\tImportando Categorías...') - # ~ for row in rows: - # ~ with database_proxy.atomic() as txn: - # ~ try: - # ~ Categorias.create(**row) - # ~ except IntegrityError: - # ~ msg = '\tCategoria: ({}) {}'.format(row['padre'], row['categoria']) - # ~ log.error(msg) - - # ~ log.info('\tCategorías importadas...') - # ~ return - - -# ~ def _get_id_unidad(unidad): - # ~ try: - # ~ if 'pieza' in unidad.lower(): - # ~ unidad = 'pieza' - # ~ if 'metros' in unidad.lower(): - # ~ unidad = 'metro' - # ~ if 'tramo' in unidad.lower(): - # ~ unidad = 'paquete' - # ~ if 'juego' in unidad.lower(): - # ~ unidad = 'par' - # ~ if 'bolsa' in unidad.lower(): - # ~ unidad = 'globo' - # ~ if unidad.lower() == 'no aplica': - # ~ unidad = 'servicio' - - # ~ obj = SATUnidades.get(SATUnidades.name.contains(unidad)) - # ~ except SATUnidades.DoesNotExist: - # ~ msg = '\tNo se encontró la unidad: {}'.format(unidad) - # ~ return unidad - - # ~ return str(obj.id) - - -# ~ def _get_impuestos(impuestos): - # ~ lines = '|' - # ~ for impuesto in impuestos: - # ~ if impuesto['tasa'] == '-2/3': - # ~ tasa = str(round(2/3, 6)) - # ~ else: - # ~ if impuesto['tasa'] == 'EXENTO': - # ~ tasa = '0.00' - # ~ else: - # ~ tasa = str(round(float(impuesto['tasa']) / 100.0, 6)) - - # ~ info = ( - # ~ IMPUESTOS.get(impuesto['nombre']), - # ~ impuesto['nombre'], - # ~ impuesto['tipo'][0], - # ~ tasa, - # ~ ) - # ~ lines += '|'.join(info) + '|' - # ~ return lines - - -# ~ def _generar_archivo_productos(archivo): - # ~ 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('Importando datos...') - # ~ app = util.ImportFacturaLibre(archivo, rfc) - # ~ if not app.is_connect: - # ~ log.error('\t{}'.format(app._error)) - # ~ return - - # ~ rows = app.import_productos() - - # ~ p, _, _, _ = util.get_path_info(archivo) - # ~ path_txt = util._join(p, 'productos_{}.txt'.format(rfc)) - # ~ log.info('\tGenerando archivo: {}'.format(path_txt)) - - # ~ fields = ( - # ~ 'clave', - # ~ 'clave_sat', - # ~ 'unidad', - # ~ 'categoria', - # ~ 'descripcion', - # ~ 'valor_unitario', - # ~ 'existencia', - # ~ 'inventario', - # ~ 'codigo_barras', - # ~ 'cuenta_predial', - # ~ 'ultimo_precio', - # ~ 'minimo', - # ~ ) - - # ~ data = ['|'.join(fields)] - # ~ not_units = [] - # ~ for row in rows: - # ~ impuestos = row.pop('impuestos', ()) - # ~ line = [str(row[r]) for r in fields] - # ~ if line[10] == 'None': - # ~ line[10] = '0.0' - # ~ line[2] = _get_id_unidad(line[2]) - # ~ try: - # ~ int(line[2]) - # ~ except ValueError: - # ~ if not line[2] in not_units: - # ~ not_units.append(line[2]) - # ~ msg = 'No se encontró la unidad: {}'.format(line[2]) - # ~ log.error(msg) - # ~ continue - # ~ line = '|'.join(line) + _get_impuestos(impuestos) - # ~ data.append(line) - - # ~ with open(path_txt, 'w') as fh: - # ~ fh.write('\n'.join(data)) - - # ~ log.info('\tArchivo generado: {}'.format(path_txt)) - # ~ return - - -# ~ def importar_bdfl(): - # ~ try: - # ~ emisor = Emisor.select()[0] - # ~ except IndexError: - # ~ msg = 'Configura primero al emisor' - # ~ return {'ok': False, 'msg': msg} - - # ~ name = '{}.sqlite'.format(emisor.rfc.lower()) - # ~ path = util._join('/tmp', name) - - # ~ log.info('Importando datos...') - # ~ app = util.ImportFacturaLibre(path, emisor.rfc) - # ~ if not app.is_connect: - # ~ msg = app._error - # ~ log.error('\t{}'.format(msg)) - # ~ return {'ok': False, 'msg': msg} - - # ~ data = app.import_data() - - # ~ _importar_socios(data['Socios']) - # ~ _importar_facturas(data['Facturas']) - # ~ _importar_categorias(data['Categorias']) - - # ~ msg = 'Importación terminada...' - # ~ log.info(msg) - - # ~ return {'ok': True, 'msg': msg} - - -# ~ def _importar_factura_libre(archivo): - # ~ 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('Importando datos...') - # ~ app = util.ImportFacturaLibre(archivo, rfc) - # ~ if not app.is_connect: - # ~ log.error('\t{}'.format(app._error)) - # ~ return - - # ~ data = app.import_data() - - # ~ _importar_socios(data['Socios']) - # ~ _importar_facturas(data['Facturas']) - # ~ _importar_categorias(data['Categorias']) - - # ~ log.info('Importación terminada...') - # ~ return - - -# ~ def _exist_ticket(row): - # ~ filters = ( - # ~ (Tickets.serie==row['serie']) & - # ~ (Tickets.folio==row['folio']) - # ~ ) - # ~ return Tickets.select().where(filters).exists() - - -# ~ def _import_tickets(rows): - # ~ log.info('\tImportando Tickets...') - # ~ for row in rows: - # ~ try: - # ~ details = row.pop('details') - # ~ taxes = row.pop('taxes') - # ~ with database_proxy.atomic() as txn: - # ~ if _exist_ticket(row): - # ~ msg = '\tTicket existente: {}{}'.format( - # ~ row['serie'], row['folio']) - # ~ log.info(msg) - # ~ continue - - # ~ if not row['factura'] is None and row['factura']: - # ~ row['factura'] = Facturas.get( - # ~ Facturas.serie==row['factura']['serie'], - # ~ Facturas.folio==row['factura']['folio']) - # ~ else: - # ~ row['factura'] = None - - # ~ obj = Tickets.create(**row) - # ~ for detail in details: - # ~ detail['ticket'] = obj - # ~ TicketsDetalle.create(**detail) - # ~ for tax in taxes: - # ~ imp = SATImpuestos.get(**tax['filter']) - # ~ new = { - # ~ 'ticket': obj, - # ~ 'impuesto': imp, - # ~ 'importe': tax['import'], - # ~ } - # ~ TicketsImpuestos.create(**new) - # ~ except IntegrityError as e: - # ~ print (e) - # ~ msg = '\tTicket: id: {}'.format(row['serie'] + str(row['folio'])) - # ~ log.error(msg) - - # ~ log.info('\tTickets importadas...') - # ~ return - - -# ~ def _importar_productos(archivo): - # ~ 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('Importando productos...') - - # ~ fields = ( - # ~ 'clave', - # ~ 'clave_sat', - # ~ 'unidad', - # ~ 'categoria', - # ~ 'descripcion', - # ~ 'valor_unitario', - # ~ 'existencia', - # ~ 'inventario', - # ~ 'codigo_barras', - # ~ 'cuenta_predial', - # ~ 'ultimo_precio', - # ~ 'minimo', - # ~ ) - - # ~ rows = util.read_file(archivo, 'r').split('\n') - # ~ for i, row in enumerate(rows): - # ~ if i == 0: - # ~ continue - # ~ data = row.split('|') - # ~ print (data) - # ~ new = {} - # ~ for i, f in enumerate(fields): - # ~ if not len(data[0]): - # ~ continue - - # ~ if i in (2, 3): - # ~ try: - # ~ new[f] = int(data[i]) - # ~ except ValueError: - # ~ continue - # ~ elif i in (5, 6, 10, 11): - # ~ new[f] = float(data[i]) - # ~ elif i == 7: - # ~ new[f] = bool(data[i]) - # ~ else: - # ~ new[f] = data[i] - - # ~ impuestos = data[i + 1:-1] - # ~ if not impuestos: - # ~ taxes = [SATImpuestos.select().where(SATImpuestos.id==6)] - # ~ else: - # ~ taxes = [] - # ~ try: - # ~ for i in range(0, len(impuestos), 4): - # ~ w = { - # ~ 'key': impuestos[i], - # ~ 'name': impuestos[i+1], - # ~ 'tipo': impuestos[i+2], - # ~ 'tasa': float(impuestos[i+3]), - # ~ } - # ~ taxes.append(SATImpuestos.get_o_crea(w)) - # ~ except IndexError: - # ~ print ('IE', data) - # ~ continue - - # ~ with database_proxy.transaction(): - # ~ try: - # ~ obj = Productos.create(**new) - # ~ obj.impuestos = taxes - # ~ except IntegrityError as e: - # ~ pass - - # ~ log.info('Importación terminada...') - # ~ return - - def _import_from_folder(path): files = util.get_files(path, 'json') if not files: @@ -10558,30 +10175,6 @@ def _import_clients(rfc, path): return -def _test(rfc): - if not rfc: - 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('Test con...') - - query = Socios.select(Socios.id, Socios.nombre, Socios.rfc).where(Socios.nombre.contains('Lopez')) - print(query) - - desconectar() - log.info('End test...') - return - - def _process_command_line_arguments(): parser = argparse.ArgumentParser( description='Empresa Libre') @@ -10660,8 +10253,6 @@ def main(args): _import_clients(args.rfc, args.file) return - # ~ _test(args.rfc) - return