refactor: renomeia pagamentos para comprovantes e adiciona novos tipos

This commit is contained in:
LS
2025-04-15 18:04:50 -03:00
parent f87e03640d
commit 63ebf09fb6
13 changed files with 765 additions and 341 deletions

186
app.py
View File

@@ -1650,6 +1650,192 @@ def create_app():
finally:
db.close()
@app.route("/comprovantes/novo", methods=["GET", "POST"])
@require_login
def novo_comprovante():
if request.method == "POST":
# Verificar permissão para tipos específicos de comprovante
tipo_comprovante = request.form.get('tipo_comprovante')
if tipo_comprovante not in ['1'] and not current_user.has_permission(Permission.MANAGE_PAYMENT_TYPES):
flash('Você não tem permissão para registrar este tipo de comprovante.', 'error')
return redirect(url_for('novo_comprovante'))
try:
militante_id = request.form.get("militante_id")
tipo_comprovante = request.form.get("tipo_comprovante")
data_comprovante = request.form.get("data_comprovante")
if not all([militante_id, tipo_comprovante, data_comprovante]):
flash("Todos os campos são obrigatórios", "danger")
return redirect(url_for("novo_comprovante"))
db = get_db_connection()
comprovante = Comprovante(
militante_id=militante_id,
tipo_comprovante=tipo_comprovante,
data_comprovante=data_comprovante
)
db.add(comprovante)
db.commit()
flash("Comprovante cadastrado com sucesso!", "success")
return redirect(url_for("listar_comprovantes"))
except Exception as e:
db.rollback()
flash(f"Erro ao cadastrar comprovante: {str(e)}", "danger")
return redirect(url_for("novo_comprovante"))
return render_template("novo_comprovante.html")
@app.route("/comprovantes")
@require_login
@require_permission(Permission.MANAGE_CELL_REPORTS)
def listar_comprovantes():
db = get_db_connection()
try:
comprovantes = db.query(Comprovante).join(Militante).all()
militantes = db.query(Militante).all()
return render_template("listar_comprovantes.html",
comprovantes=comprovantes,
militantes=militantes)
finally:
db.close()
@app.route("/comprovantes/adicionar", methods=["POST"])
@require_login
@require_permission(Permission.MANAGE_CELL_REPORTS)
def adicionar_comprovante():
# Verificar permissão para tipos específicos de comprovante
tipo_comprovante = request.form.get('tipo_comprovante')
if tipo_comprovante not in ['1'] and not current_user.has_permission(Permission.MANAGE_PAYMENT_TYPES):
flash('Você não tem permissão para registrar este tipo de comprovante.', 'error')
return redirect(url_for('listar_comprovantes'))
try:
militante_id = request.form.get("militante_id")
tipo_comprovante = request.form.get("tipo_comprovante")
data_comprovante = request.form.get("data_comprovante")
if not all([militante_id, tipo_comprovante, data_comprovante]):
flash("Todos os campos são obrigatórios", "danger")
return redirect(url_for("adicionar_comprovante"))
db = get_db_connection()
comprovante = Comprovante(
militante_id=militante_id,
tipo_comprovante=tipo_comprovante,
data_comprovante=data_comprovante
)
db.add(comprovante)
db.commit()
flash("Comprovante adicionado com sucesso!", "success")
return redirect(url_for("listar_comprovantes"))
except Exception as e:
db.rollback()
flash(f"Erro ao adicionar comprovante: {str(e)}", "danger")
return redirect(url_for("adicionar_comprovante"))
return render_template("adicionar_comprovante.html")
@app.route('/celulas/<int:celula_id>/comprovantes')
@require_login
def list_comprovantes_celula(celula_id):
@require_instance_access('celula', celula_id)
def decorated_function(celula_id):
db = get_db_connection()
try:
comprovantes = db.query(Comprovante).filter_by(celula_id=celula_id).all()
return render_template('comprovantes/list.html', comprovantes=comprovantes)
finally:
db.close()
return decorated_function(celula_id)
@app.route('/setores/<int:setor_id>/comprovantes')
@require_login
def list_comprovantes_setor(setor_id):
@require_instance_access('setor', setor_id)
def decorated_function(setor_id):
db = get_db_connection()
try:
comprovantes = db.query(Comprovante).join(Usuario).filter(Usuario.setor_id == setor_id).all()
return render_template('comprovantes/list.html', comprovantes=comprovantes)
finally:
db.close()
return decorated_function(setor_id)
@app.route('/crs/<int:cr_id>/comprovantes')
@require_login
def list_comprovantes_cr(cr_id):
@require_instance_access('cr', cr_id)
def decorated_function(cr_id):
db = get_db_connection()
try:
comprovantes = db.query(Comprovante).join(Usuario).filter(Usuario.cr_id == cr_id).all()
return render_template('comprovantes/list.html', comprovantes=comprovantes)
finally:
db.close()
return decorated_function(cr_id)
@app.route('/celulas/<int:celula_id>/comprovantes/novo', methods=['GET', 'POST'])
@require_login
@require_instance_permission('REGISTER_CELL_PAYMENT', 'celula_id')
def novo_comprovante_celula(celula_id):
if request.method == 'POST':
db = get_db_connection()
try:
comprovante = Comprovante(
valor=request.form['valor'],
data=request.form['data'],
militante_id=request.form['militante_id'],
celula_id=celula_id
)
db.add(comprovante)
db.commit()
flash('Comprovante registrado com sucesso!', 'success')
return redirect(url_for('list_comprovantes_celula', celula_id=celula_id))
finally:
db.close()
return render_template('comprovantes/form.html')
@app.route('/setores/<int:setor_id>/comprovantes/novo', methods=['GET', 'POST'])
@require_login
@require_instance_permission('REGISTER_SECTOR_PAYMENT', 'setor_id')
def novo_comprovante_setor(setor_id):
if request.method == 'POST':
db = get_db_connection()
try:
comprovante = Comprovante(
valor=request.form['valor'],
data=request.form['data'],
militante_id=request.form['militante_id'],
setor_id=setor_id
)
db.add(comprovante)
db.commit()
flash('Comprovante registrado com sucesso!', 'success')
return redirect(url_for('list_comprovantes_setor', setor_id=setor_id))
finally:
db.close()
return render_template('comprovantes/form.html')
@app.route('/crs/<int:cr_id>/comprovantes/novo', methods=['GET', 'POST'])
@require_login
@require_instance_permission('REGISTER_CR_PAYMENT', 'cr_id')
def novo_comprovante_cr(cr_id):
if request.method == 'POST':
db = get_db_connection()
try:
comprovante = Comprovante(
valor=request.form['valor'],
data=request.form['data'],
militante_id=request.form['militante_id'],
cr_id=cr_id
)
db.add(comprovante)
db.commit()
flash('Comprovante registrado com sucesso!', 'success')
return redirect(url_for('list_comprovantes_cr', cr_id=cr_id))
finally:
db.close()
return render_template('comprovantes/form.html')
return app
def init_system():

23
functions/usuario.py Normal file
View File

@@ -0,0 +1,23 @@
def get_permissoes_por_cargo(cargo_id):
permissoes = {
1: [ # Secretário Geral
'gerenciar_relatorios_celula',
'visualizar_relatorios_celula',
'gerenciar_militantes',
'gerenciar_tipos_comprovante'
],
2: [ # Admin
'gerenciar_relatorios_celula',
'visualizar_relatorios_celula',
'gerenciar_militantes',
'gerenciar_tipos_comprovante'
],
3: [ # Secretário Financeiro do Comitê Central
'gerenciar_relatorios_celula',
'visualizar_relatorios_celula',
'gerenciar_militantes',
'gerenciar_tipos_comprovante'
],
# ... existing code ...
}
return permissoes.get(cargo_id, [])

51
static/js/comprovantes.js Normal file
View File

@@ -0,0 +1,51 @@
$(document).ready(function() {
// Inicialização da tabela
$('#tabelaComprovantes').DataTable({
language: {
url: '//cdn.datatables.net/plug-ins/1.13.7/i18n/pt-BR.json'
}
});
// Modal de edição
$('#modalEditarComprovante').on('show.bs.modal', function(event) {
var button = $(event.relatedTarget);
var comprovanteId = button.data('comprovante-id');
var militanteId = button.data('militante-id');
var militanteNome = button.data('militante-nome');
var tipoComprovante = button.data('tipo-comprovante');
var valor = button.data('valor');
var dataComprovante = button.data('data-comprovante');
var modal = $(this);
modal.find('#editMilitante').val(militanteId);
modal.find('#editMilitanteNome').val(militanteNome);
modal.find('#editTipoComprovante').val(tipoComprovante);
modal.find('#editValor').val(valor);
modal.find('#editDataComprovante').val(dataComprovante);
modal.find('form').attr('action', '/comprovantes/editar/' + comprovanteId);
});
// Modal de exclusão
$('#modalExcluirComprovante').on('show.bs.modal', function(event) {
var button = $(event.relatedTarget);
var comprovanteId = button.data('comprovante-id');
var comprovanteInfo = button.data('comprovante-info');
var modal = $(this);
modal.find('#comprovanteInfo').text(comprovanteInfo);
modal.find('form').attr('action', '/comprovantes/excluir/' + comprovanteId);
});
// Formatação de valores monetários
$('.money').mask('000.000.000.000.000,00', {reverse: true});
// Validação de formulários
$('form').on('submit', function(e) {
if (!this.checkValidity()) {
e.preventDefault();
e.stopPropagation();
}
$(this).addClass('was-validated');
});
});

View File

@@ -1,316 +0,0 @@
document.addEventListener('DOMContentLoaded', function() {
console.log('Carregando script pagamentos.js...');
// Inicializar DataTable
const table = $('#tabelaPagamentos').DataTable({
language: {
url: '//cdn.datatables.net/plug-ins/1.13.7/i18n/pt-BR.json'
},
columnDefs: [
{
targets: 3, // Coluna de data
type: 'date-br',
render: function(data, type, row) {
if (type === 'sort') {
return data.split('/').reverse().join('');
}
return data;
}
},
{
targets: 2, // Coluna de valor
type: 'numeric',
render: function(data, type, row) {
if (type === 'sort') {
return parseFloat(data.replace('R$ ', '').replace(',', '.'));
}
return data;
}
},
{ targets: -1, orderable: false } // Coluna de ações
],
order: [[3, 'desc']] // Ordenar por data decrescente por padrão
});
// Configuração do modal de edição
const modalEditarPagamento = document.getElementById('modalEditarPagamento');
if (modalEditarPagamento) {
modalEditarPagamento.addEventListener('show.bs.modal', function(event) {
console.log('Modal de edição sendo exibido');
const button = event.relatedTarget;
if (!button) {
console.error('Botão não encontrado!');
return;
}
const pagamentoId = button.getAttribute('data-pagamento-id');
console.log('ID do pagamento:', pagamentoId);
// Dados do pagamento
const dados = {
militanteId: button.getAttribute('data-militante-id'),
militanteNome: button.closest('tr').querySelector('td').textContent.trim(),
tipoPagamento: button.getAttribute('data-tipo-pagamento'),
valor: button.getAttribute('data-valor'),
dataPagamento: button.getAttribute('data-data-pagamento')
};
console.log('Dados do pagamento:', dados);
// Preencher campos
document.getElementById('editMilitante').value = dados.militanteId;
document.getElementById('editMilitanteNome').value = dados.militanteNome;
document.getElementById('editTipoPagamento').value = dados.tipoPagamento;
document.getElementById('editValor').value = dados.valor;
document.getElementById('editDataPagamento').value = dados.dataPagamento;
// Configurar formulário
const form = document.getElementById('formEditarPagamento');
if (form) {
form.action = `/pagamentos/editar/${pagamentoId}`;
console.log('Action do formulário:', form.action);
// Remover listeners antigos para evitar duplicação
const newForm = form.cloneNode(true);
form.parentNode.replaceChild(newForm, form);
// Adicionar listener para o submit do formulário
newForm.addEventListener('submit', function(e) {
e.preventDefault();
console.log('Formulário submetido');
// Criar FormData com os dados do formulário
const formData = new FormData(this);
// Log dos dados sendo enviados
console.log('Dados do formulário:');
for (let [key, value] of formData.entries()) {
console.log(key + ': ' + value);
}
// Enviar requisição
fetch(this.action, {
method: 'POST',
body: formData
})
.then(response => {
console.log('Status da resposta:', response.status);
return response.json();
})
.then(data => {
console.log('Resposta:', data);
if (data.status === 'success') {
// Fechar modal
const modal = bootstrap.Modal.getInstance(modalEditarPagamento);
modal.hide();
// Recarregar página
window.location.reload();
} else {
alert('Erro ao atualizar pagamento: ' + data.message);
}
})
.catch(error => {
console.error('Erro:', error);
alert('Erro ao atualizar pagamento. Por favor, tente novamente.');
});
});
}
});
}
// Configuração do modal de exclusão
const modalExcluirPagamento = document.getElementById('modalExcluirPagamento');
if (modalExcluirPagamento) {
modalExcluirPagamento.addEventListener('show.bs.modal', function(event) {
console.log('Modal de exclusão sendo exibido');
const button = event.relatedTarget;
if (!button) {
console.error('Botão não encontrado!');
return;
}
const pagamentoId = button.getAttribute('data-pagamento-id');
const pagamentoInfo = button.getAttribute('data-pagamento-info');
console.log('ID do pagamento:', pagamentoId);
// Atualizar informações no modal
document.getElementById('pagamentoInfo').textContent = pagamentoInfo;
// Configurar formulário
const form = document.getElementById('formExcluirPagamento');
if (form) {
form.action = `/pagamentos/excluir/${pagamentoId}`;
console.log('Action do formulário:', form.action);
// Remover listeners antigos para evitar duplicação
const newForm = form.cloneNode(true);
form.parentNode.replaceChild(newForm, form);
// Adicionar listener para o submit do formulário
newForm.addEventListener('submit', function(e) {
e.preventDefault();
console.log('Formulário submetido');
// Enviar requisição
fetch(this.action, {
method: 'POST'
})
.then(response => {
console.log('Status da resposta:', response.status);
return response.json();
})
.then(data => {
console.log('Resposta:', data);
if (data.status === 'success') {
// Fechar modal
const modal = bootstrap.Modal.getInstance(modalExcluirPagamento);
modal.hide();
// Recarregar página
window.location.reload();
} else {
alert('Erro ao excluir pagamento: ' + data.message);
}
})
.catch(error => {
console.error('Erro:', error);
alert('Erro ao excluir pagamento. Por favor, tente novamente.');
});
});
}
});
}
// Configuração do formulário de novo pagamento
const formNovoPagamento = document.getElementById('formNovoPagamento');
if (formNovoPagamento) {
formNovoPagamento.addEventListener('submit', function(e) {
e.preventDefault();
console.log('Formulário de novo pagamento submetido');
// Criar FormData com os dados do formulário
const formData = new FormData(this);
// Log dos dados sendo enviados
console.log('Dados do formulário:');
for (let [key, value] of formData.entries()) {
console.log(key + ': ' + value);
}
// Enviar requisição
fetch(this.action, {
method: 'POST',
body: formData
})
.then(response => {
console.log('Status da resposta:', response.status);
return response.json();
})
.then(data => {
console.log('Resposta:', data);
if (data.status === 'success') {
// Fechar modal
const modal = bootstrap.Modal.getInstance(document.getElementById('modalNovoPagamento'));
modal.hide();
// Recarregar página
window.location.reload();
} else {
alert('Erro ao adicionar pagamento: ' + data.message);
}
})
.catch(error => {
console.error('Erro:', error);
alert('Erro ao adicionar pagamento. Por favor, tente novamente.');
});
});
}
// Configuração do botão de exportar
const btnExportar = document.getElementById('btnExportar');
if (btnExportar) {
btnExportar.addEventListener('click', function() {
console.log('Exportando dados...');
// Coletar dados da tabela
const dados = [];
table.rows().every(function() {
const row = this.data();
dados.push({
militante: row[0],
tipo_pagamento: row[1],
valor: row[2].replace('R$ ', ''),
data_pagamento: row[3]
});
});
// Converter para CSV
const csv = [
['Militante', 'Tipo de Pagamento', 'Valor', 'Data do Pagamento'],
...dados.map(row => [
row.militante,
row.tipo_pagamento,
row.valor,
row.data_pagamento
])
]
.map(row => row.join(','))
.join('\n');
// Criar blob e fazer download
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
if (link.download !== undefined) {
const url = URL.createObjectURL(blob);
link.setAttribute('href', url);
link.setAttribute('download', 'pagamentos.csv');
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
});
}
// Funções de validação e formatação de datas
function validarData(data) {
if (!data) return false;
const dataObj = new Date(data);
if (isNaN(dataObj.getTime())) return false;
const hoje = new Date();
hoje.setHours(0, 0, 0, 0);
return dataObj <= hoje;
}
function formatarData(data) {
if (!data) return '';
const dataObj = new Date(data);
if (isNaN(dataObj.getTime())) return '';
return dataObj.toLocaleDateString('pt-BR');
}
// Configurar campos de data
const camposData = document.querySelectorAll('input[type="date"]');
camposData.forEach(campo => {
// Definir data máxima como hoje
const hoje = new Date().toISOString().split('T')[0];
campo.setAttribute('max', hoje);
campo.addEventListener('change', function() {
if (!validarData(this.value)) {
this.setCustomValidity('Data inválida ou futura');
this.classList.add('is-invalid');
} else {
this.setCustomValidity('');
this.classList.remove('is-invalid');
}
});
});
});

View File

@@ -0,0 +1,43 @@
{% extends 'base.html' %}
{% block title %}Editar Comprovante{% endblock %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-8 offset-md-2">
<div class="card shadow-sm">
<div class="card-header bg-light">
<h4 class="card-title mb-0">
<i class="fas fa-money-bill-wave me-2"></i>Editar Comprovante
</h4>
</div>
<div class="card-body">
<form method="post">
{% csrf_token %}
<div class="form-group">
<label for="tipo_comprovante_id">Tipo de Comprovante</label>
<select class="form-control" id="tipo_comprovante_id" name="tipo_comprovante_id" required>
<option value="1" {% if comprovante.tipo_comprovante_id == 1 %}selected{% endif %}>1 - Comprovante Padrão</option>
{% if current_user.has_permission('gerenciar_tipos_comprovante') %}
<option value="2" {% if comprovante.tipo_comprovante_id == 2 %}selected{% endif %}>2 - Comprovante Especial</option>
<option value="3" {% if comprovante.tipo_comprovante_id == 3 %}selected{% endif %}>3 - Comprovante Extraordinário</option>
<option value="4" {% if comprovante.tipo_comprovante_id == 4 %}selected{% endif %}>4 - Jornal Avulso</option>
<option value="5" {% if comprovante.tipo_comprovante_id == 5 %}selected{% endif %}>5 - Assinatura de Jornal</option>
<option value="6" {% if comprovante.tipo_comprovante_id == 6 %}selected{% endif %}>6 - Campanha Financeira</option>
{% endif %}
</select>
</div>
<div class="mb-3">
<label for="data_pagamento" class="form-label">Data do Comprovante:</label>
<input type="date" class="form-control" id="data_pagamento" name="data_pagamento"
required max="{{ hoje }}">
</div>
<button type="submit" class="btn btn-primary">Salvar</button>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,37 @@
{% extends 'base.html' %}
{% block title %}Editar Comprovante{% endblock %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-8 offset-md-2">
<div class="card shadow-sm">
<div class="card-header bg-light">
<h4 class="card-title mb-0">
<i class="fas fa-money-bill-wave me-2"></i>Editar Comprovante
</h4>
</div>
<div class="card-body">
<form method="post">
{% csrf_token %}
<div class="mb-3">
<label for="tipo_pagamento_id" class="form-label">Tipo de Comprovante:</label>
<select class="form-select" id="tipo_pagamento_id" name="tipo_pagamento_id" required>
<option value="">Selecione o tipo de comprovante</option>
<!-- Add your options here -->
</select>
</div>
<div class="mb-3">
<label for="data_pagamento" class="form-label">Data do Comprovante:</label>
<input type="date" class="form-control" id="data_pagamento" name="data_pagamento"
required max="{{ hoje }}">
</div>
<button type="submit" class="btn btn-primary">Salvar</button>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,49 @@
{% extends 'base.html' %}
{% block title %}Lista de Comprovantes{% endblock %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="card shadow-sm">
<div class="card-header bg-light">
<h4 class="card-title mb-0">
<i class="fas fa-list me-2"></i>Lista de Comprovantes
</h4>
</div>
<div class="card-body">
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>Data</th>
<th>Valor</th>
<th>Tipo</th>
<th>Ações</th>
</tr>
</thead>
<tbody>
{% for comprovante in comprovantes %}
<tr>
<td>{{ comprovante.id }}</td>
<td>{{ comprovante.data.strftime('%d/%m/%Y') }}</td>
<td>R$ {{ "%.2f"|format(comprovante.valor) }}</td>
<td>
{% if comprovante.tipo_comprovante_id == 1 %}
1 - Comprovante Padrão
{% elif current_user.has_permission('gerenciar_tipos_comprovante') %}
{% if comprovante.tipo_comprovante_id == 2 %}
2 - Comprovante Especial
<td>{{ comprovante.tipo }}</td>
<td>{{ comprovante.data }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,37 @@
{% extends 'base.html' %}
{% block title %}Lista de Comprovantes{% endblock %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="card shadow-sm">
<div class="card-header bg-light">
<h4 class="card-title mb-0">
<i class="fas fa-list me-2"></i>Lista de Comprovantes
</h4>
</div>
<div class="card-body">
<table class="table table-striped">
<thead>
<tr>
<th>Tipo de Comprovante</th>
<th>Data do Comprovante</th>
</tr>
</thead>
<tbody>
{% for comprovante in comprovantes %}
<tr>
<td>{{ comprovante.tipo }}</td>
<td>{{ comprovante.data }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,219 @@
{% extends "base.html" %}
{% block title %}Comprovantes{% endblock %}
{% block content %}
<div class="container-fluid mt-3">
<div class="d-flex justify-content-between align-items-center mb-3">
<h2><i class="fas fa-money-bill-wave"></i> Comprovantes</h2>
<div>
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#modalNovoComprovante">
<i class="fas fa-plus"></i> Novo Comprovante
</button>
<button type="button" class="btn btn-outline-primary" id="btnExportar">
<i class="fas fa-file-export"></i> Exportar
</button>
</div>
</div>
<div class="card">
<div class="card-body">
<div class="table-responsive">
<table class="table table-striped table-hover" id="tabelaComprovantes">
<thead>
<tr>
<th>Militante</th>
<th>Tipo de Comprovante</th>
<th>Valor</th>
<th>Data do Comprovante</th>
<th>Ações</th>
</tr>
</thead>
<tbody>
{% for comprovante in comprovantes %}
<tr>
<td data-militante="{{ comprovante.militante_id }}">{{ comprovante.militante.nome if comprovante.militante else 'N/A' }}</td>
<td data-tipo="{{ comprovante.tipo_comprovante }}">
{% if comprovante.tipo_comprovante == 1 %}
Cota
{% elif comprovante.tipo_comprovante == 2 %}
Contribuição Extra
{% elif comprovante.tipo_comprovante == 3 %}
Doação
{% elif comprovante.tipo_comprovante == 4 %}
Taxa de Evento
{% elif comprovante.tipo_comprovante == 5 %}
Jornal Avulso
{% elif comprovante.tipo_comprovante == 6 %}
Assinatura de Jornal
{% elif comprovante.tipo_comprovante == 7 %}
Campanha Financeira
{% elif comprovante.tipo_comprovante == 8 %}
Outros
{% else %}
Não Definido
{% endif %}
</td>
<td data-valor="{{ comprovante.valor }}">R$ {{ "%.2f"|format(comprovante.valor) }}</td>
<td data-data="{{ comprovante.data_comprovante }}">{{ comprovante.data_comprovante.strftime('%d/%m/%Y') }}</td>
<td>
<button type="button"
class="btn btn-sm btn-outline-primary"
data-bs-toggle="modal"
data-bs-target="#modalEditarComprovante"
data-comprovante-id="{{ comprovante.id }}"
data-militante-id="{{ comprovante.militante_id }}"
data-tipo-comprovante="{{ comprovante.tipo_comprovante }}"
data-valor="{{ comprovante.valor }}"
data-data-comprovante="{{ comprovante.data_comprovante.strftime('%Y-%m-%d') }}"
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="#modalExcluirComprovante"
data-comprovante-id="{{ comprovante.id }}"
data-comprovante-info="Comprovante de {{ comprovante.militante.nome if comprovante.militante else 'N/A' }} - R$ {{ "%.2f"|format(comprovante.valor) }}"
title="Excluir">
<i class="fas fa-trash"></i>
</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Modal Novo Comprovante -->
<div class="modal fade" id="modalNovoComprovante" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-plus"></i> Novo Comprovante</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="formNovoComprovante" method="post" action="{{ url_for('adicionar_comprovante') }}">
<div class="mb-3">
<label for="militante" class="form-label">Militante:</label>
<select class="form-select" id="militante" 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="tipoComprovante" class="form-label">Tipo de Comprovante:</label>
<select class="form-select" id="tipoComprovante" name="tipo_comprovante" required>
<option value="">Selecione o tipo</option>
<option value="1">Cota</option>
{% if current_user.has_permission('gerenciar_tipos_comprovante') %}
<option value="2">Contribuição Extra</option>
<option value="3">Doação</option>
<option value="4">Taxa de Evento</option>
<option value="5">Jornal Avulso</option>
<option value="6">Assinatura de Jornal</option>
<option value="7">Campanha Financeira</option>
<option value="8">Outros</option>
{% endif %}
</select>
</div>
<div class="mb-3">
<label for="valor" class="form-label">Valor:</label>
<input type="number" step="0.01" class="form-control" id="valor" name="valor" required>
</div>
<div class="mb-3">
<label for="dataComprovante" class="form-label">Data do Comprovante:</label>
<input type="date" class="form-control" id="dataComprovante" name="data_comprovante" required>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
<button type="submit" class="btn btn-primary">Salvar</button>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- Modal Editar Comprovante -->
<div class="modal fade" id="modalEditarComprovante" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-edit"></i> Editar Comprovante</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="formEditarComprovante" method="post">
<div class="mb-3">
<label for="editMilitante" 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="editTipoComprovante" class="form-label">Tipo de Comprovante:</label>
<select class="form-select" id="editTipoComprovante" name="tipo_comprovante" required>
<option value="">Selecione o tipo</option>
<option value="1">Cota</option>
{% if current_user.has_permission('gerenciar_tipos_comprovante') %}
<option value="2">Contribuição Extra</option>
<option value="3">Doação</option>
<option value="4">Taxa de Evento</option>
<option value="5">Jornal Avulso</option>
<option value="6">Assinatura de Jornal</option>
<option value="7">Campanha Financeira</option>
<option value="8">Outros</option>
{% endif %}
</select>
</div>
<div class="mb-3">
<label for="editValor" class="form-label">Valor:</label>
<input type="number" step="0.01" class="form-control" id="editValor" name="valor" required>
</div>
<div class="mb-3">
<label for="editDataComprovante" class="form-label">Data do Comprovante:</label>
<input type="date" class="form-control" id="editDataComprovante" name="data_comprovante" required>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
<button type="submit" class="btn btn-primary">Salvar</button>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- Modal Excluir Comprovante -->
<div class="modal fade" id="modalExcluirComprovante" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-trash"></i> Excluir Comprovante</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p>Tem certeza que deseja excluir este comprovante?</p>
<p id="comprovanteInfo" class="text-muted"></p>
</div>
<div class="modal-footer">
<form id="formExcluirComprovante" method="post">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
<button type="submit" class="btn btn-danger">Excluir</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script src="{{ url_for('static', filename='js/comprovantes.js') }}"></script>
{% endblock %}

View File

@@ -1,14 +1,14 @@
{% extends "base.html" %}
{% block title %}Pagamentos{% endblock %}
{% block title %}Comprovantes{% endblock %}
{% block content %}
<div class="container-fluid mt-3">
<div class="d-flex justify-content-between align-items-center mb-3">
<h2><i class="fas fa-money-bill-wave"></i> Pagamentos</h2>
<h2><i class="fas fa-money-bill-wave"></i> Comprovantes</h2>
<div>
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#modalNovoPagamento">
<i class="fas fa-plus"></i> Novo Pagamento
<i class="fas fa-plus"></i> Novo Comprovante
</button>
<button type="button" class="btn btn-outline-primary" id="btnExportar">
<i class="fas fa-file-export"></i> Exportar
@@ -23,9 +23,9 @@
<thead>
<tr>
<th>Militante</th>
<th>Tipo de Pagamento</th>
<th>Tipo de Comprovante</th>
<th>Valor</th>
<th>Data do Pagamento</th>
<th>Data do Comprovante</th>
<th>Ações</th>
</tr>
</thead>
@@ -35,7 +35,7 @@
<td data-militante="{{ pagamento.militante_id }}">{{ pagamento.militante.nome if pagamento.militante else 'N/A' }}</td>
<td data-tipo="{{ pagamento.tipo_pagamento }}">
{% if pagamento.tipo_pagamento == 1 %}
Mensalidade
Cota
{% elif pagamento.tipo_pagamento == 2 %}
Contribuição Extra
{% elif pagamento.tipo_pagamento == 3 %}
@@ -68,7 +68,7 @@
data-bs-toggle="modal"
data-bs-target="#modalExcluirPagamento"
data-pagamento-id="{{ pagamento.id }}"
data-pagamento-info="Pagamento de {{ pagamento.militante.nome if pagamento.militante else 'N/A' }} - R$ {{ "%.2f"|format(pagamento.valor) }}"
data-pagamento-info="Comprovante de {{ pagamento.militante.nome if pagamento.militante else 'N/A' }} - R$ {{ "%.2f"|format(pagamento.valor) }}"
title="Excluir">
<i class="fas fa-trash"></i>
</button>
@@ -87,7 +87,7 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-plus"></i> Novo Pagamento</h5>
<h5 class="modal-title"><i class="fas fa-plus"></i> Novo Comprovante</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
@@ -102,10 +102,10 @@
</select>
</div>
<div class="mb-3">
<label for="tipoPagamento" class="form-label">Tipo de Pagamento:</label>
<label for="tipoPagamento" class="form-label">Tipo de Comprovante:</label>
<select class="form-select" id="tipoPagamento" name="tipo_pagamento" required>
<option value="">Selecione o tipo</option>
<option value="1">Mensalidade</option>
<option value="1">Cota</option>
<option value="2">Contribuição Extra</option>
<option value="3">Doação</option>
<option value="4">Taxa de Evento</option>
@@ -117,7 +117,7 @@
<input type="number" step="0.01" class="form-control" id="valor" name="valor" required>
</div>
<div class="mb-3">
<label for="dataPagamento" class="form-label">Data do Pagamento:</label>
<label for="dataPagamento" class="form-label">Data do Comprovante:</label>
<input type="date" class="form-control" id="dataPagamento" name="data_pagamento" required>
</div>
<div class="modal-footer">
@@ -135,7 +135,7 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-edit"></i> Editar Pagamento</h5>
<h5 class="modal-title"><i class="fas fa-edit"></i> Editar Comprovante</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
@@ -146,10 +146,10 @@
<input type="hidden" id="editMilitante" name="militante_id">
</div>
<div class="mb-3">
<label for="editTipoPagamento" class="form-label">Tipo de Pagamento:</label>
<label for="editTipoPagamento" class="form-label">Tipo de Comprovante:</label>
<select class="form-select" id="editTipoPagamento" name="tipo_pagamento" required>
<option value="">Selecione o tipo</option>
<option value="1">Mensalidade</option>
<option value="1">Cota</option>
<option value="2">Contribuição Extra</option>
<option value="3">Doação</option>
<option value="4">Taxa de Evento</option>
@@ -161,7 +161,7 @@
<input type="number" step="0.01" class="form-control" id="editValor" name="valor" required>
</div>
<div class="mb-3">
<label for="editDataPagamento" class="form-label">Data do Pagamento:</label>
<label for="editDataPagamento" class="form-label">Data do Comprovante:</label>
<input type="date" class="form-control" id="editDataPagamento" name="data_pagamento" required>
</div>
<div class="modal-footer">
@@ -179,11 +179,11 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-trash"></i> Excluir Pagamento</h5>
<h5 class="modal-title"><i class="fas fa-trash"></i> Excluir Comprovante</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p>Tem certeza que deseja excluir este pagamento?</p>
<p>Tem certeza que deseja excluir este comprovante?</p>
<p id="pagamentoInfo" class="text-muted"></p>
</div>
<div class="modal-footer">

View File

@@ -0,0 +1,95 @@
{% extends 'base.html' %}
{% block title %}Novo Comprovante{% endblock %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-8 offset-md-2">
<div class="card shadow-sm">
<div class="card-header bg-light">
<h4 class="card-title mb-0">
<i class="fas fa-money-bill-wave me-2"></i>Registrar Novo Comprovante
</h4>
</div>
<div class="card-body">
<form method="post" class="needs-validation" novalidate>
<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 class="invalid-feedback">
Por favor, selecione um militante.
</div>
</div>
<div class="form-group">
<label for="tipo_comprovante_id">Tipo de Comprovante</label>
<select class="form-control" id="tipo_comprovante_id" name="tipo_comprovante_id" required>
<option value="1">1 - Comprovante Padrão</option>
{% if current_user.has_permission('gerenciar_tipos_comprovante') %}
<option value="2">2 - Comprovante Especial</option>
<option value="3">3 - Comprovante Extraordinário</option>
<option value="4">4 - Jornal Avulso</option>
<option value="5">5 - Assinatura de Jornal</option>
<option value="6">6 - Campanha Financeira</option>
{% endif %}
</select>
</div>
<div class="mb-3">
<label for="valor" class="form-label">Valor:</label>
<div class="input-group">
<span class="input-group-text">R$</span>
<input type="text" class="form-control money" id="valor" name="valor" required>
</div>
<div class="invalid-feedback">
Por favor, informe um valor válido.
</div>
</div>
<div class="mb-3">
<label for="data_comprovante" class="form-label">Data do Comprovante:</label>
<input type="date" class="form-control" id="data_comprovante" name="data_comprovante"
required max="{{ hoje }}">
<div class="invalid-feedback">
Por favor, informe uma data válida.
</div>
</div>
<div class="d-flex gap-2">
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-1"></i>Registrar
</button>
<a href="{{ url_for('listar_comprovantes') }}" class="btn btn-secondary">
<i class="fas fa-arrow-left me-1"></i>Voltar
</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.mask/1.14.16/jquery.mask.min.js"></script>
<script>
$(document).ready(function(){
$('.money').mask('000.000.000.000.000,00', {reverse: true});
// Converter valor para formato aceito pelo backend
$('form').on('submit', function(e) {
e.preventDefault();
const valor = $('#valor').val().replace(/\./g, '').replace(',', '.');
$('#valor').val(valor);
this.submit();
});
});
</script>
{% endblock %}

View File

@@ -1,6 +1,6 @@
{% extends 'base.html' %}
{% block title %}Novo Pagamento{% endblock %}
{% block title %}Novo Comprovante{% endblock %}
{% block content %}
<div class="container">
@@ -9,7 +9,7 @@
<div class="card shadow-sm">
<div class="card-header bg-light">
<h4 class="card-title mb-0">
<i class="fas fa-money-bill-wave me-2"></i>Registrar Novo Pagamento
<i class="fas fa-money-bill-wave me-2"></i>Registrar Novo Comprovante
</h4>
</div>
<div class="card-body">
@@ -28,15 +28,15 @@
</div>
<div class="mb-3">
<label for="tipo_pagamento_id" class="form-label">Tipo de Pagamento:</label>
<label for="tipo_pagamento_id" class="form-label">Tipo de Comprovante:</label>
<select class="form-select" id="tipo_pagamento_id" name="tipo_pagamento_id" required>
<option value="">Selecione o tipo de pagamento</option>
<option value="">Selecione o tipo de comprovante</option>
{% for tipo in tipos_pagamento %}
<option value="{{ tipo.id }}">{{ tipo.descricao }}</option>
{% endfor %}
</select>
<div class="invalid-feedback">
Por favor, selecione o tipo de pagamento.
Por favor, selecione o tipo de comprovante.
</div>
</div>
@@ -52,7 +52,7 @@
</div>
<div class="mb-3">
<label for="data_pagamento" class="form-label">Data do Pagamento:</label>
<label for="data_pagamento" class="form-label">Data do Comprovante:</label>
<input type="date" class="form-control" id="data_pagamento" name="data_pagamento"
required max="{{ hoje }}">
<div class="invalid-feedback">

View File

@@ -1,12 +1,12 @@
{% extends 'base.html' %}
{% block title %}Novo Relatório de Pagamentos{% endblock %}
{% block title %}Novo Relatório de Cotas{% endblock %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-12">
<h1 class="mb-4">Novo Relatório de Pagamentos</h1>
<h1 class="mb-4">Novo Relatório de Cotas</h1>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}