From 417b5c3f96fa728c3efba99ec2c3990da8b3f587 Mon Sep 17 00:00:00 2001 From: andersonid Date: Thu, 3 Apr 2025 13:48:09 -0300 Subject: [PATCH] =?UTF-8?q?fix:=20restaura=20layout=20da=20dashboard=20e?= =?UTF-8?q?=20corrige=20exibi=C3=A7=C3=A3o=20das=20listas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.py | 593 +++++++++--------- .../__pycache__/database.cpython-312.pyc | Bin 16297 -> 16026 bytes functions/database.py | 8 +- requirements.txt | 1 + routes/cota.py | 30 - templates/base.html | 551 +++++++++++++--- templates/editar_pagamento.html | 60 ++ templates/home.html | 536 +++++++--------- templates/listar_assinaturas.html | 578 ++++++++++++++++- templates/listar_cotas.html | 380 ++++++++--- templates/listar_materiais.html | 370 +++++++++-- templates/listar_militantes.html | 518 ++++++++++++--- templates/listar_vendas_jornal.html | 537 +++++++++++++++- 13 files changed, 3149 insertions(+), 1013 deletions(-) delete mode 100644 routes/cota.py create mode 100644 templates/editar_pagamento.html diff --git a/app.py b/app.py index c38880f..319ada0 100644 --- a/app.py +++ b/app.py @@ -26,7 +26,6 @@ from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker, joinedload from datetime import datetime, timedelta 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 @@ -91,9 +90,6 @@ def generate_qr_code(user): return qr_code -# Registrar blueprints -app.register_blueprint(cota_bp) - # Configuração da sessão do SQLAlchemy db_session = get_db_connection() @@ -182,24 +178,19 @@ def index(): def login(): """Rota de login""" if request.method == "POST": - username = request.form.get("username") + email = request.form.get("email") password = request.form.get("password") - otp_code = request.form.get("otp_code") - if not all([username, password, otp_code]): + if not all([email, password]): flash("Todos os campos são obrigatórios.", "error") return redirect(url_for("login")) db = get_db_connection() try: - user = db.query(Usuario).filter_by(username=username).first() + user = db.query(Usuario).filter_by(email=email).first() if not user or not user.check_password(password): - flash("Usuário ou senha incorretos.", "error") - return redirect(url_for("login")) - - if not user.verify_otp(otp_code): - flash("Código OTP inválido.", "error") + flash("Email ou senha incorretos.", "error") return redirect(url_for("login")) # Atualizar último login @@ -244,76 +235,23 @@ def home(): nome_usuario = usuario.username if usuario else "Usuário" # Formatar data atual em português - meses = { - 1: 'Janeiro', 2: 'Fevereiro', 3: 'Março', 4: 'Abril', - 5: 'Maio', 6: 'Junho', 7: 'Julho', 8: 'Agosto', - 9: 'Setembro', 10: 'Outubro', 11: 'Novembro', 12: 'Dezembro' - } - data_atual = datetime.now() - data_formatada = f"{data_atual.day} de {meses[data_atual.month]} de {data_atual.year}" + data_atual = datetime.now().strftime("%d de %B de %Y") - # Buscar totais - total_militantes = db.query(Militante).count() - total_cotas = db.query(func.sum(CotaMensal.valor_novo)).scalar() or 0 - total_materiais = db.query(MaterialVendido).count() - total_assinaturas = db.query(AssinaturaAnual).filter( - AssinaturaAnual.data_fim >= datetime.now() - ).count() + # Buscar dados para o dashboard + total_militantes = db.query(func.count(Militante.id)).scalar() + total_cotas = db.query(func.sum(CotaMensal.valor)).scalar() or 0 + total_materiais = db.query(func.sum(MaterialVendido.valor)).scalar() or 0 + total_assinaturas = db.query(func.sum(AssinaturaAnual.valor)).scalar() or 0 - # Buscar últimos militantes cadastrados com tratamento de erro - try: - ultimos_militantes = db.query(Militante)\ - .order_by(Militante.id.desc())\ - .limit(5)\ - .all() - except Exception as e: - print(f"Erro ao buscar últimos militantes: {e}") - ultimos_militantes = [] - - # Buscar últimos pagamentos com tratamento de erro - try: - ultimos_pagamentos = db.query(Pagamento)\ - .join(Militante)\ - .order_by(Pagamento.data_pagamento.desc())\ - .limit(5)\ - .all() - except Exception as e: - print(f"Erro ao buscar últimos pagamentos: {e}") - ultimos_pagamentos = [] - - return render_template('home.html', - nome_usuario=nome_usuario, - data_atual=data_formatada, - total_militantes=total_militantes, - total_cotas="{:.2f}".format(total_cotas), - total_materiais=total_materiais, - total_assinaturas=total_assinaturas, - ultimos_militantes=ultimos_militantes, - ultimos_pagamentos=ultimos_pagamentos) - except Exception as e: - print(f"Erro na página inicial: {e}") - import traceback - traceback.print_exc() - flash('Erro ao carregar a página inicial', 'danger') - - # Mesmo com erro, tentar formatar a data e buscar o nome do usuário - try: - data_atual = datetime.now() - data_formatada = f"{data_atual.day} de {meses[data_atual.month]} de {data_atual.year}" - nome_usuario = db.query(Usuario).get(session.get('user_id')).username - except: - data_formatada = datetime.now().strftime("%d/%m/%Y") - nome_usuario = "Usuário" - - return render_template('home.html', - nome_usuario=nome_usuario, - data_atual=data_formatada, - total_militantes=db.query(Militante).count(), - total_cotas="0.00", - total_materiais=0, - total_assinaturas=0, - ultimos_militantes=[], - ultimos_pagamentos=[]) + return render_template( + "home.html", + nome_usuario=nome_usuario, + data_atual=data_atual, + total_militantes=total_militantes, + total_cotas=total_cotas, + total_materiais=total_materiais, + total_assinaturas=total_assinaturas + ) finally: db.close() @@ -459,11 +397,25 @@ def nova_cota(): ) db.add(cotas_mensais) db.commit() + + if request.headers.get('X-Requested-With') == 'XMLHttpRequest': + return jsonify({ + 'status': 'success', + 'message': 'Cota cadastrada com sucesso!' + }) + flash('Cota cadastrada com sucesso!', 'success') return redirect(url_for('listar_cotas')) except Exception as e: db.rollback() print(f"Erro ao cadastrar cota: {e}") + + if request.headers.get('X-Requested-With') == 'XMLHttpRequest': + return jsonify({ + 'status': 'error', + 'message': 'Erro ao cadastrar cota. Verifique os dados e tente novamente.' + }), 400 + flash('Erro ao cadastrar cota', 'danger') return render_template("nova_cota.html") finally: @@ -502,45 +454,68 @@ def listar_cotas(): cota.status = "pendente" print(f"Cota {cota.id}: Militante={cota.militante.nome}, Valor={cota.valor_novo}, Status={cota.status}") - return render_template("listar_cotas.html", cotas=cotas) + # Buscar militantes para o modal de nova cota + militantes = db.query(Militante).order_by(Militante.nome).all() + + return render_template("listar_cotas.html", cotas=cotas, militantes=militantes) except Exception as e: print(f"Erro ao listar cotas: {e}") flash('Erro ao listar cotas', 'danger') - return render_template("listar_cotas.html", cotas=[]) + return render_template("listar_cotas.html", cotas=[], militantes=[]) finally: db.close() # Rota para editar cota -@app.route("/cotas/editar/", methods=["GET", "POST"]) +@app.route('/cotas/editar/', methods=['GET', 'POST']) @login_required @session_timeout def editar_cota(id): db = get_db_connection() try: - cota = db.query(CotaMensal).filter_by(id=id).first() - if not cota: - flash('Cota não encontrada', 'danger') - return redirect(url_for('listar_cotas')) + cota = db.query(CotaMensal).get_or_404(id) - if request.method == "POST": - valor_novo = float(request.form.get("valor_novo")) - data_vencimento = datetime.strptime(request.form.get("data_vencimento"), "%Y-%m-%d").date() - pago = request.form.get("pago") == "true" - - cota.valor_novo = valor_novo - cota.data_vencimento = data_vencimento - cota.pago = pago - cota.data_alteracao = datetime.now().date() - - db.commit() - flash('Cota atualizada com sucesso!', 'success') - return redirect(url_for('listar_cotas')) + if request.method == 'POST': + try: + cota.militante_id = int(request.form['militante_id']) + cota.valor_antigo = float(request.form['valor_antigo']) + cota.valor_novo = float(request.form['valor_novo']) + cota.data_alteracao = datetime.strptime(request.form['data_alteracao'], '%Y-%m-%d').date() + cota.data_vencimento = datetime.strptime(request.form['data_vencimento'], '%Y-%m-%d').date() + + db.commit() + + if request.headers.get('X-Requested-With') == 'XMLHttpRequest': + return jsonify({ + 'status': 'success', + 'message': 'Cota atualizada com sucesso!' + }) + + flash('Cota atualizada com sucesso!', 'success') + return redirect(url_for('listar_cotas')) + + except Exception as e: + db.rollback() + print(f"Erro ao atualizar cota: {e}") + + if request.headers.get('X-Requested-With') == 'XMLHttpRequest': + return jsonify({ + 'status': 'error', + 'message': 'Erro ao atualizar cota. Verifique os dados e tente novamente.' + }), 400 + + flash('Erro ao atualizar cota. Verifique os dados e tente novamente.', 'danger') + return redirect(url_for('editar_cota', id=id)) + + return render_template('editar_cota.html', cota=cota) - return render_template("editar_cota.html", cota=cota) except Exception as e: - db.rollback() - print(f"Erro ao editar cota: {e}") - flash('Erro ao editar cota', 'danger') + print(f"Erro ao carregar cota: {e}") + if request.headers.get('X-Requested-With') == 'XMLHttpRequest': + return jsonify({ + 'status': 'error', + 'message': 'Erro ao carregar cota.' + }), 404 + flash('Erro ao carregar cota', 'danger') return redirect(url_for('listar_cotas')) finally: db.close() @@ -605,39 +580,44 @@ def listar_pagamentos(): @require_login @require_permission(Permission.VIEW_CELL_REPORTS) def novo_material(): - if request.method == "POST": - militante_id = request.form.get("militante_id") - tipo_material_id = request.form.get("tipo_material_id") - descricao = request.form.get("descricao") - valor = float(request.form.get("valor")) - data_venda = datetime.strptime(request.form.get("data_venda"), "%Y-%m-%d").date() - - db = get_db_connection() - try: - materiais_vendidos = MaterialVendido( - militante_id=militante_id, - tipo_material_id=tipo_material_id, - descricao=descricao, - valor=valor, - data_venda=data_venda - ) - db.add(materiais_vendidos) - db.commit() - flash('Material vendido cadastrado com sucesso!', 'success') - return redirect(url_for('listar_materiais')) - except Exception as e: - db.rollback() - print(f"Erro ao cadastrar material vendido: {e}") - flash('Erro ao cadastrar material vendido', 'danger') - return render_template("novo_material.html") - finally: - db.close() - db = get_db_connection() try: - militantes = db.query(Militante).order_by(Militante.nome).all() - tipos_material = db.query(TipoMaterial).order_by(TipoMaterial.descricao).all() - return render_template("novo_material.html", militantes=militantes, tipos_material=tipos_material) + militante_id = request.form.get('militante_id') + tipo_material_id = request.form.get('tipo_material_id') + descricao = request.form.get('descricao') + valor = float(request.form.get('valor')) + data_venda = datetime.strptime(request.form.get('data_venda'), '%Y-%m-%d') + + material = MaterialVendido( + militante_id=militante_id, + tipo_material_id=tipo_material_id, + descricao=descricao, + valor=valor, + data_venda=data_venda + ) + + db.add(material) + db.commit() + + if request.headers.get('X-Requested-With') == 'XMLHttpRequest': + return jsonify({ + 'status': 'success', + 'message': 'Material cadastrado com sucesso!' + }) + + flash('Material cadastrado com sucesso!', 'success') + return redirect(url_for('listar_materiais')) + + except Exception as e: + db.rollback() + if request.headers.get('X-Requested-With') == 'XMLHttpRequest': + return jsonify({ + 'status': 'error', + 'message': 'Erro ao cadastrar material. Por favor, tente novamente.' + }), 400 + + flash('Erro ao cadastrar material. Por favor, tente novamente.', 'error') + return redirect(url_for('listar_materiais')) finally: db.close() @@ -646,14 +626,15 @@ def novo_material(): @require_login @require_permission(Permission.VIEW_CELL_REPORTS) def listar_materiais(): + db = get_db_connection() try: - db = get_db_connection() - materiais = db.query(MaterialVendido).order_by(MaterialVendido.data_venda.desc()).all() - return render_template("listar_materiais.html", materiais=materiais) - except Exception as e: - print(f"Erro ao listar materiais: {e}") - flash('Erro ao listar materiais', 'danger') - return render_template("listar_materiais.html", materiais=[]) + materiais = db.query(MaterialVendido).join(Militante).join(TipoMaterial).all() + militantes = db.query(Militante).all() + tipos_material = db.query(TipoMaterial).all() + return render_template('listar_materiais.html', + materiais=materiais, + militantes=militantes, + tipos_material=tipos_material) finally: db.close() @@ -662,36 +643,42 @@ def listar_materiais(): @require_login @require_permission(Permission.VIEW_CELL_REPORTS) def nova_venda_jornal(): - if request.method == "POST": - militante_id = request.form.get("militante_id") - quantidade = int(request.form.get("quantidade")) - valor_total = float(request.form.get("valor_total")) - data_venda = datetime.strptime(request.form.get("data_venda"), "%Y-%m-%d").date() - - db = get_db_connection() - try: - vendas_jornais_avulsos = VendaJornalAvulso( - militante_id=militante_id, - quantidade=quantidade, - valor_total=valor_total, - data_venda=data_venda - ) - db.add(vendas_jornais_avulsos) - db.commit() - flash('Venda de jornal cadastrada com sucesso!', 'success') - return redirect(url_for('listar_vendas_jornal')) - except Exception as e: - db.rollback() - print(f"Erro ao cadastrar venda de jornal: {e}") - flash('Erro ao cadastrar venda de jornal', 'danger') - return render_template("nova_venda_jornal.html") - finally: - db.close() - db = get_db_connection() try: - militantes = db.query(Militante).order_by(Militante.nome).all() - return render_template("nova_venda_jornal.html", militantes=militantes) + militante_id = request.form.get('militante_id') + quantidade = int(request.form.get('quantidade')) + valor_total = float(request.form.get('valor_total')) + data_venda = datetime.strptime(request.form.get('data_venda'), '%Y-%m-%d') + + venda = VendaJornalAvulso( + militante_id=militante_id, + quantidade=quantidade, + valor_total=valor_total, + data_venda=data_venda + ) + + db.add(venda) + db.commit() + + if request.headers.get('X-Requested-With') == 'XMLHttpRequest': + return jsonify({ + 'status': 'success', + 'message': 'Venda cadastrada com sucesso!' + }) + + flash('Venda cadastrada com sucesso!', 'success') + return redirect(url_for('listar_vendas_jornal')) + + except Exception as e: + db.rollback() + if request.headers.get('X-Requested-With') == 'XMLHttpRequest': + return jsonify({ + 'status': 'error', + 'message': 'Erro ao cadastrar venda. Por favor, tente novamente.' + }), 400 + + flash('Erro ao cadastrar venda. Por favor, tente novamente.', 'error') + return redirect(url_for('listar_vendas_jornal')) finally: db.close() @@ -700,73 +687,13 @@ def nova_venda_jornal(): @require_login @require_permission(Permission.VIEW_CELL_REPORTS) def listar_vendas_jornal(): - try: - db = get_db_connection() - vendas = db.query(VendaJornalAvulso).order_by(VendaJornalAvulso.data_venda.desc()).all() - return render_template("listar_vendas_jornal.html", vendas=vendas) - except Exception as e: - print(f"Erro ao listar vendas de jornal: {e}") - flash('Erro ao listar vendas de jornal', 'danger') - return render_template("listar_vendas_jornal.html", vendas=[]) - finally: - db.close() - -# Rota para criar uma nova assinatura -@app.route("/assinaturas/novo", methods=["GET", "POST"]) -@require_login -@require_permission(Permission.VIEW_CELL_REPORTS) -def nova_assinatura(): - if request.method == "POST": - militante_id = request.form.get("militante_id") - tipo_material_id = request.form.get("tipo_material_id") - quantidade = int(request.form.get("quantidade")) - valor_total = float(request.form.get("valor_total")) - data_inicio = datetime.strptime(request.form.get("data_inicio"), "%Y-%m-%d").date() - data_fim = datetime.strptime(request.form.get("data_fim"), "%Y-%m-%d").date() - - db = get_db_connection() - try: - assinaturas_anuais = AssinaturaAnual( - militante_id=militante_id, - tipo_material_id=tipo_material_id, - quantidade=quantidade, - valor_total=valor_total, - data_inicio=data_inicio, - data_fim=data_fim - ) - db.add(assinaturas_anuais) - db.commit() - flash('Assinatura cadastrada com sucesso!', 'success') - return redirect(url_for('listar_assinaturas')) - except Exception as e: - db.rollback() - print(f"Erro ao cadastrar assinatura: {e}") - flash('Erro ao cadastrar assinatura', 'danger') - return render_template("nova_assinatura.html") - finally: - db.close() - db = get_db_connection() try: - militantes = db.query(Militante).order_by(Militante.nome).all() - tipos_material = db.query(TipoMaterial).order_by(TipoMaterial.descricao).all() - return render_template("nova_assinatura.html", militantes=militantes, tipos_material=tipos_material) - finally: - db.close() - -# Rota para listar assinaturas -@app.route("/assinaturas") -@require_login -@require_permission(Permission.VIEW_CELL_REPORTS) -def listar_assinaturas(): - try: - db = get_db_connection() - assinaturas = db.query(AssinaturaAnual).order_by(AssinaturaAnual.data_inicio.desc()).all() - return render_template("listar_assinaturas.html", assinaturas=assinaturas) - except Exception as e: - print(f"Erro ao listar assinaturas: {e}") - flash('Erro ao listar assinaturas', 'danger') - return render_template("listar_assinaturas.html", assinaturas=[]) + vendas = db.query(VendaJornalAvulso).join(Militante).all() + militantes = db.query(Militante).all() + return render_template('listar_vendas_jornal.html', + vendas=vendas, + militantes=militantes) finally: db.close() @@ -889,32 +816,80 @@ def editar_militante(id): try: militante = db.query(Militante).get(id) if not militante: + if request.headers.get('X-Requested-With') == 'XMLHttpRequest': + return jsonify({'status': 'error', 'message': 'Militante não encontrado'}), 404 flash('Militante não encontrado', 'danger') return redirect(url_for('listar_militantes')) if request.method == "POST": - militante.nome = request.form.get("nome") - militante.cpf = request.form.get("cpf") - militante.email = request.form.get("email") - militante.telefone = request.form.get("telefone") - militante.endereco = request.form.get("endereco") - militante.filiado = request.form.get("filiado") == "on" + nome = request.form.get("nome") + cpf = request.form.get("cpf") + email = request.form.get("email") + telefone = request.form.get("telefone") + endereco = request.form.get("endereco") + filiado = request.form.get("filiado") == "on" - if not validar_cpf(militante.cpf): + # Validar CPF + if not validar_cpf(cpf): + if request.headers.get('X-Requested-With') == 'XMLHttpRequest': + return jsonify({'status': 'error', 'message': 'CPF inválido'}), 400 flash('CPF inválido', 'danger') - return render_template("editar_militante.html", militante=militante) + return render_template('editar_militante.html', militante=militante) + + # Verificar se já existe outro militante com este CPF + militante_existente = db.query(Militante).filter( + Militante.cpf == cpf, + Militante.id != id + ).first() + if militante_existente: + if request.headers.get('X-Requested-With') == 'XMLHttpRequest': + return jsonify({'status': 'error', 'message': 'CPF já cadastrado para outro militante'}), 400 + flash('CPF já cadastrado para outro militante', 'danger') + return render_template('editar_militante.html', militante=militante) try: + militante.nome = nome + militante.cpf = cpf + militante.email = email + militante.telefone = telefone + militante.endereco = endereco + militante.filiado = filiado db.commit() + + if request.headers.get('X-Requested-With') == 'XMLHttpRequest': + return jsonify({ + 'status': 'success', + 'message': 'Militante atualizado com sucesso', + 'militante': { + 'id': militante.id, + 'nome': militante.nome, + 'cpf': militante.cpf, + 'email': militante.email, + 'telefone': militante.telefone, + 'endereco': militante.endereco, + 'filiado': militante.filiado + } + }) + flash('Militante atualizado com sucesso!', 'success') return redirect(url_for('listar_militantes')) + except Exception as e: db.rollback() print(f"Erro ao atualizar militante: {e}") + if request.headers.get('X-Requested-With') == 'XMLHttpRequest': + return jsonify({'status': 'error', 'message': 'Erro ao atualizar militante'}), 500 flash('Erro ao atualizar militante', 'danger') - return render_template("editar_militante.html", militante=militante) + return render_template('editar_militante.html', militante=militante) - return render_template("editar_militante.html", militante=militante) + return render_template('editar_militante.html', militante=militante) + + except Exception as e: + print(f"Erro ao editar militante: {e}") + if request.headers.get('X-Requested-With') == 'XMLHttpRequest': + return jsonify({'status': 'error', 'message': 'Erro ao carregar militante'}), 500 + flash('Erro ao carregar militante', 'danger') + return redirect(url_for('listar_militantes')) finally: db.close() @@ -1410,69 +1385,6 @@ def has_permission(value, permission): """Verifica se o valor contém a permissão especificada.""" return bool(value & permission) -def create_app(): - app = Flask(__name__) - # ... existing code ... - - # ... existing code ... - return app - -def init_system(): - """Inicializa o sistema com todos os usuários necessários""" - print("Inicializando sistema...") - - # Inicializar banco de dados - print("Inicializando banco de dados...") - init_database() - - # Criar admin - create_admin() - - # Criar usuários de teste - create_test_users() - - # Verificar configuração - db = get_db_connection() - try: - # Verificar admin - admin = db.query(Usuario).filter_by(username='admin').first() - if admin: - print("\nAdmin configurado:") - print(f"Username: admin") - print(f"Senha: admin123") - if os.path.exists('admin_qr.png'): - print("OTP: Usando configuração existente do arquivo admin_qr.png") - else: - print("OTP: Nova configuração gerada") - - # Verificar usuários de teste - test_users = ['aligner', 'tester', 'deployer'] - for username in test_users: - user = db.query(Usuario).filter_by(username=username).first() - if user: - print(f"\nUsuário {username} configurado:") - print(f"Username: {username}") - print(f"Senha: Test123!@#") - if user.otp_secret: - print("OTP: Configurado") - else: - print("OTP: Não configurado") - finally: - db.close() - - print("\nInstruções:") - print("1. Configure o OTP usando o QR code gerado") - print("2. Faça login com as credenciais fornecidas") - print("3. Altere a senha no primeiro login") - - print("\nCredenciais:") - print("Admin:") - print("Username: admin") - print("Senha: admin123") - print("\nUsuários de teste:") - print("Username: aligner, tester, deployer") - print("Senha: Test123!@#") - @app.route('/militante/desligar/', methods=['POST']) @login_required def desligar_militante(id): @@ -1566,6 +1478,69 @@ def session_timeout(): except Exception as e: print(f"Erro ao atualizar última atividade: {e}") +def create_app(): + app = Flask(__name__) + # ... existing code ... + + # ... existing code ... + return app + +def init_system(): + """Inicializa o sistema com todos os usuários necessários""" + print("Inicializando sistema...") + + # Inicializar banco de dados + print("Inicializando banco de dados...") + init_database() + + # Criar admin + create_admin() + + # Criar usuários de teste + create_test_users() + + # Verificar configuração + db = get_db_connection() + try: + # Verificar admin + admin = db.query(Usuario).filter_by(username='admin').first() + if admin: + print("\nAdmin configurado:") + print(f"Username: admin") + print(f"Senha: admin123") + if os.path.exists('admin_qr.png'): + print("OTP: Usando configuração existente do arquivo admin_qr.png") + else: + print("OTP: Nova configuração gerada") + + # Verificar usuários de teste + test_users = ['aligner', 'tester', 'deployer'] + for username in test_users: + user = db.query(Usuario).filter_by(username=username).first() + if user: + print(f"\nUsuário {username} configurado:") + print(f"Username: {username}") + print(f"Senha: Test123!@#") + if user.otp_secret: + print("OTP: Configurado") + else: + print("OTP: Não configurado") + finally: + db.close() + + print("\nInstruções:") + print("1. Configure o OTP usando o QR code gerado") + print("2. Faça login com as credenciais fornecidas") + print("3. Altere a senha no primeiro login") + + print("\nCredenciais:") + print("Admin:") + print("Username: admin") + print("Senha: admin123") + print("\nUsuários de teste:") + print("Username: aligner, tester, deployer") + print("Senha: Test123!@#") + if __name__ == '__main__': init_system() app.run(debug=True) diff --git a/functions/__pycache__/database.cpython-312.pyc b/functions/__pycache__/database.cpython-312.pyc index 72b50420ff87b1f00c737f6de99d2a9d073f684b..3a339b70346946e8e6d3767ce4ea297500cf0f3d 100644 GIT binary patch delta 3682 zcmbVPYiv{J89rZP$1#b6otP4n#JQMYyBR5jw1Ftea#_P|1O|a#rq1)?LuQ)T>319; zQG!!iZ7Z<0+FQziP`WZrrS0gVu&&cKlulF8Hce$4+Pb5v+q6j&{Af|z9}`u#eb0Lw z$lOeXS@PrWcHZ}Sub^zo}DwrLQiI+1{PHLxeX2(Js)1-ThF4-!*2enx4 zxHus8iGAAs+y}IMB5b2+dl&uN9uf`;e%!MnSh^z3uc{w>N0%EV!l4LXR)74HB|+MzIFirM^Z3 zz3&Z|cq+dA@TKNWmpz*w-DJ^=vJXyqE~eXY9)gq)FuYH}XmKh&lr$nk(f;V5o=7F@ z<1u+Zc99qvy6jr8WogVB^OkNIutk~57{CSi z0$>Lqi{>6pb7)ebwui2IGt~C5i?=WNM;cW4;W;k8o^f%8Tik$)*QtXU@80mXmBQ?2 zC|E!`LfcJ0yHgLr(X&2?C^a|D%E#G$06LJlC>BjcBYX8kENWTIff>Mlmc?umz@Q~d z4_UG3V1q9#Ju5ottECk?Z=}=wCT)jUE8E=*35I17nWe$d5FX`_%vW`t*ab{b*tgr_ z*2&z?`Q8^@^xQI^l`TkhnNTo4L5}qQ?Rc4lbOXR%PGQ7eE{H|xo-&`6_47Fw8OmvT zSPhJ?)3T+lbiS-d?4+p@KUJ5XD4d(@bg{ffw9(b_JHxl=QxMCs-$<<$YkgTE9mO`_ zI6YZ$pXIjEOnD8{h`;7jB{rrGbJLEi`eH{NP%F1nTN|O1137`wqkt@0k7EsRhL%-s zwrD+`>0aNVv6>ZPj6PrG7pv&us@m=wWs?ah8#MtD%nx!F(5dJTRht6icJf|4*ncrPKxb;IV;rO>aQF~lUTSRd?NEaxt_0+UJVK#B`N}6TWH0_t&IpVI zyiShgCfWBxtBi-1=F=K)z+%V7rmf-cs-XJu`g%lq1w(V>utImj2lKY*%Njx*jUuOX-~xW>H}8Ns@w~z zyJyG}meh@G>L4cppOAfZuSNZsqJAcQK`nlo?>e0SsRGlP5gJW0>)UQkF-YRd`Uyr^ zE}d~=aYliI4mE7Dh|O>(_^{!zG&69sPa|`TdsdzA4GXzi{f@eBZr~39TMDDBDw@Fv znr}^?uI*^0ZH*sgQU2>BcsjI8JY^ zZ7N3Ct_TGx|1p+7qtbP23pjPH;-)XIn-Z1eYOYM1d3vHhp3r4EPKE)_kU#p)Vd4t`8SfNBM*aBDDrQ-QfC8XJr!B5<_z zieN)rGA(ARxy1fEHT6yZviaviyg)}V+V3zzIg7*jOA6E-TQ9wIj z51<3k3E(T5FBe=Xd>e3kwaSFUID-xLFZ5gGG1vP=ih6#_U@|s5pts4tkh5*l?h-{G M`st;%kGb*v2Ls#{VE_OC delta 4024 zcmbVP4NP0t6@Jgy#s(WONeI8j{2CKVL&*xI5THp}(h#RAG)bF9?kn$wIr9&>&p^|K znn1TMUD`Ce>$(xrW$n@_S~n$Q-MVO5M^jCj)=7<;N$e@ovQF9*?T@aJRwdH5^WEnZ z6&#WvP$;s-#5DjhWRF zD;tv&o*Qj7Be8;?vHI3dNUC*Aeo7vdN2Mn+9+5`nD(TafOn&;3#A1~GObY=UciJ{M zjZ9tB^>94Kdu??cS%$06Z1%{@Iv9MgW3D?D>yVho?j+qB z4EsF6xOCMa<@(;wsTiNx<(SCG#SGST1KRlCvVUUVDVqGWuZYjO{^06F^C5zM*34gb zd+%<6qC*S%mcRYXLBaAL->V}uDUL0_W;tk3vz(R=wFZX53`H-Q^g+saQ_-)<*02&0k!3y95e&GNLlogqnBh;BQLNd@+kK${sYLW~8Lk)2p=A;ftJ{@SWl zn}in*bnOK=35+aNNhqNaEv71_gI(x~1JWE69sq`UM!~bDe;b8=uNRz3-cKDyfv}^8 z_QqLE3F{$cWH_S7^+hf(?b*Xv&aAt<U5Y9yVM0KjXxMN zzyx1ixX0vwzu-Sz_)ocqA1*2~69|dV_7N;iR&4(Ny%&tZ`UpVm#3&@O6WOZ5tBZ@O zx}aHV1CX>LbD!gn6~ADfEYOLQCAVbCwfJP=Zk6mSRW#tnG|FEgX-gQkdN1N7EMzh3j&T4=vD&r3Bc!S%H`kl zk84`!pkG_Nd-W^SWjKZuT_24zRkw(d%+J4D>z6O{cWVp92AD4s{-4@r?@1a#&o2*s zvDX0KBUqTX&A&$e6aS*$EAO0{@^7%zoWPiBfFSiUNrtt5TmOTGeT43iJ^KSz_6oPu z`(;0`udkB-%!BpQH2JUf&2pZYj4ABffFJP2h63RnF#vORnqXn^-3>MJRX*9UUBg2m zU0rvP}kvmXI| z3P{UsvGIUk@i*@HhgttH!BFACjE@881K9}CtMHBl2GFwOAPr4U6@O}_3!#lIaum%H#TYDf*lQ(Io;YyioRN}}Y z;fC12cVI2e7jP6Y%jcfvpy|O|Gg|Wwt31umwY218+pdtCC_>2Wm)zdknk}-|ta!e; z^^9E2ueX+aSOrFu0U7`kyz!m_Gs!X(Rb};1ioD$d33Sb4;{>EC0Dc`X9|6^ADdb-h z7+H!M4aY)=rTH^s;fO)EI~)rqLU;^4uIL)ONV*bi&6f$rXU^aAGg&^#$L^h$U*^T_ zeM!Ocw=gcK) {% block title %}{% endblock %} - Controles OCI - {{ bootstrap.load_css() }} - - - - - + + + + + + + {% block extra_css %}{% endblock %} - {% if current_user is defined and current_user.is_authenticated %} + - {% endif %} - + +
{% with messages = get_flashed_messages(with_categories=true) %} {% if messages %} {% for category, message in messages %} - {% if not (category == 'success' and message == 'Login realizado com sucesso!') %} - - {{ bootstrap.load_js() }} - - - {% block extra_js %}{% endblock %} - + + + {% block scripts %}{% endblock %} \ No newline at end of file diff --git a/templates/editar_pagamento.html b/templates/editar_pagamento.html new file mode 100644 index 0000000..9dfd363 --- /dev/null +++ b/templates/editar_pagamento.html @@ -0,0 +1,60 @@ +{% extends "base.html" %} + +{% block title %}Editar Pagamento{% endblock %} + +{% block content %} +
+
+
+
+
+
+ Editar Pagamento +
+
+
+
+
+
+ +
+ R$ + +
+
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+ + Cancelar + + +
+
+
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/templates/home.html b/templates/home.html index 893d7cc..b00e27f 100644 --- a/templates/home.html +++ b/templates/home.html @@ -13,159 +13,126 @@
- {% if current_user.has_permission('view_cell_data') %} - +
-
-
-
-
-
Total de Militantes
-

{{ total_militantes }}

-
-
- -
-
- - Ver detalhes - -
-
-
- {% endif %} - - {% if current_user.has_permission('view_cell_reports') %} - -
-
-
-
Total de Cotas
-
-
-

R$ {{ total_cotas }}

-
-
- -
-
- - Ver detalhes - +
+
Total de Militantes
+
{{ total_militantes }}
+ + Ver detalhes + +
+
-
-
-
-
-
-
Materiais Vendidos
-

{{ total_materiais }}

-
-
- -
-
- - Ver detalhes - +
+
Total de Cotas
+
R$ {{ total_cotas }}
+ + Ver detalhes + +
+
-
-
-
-
-
-
Assinaturas Ativas
-

{{ total_assinaturas }}

-
-
- -
-
- - Ver detalhes - +
+
Materiais Vendidos
+
{{ total_materiais }}
+ + Ver detalhes + +
+ +
+
+
+ +
+
+
Assinaturas Ativas
+
{{ total_assinaturas }}
+ + Ver detalhes + +
+
- {% endif %}
- {% if current_user.has_permission('view_cell_data') %}
-
- Últimos Militantes Cadastrados +
+ Últimos Militantes Cadastrados
-
+
{% if ultimos_militantes %}
{% for militante in ultimos_militantes %} - -
+ - {% else %} -

Nenhum militante cadastrado recentemente.

- {% endif %} -
-
-
- {% endif %} - - {% if current_user.has_permission('view_cell_reports') %} - -
-
-
-
- Últimos Pagamentos -
-
-
- {% if ultimos_pagamentos %} -
- {% for pagamento in ultimos_pagamentos %} -
-
-
{{ pagamento.militante.nome }}
- R$ {{ "%.2f"|format(pagamento.valor) }} -
- {{ pagamento.data_pagamento.strftime('%d/%m/%Y') }} +
{% endfor %}
{% else %} -

Nenhum pagamento registrado recentemente.

+

Nenhum militante cadastrado recentemente.

+ {% endif %} +
+
+
+ + +
+
+
+
+ Últimos Pagamentos +
+
+
+ {% if ultimos_pagamentos %} +
+ {% for pagamento in ultimos_pagamentos %} +
+
+
{{ pagamento.militante.nome }}
+ {{ pagamento.data_pagamento.strftime('%d/%m/%Y') }} +
+ R$ {{ "%.2f"|format(pagamento.valor) }} +
+ {% endfor %} +
+ {% else %} +

Nenhum pagamento registrado recentemente.

{% endif %}
- {% endif %}
- - - @@ -27,23 +27,10 @@
-
-
-
- - -
- -
+
+
@@ -52,30 +39,36 @@ Militante - Valor - Vencimento - Status + Valor Antigo + Valor Novo + Data de Alteração + Data de Vencimento Ações {% for cota in cotas %} - + {{ cota.militante.nome }} - R$ {{ "%.2f"|format(cota.valor_novo) }} + R$ {{ "%.2f"|format(cota.valor_antigo) }} + R$ {{ "%.2f"|format(cota.valor_novo) }} + {{ cota.data_alteracao.strftime('%d/%m/%Y') }} {{ cota.data_vencimento.strftime('%d/%m/%Y') }} - - - {{ cota.status.title() }} - - +
+
-
-
- Mostrando {{ cotas|length }} cotas + + - + + + +