from flask import Flask, request, render_template, redirect, url_for, flash, session from functions.database import ( Base, Militante, CotaMensal, TipoPagamento, Pagamento, MaterialVendido, TipoMaterial, VendaJornalAvulso, engine, AssinaturaAnual, RelatorioCotasMensais, RelatorioVendasMateriais, Usuario, get_db_connection, ) from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from datetime import datetime from flask_bootstrap import Bootstrap5 from routes.cota import cota_bp from functions.validations import validar_cpf from functools import wraps from pathlib import Path app = Flask(__name__) app.secret_key = 'sua_chave_secreta_aqui' # Necessário para sessões do Flask bootstrap = Bootstrap5(app) # Configuração da sessão do SQLAlchemy db_session = get_db_connection() # Decorator para verificar se o usuário está logado def login_required(f): @wraps(f) def decorated_function(*args, **kwargs): if 'user_id' not in session: flash('Por favor, faça login para acessar esta página.', 'warning') return redirect(url_for('login', next=request.url)) return f(*args, **kwargs) return decorated_function # Rota raiz - redireciona para login se não estiver autenticado @app.route("/") def index(): if 'user_id' not in session: return redirect(url_for('login')) return redirect(url_for('home')) # Rota de login @app.route("/login", methods=["GET", "POST"]) def login(): if 'user_id' in session: return redirect(url_for('home')) if request.method == "POST": username = request.form.get("username") password = request.form.get("password") otp = request.form.get("otp") user = db_session.query(Usuario).filter_by(username=username).first() if user and user.check_password(password) and user.verify_otp(otp): session['user_id'] = user.id # Usando a sessão do Flask session['is_admin'] = user.is_admin next_page = request.args.get('next') flash('Login realizado com sucesso!', 'success') return redirect(next_page or url_for('home')) else: flash('Credenciais inválidas', 'danger') return render_template('login.html') # Rota de logout @app.route("/logout") def logout(): session.clear() # Limpa a sessão do Flask flash('Você foi desconectado com sucesso.', 'info') return redirect(url_for('login')) # Rota home (protegida) @app.route("/home") @login_required def home(): """Página inicial do sistema""" links = [] for rule in app.url_map.iter_rules(): if "GET" in rule.methods and has_no_empty_params(rule): url = url_for(rule.endpoint, **(rule.defaults or {})) endpoint_name = 'Início' if rule.endpoint == 'home' else rule.endpoint links.append((url, endpoint_name)) return render_template('home.html', links=links) # Rota para criar um novo militante @app.route("/militantes/novo", methods=["GET", "POST"]) @login_required def novo_militante(): if request.method == "POST": cpf = request.form["cpf"] if not validar_cpf(cpf): flash('CPF inválido. Por favor, verifique o número informado.', 'error') return render_template("novo_militante.html", dados_anteriores=request.form) novo_militante = Militante( nome=request.form["nome"], cpf=cpf, email=request.form["email"], telefone=request.form["telefone"], endereco=request.form["endereco"], filiado=bool(request.form.get("filiado", False)) ) db_session.add(novo_militante) try: db_session.commit() flash('Militante cadastrado com sucesso!', 'success') return redirect(url_for("listar_militantes")) except Exception as e: print(e) db_session.rollback() flash('Erro ao cadastrar militante. Verifique se o CPF ou email já não estão cadastrados.', 'error') return render_template("novo_militante.html", dados_anteriores=request.form) return render_template("novo_militante.html") # Rota para listar militantes @app.route("/militantes") @login_required def listar_militantes(): militantes = db_session.query(Militante).all() return render_template("listar_militantes.html", militantes=militantes) # Rota para criar uma nova cota mensal @app.route("/cotas/novo", methods=["GET", "POST"]) @login_required def nova_cota(): if request.method == "POST": cotas_mensais = CotaMensal( militante_id=request.form["militante_id"], valor_antigo=request.form["valor_antigo"], valor_novo=request.form["valor_novo"], data_alteracao=datetime.strptime(request.form["data_alteracao"], "%Y-%m-%d") ) db_session.add(cotas_mensais) try: db_session.commit() return redirect(url_for("listar_cotas")) except Exception as e: print(e) db_session.rollback() flash('Erro ao cadastrar cota mensal. Verifique se os dados fornecidos são válidos.', 'error') return render_template("nova_cota.html") return render_template("nova_cota.html") # Rota para listar cotas mensais @app.route("/cotas") @login_required def listar_cotas(): cotas = db_session.query(CotaMensal).all() return render_template("listar_cotas.html", cotas=cotas) # Rota para criar um novo pagamento @app.route("/pagamentos/novo", methods=["GET", "POST"]) @login_required def novo_pagamento(): if request.method == "POST": pagamentos = Pagamento( militante_id=request.form["militante_id"], tipo_pagamento_id=request.form["tipo_pagamento_id"], valor=request.form["valor"], data_pagamento=datetime.strptime(request.form["data_pagamento"], "%Y-%m-%d") ) db_session.add(pagamentos) try: db_session.commit() return redirect(url_for("listar_pagamentos")) except Exception as e: print(e) db_session.rollback() flash('Erro ao cadastrar pagamento. Verifique se os dados fornecidos são válidos.', 'error') return render_template("novo_pagamento.html") return render_template("novo_pagamento.html") # Rota para listar pagamentos @app.route("/pagamentos") @login_required def listar_pagamentos(): pagamentos = db_session.query(Pagamento).all() return render_template("listar_pagamentos.html", pagamentos=pagamentos) # Rota para criar um novo material vendido @app.route("/materiais/novo", methods=["GET", "POST"]) @login_required def novo_material(): if request.method == "POST": materiais_vendidos = MaterialVendido( militante_id=request.form["militante_id"], tipo_material_id=request.form["tipo_material_id"], descricao=request.form["descricao"], valor=request.form["valor"], data_venda=datetime.strptime(request.form["data_venda"], "%Y-%m-%d"), ) db_session.add(materiais_vendidos) try: db_session.commit() return redirect(url_for("listar_materiais")) except Exception as e: print(e) db_session.rollback() flash('Erro ao cadastrar material vendido. Verifique se os dados fornecidos são válidos.', 'error') return render_template("novo_material.html") return render_template("novo_material.html") # Rota para listar materiais vendidos @app.route("/materiais") @login_required def listar_materiais(): materiais = db_session.query(MaterialVendido).all() return render_template("listar_materiais.html", materiais=materiais) # Rota para criar uma nova venda de jornais avulsos @app.route("/jornais/novo", methods=["GET", "POST"]) @login_required def nova_venda_jornal(): if request.method == "POST": vendas_jornais_avulsos = VendaJornalAvulso( militante_id=request.form["militante_id"], quantidade=request.form["quantidade"], valor_total=request.form["valor_total"], data_venda=datetime.strptime(request.form["data_venda"], "%Y-%m-%d"), ) db_session.add(vendas_jornais_avulsos) try: db_session.commit() return redirect(url_for("listar_vendas_jornal")) except Exception as e: print(e) db_session.rollback() flash('Erro ao cadastrar venda de jornal avulso. Verifique se os dados fornecidos são válidos.', 'error') return render_template("nova_venda_jornal.html") return render_template("nova_venda_jornal.html") # Rota para listar vendas de jornais avulsos @app.route("/jornais") @login_required def listar_vendas_jornal(): vendas = db_session.query(VendaJornalAvulso).all() return render_template("listar_vendas_jornal.html", vendas=vendas) # Rota para criar uma nova assinatura anual @app.route("/assinaturas/novo", methods=["GET", "POST"]) @login_required def nova_assinatura(): if request.method == "POST": assinaturas_anuais = AssinaturaAnual( militante_id=request.form["militante_id"], tipo_material_id=request.form["tipo_material_id"], quantidade=request.form["quantidade"], valor_total=request.form["valor_total"], data_inicio=datetime.strptime(request.form["data_inicio"], "%Y-%m-%d"), data_fim=datetime.strptime(request.form["data_fim"], "%Y-%m-%d") ) db_session.add(assinaturas_anuais) try: db_session.commit() return redirect(url_for("listar_assinaturas")) except Exception as e: print(e) db_session.rollback() flash('Erro ao cadastrar assinatura anual. Verifique se os dados fornecidos são válidos.', 'error') return render_template("nova_assinatura.html") return render_template("nova_assinatura.html") # Rota para listar assinaturas anuais @app.route("/assinaturas") @login_required def listar_assinaturas(): assinaturas = db_session.query(AssinaturaAnual).all() return render_template("listar_assinaturas.html", assinaturas=assinaturas) # Rota para criar um novo relatório de cotas mensais @app.route("/relatorios/cotas/novo", methods=["GET", "POST"]) @login_required def novo_relatorio_cotas(): if request.method == "POST": relatorio_cotas_mensais = RelatorioCotasMensais( setor_id=request.form["setor_id"], comite_id=request.form["comite_id"], total_cotas=request.form["total_cotas"], data_relatorio=datetime.strptime(request.form["data_relatorio"], "%Y-%m-%d") ) db_session.add(relatorio_cotas_mensais) try: db_session.commit() return redirect(url_for("listar_relatorios_cotas")) except Exception as e: print(e) db_session.rollback() flash('Erro ao cadastrar relatório de cotas mensais. Verifique se os dados fornecidos são válidos.', 'error') return render_template("novo_relatorio_cotas.html") return render_template("novo_relatorio_cotas.html") # Rota para listar relatórios de cotas mensais @app.route("/relatorios/cotas") @login_required def listar_relatorios_cotas(): relatorios = db_session.query(RelatorioCotasMensais).all() return render_template("listar_relatorios_cotas.html", relatorios=relatorios) # Rota para criar um novo relatório de vendas de materiais @app.route("/relatorios/vendas/novo", methods=["GET", "POST"]) @login_required def novo_relatorio_vendas(): if request.method == "POST": relatorio_vendas_materiais = RelatorioVendasMateriais( setor_id=request.form["setor_id"], comite_id=request.form["comite_id"], total_vendas=request.form["total_vendas"], data_relatorio=datetime.strptime(request.form["data_relatorio"], "%Y-%m-%d") ) db_session.add(relatorio_vendas_materiais) try: db_session.commit() return redirect(url_for("listar_relatorios_vendas")) except Exception as e: print(e) db_session.rollback() flash('Erro ao cadastrar relatório de vendas de materiais. Verifique se os dados fornecidos são válidos.', 'error') return render_template("novo_relatorio_vendas.html") return render_template("novo_relatorio_vendas.html") # Rota para listar relatórios de vendas de materiais @app.route("/relatorios/vendas") @login_required def listar_relatorios_vendas(): relatorios = db_session.query(RelatorioVendasMateriais).all() return render_template("listar_relatorios_vendas.html", relatorios=relatorios) @app.route("/militantes/editar/", methods=["GET", "POST"]) @login_required def editar_militante(id): militante = db_session.query(Militante).get(id) if not militante: flash('Militante não encontrado.', 'error') return redirect(url_for('listar_militantes')) if request.method == "POST": cpf = request.form["cpf"] if cpf != militante.cpf and not validar_cpf(cpf): # Só valida se o CPF foi alterado flash('CPF inválido. Por favor, verifique o número informado.', 'error') return render_template("editar_militante.html", militante=militante) try: militante.nome = request.form["nome"] militante.cpf = cpf militante.email = request.form["email"] militante.telefone = request.form["telefone"] militante.endereco = request.form["endereco"] militante.filiado = bool(request.form.get("filiado", False)) db_session.commit() flash('Militante atualizado com sucesso!', 'success') return redirect(url_for('listar_militantes')) except Exception as e: db_session.rollback() flash('Erro ao atualizar militante. Verifique se o CPF ou email já não estão cadastrados.', 'error') return render_template("editar_militante.html", militante=militante) return render_template("editar_militante.html", militante=militante) # Rota para criar novo usuário @app.route("/usuarios/novo", methods=["GET", "POST"]) @login_required def novo_usuario(): if not session.get('is_admin'): flash('Acesso negado. Apenas administradores podem criar novos usuários.', 'danger') return redirect(url_for('home')) if request.method == "POST": username = request.form.get("username") email = request.form.get("email") password = request.form.get("password") confirm_password = request.form.get("confirm_password") is_admin = bool(request.form.get("is_admin")) # Validações if password != confirm_password: flash('As senhas não conferem.', 'danger') return render_template('novo_usuario.html') # Verificar se usuário já existe if db_session.query(Usuario).filter_by(username=username).first(): flash('Nome de usuário já existe.', 'danger') return render_template('novo_usuario.html') if db_session.query(Usuario).filter_by(email=email).first(): flash('E-mail já cadastrado.', 'danger') return render_template('novo_usuario.html') # Criar novo usuário try: novo_usuario = Usuario( username=username, password=password, is_admin=is_admin ) novo_usuario.email = email db_session.add(novo_usuario) db_session.commit() # Gerar QR code para configuração do OTP qr_uri = novo_usuario.get_otp_uri() flash('Usuário criado com sucesso!', 'success') # Modificação alternativa para salvar no diretório atual qr_path = Path('admin_qr.png') img = qrcode.QRCode(version=1, box_size=10, border=5) img.add_data(qr_uri) img.make(fit=True) img.save(str(qr_path)) return render_template('mostrar_qr_code.html', qr_uri=qr_uri) except Exception as e: db_session.rollback() flash('Erro ao criar usuário. Por favor, tente novamente.', 'danger') print(f"Erro: {e}") return render_template('novo_usuario.html') return render_template('novo_usuario.html') def create_app(): app = Flask(__name__) # ... existing code ... app.register_blueprint(cota_bp) # ... existing code ... return app # Iniciar o servidor Flask if __name__ == "__main__": app.run(debug=True)