feat: implementa sistema de comprovantes com centralizações e PIX
This commit is contained in:
323
app.py
323
app.py
@@ -24,14 +24,14 @@ from functions.database import (
|
||||
Endereco,
|
||||
TipoComprovante,
|
||||
Comprovante,
|
||||
VendaJornal,
|
||||
AssinaturaJornal,
|
||||
CampanhaFinanceira,
|
||||
TransacaoPIX,
|
||||
Permission,
|
||||
Role,
|
||||
RolePermission,
|
||||
UserRole
|
||||
Atividade,
|
||||
MaterialAtividade,
|
||||
Relatorio,
|
||||
CentralizacaoComprovante
|
||||
)
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker, joinedload
|
||||
@@ -73,10 +73,6 @@ def create_app():
|
||||
csrf = CSRFProtect()
|
||||
csrf.init_app(app)
|
||||
|
||||
# Configurar cabeçalhos CSRF personalizados
|
||||
app.config['WTF_CSRF_CHECK_DEFAULT'] = False
|
||||
app.config['WTF_CSRF_HEADERS'] = ['X-CSRFToken']
|
||||
|
||||
# Configurar Flask-Login
|
||||
login_manager = LoginManager()
|
||||
login_manager.init_app(app)
|
||||
@@ -88,6 +84,13 @@ def create_app():
|
||||
"""Filtro para operação bit a bit AND"""
|
||||
return value1 & value2
|
||||
|
||||
@app.before_request
|
||||
def before_request():
|
||||
"""Configurações antes de cada requisição"""
|
||||
session.permanent = True
|
||||
app.permanent_session_lifetime = timedelta(minutes=30)
|
||||
session.modified = True
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
"""Carrega o usuário pelo ID"""
|
||||
@@ -214,14 +217,15 @@ def create_app():
|
||||
flash("Email/usuário ou senha incorretos.", "danger")
|
||||
return redirect(url_for("login"))
|
||||
|
||||
# Verificar OTP se o usuário tiver configurado
|
||||
if user.otp_secret and not otp:
|
||||
flash("Código OTP é obrigatório para sua conta.", "danger")
|
||||
return redirect(url_for("login"))
|
||||
|
||||
if user.otp_secret and not user.verify_otp(otp):
|
||||
flash("Código OTP inválido.", "danger")
|
||||
return redirect(url_for("login"))
|
||||
# Verificar OTP apenas se o usuário tiver configurado
|
||||
if user.otp_secret:
|
||||
if not otp:
|
||||
flash("Código OTP é obrigatório para sua conta.", "danger")
|
||||
return redirect(url_for("login"))
|
||||
|
||||
if not user.verify_otp(otp):
|
||||
flash("Código OTP inválido.", "danger")
|
||||
return redirect(url_for("login"))
|
||||
|
||||
# Atualizar último login
|
||||
user.ultimo_login = datetime.utcnow()
|
||||
@@ -281,28 +285,23 @@ def create_app():
|
||||
.order_by(Militante.id.desc())\
|
||||
.limit(5)\
|
||||
.all()
|
||||
|
||||
# Buscar últimos comprovantes
|
||||
ultimos_comprovantes = db.query(Comprovante)\
|
||||
.join(Militante)\
|
||||
.order_by(Comprovante.data_comprovante.desc())\
|
||||
|
||||
# Buscar últimos pagamentos
|
||||
ultimos_pagamentos = db.query(Pagamento)\
|
||||
.order_by(Pagamento.data_pagamento.desc())\
|
||||
.limit(5)\
|
||||
.all()
|
||||
|
||||
# Buscar tipos de comprovante
|
||||
tipos_comprovante = db.query(TipoComprovante).all()
|
||||
|
||||
return render_template('home.html',
|
||||
nome_usuario=nome_usuario,
|
||||
data_atual=data_atual,
|
||||
total_militantes=total_militantes,
|
||||
total_cotas="{:.2f}".format(total_cotas),
|
||||
total_materiais=total_materiais,
|
||||
total_assinaturas=total_assinaturas,
|
||||
ultimos_militantes=ultimos_militantes,
|
||||
ultimos_comprovantes=ultimos_comprovantes,
|
||||
tipos_comprovante=tipos_comprovante,
|
||||
user=current_user)
|
||||
nome_usuario=nome_usuario,
|
||||
data_atual=data_atual,
|
||||
total_militantes=total_militantes,
|
||||
total_cotas=total_cotas,
|
||||
total_materiais=total_materiais,
|
||||
total_assinaturas=total_assinaturas,
|
||||
ultimos_militantes=ultimos_militantes,
|
||||
ultimos_pagamentos=ultimos_pagamentos,
|
||||
Militante=Militante)
|
||||
except Exception as e:
|
||||
print(f"Erro na página inicial: {e}")
|
||||
import traceback
|
||||
@@ -316,7 +315,7 @@ def create_app():
|
||||
total_materiais=0,
|
||||
total_assinaturas=0,
|
||||
ultimos_militantes=[],
|
||||
ultimos_comprovantes=[],
|
||||
ultimos_pagamentos=[],
|
||||
Militante=Militante)
|
||||
finally:
|
||||
db.close()
|
||||
@@ -819,7 +818,7 @@ def create_app():
|
||||
return redirect(url_for("nova_venda_jornal"))
|
||||
|
||||
db = get_db_connection()
|
||||
venda_jornal = VendaJornal(
|
||||
venda_jornal = VendaJornalAvulso(
|
||||
militante_id=militante_id,
|
||||
quantidade=quantidade,
|
||||
data_venda=data_venda
|
||||
@@ -842,7 +841,7 @@ def create_app():
|
||||
@require_permission(Permission.MANAGE_MATERIALS)
|
||||
def listar_vendas_jornal():
|
||||
db = get_db_connection()
|
||||
vendas_jornal = db.query(VendaJornal).all()
|
||||
vendas_jornal = db.query(VendaJornalAvulso).all()
|
||||
return render_template("listar_vendas_jornal.html", vendas_jornal=vendas_jornal)
|
||||
|
||||
# Rota para criar um novo relatório de cotas
|
||||
@@ -888,8 +887,8 @@ def create_app():
|
||||
return redirect(url_for("novo_relatorio_jornais"))
|
||||
|
||||
db = get_db_connection()
|
||||
jornais = db.query(VendaJornal).filter(
|
||||
VendaJornal.data_venda.between(data_inicio, data_fim)
|
||||
jornais = db.query(VendaJornalAvulso).filter(
|
||||
VendaJornalAvulso.data_venda.between(data_inicio, data_fim)
|
||||
).all()
|
||||
|
||||
return render_template("relatorio_jornais.html",
|
||||
@@ -916,8 +915,8 @@ def create_app():
|
||||
return redirect(url_for("novo_relatorio_assinaturas"))
|
||||
|
||||
db = get_db_connection()
|
||||
assinaturas = db.query(AssinaturaJornal).filter(
|
||||
AssinaturaJornal.data_assinatura.between(data_inicio, data_fim)
|
||||
assinaturas = db.query(AssinaturaAnual).filter(
|
||||
AssinaturaAnual.data_assinatura.between(data_inicio, data_fim)
|
||||
).all()
|
||||
|
||||
return render_template("relatorio_assinaturas.html",
|
||||
@@ -1406,227 +1405,41 @@ def create_app():
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
@app.route('/novo_comprovante', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def novo_comprovante():
|
||||
"""Rota para criar um novo comprovante"""
|
||||
try:
|
||||
session = get_db_connection()
|
||||
if request.method == 'POST':
|
||||
militante_id = request.form.get('militante_id')
|
||||
tipo_comprovante = request.form.get('tipo_comprovante')
|
||||
valor = request.form.get('valor')
|
||||
data_comprovante = request.form.get('data_comprovante')
|
||||
|
||||
if not all([militante_id, tipo_comprovante, valor, data_comprovante]):
|
||||
flash('Todos os campos são obrigatórios', 'error')
|
||||
return redirect(url_for('novo_comprovante'))
|
||||
|
||||
comprovante = Comprovante(
|
||||
militante_id=militante_id,
|
||||
tipo_comprovante=tipo_comprovante,
|
||||
valor=float(valor.replace('R$', '').replace('.', '').replace(',', '.')),
|
||||
data_comprovante=datetime.strptime(data_comprovante, '%Y-%m-%d')
|
||||
)
|
||||
|
||||
session.add(comprovante)
|
||||
session.commit()
|
||||
flash('Comprovante criado com sucesso!', 'success')
|
||||
return redirect(url_for('listar_comprovantes'))
|
||||
|
||||
militantes = session.query(Militante).all()
|
||||
tipos_comprovante = session.query(TipoComprovante).all()
|
||||
return render_template(
|
||||
'novo_comprovante.html',
|
||||
militantes=militantes,
|
||||
tipos_comprovante=tipos_comprovante
|
||||
)
|
||||
except Exception as e:
|
||||
flash(f'Erro ao criar comprovante: {str(e)}', 'error')
|
||||
return redirect(url_for('listar_comprovantes'))
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
@app.route('/listar_comprovantes')
|
||||
@app.route('/comprovantes')
|
||||
@login_required
|
||||
@require_permission(Permission.MANAGE_MATERIALS)
|
||||
def listar_comprovantes():
|
||||
"""Rota para listar todos os comprovantes"""
|
||||
try:
|
||||
session = get_db_connection()
|
||||
comprovantes = session.query(Comprovante).all()
|
||||
return render_template('listar_comprovantes.html', comprovantes=comprovantes)
|
||||
db = get_db_connection()
|
||||
comprovantes = db.query(Comprovante)\
|
||||
.options(joinedload(Comprovante.centralizacoes))\
|
||||
.options(joinedload(Comprovante.militante))\
|
||||
.options(joinedload(Comprovante.campanha))\
|
||||
.all()
|
||||
militantes = db.query(Militante).order_by(Militante.nome).all()
|
||||
campanhas = db.query(CampanhaFinanceira).all()
|
||||
return render_template('listar_comprovantes.html',
|
||||
comprovantes=comprovantes,
|
||||
militantes=militantes,
|
||||
campanhas=campanhas)
|
||||
except Exception as e:
|
||||
flash(f'Erro ao listar comprovantes: {str(e)}', 'error')
|
||||
return redirect(url_for('dashboard'))
|
||||
finally:
|
||||
session.close()
|
||||
db.close()
|
||||
|
||||
@app.route('/adicionar_comprovante', methods=['POST'])
|
||||
@app.route('/comprovantes/<int:id>', methods=['DELETE'])
|
||||
@login_required
|
||||
def adicionar_comprovante():
|
||||
"""Rota para adicionar um novo comprovante via AJAX"""
|
||||
@require_permission(Permission.MANAGE_MATERIALS)
|
||||
def excluir_comprovante(id):
|
||||
try:
|
||||
session = get_db_connection()
|
||||
data = request.get_json()
|
||||
|
||||
comprovante = Comprovante(
|
||||
militante_id=data['militante_id'],
|
||||
tipo_comprovante=data['tipo_comprovante'],
|
||||
valor=float(data['valor'].replace('R$', '').replace('.', '').replace(',', '.')),
|
||||
data_comprovante=datetime.strptime(data['data_comprovante'], '%Y-%m-%d')
|
||||
)
|
||||
|
||||
session.add(comprovante)
|
||||
session.commit()
|
||||
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
'message': 'Comprovante adicionado com sucesso!'
|
||||
})
|
||||
comprovante = Comprovante.query.get_or_404(id)
|
||||
db.session.delete(comprovante)
|
||||
db.session.commit()
|
||||
return jsonify({'success': True})
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': f'Erro ao adicionar comprovante: {str(e)}'
|
||||
})
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
@app.route("/relatorios/comprovantes/novo", methods=["GET", "POST"])
|
||||
@require_login
|
||||
@require_permission(Permission.MANAGE_CELL_REPORTS)
|
||||
def novo_relatorio_comprovantes():
|
||||
if request.method == "POST":
|
||||
try:
|
||||
data_inicio = request.form.get("data_inicio")
|
||||
data_fim = request.form.get("data_fim")
|
||||
|
||||
if not all([data_inicio, data_fim]):
|
||||
flash("Todos os campos são obrigatórios", "danger")
|
||||
return redirect(url_for("novo_relatorio_comprovantes"))
|
||||
|
||||
db = get_db_connection()
|
||||
comprovantes = db.query(Comprovante).filter(
|
||||
Comprovante.data_comprovante.between(data_inicio, data_fim)
|
||||
).all()
|
||||
|
||||
return render_template("relatorio_comprovantes.html",
|
||||
comprovantes=comprovantes,
|
||||
data_inicio=data_inicio,
|
||||
data_fim=data_fim)
|
||||
except Exception as e:
|
||||
flash(f"Erro ao gerar relatório: {str(e)}", "danger")
|
||||
return redirect(url_for("novo_relatorio_comprovantes"))
|
||||
return render_template("novo_relatorio_comprovantes.html")
|
||||
|
||||
@app.route("/relatorios/comprovantes/celula/<int:celula_id>", methods=["GET", "POST"])
|
||||
@require_login
|
||||
@require_permission(Permission.MANAGE_CELL_REPORTS)
|
||||
def relatorio_comprovantes_celula(celula_id):
|
||||
if request.method == "POST":
|
||||
try:
|
||||
data_inicio = request.form.get("data_inicio")
|
||||
data_fim = request.form.get("data_fim")
|
||||
|
||||
if not all([data_inicio, data_fim]):
|
||||
flash("Todos os campos são obrigatórios", "danger")
|
||||
return redirect(url_for("relatorio_comprovantes_celula", celula_id=celula_id))
|
||||
|
||||
db = get_db_connection()
|
||||
comprovantes = db.query(Comprovante).join(Militante).filter(
|
||||
Militante.celula_id == celula_id,
|
||||
Comprovante.data_comprovante.between(data_inicio, data_fim)
|
||||
).all()
|
||||
|
||||
return render_template("relatorio_comprovantes.html",
|
||||
comprovantes=comprovantes,
|
||||
data_inicio=data_inicio,
|
||||
data_fim=data_fim)
|
||||
except Exception as e:
|
||||
flash(f"Erro ao gerar relatório: {str(e)}", "danger")
|
||||
return redirect(url_for("relatorio_comprovantes_celula", celula_id=celula_id))
|
||||
return render_template("novo_relatorio_comprovantes.html", celula_id=celula_id)
|
||||
|
||||
@app.route("/assinaturas/jornal/novo", methods=["GET", "POST"])
|
||||
@require_login
|
||||
@require_permission(Permission.MANAGE_MATERIALS)
|
||||
def nova_assinatura_jornal():
|
||||
if request.method == "POST":
|
||||
try:
|
||||
militante_id = request.form.get("militante_id")
|
||||
data_inicio = request.form.get("data_inicio")
|
||||
data_fim = request.form.get("data_fim")
|
||||
|
||||
if not all([militante_id, data_inicio, data_fim]):
|
||||
flash("Todos os campos são obrigatórios", "danger")
|
||||
return redirect(url_for("nova_assinatura_jornal"))
|
||||
|
||||
db = get_db_connection()
|
||||
assinatura = AssinaturaJornal(
|
||||
militante_id=militante_id,
|
||||
data_inicio=data_inicio,
|
||||
data_fim=data_fim
|
||||
)
|
||||
db.add(assinatura)
|
||||
db.commit()
|
||||
|
||||
flash("Assinatura de jornal registrada com sucesso", "success")
|
||||
return redirect(url_for("listar_assinaturas_jornal"))
|
||||
except Exception as e:
|
||||
flash(f"Erro ao registrar assinatura de jornal: {str(e)}", "danger")
|
||||
return redirect(url_for("nova_assinatura_jornal"))
|
||||
|
||||
db = get_db_connection()
|
||||
militantes = db.query(Militante).all()
|
||||
return render_template("nova_assinatura_jornal.html", militantes=militantes)
|
||||
|
||||
@app.route("/assinaturas/jornal")
|
||||
@require_login
|
||||
@require_permission(Permission.MANAGE_MATERIALS)
|
||||
def listar_assinaturas_jornal():
|
||||
db = get_db_connection()
|
||||
assinaturas = db.query(AssinaturaJornal).all()
|
||||
return render_template("listar_assinaturas_jornal.html", assinaturas=assinaturas)
|
||||
|
||||
@app.route("/campanhas/financeira/novo", methods=["GET", "POST"])
|
||||
@require_login
|
||||
@require_permission(Permission.MANAGE_MATERIALS)
|
||||
def nova_campanha_financeira():
|
||||
if request.method == "POST":
|
||||
try:
|
||||
militante_id = request.form.get("militante_id")
|
||||
valor = request.form.get("valor")
|
||||
data_campanha = request.form.get("data_campanha")
|
||||
|
||||
if not all([militante_id, valor, data_campanha]):
|
||||
flash("Todos os campos são obrigatórios", "danger")
|
||||
return redirect(url_for("nova_campanha_financeira"))
|
||||
|
||||
db = get_db_connection()
|
||||
campanha = CampanhaFinanceira(
|
||||
militante_id=militante_id,
|
||||
valor=valor,
|
||||
data_campanha=data_campanha
|
||||
)
|
||||
db.add(campanha)
|
||||
db.commit()
|
||||
|
||||
flash("Campanha financeira registrada com sucesso", "success")
|
||||
return redirect(url_for("listar_campanhas_financeira"))
|
||||
except Exception as e:
|
||||
flash(f"Erro ao registrar campanha financeira: {str(e)}", "danger")
|
||||
return redirect(url_for("nova_campanha_financeira"))
|
||||
|
||||
db = get_db_connection()
|
||||
militantes = db.query(Militante).all()
|
||||
return render_template("nova_campanha_financeira.html", militantes=militantes)
|
||||
|
||||
@app.route("/campanhas/financeira")
|
||||
@require_login
|
||||
@require_permission(Permission.MANAGE_MATERIALS)
|
||||
def listar_campanhas_financeira():
|
||||
db = get_db_connection()
|
||||
campanhas = db.query(CampanhaFinanceira).all()
|
||||
return render_template("listar_campanhas_financeira.html", campanhas=campanhas)
|
||||
db.session.rollback()
|
||||
return jsonify({'success': False, 'message': str(e)})
|
||||
|
||||
return app
|
||||
|
||||
@@ -1696,6 +1509,10 @@ def main():
|
||||
# Inicializar o sistema
|
||||
init_system()
|
||||
|
||||
# Configurar modo debug
|
||||
app.debug = True
|
||||
app.config['DEBUG'] = True
|
||||
|
||||
return app
|
||||
|
||||
# Criar a aplicação usando a função main
|
||||
@@ -1704,6 +1521,6 @@ app = main()
|
||||
if __name__ == '__main__':
|
||||
app.run(
|
||||
host='0.0.0.0',
|
||||
port=5000,
|
||||
debug=os.getenv('FLASK_ENV') == 'development'
|
||||
port=int(os.getenv('FLASK_PORT', 5000)),
|
||||
debug=True
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user