fix: restaura layout da dashboard e corrige exibição das listas

This commit is contained in:
andersonid
2025-04-03 13:48:09 -03:00
parent 10ff9cab3b
commit 417b5c3f96
13 changed files with 3149 additions and 1013 deletions

593
app.py
View File

@@ -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/<int:id>", methods=["GET", "POST"])
@app.route('/cotas/editar/<int:id>', 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/<int:id>', 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)