fix: corrige edição de cotas - Ajusta exibição do militante e valores no modal de edição

This commit is contained in:
andersonid
2025-04-03 17:08:53 -03:00
parent 9cc3f408f8
commit 77cf5ad99c
3 changed files with 186 additions and 211 deletions

43
app.py
View File

@@ -519,13 +519,21 @@ def editar_cota(id):
if request.method == 'POST': if request.method == 'POST':
try: try:
print("Dados recebidos:", request.form)
cota.militante_id = int(request.form['militante_id']) cota.militante_id = int(request.form['militante_id'])
cota.valor_antigo = float(request.form['valor_antigo']) cota.valor_antigo = float(request.form['valor_antigo'])
cota.valor_novo = float(request.form['valor_novo']) cota.valor_novo = float(request.form['valor_novo'])
cota.data_alteracao = datetime.strptime(request.form['data_alteracao'], '%Y-%m-%d').date() cota.data_alteracao = datetime.strptime(request.form['data_alteracao'], '%Y-%m-%d').date()
cota.data_vencimento = datetime.strptime(request.form['data_vencimento'], '%Y-%m-%d').date() cota.data_vencimento = datetime.strptime(request.form['data_vencimento'], '%Y-%m-%d').date()
# Processar o campo pago
pago = request.form.get('pago', '').lower()
print("Valor do campo pago recebido:", pago)
cota.pago = pago == 'true'
print("Status final do pago:", cota.pago)
db.commit() db.commit()
print("Commit realizado com sucesso")
if request.headers.get('X-Requested-With') == 'XMLHttpRequest': if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return jsonify({ return jsonify({
@@ -538,12 +546,15 @@ def editar_cota(id):
except Exception as e: except Exception as e:
db.rollback() db.rollback()
print(f"Erro ao atualizar cota: {e}") print(f"Erro ao atualizar cota: {str(e)}")
print(f"Tipo do erro: {type(e)}")
import traceback
traceback.print_exc()
if request.headers.get('X-Requested-With') == 'XMLHttpRequest': if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return jsonify({ return jsonify({
'status': 'error', 'status': 'error',
'message': 'Erro ao atualizar cota. Verifique os dados e tente novamente.' 'message': f'Erro ao atualizar cota: {str(e)}'
}), 400 }), 400
flash('Erro ao atualizar cota. Verifique os dados e tente novamente.', 'danger') flash('Erro ao atualizar cota. Verifique os dados e tente novamente.', 'danger')
@@ -552,11 +563,11 @@ def editar_cota(id):
return render_template('editar_cota.html', cota=cota) return render_template('editar_cota.html', cota=cota)
except Exception as e: except Exception as e:
print(f"Erro ao carregar cota: {e}") print(f"Erro ao carregar cota: {str(e)}")
if request.headers.get('X-Requested-With') == 'XMLHttpRequest': if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return jsonify({ return jsonify({
'status': 'error', 'status': 'error',
'message': 'Erro ao carregar cota.' 'message': f'Erro ao carregar cota: {str(e)}'
}), 404 }), 404
flash('Erro ao carregar cota', 'danger') flash('Erro ao carregar cota', 'danger')
return redirect(url_for('listar_cotas')) return redirect(url_for('listar_cotas'))
@@ -1585,6 +1596,30 @@ def delete_pagamento(id):
db.close() db.close()
>>>>>>> 324660d (refactor: melhorias na interface e funcionalidades - Atualização do layout do dashboard com Bootstrap 5 - Remoção do template editar_pagamento.html (integrado ao modal) - Melhorias no template home.html com cards estatísticos - Ajustes nos estilos e responsividade - Correções nas rotas e conexões do banco de dados - Implementação do modal de edição de pagamentos - Adição de efeitos hover e melhorias visuais) >>>>>>> 324660d (refactor: melhorias na interface e funcionalidades - Atualização do layout do dashboard com Bootstrap 5 - Remoção do template editar_pagamento.html (integrado ao modal) - Melhorias no template home.html com cards estatísticos - Ajustes nos estilos e responsividade - Correções nas rotas e conexões do banco de dados - Implementação do modal de edição de pagamentos - Adição de efeitos hover e melhorias visuais)
@app.route("/cotas/excluir/<int:id>", methods=["POST"])
@login_required
@session_timeout
def excluir_cota(id):
"""Exclui uma cota mensal"""
db = get_db_connection()
try:
cota = db.query(CotaMensal).get(id)
if not cota:
flash('Cota não encontrada.', 'danger')
return redirect(url_for('listar_cotas'))
# Excluir a cota
db.delete(cota)
db.commit()
flash('Cota excluída com sucesso!', 'success')
except Exception as e:
db.rollback()
flash('Erro ao excluir cota. Por favor, tente novamente.', 'danger')
print(f"Erro ao excluir cota: {e}")
finally:
db.close()
return redirect(url_for('listar_cotas'))
def create_app(): def create_app():
app = Flask(__name__) app = Flask(__name__)
# ... existing code ... # ... existing code ...

127
static/js/cotas.js Normal file
View File

@@ -0,0 +1,127 @@
document.addEventListener('DOMContentLoaded', function() {
console.log('Carregando script cotas.js...');
// Configuração do modal de edição
const modalEditarCota = document.getElementById('modalEditarCota');
if (modalEditarCota) {
modalEditarCota.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 cotaId = button.getAttribute('data-cota-id');
console.log('ID da cota:', cotaId);
// Dados da cota
const dados = {
militanteId: button.getAttribute('data-cota-militante'),
militanteNome: button.closest('tr').querySelector('td').textContent.trim(),
valorAntigo: button.closest('tr').querySelector('td[data-valor_antigo]').getAttribute('data-valor_antigo'),
valorNovo: button.closest('tr').querySelector('td[data-valor_novo]').getAttribute('data-valor_novo'),
dataAlteracao: button.getAttribute('data-cota-data-alteracao'),
dataVencimento: button.getAttribute('data-cota-data-vencimento'),
pago: button.getAttribute('data-cota-pago') === 'true'
};
console.log('Dados da cota:', dados);
// Preencher campos
document.getElementById('editMilitante').value = dados.militanteId;
document.getElementById('editMilitanteNome').value = dados.militanteNome;
document.getElementById('editValorAntigo').value = dados.valorAntigo;
document.getElementById('editValorNovo').value = dados.valorNovo;
document.getElementById('editDataAlteracao').value = dados.dataAlteracao;
document.getElementById('editDataVencimento').value = dados.dataVencimento;
document.getElementById('editPago').checked = dados.pago;
// Configurar formulário
const form = document.getElementById('formEditarCota');
if (form) {
form.action = `/cotas/editar/${cotaId}`;
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);
// Adicionar campo pago com o valor correto
const isPago = document.getElementById('editPago').checked;
formData.set('pago', isPago ? 'true' : 'false');
// 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(modalEditarCota);
modal.hide();
// Recarregar página
window.location.reload();
} else {
alert('Erro ao atualizar cota: ' + data.message);
}
})
.catch(error => {
console.error('Erro:', error);
alert('Erro ao atualizar cota. Por favor, tente novamente.');
});
});
}
});
}
// Configuração do modal de exclusão
const deleteModal = document.getElementById('deleteModal');
if (deleteModal) {
deleteModal.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 cotaId = button.getAttribute('data-cota-id');
const cotaInfo = button.getAttribute('data-cota-info');
console.log('ID da cota:', cotaId);
console.log('Info da cota:', cotaInfo);
// Atualizar texto do modal
document.getElementById('cotaInfo').textContent = cotaInfo;
// Configurar formulário de exclusão
const form = document.getElementById('deleteForm');
if (form) {
form.action = `/cotas/excluir/${cotaId}`;
console.log('Action do formulário:', form.action);
}
});
}
});

View File

@@ -43,6 +43,7 @@
<th data-sort="valor_novo">Valor Novo <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_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="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> <th class="text-end">Ações</th>
</tr> </tr>
</thead> </thead>
@@ -54,6 +55,15 @@
<td data-valor_novo="{{ cota.valor_novo }}">R$ {{ "%.2f"|format(cota.valor_novo) }}</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_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-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"> <td class="text-end">
<div class="btn-group"> <div class="btn-group">
<button type="button" <button type="button"
@@ -66,6 +76,7 @@
data-cota-valor-novo="{{ cota.valor_novo }}" data-cota-valor-novo="{{ cota.valor_novo }}"
data-cota-data-alteracao="{{ cota.data_alteracao.strftime('%Y-%m-%d') }}" data-cota-data-alteracao="{{ cota.data_alteracao.strftime('%Y-%m-%d') }}"
data-cota-data-vencimento="{{ cota.data_vencimento.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"> title="Editar">
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</button> </button>
@@ -150,13 +161,9 @@
<div class="modal-body"> <div class="modal-body">
<form id="formEditarCota" method="post"> <form id="formEditarCota" method="post">
<div class="mb-3"> <div class="mb-3">
<label for="editMilitante" class="form-label">Militante:</label> <label for="editMilitanteNome" class="form-label">Militante:</label>
<select class="form-select" id="editMilitante" name="militante_id" required> <input type="text" class="form-control bg-light" id="editMilitanteNome" readonly>
<option value="">Selecione um militante</option> <input type="hidden" id="editMilitante" name="militante_id">
{% for militante in militantes %}
<option value="{{ militante.id }}">{{ militante.nome }}</option>
{% endfor %}
</select>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="editValorAntigo" class="form-label">Valor Antigo:</label> <label for="editValorAntigo" class="form-label">Valor Antigo:</label>
@@ -174,6 +181,10 @@
<label for="editDataVencimento" class="form-label">Data de Vencimento:</label> <label for="editDataVencimento" class="form-label">Data de Vencimento:</label>
<input type="date" class="form-control" id="editDataVencimento" name="data_vencimento" required> <input type="date" class="form-control" id="editDataVencimento" name="data_vencimento" required>
</div> </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> </form>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
@@ -211,206 +222,8 @@
</div> </div>
{% endblock %} {% endblock %}
{% block extra_js %} {% block scripts %}
<script> <script src="{{ url_for('static', filename='js/cotas.js') }}"></script>
document.addEventListener('DOMContentLoaded', function() {
// Configuração do modal de exclusão
const deleteModal = document.getElementById('deleteModal');
deleteModal.addEventListener('show.bs.modal', function(event) {
const button = event.relatedTarget;
const cotaId = button.getAttribute('data-cota-id');
const cotaInfo = button.getAttribute('data-cota-info');
document.getElementById('cotaInfo').textContent = cotaInfo;
document.getElementById('deleteForm').action = `/cotas/excluir/${cotaId}`;
});
// Envio do formulário de nova cota via AJAX
const formNovaCota = document.getElementById('formNovaCota');
formNovaCota.addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
fetch(this.action, {
method: 'POST',
body: formData,
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
// Fechar o modal
bootstrap.Modal.getInstance(document.getElementById('modalNovaCota')).hide();
// Atualizar a lista
location.reload();
// Mostrar mensagem de sucesso
const alertDiv = document.createElement('div');
alertDiv.className = 'alert alert-success alert-dismissible fade show';
alertDiv.innerHTML = `
${data.message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.querySelector('.container').insertBefore(alertDiv, document.querySelector('.container').firstChild);
} else {
// Mostrar erro
const alertDiv = document.createElement('div');
alertDiv.className = 'alert alert-danger alert-dismissible fade show';
alertDiv.innerHTML = `
${data.message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.querySelector('.modal-body').insertBefore(alertDiv, formNovaCota);
}
})
.catch(error => {
console.error('Erro:', error);
const alertDiv = document.createElement('div');
alertDiv.className = 'alert alert-danger alert-dismissible fade show';
alertDiv.innerHTML = `
Erro ao cadastrar cota. Tente novamente.
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.querySelector('.modal-body').insertBefore(alertDiv, formNovaCota);
});
});
// Configuração do modal de edição
const modalEditarCota = document.getElementById('modalEditarCota');
modalEditarCota.addEventListener('show.bs.modal', function(event) {
const button = event.relatedTarget;
const cotaId = button.getAttribute('data-cota-id');
const militanteId = button.getAttribute('data-cota-militante');
const valorAntigo = button.getAttribute('data-cota-valor-antigo');
const valorNovo = button.getAttribute('data-cota-valor-novo');
const dataAlteracao = button.getAttribute('data-cota-data-alteracao');
const dataVencimento = button.getAttribute('data-cota-data-vencimento');
const form = document.getElementById('formEditarCota');
form.action = `/cotas/editar/${cotaId}`;
document.getElementById('editMilitante').value = militanteId;
document.getElementById('editValorAntigo').value = valorAntigo;
document.getElementById('editValorNovo').value = valorNovo;
document.getElementById('editDataAlteracao').value = dataAlteracao;
document.getElementById('editDataVencimento').value = dataVencimento;
});
// Envio do formulário de edição via AJAX
const formEditarCota = document.getElementById('formEditarCota');
formEditarCota.addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
fetch(this.action, {
method: 'POST',
body: formData,
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
// Fechar o modal
bootstrap.Modal.getInstance(document.getElementById('modalEditarCota')).hide();
// Atualizar a lista
location.reload();
} else {
alert(data.message || 'Erro ao editar cota');
}
})
.catch(error => {
console.error('Erro:', error);
alert('Erro ao editar cota');
});
});
// Limpar alertas quando os modais forem fechados
[modalEditarCota, document.getElementById('modalNovaCota')].forEach(modal => {
modal.addEventListener('hidden.bs.modal', function () {
const alerts = this.querySelectorAll('.alert');
alerts.forEach(alert => alert.remove());
});
});
// Pesquisa em tempo real
const searchInput = document.getElementById('searchInput');
searchInput.addEventListener('input', function() {
const searchTerm = this.value.toLowerCase();
const rows = document.querySelectorAll('#cotasTable tbody tr');
rows.forEach(row => {
const text = row.textContent.toLowerCase();
row.style.display = text.includes(searchTerm) ? '' : 'none';
});
});
// Ordenação
const headers = document.querySelectorAll('#cotasTable th[data-sort]');
headers.forEach(header => {
header.addEventListener('click', function() {
const column = this.getAttribute('data-sort');
const tbody = document.querySelector('#cotasTable tbody');
const rows = Array.from(tbody.querySelectorAll('tr'));
const isAsc = !this.classList.contains('sort-asc');
// Remover classes de ordenação de todos os headers
headers.forEach(h => {
h.classList.remove('sort-asc', 'sort-desc');
h.querySelector('i').className = 'fas fa-sort';
});
// Adicionar classe de ordenação ao header clicado
this.classList.add(isAsc ? 'sort-asc' : 'sort-desc');
this.querySelector('i').className = `fas fa-sort-${isAsc ? 'up' : 'down'}`;
// Ordenar linhas
rows.sort((a, b) => {
const aVal = a.querySelector(`td[data-${column}]`).getAttribute(`data-${column}`);
const bVal = b.querySelector(`td[data-${column}]`).getAttribute(`data-${column}`);
return isAsc ? aVal.localeCompare(bVal) : bVal.localeCompare(aVal);
});
// Reposicionar linhas
rows.forEach(row => tbody.appendChild(row));
});
});
// Exportar para CSV
document.getElementById('btnExportar').addEventListener('click', function() {
const rows = document.querySelectorAll('#cotasTable tbody tr:not([style*="display: none"])');
const headers = ['Militante', 'Valor Antigo', 'Valor Novo', 'Data de Alteração', 'Data de Vencimento'];
let csv = headers.join(',') + '\n';
rows.forEach(row => {
const cols = row.querySelectorAll('td');
const values = [
cols[0].textContent,
cols[1].textContent,
cols[2].textContent,
cols[3].textContent,
cols[4].textContent
].map(val => `"${val}"`);
csv += values.join(',') + '\n';
});
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.setAttribute('download', 'cotas.csv');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});
});
</script>
{% endblock %} {% endblock %}
{% block extra_css %} {% block extra_css %}