Import clients form CSV

This commit is contained in:
Mauricio Baeza 2021-02-10 23:02:12 -06:00
commit b5bdcbbeb3
5 changed files with 69 additions and 397 deletions

View File

@ -1,3 +1,8 @@
v 1.41.0 [10-Feb-2021]
----------------------
- Importar clientes desde archivo CSV
v 1.40.1 [09-Feb-2021] v 1.40.1 [09-Feb-2021]
---------------------- ----------------------
- Fix #422 - Fix #422

View File

@ -1 +1 @@
1.40.1 1.41.0

View File

@ -18,6 +18,7 @@
import base64 import base64
import collections import collections
import csv
import datetime import datetime
import getpass import getpass
import json import json
@ -30,6 +31,7 @@ import smtplib
import sqlite3 import sqlite3
import subprocess import subprocess
import threading import threading
import unicodedata
import zipfile import zipfile
from pathlib import Path from pathlib import Path
from xml.sax.saxutils import escape from xml.sax.saxutils import escape
@ -759,3 +761,20 @@ def get_status_sat(xml):
return node.text 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 = [r for r in reader]
return rows

View File

@ -9797,389 +9797,6 @@ def _importar_valores(archivo='', rfc=''):
return 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): def _import_from_folder(path):
files = util.get_files(path, 'json') files = util.get_files(path, 'json')
if not files: if not files:
@ -10505,32 +10122,59 @@ def _migrate_cert(rfc):
return return
def _test(rfc): def _import_clients(rfc, path):
if not rfc:
rfc = input('Introduce el RFC: ').strip().upper()
if not rfc: if not rfc:
msg = 'El RFC es requerido' msg = 'El RFC es requerido'
log.error(msg) log.error(msg)
return return
if not path:
msg = 'El archivo CSV es necesario'
log.error(msg)
return
args = util.get_con(rfc) args = util.get_con(rfc)
if not args: if not args:
return return
conectar(args) conectar(args)
log.info('Test con...') log.info('Importando clientes...')
query = Socios.select(Socios.id, Socios.nombre, Socios.rfc).where(Socios.nombre.contains('Lopez')) data = utils.read_csv(path)
print(query) t = len(data)
ok = 0
for i, row in enumerate(data):
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 = '\tYa existe el RFC y Razón Social'
log.info(msg)
continue
row['es_cliente'] = True
row['forma_pago'] = SATFormaPago.get(SATFormaPago.id==row['forma_pago'])
try:
obj = Socios.create(**row)
ok += 1
except Exception as e:
log.error(e)
break
desconectar() desconectar()
log.info('End test...') msg = f'Total de clientes: {t}'
log.info(msg)
msg = f'Clientes nuevos: {ok}'
log.info(msg)
log.info('Proceso terminado correctamente...')
return return
def _process_command_line_arguments(): def _process_command_line_arguments():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description='Empresa Libre') description='Empresa Libre')
@ -10554,11 +10198,13 @@ def _process_command_line_arguments():
action='store_true', default=False, required=False) action='store_true', default=False, required=False)
parser.add_argument('-ed', '--export-documents', dest='export_documents', parser.add_argument('-ed', '--export-documents', dest='export_documents',
action='store_true', default=False, required=False) 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', parser.add_argument('-mc' , '--migrate-cert', dest='migrate_cert',
action='store_true', default=False, required=False) action='store_true', default=False, required=False)
parser.add_argument('-r', '--rfc', dest='rfc', default='') parser.add_argument('-r', '--rfc', dest='rfc', default='')
parser.add_argument('-f', '--file', dest='file', default='')
return parser.parse_args() return parser.parse_args()
@ -10603,7 +10249,9 @@ def main(args):
_migrate_cert(args.rfc) _migrate_cert(args.rfc)
return return
# ~ _test(args.rfc) if args.import_clients:
_import_clients(args.rfc, args.file)
return
return return

View File

@ -42,7 +42,7 @@ except ImportError:
DEBUG = DEBUG DEBUG = DEBUG
VERSION = '1.40.1' VERSION = '1.41.0'
EMAIL_SUPPORT = ('soporte@empresalibre.mx',) EMAIL_SUPPORT = ('soporte@empresalibre.mx',)
TITLE_APP = '{} v{}'.format(TITLE_APP, VERSION) TITLE_APP = '{} v{}'.format(TITLE_APP, VERSION)