Check unique clients

This commit is contained in:
Mauricio Baeza 2020-01-11 22:14:38 -06:00
parent e1b7e525df
commit aeb045e76a
7 changed files with 307 additions and 658 deletions

View File

@ -1,3 +1,15 @@
v 1.33.0 [11-ene-2020]
----------------------
- Mejora: Cambio del mensaje para cuando se intenta dar de alta un cliente ya existente.
- Mejora: Solo los admins pueden ver la nómina.
v 1.32.0 [05-ene-2020]
----------------------
- Mejora: Recuperar facturas no aceptas para cancelación por el receptor
v 1.31.2 [28-oct-2019]
----------------------
- Error: Al generar PDF con tags en las series

View File

@ -1 +1 @@
1.32.0
1.33.0

View File

@ -2738,521 +2738,6 @@ def sync_cfdi(auth, files):
return
class ImportFacturaLibreGambas(object):
def __init__(self, conexion, rfc):
self._rfc = rfc
self._con = None
self._cursor = None
self._error = ''
self._is_connect = self._connect(conexion)
self._clientes = []
self._clientes_rfc = []
@property
def error(self):
return self._error
@property
def is_connect(self):
return self._is_connect
def _validate_rfc(self):
sql = "SELECT rfc FROM emisor LIMIT 1"
self._cursor.execute(sql)
obj = self._cursor.fetchone()
if obj is None:
self._error = 'No se encontró al emisor: {}'.format(self._rfc)
return False
if not DEBUG:
if obj['rfc'] != self._rfc:
self._error = 'Los datos no corresponden al RFC: {}'.format(self._rfc)
return False
return True
def _connect(self, conexion):
import psycopg2
import psycopg2.extras
try:
self._con = psycopg2.connect(conexion)
self._cursor = self._con.cursor(cursor_factory=psycopg2.extras.DictCursor)
return self._validate_rfc()
except Exception as e:
log.error(e)
self._error = 'No se pudo conectar a la base de datos'
return False
def close(self):
try:
self._cursor.close()
self._con.close()
except:
pass
return
def import_data(self):
data = {}
tables = (
('receptores', 'Socios'),
('cfdifacturas', 'Facturas'),
('categorias', 'Categorias'),
('productos', 'Productos'),
('tickets', 'Tickets'),
)
for source, target in tables:
data[target] = self._get_table(source)
data['Socios'] += self._clientes
return data
def _get_table(self, table):
return getattr(self, '_{}'.format(table))()
def _tickets(self):
sql = "SELECT * FROM tickets"
self._cursor.execute(sql)
rows = self._cursor.fetchall()
fields = (
('serie', 'serie'),
('folio', 'folio'),
('fecha', 'fecha'),
('formadepago', 'forma_pago'),
('subtotal', 'subtotal'),
('descuento', 'descuento'),
('total', 'total'),
('notas', 'notas'),
('factura', 'factura'),
('cancelada', 'cancelado'),
('vendedor', 'vendedor'),
)
data = []
totals = len(rows)
for i, row in enumerate(rows):
msg = '\tImportando ticket {} de {}'.format(i+1, totals)
log.info(msg)
new = {t: row[s] for s, t in fields}
new['notas'] = ''
new['fecha'] = new['fecha'].replace(microsecond=0)
new['estatus'] = 'Generado'
if new['cancelado']:
new['estatus'] = 'Cancelado'
new['factura'] = self._get_invoice_ticket(new['factura'])
new['details'] = self._get_details_ticket(row['id'])
new['taxes'] = self._get_taxes_ticket(row['id'])
data.append(new)
return data
def _get_invoice_ticket(self, invoice):
if not invoice:
return None
sql = "SELECT serie, folio FROM cfdifacturas WHERE id=%s"
self._cursor.execute(sql, [invoice])
row = self._cursor.fetchone()
if row is None:
return {}
return dict(row)
def _get_details_ticket(self, id):
sql = "SELECT * FROM t_detalle WHERE id_cfdi=%s"
self._cursor.execute(sql, [id])
rows = self._cursor.fetchall()
fields = (
('descripcion', 'descripcion'),
('cantidad', 'cantidad'),
('valorunitario', 'valor_unitario'),
('importe', 'importe'),
('precio', 'precio_final'),
)
data = []
for row in rows:
new = {t: row[s] for s, t in fields if row[s]}
data.append(new)
return data
def _get_taxes_ticket(self, id):
sql = "SELECT * FROM t_impuestos WHERE id_cfdi=%s"
self._cursor.execute(sql, [id])
rows = self._cursor.fetchall()
tasas = {
'0': 0.0,
'16': 0.16,
'16.00': 0.16,
'0.16': 0.16,
'11': 0.11,
'-10': 0.10,
'-2': 0.02,
'-0.5': 0.005,
'-2/3': 0.106667,
'-10.6667': 0.106667,
'-10.6666': 0.106667,
'-10.666666': 0.106667,
'-10.66660': 0.106667,
}
data = []
for row in rows:
filtro = {
'name': row['impuesto'],
'tasa': tasas[row['tasa']],
'tipo': row['tipo'][0],
}
new = {
'import': row['importe'],
'filter': filtro
}
data.append(new)
return data
def _productos(self):
UNIDADES = {
'k': 'KGM',
'kg': 'KGM',
'kg.': 'KGM',
'pieza': 'H87',
'pza': 'H87',
'pz': 'H87',
'bulto': 'H87',
'b': 'H87',
'exb': 'H87',
'ex': 'H87',
'caja': 'XBX',
'c': 'XBX',
'rollo': 'XRO',
'tira': 'SR',
't': 'SR',
'cono': 'XAJ',
'paquete': 'XPK',
'pq': 'XPK',
}
sql = "SELECT * FROM productos"
self._cursor.execute(sql)
rows = self._cursor.fetchall()
fields = (
('id_categoria', 'categoria'),
('noidentificacion', 'clave'),
('descripcion', 'descripcion'),
# ~ ('unidad', 'unidad'),
('id_unidad', 'unidad'),
# ~ ('costo', 'ultimo_costo'),
('valorunitario', 'valor_unitario'),
# ~ ('existencia', 'existencia'),
# ~ ('minimo', 'minimo'),
('inventario', 'inventario'),
('codigobarras', 'codigo_barras'),
('cuentapredial', 'cuenta_predial'),
)
data = []
sql = """
SELECT nombre, tasa, tipo
FROM impuestos
WHERE id=%s
"""
totals = len(rows)
for i, row in enumerate(rows):
msg = '\tImportando producto {} de {}'.format(i+1, totals)
log.info(msg)
# ~ print (i, dict(row))
new = {t: row[s] for s, t in fields}
# ~ print (new['unidad'])
if new['unidad'] == 2:
new['unidad'] = 'servicio'
u = new['unidad'].lower().strip()
if u in ('sin',):
continue
if not u:
u = 'pieza'
if not new['categoria']:
new['categoria'] = None
new['codigo_barras'] = new['codigo_barras'] or ''
new['cuenta_predial'] = new['cuenta_predial'] or ''
new['descripcion'] = ' '.join(new['descripcion'].split())
new['clave_sat'] = DEFAULT_SAT_PRODUCTO
new['unidad'] = UNIDADES.get(u, new['unidad'])
self._cursor.execute(sql, [row['id_impuesto1']])
impuestos = self._cursor.fetchall()
new['impuestos'] = tuple(impuestos)
data.append(new)
return data
def _categorias(self):
sql = "SELECT * FROM categorias ORDER BY id_padre"
self._cursor.execute(sql)
rows = self._cursor.fetchall()
fields = (
('id', 'id'),
('categoria', 'categoria'),
('id_padre', 'padre'),
)
data = []
for row in rows:
new = {t: row[s] for s, t in fields}
if new['padre'] == 0:
new['padre'] = None
data.append(new)
return data
def _get_cliente(self, invoice):
sql = "SELECT rfc, nombre FROM receptores WHERE id=%s"
self._cursor.execute(sql, [invoice['id_cliente']])
obj = self._cursor.fetchone()
if not obj is None:
data = {
'rfc': obj['rfc'],
'slug': to_slug(obj['nombre']),
}
return data
if not invoice['xml']:
return {}
doc = parse_xml(invoice['xml'])
version = doc.attrib['version']
node = doc.find('{}Receptor'.format(PRE[version]))
rfc = node.attrib['rfc']
nombre = node.attrib['nombre']
tipo_persona = 1
if rfc == 'XEXX010101000':
tipo_persona = 4
elif rfc == 'XAXX010101000':
tipo_persona = 3
elif len(rfc) == 12:
tipo_persona = 2
data = {
'tipo_persona': tipo_persona,
'rfc': rfc,
'nombre': nombre,
'slug': to_slug(nombre),
'es_cliente': True,
'es_activo': False,
}
if not rfc in self._clientes_rfc:
self._clientes_rfc.append(rfc)
self._clientes.append(data)
data = {
'rfc': data['rfc'],
'slug': data['slug'],
}
return data
def _get_detalles(self, id):
sql = "SELECT * FROM cfdidetalle WHERE id_cfdi=%s"
self._cursor.execute(sql, [id])
rows = self._cursor.fetchall()
fields = (
('categoria', 'categoria'),
('cantidad', 'cantidad'),
('unidad', 'unidad'),
('noidentificacion', 'clave'),
('descripcion', 'descripcion'),
('valorunitario', 'valor_unitario'),
('importe', 'importe'),
('numero', 'pedimento'),
('fecha', 'fecha_pedimento'),
('aduana', 'aduana'),
('cuentapredial', 'cuenta_predial'),
('descuento', 'descuento'),
('precio', 'precio_final'),
)
data = []
for row in rows:
new = {t: row[s] for s, t in fields if row[s]}
data.append(new)
return data
def _get_impuestos(self, id):
sql = "SELECT * FROM cfdiimpuestos WHERE id_cfdi=%s"
self._cursor.execute(sql, [id])
rows = self._cursor.fetchall()
tasas = {
'0': 0.0,
'16': 0.16,
'16.00': 0.16,
'11': 0.11,
'-10': 0.10,
'-2': 0.02,
'-0.5': 0.005,
'-2/3': 0.106667,
'-10.6666': 0.106667,
'-10.666666': 0.106667,
'-10.66660': 0.106667,
'-4': 0.04,
}
data = []
for row in rows:
filtro = {
'name': row['impuesto'],
'tasa': tasas[row['tasa']],
'tipo': row['tipo'][0],
}
new = {
'importe': row['importe'],
'filtro': filtro
}
data.append(new)
return data
def _cfdifacturas(self):
sql = "SELECT * FROM cfdifacturas"
self._cursor.execute(sql)
rows = self._cursor.fetchall()
fields = (
('version', 'version'),
('serie', 'serie'),
('folio', 'folio'),
('fecha', 'fecha'),
('fecha_timbrado', 'fecha_timbrado'),
('formadepago', 'forma_pago'),
('condicionesdepago', 'condiciones_pago'),
('subtotal', 'subtotal'),
('descuento', 'descuento'),
('tipocambio', 'tipo_cambio'),
('moneda', 'moneda'),
('total', 'total'),
('tipodecomprobante', 'tipo_comprobante'),
('metododepago', 'metodo_pago'),
('lugarexpedicion', 'lugar_expedicion'),
('totalimpuestosretenidos', 'total_retenciones'),
('totalimpuestostrasladados', 'total_traslados'),
('xml', 'xml'),
('id_cliente', 'cliente'),
('notas', 'notas'),
('uuid', 'uuid'),
('cancelada', 'cancelada'),
)
data = []
totals = len(rows)
for i, row in enumerate(rows):
msg = '\tImportando factura {} de {}'.format(i+1, totals)
log.info(msg)
new = {t: row[s] for s, t in fields}
for _, f in fields:
new[f] = new[f] or ''
new['fecha'] = new['fecha'].replace(microsecond=0)
if new['fecha_timbrado']:
new['fecha_timbrado'] = new['fecha_timbrado'].replace(microsecond=0)
else:
new['fecha_timbrado'] = None
new['estatus'] = 'Timbrada'
if new['cancelada']:
new['estatus'] = 'Cancelada'
if not new['uuid']:
new['uuid'] = None
elif new['uuid'] in('ok', '123', '??', 'X'):
new['uuid'] = None
new['estatus'] = 'Cancelada'
new['cancelada'] = True
if new['xml'] is None:
new['xml'] = ''
new['pagada'] = True
new['total_mn'] = round(row['tipocambio'] * row['total'], 2)
new['detalles'] = self._get_detalles(row['id'])
new['impuestos'] = self._get_impuestos(row['id'])
new['cliente'] = self._get_cliente(row)
data.append(new)
return data
def _receptores(self):
sql = "SELECT * FROM receptores"
self._cursor.execute(sql)
rows = self._cursor.fetchall()
fields = (
('rfc', 'rfc'),
('nombre', 'nombre'),
('calle', 'calle'),
('noexterior', 'no_exterior'),
('nointerior', 'no_interior'),
('colonia', 'colonia'),
('municipio', 'municipio'),
('estado', 'estado'),
('pais', 'pais'),
('codigopostal', 'codigo_postal'),
('extranjero', 'es_extranjero'),
('activo', 'es_activo'),
('fechaalta', 'fecha_alta'),
('notas', 'notas'),
)
data = []
sql1 = "SELECT correo FROM correos WHERE id_padre=%s"
sql2 = "SELECT telefono FROM telefonos WHERE id_padre=%s"
totals = len(rows)
for i, row in enumerate(rows):
msg = '\tImportando cliente {} de {}'.format(i+1, totals)
log.info(msg)
new = {t: row[s] for s, t in fields}
new['slug'] = to_slug(new['nombre'])
new['es_cliente'] = True
if new['fecha_alta'] is None:
new['fecha_alta'] = str(now())
else:
new['fecha_alta'] = str(new['fecha_alta'])
for _, f in fields:
new[f] = new[f] or ''
if new['es_extranjero']:
new['tipo_persona'] = 4
elif new['rfc'] == 'XAXX010101000':
new['tipo_persona'] = 3
elif len(new['rfc']) == 12:
new['tipo_persona'] = 2
self._cursor.execute(sql1, (row['id'],))
tmp = self._cursor.fetchall()
if tmp:
new['correo_facturas'] = ', '.join([r[0] for r in tmp])
self._cursor.execute(sql2, (row['id'],))
tmp = self._cursor.fetchall()
if tmp:
new['telefonos'] = ', '.join([r[0] for r in tmp])
data.append(new)
return data
class ImportFacturaLibre(object):
def __init__(self, path, rfc):

View File

@ -23,6 +23,7 @@ import json
import logging
import math
import os
import shlex
import shutil
import smtplib
import sqlite3
@ -48,6 +49,8 @@ from dateutil import parser
import seafileapi
from settings import DEBUG, DB_COMPANIES, PATHS
LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
LOG_DATE = '%d/%m/%Y %H:%M:%S'
@ -61,6 +64,11 @@ logging.getLogger('peewee').setLevel(logging.WARNING)
TIMEOUT = 10
PATH_INVOICES = 'facturas'
PG_DUMP = 'pg_dump -U postgres'
PSQL = 'psql -U postgres'
if DEBUG:
PG_DUMP = 'pg_dump -h localhost -U postgres'
PSQL = 'psql -h localhost -U postgres'
#~ https://github.com/kennethreitz/requests/blob/v1.2.3/requests/structures.py#L37
@ -259,6 +267,16 @@ def _call(args):
return subprocess.check_output(args, shell=True).decode()
def _run(args, wait=False):
result = ''
cmd = shlex.split(args)
if wait:
result = subprocess.run(cmd, shell=True, check=True).stdout.decode()
else:
subprocess.run(cmd)
return result
def _join(*paths):
return os.path.join(*paths)
@ -332,29 +350,96 @@ def to_zip(files):
return zip_buffer.getvalue()
def db_delete(user, path):
dt = datetime.datetime.now().strftime('%y%m%d_%H%M')
path_bk = _join(path, 'tmp', '{}_{}.bk'.format(user, dt))
args = 'pg_dump -U postgres -Fc {} > "{}"'.format(user, path_bk)
try:
_call(args)
except:
pass
def dumps(data):
return json.dumps(data, default=str)
args = 'psql -U postgres -c "DROP DATABASE {0};"'.format(user)
try:
_call(args)
except:
pass
args = 'psql -U postgres -c "DROP ROLE {0};"'.format(user)
try:
_call(args)
except:
pass
def loads(data):
return json.loads(data)
def _validate_db_rfc():
con = sqlite3.connect(DB_COMPANIES)
sql = """
CREATE TABLE IF NOT EXISTS names(
rfc TEXT NOT NULL COLLATE NOCASE UNIQUE,
con TEXT NOT NULL
);
"""
cursor = con.cursor()
cursor.executescript(sql)
cursor.close()
con.close()
return
def _sql_companies(sql, args=()):
_validate_db_rfc()
con = sqlite3.connect(DB_COMPANIES)
cursor = con.cursor()
try:
cursor.execute(sql, args)
data = cursor.fetchall()
except sqlite3.IntegrityError as e:
log.error(e)
return False
con.commit()
cursor.close()
con.close()
return data
def rfc_get():
sql = "SELECT * FROM names"
data = _sql_companies(sql)
return data
def rfc_exists(rfc):
sql = "SELECT rfc FROM names WHERE rfc = ?"
data = _sql_companies(sql, (rfc,))
if isinstance(data, bool):
return
return bool(data)
def rfc_add(rfc, con):
sql = "INSERT INTO names VALUES (?, ?)"
data = _sql_companies(sql, (rfc.upper(), dumps(con)))
return True
def db_create(user):
args = f'{PSQL} -c "CREATE ROLE {user} WITH LOGIN ENCRYPTED PASSWORD \'{user}\';"'
_run(args)
args = f'{PSQL} -c "CREATE DATABASE {user} WITH OWNER {user};"'
_run(args)
return True
def db_delete(user, path, no_database=False):
sql = "DELETE FROM names WHERE rfc = ?"
data = _sql_companies(sql, (user,))
if no_database:
return True
user = user.replace('&', '').lower()
dt = now().strftime('%y%m%d_%H%M')
path_bk = _join(path, f'{user}_{dt}.bk')
args = f'{PG_DUMP} -d {user} -Fc -f "{path_bk}"'
_run(args)
args = f'{PSQL} -c "DROP DATABASE {user};"'
_run(args)
args = f'{PSQL} -c "DROP ROLE {user};"'
_run(args)
return True
def _get_pass(rfc):
return rfc
@ -417,6 +502,35 @@ def db_backup(path_companies, path_bk, is_mv, url_seafile):
return
def _validate_path_local():
path_bk = _join(str(Path.home()), PATHS['LOCAL'])
if not os.path.isdir(path_bk):
path_bk = ''
return path_bk
def db_backup_local():
path_bk = _validate_path_local()
if not path_bk:
msg = 'No existe la carpeta local'
return {'ok': False, 'msg': msg}
data = rfc_get()
if not len(data):
msg = 'Sin bases de datos a respaldar'
return {'ok': False, 'msg': msg}
for row in data:
user = row[0].lower()
db = loads(row[1])['name']
path = _join(path_bk, '{}.bk'.format(user))
args = f'{PG_DUMP} -d {user} -Fc -f "{path}"'
_run(args)
msg = 'Bases de datos respaldadas correctamente'
result = {'ok': True, 'msg': msg}
return result
def now():
return datetime.datetime.now().replace(microsecond=0)

View File

@ -34,19 +34,18 @@ class StorageEngine(object):
return main.CfdiNomina.get_by(values)
def empresa_agregar(self, values):
return main.empresa_agregar(values['alta_rfc'], False)
# ~ return main.empresa_agregar(values['alta_rfc'], False)
return main._new_client(values['alta_rfc'], False)
def empresa_borrar(self, values):
return main.empresa_borrar(values['rfc'])
# ~ return main.empresa_borrar(values['rfc'])
return main._delete_client(values['rfc'], False, False)
def respaldar_dbs(self):
return main.respaldar_dbs()
def _get_empresas(self, values):
return main.get_empresas()
def get_values(self, table, values=None, session=None):
if table in ('allusuarios', 'usuarioupdate'):
if table in ('allusuarios', 'usuarioupdate', 'main'):
return getattr(self, '_get_{}'.format(table))(values, session)
return getattr(self, '_get_{}'.format(table))(values)
@ -71,8 +70,8 @@ class StorageEngine(object):
def _get_importinvoice(self, values):
return main.import_invoice()
def _get_main(self, values):
return main.config_main()
def _get_main(self, values, session):
return main.config_main(session['userobj'])
def _get_configtimbrar(self, values):
return main.config_timbrar()
@ -463,3 +462,7 @@ class StorageEngine(object):
def nomina(self, values, user):
return main.CfdiNomina.post(values, user)
# Companies only in MV
def _get_empresas(self, values):
return main.companies_get()

View File

@ -16,6 +16,7 @@
# ~ You should have received a copy of the GNU General Public License
# ~ along with this program. If not, see <http://www.gnu.org/licenses/>.
import argparse
from decimal import Decimal
import sqlite3
import click
@ -31,7 +32,7 @@ if __name__ == '__main__':
from controllers import util
from settings import log, DEBUG, COMPANIES, VERSION, PATH_CP, PRE, CURRENT_CFDI, \
from settings import log, COMPANIES, VERSION, PATH_CP, PRE, CURRENT_CFDI, \
INIT_VALUES, DEFAULT_PASSWORD, DECIMALES, IMPUESTOS, DEFAULT_SAT_PRODUCTO, \
CANCEL_SIGNATURE, PUBLIC, DEFAULT_SERIE_TICKET, CURRENT_CFDI_NOMINA, \
DEFAULT_SAT_NOMINA, DECIMALES_TAX, TITLE_APP, MV, DECIMALES_PRECIOS, \
@ -40,6 +41,7 @@ from settings import log, DEBUG, COMPANIES, VERSION, PATH_CP, PRE, CURRENT_CFDI,
# ~ v2
from controllers import utils
from settings import (
DEBUG,
DB_COMPANIES,
EXT,
IS_MV,
@ -249,14 +251,18 @@ def get_doc(type_doc, id, rfc):
return data, file_name, content_type
def config_main():
def config_main(user):
try:
obj = Emisor.select()[0]
except IndexError:
obj = None
punto_de_venta = Configuracion.get_bool('chk_usar_punto_de_venta')
nomina = Configuracion.get_bool('chk_usar_nomina')
if not user.es_admin:
nomina = False
data = {
'empresa': get_title_app(3),
'punto_de_venta': punto_de_venta,
@ -2632,10 +2638,17 @@ class Socios(BaseModel):
def add(cls, values):
accounts = util.loads(values.pop('accounts', '[]'))
fields = cls._clean(cls, values)
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 = 'Ya existe el RFC y Razón Social'
msg = 'Ocurrio un error, al dar de alta el emisor'
data = {'ok': False, 'row': {}, 'new': True, 'msg': msg}
return data
@ -9349,49 +9362,20 @@ def _iniciar_bd():
return
def _agregar_rfc(no_bd):
rfc = input('Introduce el nuevo RFC: ').strip().upper()
if not rfc:
msg = 'El RFC es requerido'
log.error(msg)
return
datos = input('Introduce los datos de conexión: ').strip()
if not datos:
msg = 'Los datos de conexión son requeridos'
log.error(msg)
return
opt = util.parse_con(datos)
if not opt:
log.error('Datos de conexión incompletos')
return
args = opt.copy()
if conectar(args):
if _add_emisor(rfc, util.dumps(opt)):
if no_bd:
log.info('RFC agregado correctamente...')
return
_crear_tablas(rfc)
log.info('RFC agregado correctamente...')
return
log.error('No se pudo agregar el RFC')
return
def _borrar_rfc():
rfc = input('Introduce el RFC a borrar: ').strip().upper()
rfc = input('Introduce el RFC a borrar: ').strip().lower()
if not rfc:
msg = 'El RFC es requerido'
log.error(msg)
return
confirm = input('¿Estás seguro de borrar el RFC?')
confirm = input('¿Estás seguro de borrar el RFC? [si]')
if confirm != 'si':
log.info('Proceso cancelado...')
return
if _delete_emisor(rfc):
util.delete_db(rfc.lower())
if _delete_emisor(rfc.upper()):
utils.db_delete(rfc, PATHS['BK'])
log.info('RFC borrado correctamente...')
return
@ -9460,9 +9444,7 @@ def empresa_agregar(rfc, no_bd):
def empresa_borrar(rfc):
if _delete_emisor(rfc):
# ~ util.delete_db(rfc.lower())
utils.db_delete(rfc.lower(), PATHS['DOCS'])
utils.db_delete(rfc.lower(), PATHS['BK'])
return True
@ -9471,21 +9453,8 @@ def respaldar_dbs():
msg = 'Solo MV'
return {'ok': False, 'msg': msg}
result = util.validate_path_bk()
if not result['ok']:
return result
path_bk = result['msg']
data = util.get_rfcs()
if not len(data):
msg = 'Sin bases de datos a respaldar'
return {'ok': False, 'msg': msg}
for row in data:
util.respaldar_db(row, path_bk)
msg = 'Bases de datos respaldadas correctamente'
return {'ok': True, 'msg': msg}
result = utils.db_backup_local()
return result
def _importar_valores(archivo='', rfc=''):
@ -9776,37 +9745,6 @@ def _importar_factura_libre(archivo):
return
def _importar_factura_libre_gambas(conexion):
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.ImportFacturaLibreGambas(conexion, 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'])
_importar_productos_gambas(data['Productos'])
_import_tickets(data['Tickets'])
log.info('Importación terminada...')
return
def _exist_ticket(row):
filters = (
(Tickets.serie==row['serie']) &
@ -10074,18 +10012,126 @@ def _exportar_documentos():
return
def _test():
rfc = input('Introduce el RFC: ').strip().upper()
# ~ v2
def companies_get():
data = utils.rfc_get()
rows = []
for row in data:
rows.append({'delete': '-', 'rfc': row[0].upper()})
return tuple(rows)
def _list_clients():
rows = utils.rfc_get()
for row in rows:
msg = f'RFC: {row[0].upper()}'
print(msg)
return
def _new_client(rfc, no_database):
rfc = rfc.lower()
if not rfc:
msg = 'El RFC es requerido'
log.error('Falta el RFC')
return
if utils.rfc_exists(rfc):
msg = 'El RFC ya esta dado de alta'
log.error(msg)
return {'ok': False, 'msg': msg}
user = rfc.replace('&', '').lower()
if not no_database:
if not utils.db_create(user):
msg = 'No se pudo crear la base de datos'
log.error(msg)
return {'ok': False, 'msg': msg}
args = {
'type': 'postgres',
'name': user,
'user': user,
'password': user,
}
if not conectar(args.copy()):
msg = 'No se pudo conectar a la base de datos'
log.error(msg)
return {'ok': False, 'msg': msg}
if not utils.rfc_add(rfc, args):
msg = 'No se pudo guardar el nuevo emisor'
log.error(msg)
return {'ok': False, 'msg': msg}
if not no_database:
if not _crear_tablas(rfc):
msg = 'No se pudo crear las tablas'
log.error(msg)
return {'ok': False, 'msg': msg}
desconectar()
msg = 'Emisor dado de alta correctamente'
row = {'delete': '-', 'rfc': rfc.upper()}
result = {'ok': True, 'msg': msg, 'row': row}
return result
def _delete_client(rfc, no_database, ask=True):
rfc = rfc.lower()
if not rfc:
log.error('Falta el RFC')
return
args = util.get_con(rfc)
if not args:
if not utils.rfc_exists(rfc):
msg = 'El RFC no esta dado de alta'
log.error(msg)
return {'ok': False, 'msg': msg}
if ask:
confirm = input('¿Estás seguro de borrar el RFC? [si]')
if confirm != 'si':
log.info('Proceso cancelado...')
return False
if utils.db_delete(rfc, PATHS['BK'], no_database):
log.info('RFC borrado correctamente...')
result = True
else:
log.error('No se pudo borrar el RFC')
result = False
return result
def _process_command_line_arguments():
parser = argparse.ArgumentParser(
description='Empresa Libre')
parser.add_argument('-lc', '--list-clients', dest='list_clients',
action='store_true', default=False, required=False)
parser.add_argument('-nc', '--new-client', dest='new_client',
action='store_true', default=False, required=False)
parser.add_argument('-dc', '--delete-client', dest='delete_client',
action='store_true', default=False, required=False)
parser.add_argument('-ndb', '--no-database', dest='no_database',
action='store_true', default=False, required=False)
parser.add_argument('-r', '--rfc', dest='rfc', default='')
return parser.parse_args()
def main2(args):
if args.list_clients:
_list_clients()
return
conectar(args)
if args.new_client:
_new_client(args.rfc, args.no_database)
return
if args.delete_client:
_delete_client(args.rfc, args.no_database)
return
return
@ -10108,14 +10154,12 @@ help_lr = 'Listar RFCs'
is_flag=True, default=False)
@click.option('-cc', '--cambiar-contraseña', help=help_change_pass,
is_flag=True, default=False)
@click.option('-ar', '--agregar-rfc', help=help_rfc, is_flag=True, default=False)
@click.option('-br', '--borrar-rfc', help=help_br, is_flag=True, default=False)
@click.option('-lr', '--listar-rfc', help=help_lr, is_flag=True, default=False)
@click.option('-i', '--importar-valores', is_flag=True, default=False)
@click.option('-f', '--archivo')
@click.option('-c', '--conexion')
@click.option('-fl', '--factura-libre', is_flag=True, default=False)
@click.option('-flg', '--factura-libre-gambas', is_flag=True, default=False)
@click.option('-t', '--test', is_flag=True, default=False)
@click.option('-gap', '--generar-archivo-productos', is_flag=True, default=False)
@click.option('-ip', '--importar-productos', is_flag=True, default=False)
@ -10127,22 +10171,19 @@ help_lr = 'Listar RFCs'
@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,
borrar_rfc, listar_rfc, importar_valores, archivo, conexion,
factura_libre, test, generar_archivo_productos,
importar_productos, backup_dbs, no_bd, alta, rfc, detalle,
importar_directorio, exportar_documentos):
opt = locals()
if opt['test']:
_test()
sys.exit(0)
if opt['alta']:
if not opt['rfc']:
msg = 'Falta el RFC'
raise click.ClickException(msg)
empresa_agregar(opt['rfc'], no_bd)
# ~ empresa_agregar(opt['rfc'], no_bd)
_new_client(opt['rfc'], no_bd)
sys.exit(0)
if opt['iniciar_bd']:
@ -10161,10 +10202,6 @@ def main(iniciar_bd, migrar_bd, nuevo_superusuario, cambiar_contraseña,
_cambiar_contraseña()
sys.exit(0)
if opt['agregar_rfc']:
_agregar_rfc(no_bd)
sys.exit(0)
if opt['borrar_rfc']:
_borrar_rfc()
sys.exit(0)
@ -10199,13 +10236,6 @@ def main(iniciar_bd, migrar_bd, nuevo_superusuario, cambiar_contraseña,
_importar_factura_libre(opt['archivo'])
sys.exit(0)
if opt['factura_libre_gambas']:
if not opt['conexion']:
msg = 'Falta los datos de conexión'
raise click.ClickException(msg)
_importar_factura_libre_gambas(opt['conexion'])
sys.exit(0)
if opt['generar_archivo_productos']:
if not opt['archivo']:
msg = 'Falta la ruta de la base de datos'
@ -10252,5 +10282,8 @@ def main(iniciar_bd, migrar_bd, nuevo_superusuario, cambiar_contraseña,
if __name__ == '__main__':
main()
args = _process_command_line_arguments()
main2(args)
# ~ main()

View File

@ -47,7 +47,7 @@ except ImportError:
DEBUG = DEBUG
VERSION = '1.32.0'
VERSION = '1.33.0'
EMAIL_SUPPORT = ('soporte@empresalibre.mx',)
TITLE_APP = '{} v{}'.format(TITLE_APP, VERSION)
@ -200,6 +200,7 @@ CURRENCY_MN = 'MXN'
IS_MV = MV
DB_COMPANIES = os.path.abspath(os.path.join(BASE_DIR, '..', 'db', 'rfc.db'))
path_bk = os.path.join(path_docs, 'tmp')
path_local = 'facturas'
EXT = {
'CSS': 'css',
@ -218,6 +219,7 @@ PATHS = {
'USER': path_user_template,
'LOGOS': path_user_logos,
'BK': path_bk,
'LOCAL': path_local,
}
VALUES_PDF = {