Files
controles/models/pagamento_model.py

184 lines
6.2 KiB
Python
Raw Normal View History

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
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
}