#!/usr/bin/env python import sqlite3 import click from peewee import * from playhouse.fields import PasswordField, ManyToManyField if __name__ == '__main__': import os, sys parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, parent_dir) from controllers import util from settings import log, VERSION, PATH_CP, COMPANIES database_proxy = Proxy() class BaseModel(Model): class Meta: database = database_proxy def conectar(opt): db = { 'sqlite': SqliteDatabase, 'postgres': PostgresqlDatabase, 'mysql': MySQLDatabase, } db_type = opt.pop('type') db_name = opt.pop('name') if not db_type in db: log.error('Tipo de base de datos no soportado') return False database = db[db_type](db_name, **opt) try: database_proxy.initialize(database) database_proxy.connect() log.info('Conectado a la BD...') return True except OperationalError as e: log.error('Error al intentar conectar a la base de datos') log.error(e) return False class Configuracion(BaseModel): clave = TextField() valor = TextField(default='') class Meta: order_by = ('clave',) indexes = ( (('clave', 'valor'), True), ) class Tags(BaseModel): tag = TextField(index=True, unique=True) class Meta: order_by = ('tag',) class Usuarios(BaseModel): usuario = TextField(unique=True) nombre = TextField(default='') apellidos = TextField(default='') correo = TextField(default='') contraseña = PasswordField() es_superusuario = BooleanField(default=False) es_admin = BooleanField(default=False) es_activo = BooleanField(default=True) fecha_ingreso = DateTimeField(default=util.now) ultimo_ingreso = DateTimeField(null=True) def __str__(self): t = '{} {} ({})' return t.format(self.nombre, self.apellidos, self.usuario) class Meta: order_by = ('nombre', 'apellidos') class SATRegimenes(BaseModel): key = TextField(index=True, unique=True) name = TextField(index=True) activo = BooleanField(default=False) default = BooleanField(default=False) fisica = BooleanField(default=False) moral = BooleanField(default=False) class Meta: order_by = ('name',) indexes = ( (('key', 'name'), True), ) class Emisor(BaseModel): rfc = TextField(index=True) nombre = TextField() calle = TextField(default='') no_exterior = TextField(default='') no_interior = TextField(default='') colonia = TextField(default='') municipio = TextField(default='') estado = TextField(default='') pais = TextField(default='') codigo_postal = TextField(default='') cp_expedicion = TextField(default='') es_moral = BooleanField(default=False) es_ong = BooleanField(default=False) es_escuela = BooleanField(default=False) autorizacion = TextField(default='') fecha_autorizacion = DateField(null=True) fecha_dof = DateField(null=True) telefono = TextField(default='') correo = TextField(default='') web = TextField(default='') curp = TextField(default='') regimenes = ManyToManyField(SATRegimenes, related_name='emisores') def __str__(self): t = '{} ({})' return t.format(self.nombre, self.rfc) class Meta: order_by = ('nombre',) class Certificado(BaseModel): key = BlobField() key_enc = TextField(default='') cer = BlobField() cer_pem = TextField(default='') cer_txt = TextField(default='') p12 = BlobField() serie = TextField(default='') rfc = TextField(default='') desde = DateTimeField() hasta = DateTimeField() def __str__(self): return self.serie class Folios(BaseModel): serie = TextField() inicio = IntegerField(default=1) default = BooleanField(default=False) usar_con = TextField(default='T') class Meta: order_by = ('-default', 'serie', 'inicio') indexes = ( (('serie', 'inicio'), True), ) class Categorias(BaseModel): categoria = TextField() id_padre = IntegerField(default=0) class Meta: order_by = ('categoria',) indexes = ( (('categoria', 'id_padre'), True), ) class CondicionesPago(BaseModel): condicion = TextField(unique=True) class Meta: order_by = ('condicion',) class SATUnidades(BaseModel): key = TextField(unique=True, index=True) name = TextField(default='', index=True) activo = BooleanField(default=False) default = BooleanField(default=False) class Meta: order_by = ('name',) indexes = ( (('key', 'name'), True), ) class SATFormaPago(BaseModel): key = TextField(unique=True, index=True) name = TextField(default='', index=True) activo = BooleanField(default=False) default = BooleanField(default=False) class Meta: order_by = ('-default', 'name',) indexes = ( (('key', 'name'), True), ) class SATAduanas(BaseModel): key = TextField(unique=True, index=True) name = TextField(default='', index=True) activo = BooleanField(default=False) default = BooleanField(default=False) class Meta: order_by = ('-default', 'name',) indexes = ( (('key', 'name'), True), ) class SATMonedas(BaseModel): key = TextField(unique=True, index=True) name = TextField(default='', index=True) activo = BooleanField(default=False) default = BooleanField(default=False) class Meta: order_by = ('-default', 'name',) indexes = ( (('key', 'name'), True), ) class SATImpuestos(BaseModel): key = TextField(index=True) name = TextField(default='', index=True) factor = TextField(default='T') tipo = TextField(default='T') tasa = DecimalField(default=0.0, decimal_places=6, auto_round=True) activo = BooleanField(default=False) default = BooleanField(default=False) class Meta: order_by = ('-default', 'name',) indexes = ( (('key', 'factor', 'tipo', 'tasa'), True), ) class SATUsoCfdi(BaseModel): key = TextField(index=True, unique=True) name = TextField(default='', index=True) activo = BooleanField(default=False) default = BooleanField(default=False) fisica = BooleanField(default=True) moral = BooleanField(default=False) class Meta: order_by = ('-default', 'name',) indexes = ( (('key', 'name'), True), ) class Addendas(BaseModel): nombre = TextField(unique=True) addenda = TextField() class Meta: order_by = ('nombre',) class Socios(BaseModel): rfc = TextField(index=True) nombre = TextField(index=True) nombre_comercial = TextField(index=True) calle = TextField(default='') no_exterior = TextField(default='') no_interior = TextField(default='') colonia = TextField(default='') municipio = TextField(default='') estado = TextField(default='') pais = TextField(default='') codigo_postal = TextField(default='') notas = TextField(default='') telefonos = TextField(default='') es_activo = BooleanField(default=True) es_moral = BooleanField(default=False) es_extranjero = BooleanField(default=False) es_ong = BooleanField(default=False) fecha_alta = DateField(default=util.now) dias_pago = IntegerField(default=0) dias_habiles = BooleanField(default=False) es_cliente = BooleanField(default=False) es_proveedor = BooleanField(default=False) cuenta_cliente = TextField(default='') cuenta_proveedor = TextField(default='') web = TextField(default='') correo_facturas = TextField(default='') condicion_pago = ForeignKeyField(CondicionesPago, null=True) addenda = ForeignKeyField(Addendas, null=True) tags = ManyToManyField(Tags, related_name='socios_tags') def __str__(self): t = '{} ({})' return t.format(self.nombre, self.rfc) class Meta: order_by = ('nombre',) indexes = ( (('rfc', 'nombre'), True), ) class Productos(BaseModel): categoria = ForeignKeyField(Categorias, null=True) clave = TextField(unique=True, index=True) clave_sat = TextField() descripcion = TextField(index=True) unidad = ForeignKeyField(SATUnidades) valor_unitario = DecimalField(default=0.0, decimal_places=6, auto_round=True) ultimo_costo = DecimalField(default=0.0, decimal_places=6, auto_round=True) descuento = DecimalField(default=0.0, decimal_places=6, auto_round=True) inventario = BooleanField(default=False) existencia = DoubleField(default=0.0) minimo = DoubleField(default=0.0) codigo_barras = TextField(default='') cuenta_predial = TextField(default='') es_activo = BooleanField(default=True) impuestos = ManyToManyField(SATImpuestos, related_name='productos') tags = ManyToManyField(Tags, related_name='productos_tags') class Meta: order_by = ('descripcion',) class Facturas(BaseModel): cliente = ForeignKeyField(Socios) serie = TextField(default='') folio = IntegerField(default=0) fecha = DateTimeField(default=util.now) fecha_timbrado = DateTimeField(null=True) forma_pago = TextField(default='') condiciones_pago = TextField(default='') subtotal = DecimalField(default=0.0, decimal_places=6, auto_round=True) descuento = DecimalField(default=0.0, decimal_places=6, auto_round=True) moneda = TextField(default='MXN') tipo_cambio = DecimalField(default=1.0, decimal_places=6, auto_round=True) total = DecimalField(default=0.0, decimal_places=6, auto_round=True) total_mn = DecimalField(default=0.0, decimal_places=6, auto_round=True) tipo_comprobante = TextField(default='I') metodo_pago = TextField(default='PUE') lugar_expedicion = TextField(default='') confirmacion = TextField(default='') uso_cfdi = TextField(default='') total_retenciones = DecimalField( decimal_places=6, auto_round=True, null=True) total_trasladados = DecimalField( decimal_places=6, auto_round=True, null=True) xml = TextField(default='') uuid = UUIDField(null=True) estatus = TextField(default='Guardada') regimen_fiscal = TextField(default='') notas = TextField(default='') pagada = BooleanField(default=False) error = TextField(default='') class Meta: order_by = ('fecha',) class FacturasDetalle(BaseModel): factura = ForeignKeyField(Facturas) producto = ForeignKeyField(Productos, null=True) cantidad = DecimalField(default=0.0, decimal_places=6, auto_round=True) valor_unitario = DecimalField(default=0.0, decimal_places=6, auto_round=True) descuento = DecimalField(default=0.0, decimal_places=6, auto_round=True) precio_final = DecimalField(default=0.0, decimal_places=6, auto_round=True) importe = DecimalField(default=0.0, decimal_places=6, auto_round=True) descripcion = TextField(default='') unidad = TextField(default='') clave = TextField(default='') clave_sat = TextField(default='') categoria = TextField(default='') aduana = TextField(default='') pedimento = TextField(default='') fecha_pedimento = DateField(null=True) alumno = TextField(default='') curp = TextField(default='') nivel = TextField(default='') autorizacion = TextField(default='') cuenta_predial = TextField(default='') class Meta: order_by = ('factura',) class FacturasImpuestos(BaseModel): factura = ForeignKeyField(Facturas) impuesto = ForeignKeyField(SATImpuestos) base = DecimalField(default=0.0, decimal_places=6, auto_round=True) importe = DecimalField(default=0.0, decimal_places=6, auto_round=True) class Meta: order_by = ('factura',) indexes = ( (('factura', 'impuesto'), True), ) def authenticate(usuario, contraseña): respuesta = {'login': False, 'msg': 'No Autorizado', 'user': ''} try: obj = Usuarios.get(usuario=usuario, es_activo=True) except Usuarios.DoesNotExist: return respuesta if not obj.contraseña.check_password(contraseña): return respuesta obj.ultimo_ingreso = util.now() obj.save() respuesta['msg'] = '' respuesta['login'] = True respuesta['user'] = str(obj) respuesta['super'] = obj.es_superusuario return respuesta def get_partners(values): if values: id = int(values['id']) row = Clients.select().where(Clients.id==id).dicts()[0] return row rows = Clients.select(Clients.id, Clients.rfc, Clients.name).dicts() return {'ok': True, 'rows': tuple(rows)} def new_partner(values): fields = util.clean(values) fields['rfc'] = fields['rfc'].upper() obj = Clients.create(**fields) row = { 'id': obj.id, 'cia': obj.cia, 'rfc': obj.rfc, 'name': obj.name, } data = {'ok': True, 'row': row, 'new': True} return data def get_cp(cp): con = sqlite3.connect(PATH_CP) cursor = con.cursor() sql = """ SELECT colonia, municipio, estado FROM colonias, municipios, estados WHERE colonias.id_municipio=municipios.id AND municipios.id_estado=estados.id AND cp=? ORDER BY colonia""" cursor.execute(sql, (cp,)) rows = cursor.fetchall() cursor.close() con.close() data = {} if rows: data = { 'estado': rows[0][2], 'municipio': rows[0][1], } if len(rows) == 1: data['colonia'] = rows[0][0] else: data['colonia'] = [r[0] for r in rows] return data def _init_values(): data = ( {'key': 'version', 'value': VERSION}, {'key': 'rfc_publico', 'value': 'XAXX010101000'}, {'key': 'rfc_extranjero', 'value': 'XEXX010101000'}, ) for row in data: try: Configuration.create(**row) except IntegrityError: pass log.info('Valores iniciales insertados...') return def _crear_tablas(): rfc = input('Introduce el RFC: ').strip().upper() if not rfc: msg = 'El RFC es requerido' log.error(msg) return opt = _get_con(rfc) if not conectar(opt): return tablas = [Addendas, Categorias, Certificado, CondicionesPago, Configuracion, Emisor, Facturas, FacturasDetalle, FacturasImpuestos, Folios, Productos, SATAduanas, SATFormaPago, SATImpuestos, SATMonedas, SATRegimenes, SATUnidades, SATUsoCfdi, Socios, Tags, Usuarios, Emisor.regimenes.get_through_model(), Socios.tags.get_through_model(), Productos.impuestos.get_through_model(), Productos.tags.get_through_model(), ] database_proxy.create_tables(tablas, True) log.info('Tablas creadas correctamente...') return def migrate_tables(): connect() log.info('Tablas migradas correctamente...') return def _agregar_superusuario(): conectar() usuario = input('Introduce el nuevo nombre para el superusuario: ').strip() if not usuario: msg = 'El nombre de usuario es requerido' log.erro(msg) return ok, contraseña = util.get_pass() if not ok: log.error(contraseña) return try: obj = Usuarios.create( usuario=usuario, contraseña=contraseña, es_superusuario=True) except IntegrityError: msg = 'El usuario ya existe' log.error(msg) return log.info('SuperUsuario creado correctamente...') return def _cambiar_contraseña(): conectar() usuario = input('Introduce el nombre de usuario: ').strip() if not usuario: msg = 'El nombre de usuario es requerido' log.error(msg) return try: obj = Usuarios.get(usuario=usuario) except Usuarios.DoesNotExist: msg = 'El usuario no existe' log.error(msg) return ok, contraseña = util.get_pass() if not ok: log.error(contraseña) return obj.contraseña = contraseña obj.save() log.info('Contraseña cambiada correctamente...') return def _agregar_rfc(): 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 if conectar(opt): log.info('RFC agregado correctamente...') else: log.error('No se pudo agregar el RFC') return CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help']) help_create_tables = 'Crea las tablas en la base de datos' help_migrate_db = 'Migra las tablas en la base de datos' help_superuser = 'Crea un nuevo super usuario' help_change_pass = 'Cambia la contraseña a un usuario' help_rfc = 'Agrega un nuevo RFC' @click.command(context_settings=CONTEXT_SETTINGS) @click.option('-bd', '--iniciar-bd',help=help_create_tables, is_flag=True, default=False) @click.option('-m', '--migrar-bd', help=help_migrate_db, is_flag=True, default=False) @click.option('-ns', '--nuevo-superusuario', help=help_superuser, is_flag=True, default=False) @click.option('-cc', '--cambiar-contraseña', help=help_change_pass, is_flag=True, default=False) @click.option('-rfc', '--rfc', help=help_rfc, is_flag=True, default=False) def main(iniciar_bd, migrar_bd, nuevo_superusuario, cambiar_contraseña, rfc): opt = locals() if opt['iniciar_bd']: _crear_tablas() sys.exit(0) if opt['migrar_bd']: migrate_tables() sys.exit(0) if opt['nuevo_superusuario']: _agregar_superusuario() sys.exit(0) if opt['cambiar_contraseña']: _cambiar_contraseña() sys.exit(0) if opt['rfc']: _agregar_rfc() sys.exit(0) return if __name__ == '__main__': main()