Files
controles/templates/listar_cotas.html
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

324 lines
13 KiB
HTML

{% extends "base.html" %}
{% block title %}Cotas{% endblock %}
{% block content %}
<div class="row mb-4">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center">
<h1 class="mb-0">
<i class="fas fa-money-bill me-2"></i>Cotas
</h1>
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#modalNovaCota">
<i class="fas fa-plus me-2"></i>Nova Cota
</button>
</div>
</div>
</div>
<div class="card shadow-sm">
<div class="card-body">
<div class="row mb-4">
<div class="col-md-6">
<div class="input-group">
<span class="input-group-text">
<i class="fas fa-search"></i>
</span>
<input type="text" class="form-control" id="searchInput" placeholder="Pesquisar cotas...">
</div>
</div>
<div class="col-md-6 text-end">
<button id="btnExportar" class="btn btn-outline-primary">
<i class="fas fa-download me-2"></i>Exportar
</button>
</div>
</div>
<div class="table-responsive">
<table class="table table-hover" id="cotasTable">
<thead>
<tr>
<th data-sort="militante">Militante <i class="fas fa-sort"></i></th>
<th data-sort="valor_antigo">Valor Antigo <i class="fas fa-sort"></i></th>
<th data-sort="valor_novo">Valor Novo <i class="fas fa-sort"></i></th>
<th data-sort="data_alteracao">Data de Alteração <i class="fas fa-sort"></i></th>
<th data-sort="data_vencimento">Data de Vencimento <i class="fas fa-sort"></i></th>
<th data-sort="status">Status <i class="fas fa-sort"></i></th>
<th class="text-end">Ações</th>
</tr>
</thead>
<tbody>
{% for cota in cotas %}
<tr>
<td data-militante="{{ cota.militante.nome }}">{{ cota.militante.nome }}</td>
<td data-valor_antigo="{{ cota.valor_antigo }}">R$ {{ "%.2f"|format(cota.valor_antigo) }}</td>
<td data-valor_novo="{{ cota.valor_novo }}">R$ {{ "%.2f"|format(cota.valor_novo) }}</td>
<td data-data_alteracao="{{ cota.data_alteracao }}">{{ cota.data_alteracao.strftime('%d/%m/%Y') }}</td>
<td data-data_vencimento="{{ cota.data_vencimento }}">{{ cota.data_vencimento.strftime('%d/%m/%Y') }}</td>
<td data-status="{{ cota.status }}">
{% if cota.status == 'paga' %}
<span class="badge bg-success">Paga</span>
{% elif cota.status == 'atrasada' %}
<span class="badge bg-danger">Atrasada</span>
{% else %}
<span class="badge bg-warning text-dark">Pendente</span>
{% endif %}
</td>
<td class="text-end">
<div class="btn-group">
<button type="button"
class="btn btn-sm btn-outline-primary"
data-bs-toggle="modal"
data-bs-target="#modalEditarCota"
data-cota-id="{{ cota.id }}"
data-cota-militante="{{ cota.militante_id }}"
data-cota-valor-antigo="{{ cota.valor_antigo }}"
data-cota-valor-novo="{{ cota.valor_novo }}"
data-cota-data-alteracao="{{ cota.data_alteracao.strftime('%Y-%m-%d') }}"
data-cota-data-vencimento="{{ cota.data_vencimento.strftime('%Y-%m-%d') }}"
data-cota-pago="{{ 'true' if cota.pago else 'false' }}"
title="Editar">
<i class="fas fa-edit"></i>
</button>
<button type="button"
class="btn btn-sm btn-outline-danger"
data-bs-toggle="modal"
data-bs-target="#deleteModal"
data-cota-id="{{ cota.id }}"
data-cota-info="{{ cota.militante.nome }} - R$ {{ "%.2f"|format(cota.valor_novo) }}"
title="Excluir">
<i class="fas fa-trash"></i>
</button>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<!-- Modal Nova Cota -->
<div class="modal fade" id="modalNovaCota" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
<i class="fas fa-plus me-2"></i>Nova Cota
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="formNovaCota" method="post" action="{{ url_for('cota.novo') }}">
<div class="mb-3">
<label for="militante_id" class="form-label">Militante:</label>
<select class="form-select" id="militante_id" name="militante_id" required>
<option value="">Selecione um militante</option>
{% for militante in militantes %}
<option value="{{ militante.id }}">{{ militante.nome }}</option>
{% endfor %}
</select>
</div>
<div class="mb-3">
<label for="valor_antigo" class="form-label">Valor Antigo:</label>
<input type="number" step="0.01" class="form-control" id="valor_antigo" name="valor_antigo" required>
</div>
<div class="mb-3">
<label for="valor_novo" class="form-label">Valor Novo:</label>
<input type="number" step="0.01" class="form-control" id="valor_novo" name="valor_novo" required>
</div>
<div class="mb-3">
<label for="data_alteracao" class="form-label">Data de Alteração:</label>
<input type="date" class="form-control" id="data_alteracao" name="data_alteracao" required>
</div>
<div class="mb-3">
<label for="data_vencimento" class="form-label">Data de Vencimento:</label>
<input type="date" class="form-control" id="data_vencimento" name="data_vencimento" required>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
<button type="submit" form="formNovaCota" class="btn btn-success">
<i class="fas fa-save me-2"></i>Salvar
</button>
</div>
</div>
</div>
</div>
<!-- Modal de Edição -->
<div class="modal fade" id="modalEditarCota" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
<i class="fas fa-edit me-2"></i>Editar Cota
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="formEditarCota" method="post">
<div class="mb-3">
<label for="editMilitanteNome" class="form-label">Militante:</label>
<input type="text" class="form-control bg-light" id="editMilitanteNome" readonly>
<input type="hidden" id="editMilitante" name="militante_id">
</div>
<div class="mb-3">
<label for="editValorAntigo" class="form-label">Valor Antigo:</label>
<input type="number" step="0.01" class="form-control" id="editValorAntigo" name="valor_antigo" required>
</div>
<div class="mb-3">
<label for="editValorNovo" class="form-label">Valor Novo:</label>
<input type="number" step="0.01" class="form-control" id="editValorNovo" name="valor_novo" required>
</div>
<div class="mb-3">
<label for="editDataAlteracao" class="form-label">Data de Alteração:</label>
<input type="date" class="form-control" id="editDataAlteracao" name="data_alteracao" required>
</div>
<div class="mb-3">
<label for="editDataVencimento" class="form-label">Data de Vencimento:</label>
<input type="date" class="form-control" id="editDataVencimento" name="data_vencimento" required>
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="editPago" name="pago">
<label class="form-check-label" for="editPago">Pago</label>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
<button type="submit" form="formEditarCota" class="btn btn-success">
<i class="fas fa-save me-2"></i>Salvar
</button>
</div>
</div>
</div>
</div>
<!-- Modal de Exclusão -->
<div class="modal fade" id="deleteModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Confirmar Exclusão</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p>Tem certeza que deseja excluir a cota de <strong id="cotaInfo"></strong>?</p>
<p class="text-danger mb-0">Esta ação não pode ser desfeita.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
<form action="" method="POST" id="deleteForm" class="d-inline">
<button type="submit" class="btn btn-danger">
<i class="fas fa-trash me-2"></i>Excluir
</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script src="{{ url_for('static', filename='js/cotas.js') }}"></script>
{% endblock %}
{% block extra_css %}
<style>
/* Estilo para colunas ordenáveis */
th[data-sort] {
cursor: pointer;
user-select: none;
}
th[data-sort] i {
margin-left: 5px;
color: #ccc;
}
th[data-sort].sort-asc i,
th[data-sort].sort-desc i {
color: var(--primary-color);
}
/* Animação para linhas da tabela */
#cotasTable tbody tr {
transition: all 0.3s ease;
}
#cotasTable tbody tr:hover {
background-color: rgba(0,0,0,0.02);
transform: translateX(5px);
}
/* Estilo para botões de ação */
.btn-group .btn {
padding: 0.25rem 0.5rem;
}
.btn-group .btn i {
width: 16px;
text-align: center;
}
/* Responsividade */
@media (max-width: 768px) {
.btn-group {
display: flex;
margin-top: 1rem;
}
.btn-group .btn {
flex: 1;
}
}
/* Estilo para o backdrop com blur em todos os modais */
.modal-backdrop.show {
backdrop-filter: blur(8px);
background-color: rgba(0, 0, 0, 0.7);
}
/* Estilo para o botão de fechar dos modais */
.btn-close {
background-color: transparent;
padding: 0.5rem;
opacity: 0.8;
transition: opacity 0.2s;
filter: invert(1) grayscale(100%) brightness(200%);
}
.btn-close:hover {
opacity: 1;
background-color: transparent;
}
/* Estilo para modais */
.modal-content {
border: none;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
}
.modal-header {
background: linear-gradient(to right, var(--bs-gray-dark), var(--bs-gray));
color: white;
border-radius: 12px 12px 0 0;
border-bottom: none;
padding: 1rem;
}
.modal-body {
padding: 1.5rem;
}
.modal-footer {
border-top: 1px solid #eee;
padding: 1rem;
}
</style>
{% endblock %}