diff --git a/app.py b/app.py index e50bb1f..0fddc3f 100644 --- a/app.py +++ b/app.py @@ -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//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//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//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//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//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//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(): diff --git a/functions/usuario.py b/functions/usuario.py new file mode 100644 index 0000000..bf67534 --- /dev/null +++ b/functions/usuario.py @@ -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, []) \ No newline at end of file diff --git a/static/js/comprovantes.js b/static/js/comprovantes.js new file mode 100644 index 0000000..c55d31f --- /dev/null +++ b/static/js/comprovantes.js @@ -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'); + }); +}); \ No newline at end of file diff --git a/static/js/pagamentos.js b/static/js/pagamentos.js deleted file mode 100644 index 75e4300..0000000 --- a/static/js/pagamentos.js +++ /dev/null @@ -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'); - } - }); - }); -}); \ No newline at end of file diff --git a/templates/editar_comprovante.html b/templates/editar_comprovante.html new file mode 100644 index 0000000..7b1859f --- /dev/null +++ b/templates/editar_comprovante.html @@ -0,0 +1,43 @@ +{% extends 'base.html' %} + +{% block title %}Editar Comprovante{% endblock %} + +{% block content %} +
+
+
+
+
+

+ Editar Comprovante +

+
+
+
+ {% csrf_token %} +
+ + +
+
+ + +
+ +
+
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/templates/editar_pagamento.html b/templates/editar_pagamento.html new file mode 100644 index 0000000..0ff6158 --- /dev/null +++ b/templates/editar_pagamento.html @@ -0,0 +1,37 @@ +{% extends 'base.html' %} + +{% block title %}Editar Comprovante{% endblock %} + +{% block content %} +
+
+
+
+
+

+ Editar Comprovante +

+
+
+
+ {% csrf_token %} +
+ + +
+
+ + +
+ +
+
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/templates/lista_comprovantes.html b/templates/lista_comprovantes.html new file mode 100644 index 0000000..ed195fd --- /dev/null +++ b/templates/lista_comprovantes.html @@ -0,0 +1,49 @@ +{% extends 'base.html' %} + +{% block title %}Lista de Comprovantes{% endblock %} + +{% block content %} +
+
+
+
+
+

+ Lista de Comprovantes +

+
+
+ + + + + + + + + + + + {% for comprovante in comprovantes %} + + + + + + + + {% endfor %} + +
IDDataValorTipoAções
{{ comprovante.id }}{{ comprovante.data.strftime('%d/%m/%Y') }}R$ {{ "%.2f"|format(comprovante.valor) }} + {% 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 + {{ comprovante.tipo }}{{ comprovante.data }}
+
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/templates/lista_pagamentos.html b/templates/lista_pagamentos.html new file mode 100644 index 0000000..fd69b5e --- /dev/null +++ b/templates/lista_pagamentos.html @@ -0,0 +1,37 @@ +{% extends 'base.html' %} + +{% block title %}Lista de Comprovantes{% endblock %} + +{% block content %} +
+
+
+
+
+

+ Lista de Comprovantes +

+
+
+ + + + + + + + + {% for comprovante in comprovantes %} + + + + + {% endfor %} + +
Tipo de ComprovanteData do Comprovante
{{ comprovante.tipo }}{{ comprovante.data }}
+
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/templates/listar_comprovantes.html b/templates/listar_comprovantes.html new file mode 100644 index 0000000..3781c48 --- /dev/null +++ b/templates/listar_comprovantes.html @@ -0,0 +1,219 @@ +{% extends "base.html" %} + +{% block title %}Comprovantes{% endblock %} + +{% block content %} +
+
+

Comprovantes

+
+ + +
+
+ +
+
+
+ + + + + + + + + + + + {% for comprovante in comprovantes %} + + + + + + + + {% endfor %} + +
MilitanteTipo de ComprovanteValorData do ComprovanteAções
{{ comprovante.militante.nome if comprovante.militante else 'N/A' }} + {% 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 %} + R$ {{ "%.2f"|format(comprovante.valor) }}{{ comprovante.data_comprovante.strftime('%d/%m/%Y') }} + + +
+
+
+
+
+ + + + + + + + + +{% endblock %} + +{% block scripts %} + +{% endblock %} + diff --git a/templates/listar_pagamentos.html b/templates/listar_pagamentos.html index 8ffe951..3afaff2 100644 --- a/templates/listar_pagamentos.html +++ b/templates/listar_pagamentos.html @@ -1,14 +1,14 @@ {% extends "base.html" %} -{% block title %}Pagamentos{% endblock %} +{% block title %}Comprovantes{% endblock %} {% block content %}
-

Pagamentos

+

Comprovantes

@@ -87,7 +87,7 @@