All checks were successful
continuous-integration/drone/push Build is passing
804 lines
24 KiB
HTML
Executable File
804 lines
24 KiB
HTML
Executable File
{% extends "index.html" %}
|
|
{% block title %}Parâmetros de Cálculo{% endblock %}
|
|
{% block content %}
|
|
|
|
<h1 style="font-size: 1.6rem; margin-bottom: 1rem; display:flex; align-items:center; gap:0.5rem;">
|
|
⚙️ Parâmetros de Cálculo
|
|
</h1>
|
|
|
|
<div class="tabs">
|
|
<button class="tab active" onclick="switchTab('formulas')">📄 Fórmulas</button>
|
|
<button class="tab" onclick="switchTab('selic')">📊 Gestão SELIC</button>
|
|
<button class="tab" onclick="switchTab('aliquotas')">🧾 Cadastro de Alíquotas por Estado</button>
|
|
</div>
|
|
|
|
<!-- ABA FÓRMULAS -->
|
|
<div id="formulas" class="tab-content active">
|
|
<form method="post" class="formulario-box">
|
|
<div class="grid" style="grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));">
|
|
<div class="form-group">
|
|
<label for="nome">Nome:</label>
|
|
<input type="text" name="nome" id="nome" value="{{ parametros.nome or '' }}" required />
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="aliquota_icms">Alíquota de ICMS (%):</label>
|
|
<input type="text" id="aliquota" name="aliquota" inputmode="decimal" pattern="[0-9]+([,][0-9]+)?" placeholder="Ex: 20,7487">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="formula">Fórmula:</label>
|
|
<div class="editor-box">
|
|
<div style="margin-bottom: 0.5rem;">
|
|
<strong>Campos disponíveis:</strong>
|
|
<div class="campo-badges">
|
|
{% for campo in (campos_fatura or []) %}
|
|
<span class="badge-campo" onclick="inserirNoEditor('{{ campo }}')">{{ campo }}</span>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
|
|
<div style="margin-bottom: 0.5rem;">
|
|
<strong>Operadores:</strong>
|
|
<div class="campo-badges">
|
|
{% for op in ['+', '-', '*', '/', '(', ')'] %}
|
|
<span class="badge-operador" onclick="inserirNoEditor('{{ op }}')">{{ op }}</span>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
<textarea name="formula" id="formula" rows="3" required>{{ parametros.formula or '' }}</textarea>
|
|
<div class="actions-inline">
|
|
<button type="button" class="btn btn-secondary" onclick="testarFormula()">🧪 Testar Fórmula</button>
|
|
<span class="exemplo">Ex: (pis_base - (pis_base - icms_valor)) * pis_aliq</span>
|
|
</div>
|
|
<div id="resultado-teste" class="mensagem-info" style="display:none;"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<button type="submit" class="btn btn-primary">💾 Salvar Parâmetro</button>
|
|
<button type="button" class="btn btn-primary pulse" onclick="limparFormulario()">🔁 Novo Parâmetro</button>
|
|
|
|
</form>
|
|
<hr style="margin-top: 2rem; margin-bottom: 1rem;">
|
|
<h3 style="margin-top: 2rem;">📋 Fórmulas Salvas</h3>
|
|
<div class="card-list">
|
|
{% for param in (formulas or []) %}
|
|
<div class="param-card {{ 'ativo' if param.ativo else 'inativo' }}" id="card-{{ param.id }}">
|
|
<div style="display:flex; justify-content:space-between; align-items:center;">
|
|
<input type="text" class="edit-nome" value="{{ param.nome }}" data-id="{{ param.id }}"
|
|
onkeydown="if(event.key==='Enter'){ event.preventDefault(); salvarInline('{{ param.id }}') }" />
|
|
<span class="badge-status">{{ 'Ativo ✅' if param.ativo else 'Inativo ❌' }}</span>
|
|
</div>
|
|
<textarea class="edit-formula" data-id="{{ param.id }}" title="{{ param.formula }}">{{ param.formula }}</textarea>
|
|
|
|
<div style="display: flex; justify-content: space-between; align-items:center;">
|
|
<label>
|
|
<input type="checkbox" class="toggle-ativo" data-id="{{ param.id }}" {% if param.ativo %}checked{% endif %}>
|
|
Ativo
|
|
</label>
|
|
<div class="actions">
|
|
<button type="button" class="btn btn-sm btn-secondary btn-testar" data-id="{{ param.id }}">🧪 Testar</button>
|
|
<button class="btn btn-sm btn-primary" onclick="salvarInline('{{ param.id }}')">💾 Salvar</button>
|
|
<a href="/parametros/delete/{{ param.id }}" class="btn btn-sm btn-danger" onclick="return confirm('Deseja excluir?')">🗑️ Excluir</a>
|
|
</div>
|
|
</div>
|
|
<div class="mensagem-info" id="resultado-inline-{{ param.id }}" style="margin-top: 0.5rem; display:none;"></div>
|
|
</div>
|
|
{% else %}
|
|
<p style="color:gray;">Nenhuma fórmula cadastrada.</p>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ABA SELIC -->
|
|
<div id="selic" class="tab-content">
|
|
<div class="formulario-box">
|
|
<p>Utilize o botão abaixo para importar os fatores SELIC automaticamente a partir da API do Banco Central.</p>
|
|
<form method="post" action="/parametros/selic/importar" onsubmit="mostrarLoadingSelic()">
|
|
<div class="form-group">
|
|
<label for="data_maxima">Data máxima para cálculo da SELIC:</label>
|
|
<input type="date" id="data_maxima" name="data_maxima" value="{{ data_maxima or '' }}" />
|
|
</div>
|
|
<div style="display: flex; gap: 1rem; flex-wrap: wrap;">
|
|
<button type="submit" class="btn btn-primary">⬇️ Atualizar Fatores SELIC</button>
|
|
<button type="button" class="btn btn-secondary" onclick="mostrarFeedback('🔁 Atualização', 'Função de recarga futura')">🔄 Recarregar</button>
|
|
</div>
|
|
<div class="mensagem-info" style="margin-top:1rem;">Última data coletada da SELIC: <strong>{{ ultima_data_selic or '-' }}</strong></div>
|
|
</form>
|
|
<table class="selic-table">
|
|
<thead><tr><th>Competência</th><th>Fator</th></tr></thead>
|
|
<tbody>
|
|
{% for item in selic_dados %}
|
|
<tr>
|
|
<td>{{ "%02d"|format(item.mes) }}/{{ item.ano }}</td>
|
|
<td>{{ "%.4f"|format(item.percentual) }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ABA ALÍQUOTAS -->
|
|
<div id="aliquotas" class="tab-content">
|
|
<div class="formulario-box">
|
|
<form onsubmit="return salvarAliquota(this, event)">
|
|
<div class="grid" style="grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));">
|
|
<div class="form-group">
|
|
<label>UF:</label>
|
|
<select name="uf" required>
|
|
<option value="">Selecione o Estado</option>
|
|
{% for uf in ['AC','AL','AP','AM','BA','CE','DF','ES','GO','MA','MT','MS','MG','PA','PB','PR','PE','PI','RJ','RN','RS','RO','RR','SC','SP','SE','TO'] %}
|
|
<option value="{{ uf }}">{{ uf }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Exercício:</label>
|
|
<input name="exercicio" type="number" required />
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Alíquota ICMS (%):</label>
|
|
<input id="aliquota-uf" name="aliquota"
|
|
type="text" inputmode="decimal"
|
|
pattern="[0-9]+([,][0-9]+)?"
|
|
placeholder="Ex: 20,7487" required />
|
|
</div>
|
|
</div>
|
|
<!-- Bloco com espaçamento e alinhamento central -->
|
|
<div class="grupo-botoes">
|
|
<!-- Botão salvar -->
|
|
<button class="btn btn-primary" type="submit">💾 Salvar Alíquota</button>
|
|
|
|
<!-- Importação e template -->
|
|
<div style="display: flex; flex-wrap: wrap; justify-content: center; gap: 1rem;">
|
|
<label for="arquivo_aliquotas" class="btn btn-secondary" style="cursor: pointer;">
|
|
📎 Importar CSV
|
|
<input type="file" name="arquivo_aliquotas" accept=".csv" onchange="enviarArquivoAliquotas(this)" style="display: none;" />
|
|
</label>
|
|
|
|
<a href="/parametros/aliquotas/template" class="btn btn-secondary">📥 Baixar Template CSV</a>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<input type="hidden" id="orig-uf" name="original_uf">
|
|
<input type="hidden" id="orig-exercicio" name="original_exercicio">
|
|
|
|
</form>
|
|
|
|
<!-- Filtro de UF para a tabela -->
|
|
<div style="display:flex; align-items:center; gap:12px; margin:14px 0;">
|
|
<label for="filtro-uf" style="font-weight:600;">Filtrar por UF:</label>
|
|
<select id="filtro-uf" style="min-width:220px; padding:.5rem .75rem; border:1px solid #ddd; border-radius:8px;">
|
|
<option value="">Todas</option>
|
|
{% for uf in ['AC','AL','AP','AM','BA','CE','DF','ES','GO','MA','MT','MS','MG','PA','PB','PR','PE','PI','RJ','RN','RS','RO','RR','SC','SP','SE','TO'] %}
|
|
<option value="{{ uf }}">{{ uf }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
<span id="total-aliquotas" class="muted"></span>
|
|
</div>
|
|
|
|
<table class="selic-table">
|
|
<thead>
|
|
<tr>
|
|
<th>UF</th>
|
|
<th>Exercício</th>
|
|
<th>Alíquota</th>
|
|
<th style="width:140px;">Ações</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="tabela-aliquotas"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ESTILOS -->
|
|
<style>
|
|
/* Abas */
|
|
.tabs {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 1rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
.tab {
|
|
background: none;
|
|
border: none;
|
|
font-weight: bold;
|
|
cursor: pointer;
|
|
padding: 0.5rem 1rem;
|
|
border-bottom: 2px solid transparent;
|
|
}
|
|
.tab.active {
|
|
border-bottom: 2px solid #2563eb;
|
|
color: #2563eb;
|
|
}
|
|
.tab-content {
|
|
display: none;
|
|
}
|
|
.tab-content.active {
|
|
display: block;
|
|
}
|
|
|
|
/* Formulário principal */
|
|
.formulario-box {
|
|
background: #fff;
|
|
padding: 2rem;
|
|
border-radius: 10px;
|
|
box-shadow: 0 0 10px rgba(0,0,0,0.05);
|
|
max-width: 850px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.form-group {
|
|
margin-bottom: 1rem;
|
|
}
|
|
.form-group label {
|
|
display: block;
|
|
font-weight: bold;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
.form-group input[type="text"],
|
|
.form-group input[type="number"],
|
|
.form-group input[type="date"],
|
|
.form-group textarea {
|
|
width: 100%;
|
|
padding: 0.6rem;
|
|
border-radius: 6px;
|
|
border: 1px solid #ccc;
|
|
}
|
|
|
|
.form-group.check-group {
|
|
display: flex;
|
|
gap: 1rem;
|
|
flex-wrap: wrap;
|
|
}
|
|
.check-group label {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
/* Grade do formulário */
|
|
.grid {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 1rem;
|
|
}
|
|
|
|
/* Botões */
|
|
.btn {
|
|
padding: 0.5rem 1rem;
|
|
border: none;
|
|
border-radius: 6px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
}
|
|
.btn-primary {
|
|
background-color: #2563eb;
|
|
color: white;
|
|
}
|
|
.btn-secondary {
|
|
background-color: #6c757d;
|
|
color: white;
|
|
}
|
|
.btn-danger {
|
|
background-color: #dc3545;
|
|
color: white;
|
|
}
|
|
.btn-sm {
|
|
font-size: 0.85rem;
|
|
padding: 0.3rem 0.6rem;
|
|
}
|
|
|
|
/* Cards de fórmulas salvas */
|
|
.card-list {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
|
|
gap: 1.5rem;
|
|
margin-top: 2rem;
|
|
align-items: start;
|
|
}
|
|
|
|
.param-card {
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: flex-start;
|
|
background: #f9f9f9;
|
|
padding: 1rem;
|
|
border-radius: 8px;
|
|
border-left: 4px solid #2563eb;
|
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
|
|
box-sizing: border-box;
|
|
min-height: 200px;
|
|
}
|
|
|
|
.param-card.ativo {
|
|
border-left-color: #198754;
|
|
background: #f6fff9;
|
|
}
|
|
|
|
.param-card.inativo {
|
|
border-left-color: #adb5bd;
|
|
background: #f9f9f9;
|
|
}
|
|
|
|
.param-card input.edit-nome {
|
|
font-weight: bold;
|
|
font-size: 1rem;
|
|
border: none;
|
|
background: transparent;
|
|
outline: none;
|
|
flex: 1;
|
|
width: 100%;
|
|
padding: 0.25rem 0;
|
|
}
|
|
|
|
.edit-formula {
|
|
width: 100%;
|
|
font-family: monospace;
|
|
border: 1px solid #ccc;
|
|
border-radius: 6px;
|
|
padding: 0.5rem;
|
|
margin: 0.5rem 0;
|
|
resize: vertical;
|
|
min-height: 60px;
|
|
}
|
|
|
|
.param-card .actions {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: flex-end;
|
|
gap: 0.5rem;
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
.badge-status {
|
|
font-size: 0.75rem;
|
|
font-weight: bold;
|
|
padding: 0.2rem 0.6rem;
|
|
border-radius: 12px;
|
|
color: white;
|
|
background: #198754;
|
|
}
|
|
|
|
.param-card.inativo .badge-status {
|
|
background: #adb5bd;
|
|
}
|
|
|
|
/* Badge de campos e operadores */
|
|
.campo-badges {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.5rem;
|
|
margin: 0.5rem 0;
|
|
}
|
|
|
|
.badge-campo, .badge-operador {
|
|
background: #e0e7ff;
|
|
color: #1e3a8a;
|
|
padding: 0.3rem 0.6rem;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
font-family: monospace;
|
|
transition: background 0.2s ease;
|
|
}
|
|
|
|
.badge-campo:hover, .badge-operador:hover {
|
|
background: #c7d2fe;
|
|
}
|
|
|
|
/* Mensagens */
|
|
.mensagem-info {
|
|
background: #e0f7fa;
|
|
padding: 1rem;
|
|
border-left: 4px solid #2563eb;
|
|
border-radius: 6px;
|
|
color: #007b83;
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
/* Tabela SELIC */
|
|
.selic-table {
|
|
width: 100%;
|
|
margin-top: 1rem;
|
|
border-collapse: collapse;
|
|
}
|
|
.selic-table th,
|
|
.selic-table td {
|
|
padding: 0.6rem;
|
|
border-bottom: 1px solid #ccc;
|
|
}
|
|
.selic-table th {
|
|
text-align: left;
|
|
background: #f1f1f1;
|
|
}
|
|
|
|
/* Popup de feedback */
|
|
.feedback-popup {
|
|
position: fixed;
|
|
top: 0; left: 0; right: 0; bottom: 0;
|
|
background-color: rgba(0, 0, 0, 0.6);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 99999;
|
|
}
|
|
|
|
.feedback-content {
|
|
background-color: #fff;
|
|
padding: 2rem;
|
|
border-radius: 12px;
|
|
text-align: center;
|
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.25);
|
|
}
|
|
|
|
.feedback-content h3 {
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.feedback-content p {
|
|
margin: 0.25rem 0;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.hidden {
|
|
display: none !important;
|
|
}
|
|
|
|
.form-group select {
|
|
width: 100%;
|
|
padding: 0.6rem;
|
|
border-radius: 6px;
|
|
border: 1px solid #ccc;
|
|
font-family: inherit;
|
|
}
|
|
|
|
.btn {
|
|
text-decoration: none;
|
|
}
|
|
|
|
.grupo-botoes {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
margin-top: 1.5rem;
|
|
gap: 1rem;
|
|
}
|
|
|
|
|
|
.btn {
|
|
padding: 0.5rem 1rem;
|
|
font-weight: 600;
|
|
font-size: 1rem;
|
|
border-radius: 6px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
text-align: center;
|
|
text-decoration: none !important;
|
|
}
|
|
|
|
|
|
</style>
|
|
|
|
{% block scripts %}
|
|
<script>
|
|
// 🟡 Alterna entre abas
|
|
function switchTab(tabId) {
|
|
// Remove classe 'active' de todos os botões e abas
|
|
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
|
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
|
|
|
|
// Ativa a aba correspondente
|
|
document.getElementById(tabId).classList.add('active');
|
|
|
|
// Ativa o botão correspondente
|
|
const button = document.querySelector(`.tab[onclick="switchTab('${tabId}')"]`);
|
|
if (button) button.classList.add('active');
|
|
}
|
|
|
|
|
|
// ✅ Insere valores no editor de fórmulas
|
|
function inserirNoEditor(valor) {
|
|
const formula = document.getElementById("formula");
|
|
const start = formula.selectionStart;
|
|
const end = formula.selectionEnd;
|
|
formula.value = formula.value.slice(0, start) + valor + formula.value.slice(end);
|
|
formula.focus();
|
|
formula.setSelectionRange(start + valor.length, start + valor.length);
|
|
}
|
|
|
|
// ✅ Feedback visual em popup
|
|
function mostrarFeedback(titulo, mensagem) {
|
|
document.getElementById("feedback-titulo").innerText = titulo;
|
|
document.getElementById("feedback-mensagem").innerText = mensagem;
|
|
document.getElementById("parametros-feedback").classList.remove("hidden");
|
|
}
|
|
|
|
function fecharFeedbackParametros() {
|
|
document.getElementById("parametros-feedback").classList.add("hidden");
|
|
}
|
|
|
|
// ✅ Testa fórmula principal (formulário superior)
|
|
function testarFormula() {
|
|
const nome = document.getElementById("nome").value.trim();
|
|
const formula = document.getElementById("formula").value.trim();
|
|
const output = document.getElementById("resultado-teste");
|
|
|
|
if (!nome || !formula) {
|
|
output.innerText = "❌ Preencha o nome e a fórmula para testar.";
|
|
output.style.display = "block";
|
|
return;
|
|
}
|
|
|
|
fetch("/parametros/testar", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ nome, formula })
|
|
})
|
|
.then(res => res.json())
|
|
.then(data => {
|
|
output.style.display = "block";
|
|
if (data.success) {
|
|
output.innerText = `✅ Fórmula válida. Resultado: ${data.resultado}`;
|
|
} else {
|
|
output.innerText = `❌ Erro: ${data.error}`;
|
|
}
|
|
});
|
|
}
|
|
|
|
// ✅ Testa fórmula inline nos cards salvos
|
|
function testarFormulaInline(id) {
|
|
const nome = document.querySelector(`.edit-nome[data-id='${id}']`)?.value;
|
|
const formula = document.querySelector(`.edit-formula[data-id='${id}']`)?.value;
|
|
const output = document.getElementById(`resultado-inline-${id}`);
|
|
|
|
if (!formula) {
|
|
output.innerText = "❌ Fórmula não preenchida.";
|
|
output.style.display = 'block';
|
|
return;
|
|
}
|
|
|
|
fetch("/parametros/testar", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ nome, formula })
|
|
})
|
|
.then(res => res.json())
|
|
.then(data => {
|
|
output.style.display = 'block';
|
|
if (data.success) {
|
|
output.innerText = `✅ Fórmula válida. Resultado: ${data.resultado}`;
|
|
} else {
|
|
output.innerText = `❌ Erro: ${data.error}`;
|
|
}
|
|
});
|
|
}
|
|
|
|
// ✅ Salva edição inline nos cards
|
|
async function salvarInline(id) {
|
|
const inputNome = document.querySelector(`.edit-nome[data-id='${id}']`);
|
|
const textareaFormula = document.querySelector(`.edit-formula[data-id='${id}']`);
|
|
|
|
const nome = inputNome.value.trim();
|
|
const formula = textareaFormula.value.trim();
|
|
|
|
if (!nome || !formula) {
|
|
alert("Preencha todos os campos.");
|
|
return;
|
|
}
|
|
|
|
const response = await fetch(`/parametros/editar/${id}`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ nome, formula })
|
|
});
|
|
|
|
if (response.ok) {
|
|
mostrarFeedback("✅ Atualizado", "Parâmetro salvo com sucesso.");
|
|
} else {
|
|
mostrarFeedback("❌ Erro", "Erro ao salvar.");
|
|
}
|
|
}
|
|
|
|
// ✅ Carrega tabela de alíquotas
|
|
async function carregarAliquotas() {
|
|
const uf = document.getElementById("filtro-uf")?.value || "";
|
|
const url = new URL("/parametros/aliquotas", window.location.origin);
|
|
if (uf) url.searchParams.set("uf", uf);
|
|
|
|
const res = await fetch(url);
|
|
const dados = await res.json();
|
|
|
|
const tbody = document.getElementById("tabela-aliquotas");
|
|
if (!dados.length) {
|
|
tbody.innerHTML = `<tr><td colspan="3" style="padding:.6rem;">Nenhum registro.</td></tr>`;
|
|
} else {
|
|
tbody.innerHTML = dados.map(a => `
|
|
<tr>
|
|
<td>${a.uf}</td>
|
|
<td>${a.exercicio}</td>
|
|
<td>${Number(a.aliquota).toLocaleString('pt-BR', {minimumFractionDigits:4, maximumFractionDigits:4})}%</td>
|
|
<td style="display:flex; gap:8px;">
|
|
<button class="btn btn-sm btn-secondary"
|
|
onclick="editarAliquota('${a.uf}', ${a.exercicio}, ${Number(a.aliquota)})">✏️ Editar</button>
|
|
<button class="btn btn-sm btn-danger"
|
|
onclick="excluirAliquota('${a.uf}', ${a.exercicio})">🗑️ Excluir</button>
|
|
</td>
|
|
</tr>
|
|
`).join('');
|
|
}
|
|
|
|
document.getElementById("total-aliquotas").textContent = `Registros: ${dados.length}`;
|
|
}
|
|
|
|
|
|
// ✅ Eventos após carregar DOM
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
document.getElementById("filtro-uf")?.addEventListener("change", carregarAliquotas);
|
|
carregarAliquotas();
|
|
|
|
// Ativar/desativar checkbox
|
|
document.querySelectorAll('.toggle-ativo').forEach(input => {
|
|
input.addEventListener('change', async function () {
|
|
const id = this.dataset.id;
|
|
const ativo = this.checked;
|
|
const response = await fetch(`/parametros/ativar/${id}`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ ativo })
|
|
});
|
|
if (response.ok) {
|
|
location.reload();
|
|
} else {
|
|
alert('Erro ao atualizar status.');
|
|
}
|
|
});
|
|
});
|
|
|
|
// Botões de teste inline
|
|
document.querySelectorAll('.btn-testar').forEach(button => {
|
|
button.addEventListener('click', function () {
|
|
const id = this.dataset.id;
|
|
testarFormulaInline(id);
|
|
});
|
|
});
|
|
});
|
|
|
|
function mostrarLoadingSelic() {
|
|
document.getElementById("selic-loading").classList.remove("hidden");
|
|
}
|
|
|
|
window.addEventListener("DOMContentLoaded", () => {
|
|
const aba = new URLSearchParams(window.location.search).get("aba");
|
|
if (aba === "formulas" || aba === "selic" || aba === "aliquotas") {
|
|
switchTab(aba);
|
|
} else {
|
|
switchTab("formulas"); // padrão
|
|
}
|
|
});
|
|
|
|
function enviarArquivoAliquotas(input) {
|
|
const file = input.files[0];
|
|
if (!file) return;
|
|
|
|
const formData = new FormData();
|
|
formData.append("arquivo", file);
|
|
|
|
fetch("/parametros/aliquotas/importar", {
|
|
method: "POST",
|
|
body: formData
|
|
})
|
|
.then(res => res.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
mostrarFeedback("✅ Importado", `${data.qtd} alíquotas foram importadas.`);
|
|
carregarAliquotas();
|
|
} else {
|
|
mostrarFeedback("❌ Erro", data.error || "Falha na importação.");
|
|
}
|
|
});
|
|
}
|
|
|
|
async function salvarAliquota(form, ev) {
|
|
ev?.preventDefault();
|
|
|
|
const uf = form.uf.value?.trim();
|
|
const exercicio = Number(form.exercicio.value?.trim());
|
|
const aliquotaStr = form.aliquota.value?.trim();
|
|
const aliquota = parseFloat(aliquotaStr.replace(',', '.')); // vírgula -> ponto
|
|
|
|
// 👇 LE OS ORIGINAIS
|
|
const original_uf = document.getElementById('orig-uf').value || null;
|
|
const original_exercicio = document.getElementById('orig-exercicio').value
|
|
? Number(document.getElementById('orig-exercicio').value)
|
|
: null;
|
|
|
|
if (!uf || !exercicio || isNaN(exercicio) || !aliquotaStr || isNaN(aliquota)) {
|
|
mostrarFeedback("❌ Erro", "Preencha UF, exercício e alíquota válidos.");
|
|
return false;
|
|
}
|
|
|
|
const res = await fetch("/parametros/aliquotas/salvar", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ uf, exercicio, aliquota, original_uf, original_exercicio })
|
|
});
|
|
|
|
if (!res.ok) {
|
|
const msg = await res.text();
|
|
mostrarFeedback("❌ Erro ao salvar", msg || "Falha na operação.");
|
|
return false;
|
|
}
|
|
|
|
mostrarFeedback("✅ Salvo", "Alíquota registrada/atualizada com sucesso.");
|
|
cancelarEdicao(); // limpa modo edição
|
|
carregarAliquotas();
|
|
return false;
|
|
}
|
|
|
|
|
|
function editarAliquota(uf, exercicio, aliquota) {
|
|
const form = document.querySelector('#aliquotas form');
|
|
form.uf.value = uf;
|
|
form.exercicio.value = String(exercicio);
|
|
|
|
// Mostrar no input com vírgula e 4 casas
|
|
const valorBR = Number(aliquota).toLocaleString('pt-BR', {
|
|
minimumFractionDigits: 4, maximumFractionDigits: 4
|
|
});
|
|
form.querySelector('[name="aliquota"]').value = valorBR;
|
|
|
|
// 👇 GUARDA A CHAVE ORIGINAL
|
|
document.getElementById('orig-uf').value = uf;
|
|
document.getElementById('orig-exercicio').value = String(exercicio);
|
|
|
|
document.getElementById('aliquotas')?.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
}
|
|
|
|
function cancelarEdicao(){
|
|
const form = document.querySelector('#aliquotas form');
|
|
form.reset();
|
|
document.getElementById('orig-uf').value = '';
|
|
document.getElementById('orig-exercicio').value = '';
|
|
}
|
|
|
|
async function excluirAliquota(uf, exercicio){
|
|
if(!confirm(`Excluir a alíquota de ${uf}/${exercicio}?`)) return;
|
|
|
|
const res = await fetch(`/parametros/aliquotas/${encodeURIComponent(uf)}/${exercicio}`, {
|
|
method: 'DELETE'
|
|
});
|
|
|
|
if(!res.ok){
|
|
const msg = await res.text();
|
|
mostrarFeedback("❌ Erro", msg || "Falha ao excluir.");
|
|
return;
|
|
}
|
|
|
|
mostrarFeedback("🗑️ Excluída", "Alíquota removida com sucesso.");
|
|
carregarAliquotas();
|
|
}
|
|
|
|
</script>
|
|
|
|
<!-- Feedback estilo popup -->
|
|
<div id="parametros-feedback" class="feedback-popup hidden">
|
|
<div class="feedback-content">
|
|
<h3 id="feedback-titulo">✅ Ação Concluída</h3>
|
|
<p id="feedback-mensagem">Parâmetro salvo com sucesso.</p>
|
|
<button onclick="fecharFeedbackParametros()" class="btn btn-primary" style="margin-top: 1rem;">OK</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="selic-loading" class="feedback-popup hidden">
|
|
<div class="feedback-content">
|
|
<h3>⏳ Atualizando SELIC</h3>
|
|
<p>Aguarde enquanto os fatores SELIC estão sendo carregados...</p>
|
|
</div>
|
|
</div>
|
|
|
|
{% endblock %}
|
|
{% endblock %} |