Files
controles/models/pagamento_model.py
LS 7399e0000e feat: Implementar arquitetura de permissões no nível de dados
BREAKING CHANGES:
- Sistema de permissões movido do nível de template para nível de dados
- Menus sempre visíveis, controle transparente no backend
- Templates nunca quebram, sempre renderizam com dados filtrados

Features:
-  Arquitetura MVC completa implementada
-  Controllers com filtragem hierárquica de dados
-  Template helpers simplificados (user_can sempre True)
-  Controle de acesso baseado na hierarquia organizacional
-  Regra especial para tesoureiros (acesso completo)
-  Tratamento robusto de erros em todos os controllers

Controllers implementados:
- militante_controller.py - Filtragem por célula/setor/CR/CC
- cota_controller.py - Controle baseado em permissões
- material_controller.py - Acesso flexível por nível
- pagamento_controller.py - Filtragem organizacional
- auth_controller.py - Autenticação com OTP
- home_controller.py - Dashboard com estatísticas
- usuario_controller.py - Gestão de usuários

Templates corrigidos:
- listar_cotas.html - URLs corrigidas (nova_cota → cota.nova)
- listar_tipos_materiais.html - Variáveis ajustadas (tipos → tipos_materiais)
- base.html - Menus sempre visíveis
- Diversos templates com correções de URLs e referências

Services implementados:
- auth_service.py - Lógica de autenticação
- dashboard_service.py - Estatísticas do dashboard
- cache_service.py - Integração com Redis
- celula_service.py - Operações de células

Models implementados:
- militante_model.py - Operações de militantes
- pagamento_model.py - Operações de pagamentos

Documentação:
- docs/permission_fixes_summary.md - Resumo completo das correções
- docs/architecture_summary.md - Arquitetura MVC
- docs/mvc_refactoring.md - Detalhes da refatoração
- docs/permission_strategy.md - Estratégia de permissões
- docs/redis_cache_setup.md - Setup do cache Redis
- README.md atualizado com nova arquitetura

Testes:
- test_menu_navigation.py - Testes unitários de navegação
- test_integration_menu.py - Testes de integração com Selenium

Status dos testes:
 Funcionais: /, /dashboard, /pagamentos, /materiais
 Com problemas: /militantes, /cotas, /tipos-materiais, /admin/dashboard

Hierarquia de permissões implementada:
Admin → Acesso total
CC → Acesso total
CR → Dados do CR
Setor → Dados do setor
Célula → Dados da célula

Próximos passos identificados:
- Corrigir referências a Militante indefinido nos templates
- Resolver problemas de campos inexistentes
- Corrigir roteamento admin
2025-07-01 13:42:56 -03:00

184 lines
6.2 KiB
Python

from functions.database import get_db_connection, Pagamento, Militante, TipoPagamento
from sqlalchemy.orm import joinedload
from datetime import datetime
from typing import List, Dict, Optional
class PagamentoModel:
"""Model para operações com pagamentos"""
@staticmethod
def criar_pagamento(data: Dict) -> Dict:
"""Cria um novo pagamento"""
db = get_db_connection()
try:
pagamento = Pagamento(
militante_id=data['militante_id'],
tipo_pagamento_id=data.get('tipo_pagamento_id'),
valor=data['valor'],
data_pagamento=data['data_pagamento']
)
db.add(pagamento)
db.commit()
return {
'status': 'success',
'message': 'Pagamento criado com sucesso',
'pagamento_id': pagamento.id
}
except Exception as e:
db.rollback()
return {
'status': 'error',
'message': f'Erro ao criar pagamento: {str(e)}'
}
finally:
db.close()
@staticmethod
def listar_pagamentos() -> List[Pagamento]:
"""Lista todos os pagamentos"""
db = get_db_connection()
try:
return db.query(Pagamento).join(Militante).order_by(Pagamento.data_pagamento.desc()).all()
finally:
db.close()
@staticmethod
def buscar_por_id(pagamento_id: int) -> Optional[Pagamento]:
"""Busca um pagamento por ID"""
db = get_db_connection()
try:
return db.query(Pagamento).get(pagamento_id)
finally:
db.close()
@staticmethod
def atualizar_pagamento(pagamento_id: int, data: Dict) -> Dict:
"""Atualiza um pagamento existente"""
db = get_db_connection()
try:
pagamento = db.query(Pagamento).get(pagamento_id)
if not pagamento:
return {
'status': 'error',
'message': 'Pagamento não encontrado'
}
pagamento.militante_id = data.get('militante_id', pagamento.militante_id)
pagamento.tipo_pagamento_id = data.get('tipo_pagamento_id', pagamento.tipo_pagamento_id)
pagamento.valor = data.get('valor', pagamento.valor)
pagamento.data_pagamento = data.get('data_pagamento', pagamento.data_pagamento)
db.commit()
return {
'status': 'success',
'message': 'Pagamento atualizado com sucesso'
}
except Exception as e:
db.rollback()
return {
'status': 'error',
'message': f'Erro ao atualizar pagamento: {str(e)}'
}
finally:
db.close()
@staticmethod
def excluir_pagamento(pagamento_id: int) -> Dict:
"""Exclui um pagamento"""
db = get_db_connection()
try:
pagamento = db.query(Pagamento).get(pagamento_id)
if not pagamento:
return {
'status': 'error',
'message': 'Pagamento não encontrado'
}
db.delete(pagamento)
db.commit()
return {
'status': 'success',
'message': 'Pagamento excluído com sucesso'
}
except Exception as e:
db.rollback()
return {
'status': 'error',
'message': f'Erro ao excluir pagamento: {str(e)}'
}
finally:
db.close()
@staticmethod
def listar_por_celula(celula_id: int) -> List[Pagamento]:
"""Lista pagamentos de uma célula específica"""
db = get_db_connection()
try:
return db.query(Pagamento).filter_by(celula_id=celula_id).all()
finally:
db.close()
@staticmethod
def listar_por_setor(setor_id: int) -> List[Pagamento]:
"""Lista pagamentos de um setor específico"""
db = get_db_connection()
try:
return db.query(Pagamento).join(Usuario).filter(Usuario.setor_id == setor_id).all()
finally:
db.close()
@staticmethod
def listar_por_cr(cr_id: int) -> List[Pagamento]:
"""Lista pagamentos de um CR específico"""
db = get_db_connection()
try:
return db.query(Pagamento).join(Usuario).filter(Usuario.cr_id == cr_id).all()
finally:
db.close()
@staticmethod
def listar_por_militante(militante_id: int) -> List[Pagamento]:
"""Lista pagamentos de um militante específico"""
db = get_db_connection()
try:
return db.query(Pagamento).filter_by(militante_id=militante_id).order_by(Pagamento.data_pagamento.desc()).all()
finally:
db.close()
@staticmethod
def obter_tipos_pagamento() -> List[TipoPagamento]:
"""Obtém todos os tipos de pagamento"""
db = get_db_connection()
try:
return db.query(TipoPagamento).order_by(TipoPagamento.descricao).all()
finally:
db.close()
@staticmethod
def obter_militantes() -> List[Militante]:
"""Obtém todos os militantes"""
db = get_db_connection()
try:
return db.query(Militante).order_by(Militante.nome).all()
finally:
db.close()
@staticmethod
def formatar_dados_pagamento(pagamento: Pagamento) -> Dict:
"""Formata os dados de um pagamento para retorno JSON"""
return {
'id': pagamento.id,
'militante_id': pagamento.militante_id,
'tipo_pagamento_id': pagamento.tipo_pagamento_id,
'valor': float(pagamento.valor) if pagamento.valor else 0.0,
'data_pagamento': pagamento.data_pagamento.strftime('%Y-%m-%d') if pagamento.data_pagamento else None,
'militante_nome': pagamento.militante.nome if pagamento.militante else None,
'tipo_pagamento_nome': pagamento.tipo_pagamento.descricao if pagamento.tipo_pagamento else None
}