forked from elmau/empresa-libre
Importar de Factura Libre Gambas
This commit is contained in:
parent
160f8c3453
commit
4699c429fa
|
@ -3,6 +3,7 @@
|
||||||
#~ import falcon
|
#~ import falcon
|
||||||
import re
|
import re
|
||||||
import smtplib
|
import smtplib
|
||||||
|
import ssl
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
@ -252,9 +253,12 @@ class SendMail(object):
|
||||||
def _login(self):
|
def _login(self):
|
||||||
try:
|
try:
|
||||||
if self._config['ssl']:
|
if self._config['ssl']:
|
||||||
self._server = smtplib.SMTP_SSL(
|
self._server = smtplib.SMTP(
|
||||||
self._config['servidor'],
|
self._config['servidor'],
|
||||||
self._config['puerto'], timeout=10)
|
self._config['puerto'], timeout=10)
|
||||||
|
self._server.ehlo()
|
||||||
|
self._server.starttls()
|
||||||
|
self._server.ehlo()
|
||||||
else:
|
else:
|
||||||
self._server = smtplib.SMTP(
|
self._server = smtplib.SMTP(
|
||||||
self._config['servidor'],
|
self._config['servidor'],
|
||||||
|
@ -265,6 +269,7 @@ class SendMail(object):
|
||||||
if '535' in str(e):
|
if '535' in str(e):
|
||||||
self._error = 'Nombre de usuario o contraseña inválidos'
|
self._error = 'Nombre de usuario o contraseña inválidos'
|
||||||
return False
|
return False
|
||||||
|
print (e)
|
||||||
if '534' in str(e) and 'gmail' in self._config['servidor']:
|
if '534' in str(e) and 'gmail' in self._config['servidor']:
|
||||||
self._error = 'Necesitas activar el acceso a otras ' \
|
self._error = 'Necesitas activar el acceso a otras ' \
|
||||||
'aplicaciones en tu cuenta de GMail'
|
'aplicaciones en tu cuenta de GMail'
|
||||||
|
|
|
@ -1594,6 +1594,360 @@ def sync_cfdi(auth, files):
|
||||||
return
|
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'),
|
||||||
|
)
|
||||||
|
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 import_productos(self):
|
||||||
|
sql = "SELECT * FROM productos"
|
||||||
|
self._cursor.execute(sql)
|
||||||
|
rows = self._cursor.fetchall()
|
||||||
|
|
||||||
|
fields = (
|
||||||
|
('id_categoria', 'categoria'),
|
||||||
|
('noIdentificacion', 'clave'),
|
||||||
|
('descripcion', 'descripcion'),
|
||||||
|
('unidad', 'unidad'),
|
||||||
|
('valorUnitario', 'valor_unitario'),
|
||||||
|
('existencia', 'existencia'),
|
||||||
|
('inventario', 'inventario'),
|
||||||
|
('codigobarras', 'codigo_barras'),
|
||||||
|
('CuentaPredial', 'cuenta_predial'),
|
||||||
|
('precio_compra', 'ultimo_precio'),
|
||||||
|
('minimo', 'minimo'),
|
||||||
|
)
|
||||||
|
data = []
|
||||||
|
|
||||||
|
sql = """
|
||||||
|
SELECT nombre, tasa, tipo
|
||||||
|
FROM impuestos, productos, productosimpuestos
|
||||||
|
WHERE productos.id=productosimpuestos.id_producto
|
||||||
|
AND productosimpuestos.id_impuesto=impuestos.id
|
||||||
|
AND productos.id = ?
|
||||||
|
"""
|
||||||
|
for row in rows:
|
||||||
|
new = {t: row[s] for s, t in fields}
|
||||||
|
new['descripcion'] = ' '.join(new['descripcion'].split())
|
||||||
|
new['clave_sat'] = DEFAULT_SAT_PRODUCTO
|
||||||
|
self._cursor.execute(sql, (row['id'],))
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = []
|
||||||
|
for row in rows:
|
||||||
|
new = {t: row[s] for s, t in fields}
|
||||||
|
|
||||||
|
for _, f in fields:
|
||||||
|
new[f] = new[f] or ''
|
||||||
|
|
||||||
|
if not new['fecha_timbrado']:
|
||||||
|
new['fecha_timbrado'] = None
|
||||||
|
|
||||||
|
new['estatus'] = 'Timbrada'
|
||||||
|
if new['cancelada']:
|
||||||
|
new['estatus'] = 'Cancelada'
|
||||||
|
|
||||||
|
if not new['uuid']:
|
||||||
|
new['uuid'] = None
|
||||||
|
elif new['uuid'] == 'ok':
|
||||||
|
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"
|
||||||
|
for row in rows:
|
||||||
|
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):
|
class ImportFacturaLibre(object):
|
||||||
|
|
||||||
def __init__(self, path, rfc):
|
def __init__(self, path, rfc):
|
||||||
|
|
|
@ -4889,6 +4889,35 @@ def _importar_factura_libre(archivo):
|
||||||
return
|
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'])
|
||||||
|
|
||||||
|
log.info('Importación terminada...')
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
def _importar_productos(archivo):
|
def _importar_productos(archivo):
|
||||||
rfc = input('Introduce el RFC: ').strip().upper()
|
rfc = input('Introduce el RFC: ').strip().upper()
|
||||||
if not rfc:
|
if not rfc:
|
||||||
|
@ -5008,14 +5037,17 @@ help_lr = 'Listar RFCs'
|
||||||
@click.option('-lr', '--listar-rfc', help=help_lr, 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('-i', '--importar-valores', is_flag=True, default=False)
|
||||||
@click.option('-a', '--archivo')
|
@click.option('-a', '--archivo')
|
||||||
|
@click.option('-c', '--conexion')
|
||||||
@click.option('-fl', '--factura-libre', is_flag=True, default=False)
|
@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('-t', '--test', is_flag=True, default=False)
|
||||||
@click.option('-gap', '--generar-archivo-productos', 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)
|
@click.option('-ip', '--importar-productos', is_flag=True, default=False)
|
||||||
@click.option('-bk', '--backup-dbs', is_flag=True, default=False)
|
@click.option('-bk', '--backup-dbs', is_flag=True, default=False)
|
||||||
def main(iniciar_bd, migrar_bd, nuevo_superusuario, cambiar_contraseña, rfc,
|
def main(iniciar_bd, migrar_bd, nuevo_superusuario, cambiar_contraseña, rfc,
|
||||||
borrar_rfc, listar_rfc, importar_valores, archivo, factura_libre, test,
|
borrar_rfc, listar_rfc, importar_valores, archivo, conexion, factura_libre,
|
||||||
generar_archivo_productos, importar_productos, backup_dbs):
|
factura_libre_gambas, test, generar_archivo_productos, importar_productos,
|
||||||
|
backup_dbs):
|
||||||
|
|
||||||
opt = locals()
|
opt = locals()
|
||||||
|
|
||||||
|
@ -5077,6 +5109,13 @@ def main(iniciar_bd, migrar_bd, nuevo_superusuario, cambiar_contraseña, rfc,
|
||||||
_importar_factura_libre(opt['archivo'])
|
_importar_factura_libre(opt['archivo'])
|
||||||
sys.exit(0)
|
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 opt['generar_archivo_productos']:
|
||||||
if not opt['archivo']:
|
if not opt['archivo']:
|
||||||
msg = 'Falta la ruta de la base de datos'
|
msg = 'Falta la ruta de la base de datos'
|
||||||
|
|
Loading…
Reference in New Issue