2025-07-28 13:29:45 -03:00
|
|
|
|
{% extends "index.html" %}
|
|
|
|
|
|
{% block title %}Relatórios{% endblock %}
|
|
|
|
|
|
{% block content %}
|
|
|
|
|
|
<h1>📊 Relatórios</h1>
|
|
|
|
|
|
|
2025-08-11 13:14:54 -03:00
|
|
|
|
<div style="display:flex; gap:16px; align-items:flex-end; flex-wrap:wrap; margin: 6px 0 18px;">
|
|
|
|
|
|
<div class="combo-wrap">
|
|
|
|
|
|
<label for="relatorio-cliente" style="font-size:13px;color:#374151">Selecionar Cliente:</label>
|
|
|
|
|
|
<select id="relatorio-cliente" class="combo" style="min-width:340px;">
|
|
|
|
|
|
<option value="">Todos</option>
|
|
|
|
|
|
{% for c in clientes %}
|
|
|
|
|
|
<option value="{{ c.id }}">{{ c.nome }}</option>
|
|
|
|
|
|
{% endfor %}
|
|
|
|
|
|
</select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="combo-wrap">
|
|
|
|
|
|
<label for="tipo-relatorio" style="font-size:13px;color:#374151">Tipo de relatório:</label>
|
|
|
|
|
|
<select id="tipo-relatorio" class="combo" style="min-width:240px;">
|
|
|
|
|
|
<option value="geral">1. Geral</option>
|
|
|
|
|
|
<option value="exclusao_icms">2. Exclusão do ICMS</option>
|
|
|
|
|
|
<option value="aliquota_icms">3. Alíquota ICMS (%)</option>
|
|
|
|
|
|
</select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="combo-wrap">
|
|
|
|
|
|
<label for="page-size" style="font-size:13px;color:#374151">Itens por página:</label>
|
|
|
|
|
|
<select id="page-size" class="combo" style="width:140px;">
|
|
|
|
|
|
<option>20</option>
|
|
|
|
|
|
<option>50</option>
|
|
|
|
|
|
<option>100</option>
|
|
|
|
|
|
</select>
|
|
|
|
|
|
</div>
|
2025-07-28 13:29:45 -03:00
|
|
|
|
|
2025-08-11 13:14:54 -03:00
|
|
|
|
<div>
|
|
|
|
|
|
<a id="link-excel" class="btn btn-primary" href="/export-excel">📥 Baixar (Excel)</a>
|
|
|
|
|
|
</div>
|
2025-08-09 16:06:20 -03:00
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-08-11 13:14:54 -03:00
|
|
|
|
<table class="table">
|
2025-07-28 13:29:45 -03:00
|
|
|
|
<thead>
|
2025-08-11 13:14:54 -03:00
|
|
|
|
<tr>
|
|
|
|
|
|
<th>Cliente</th>
|
|
|
|
|
|
<th>UC</th>
|
|
|
|
|
|
<th>Referência</th>
|
|
|
|
|
|
<th>Nota Fiscal</th>
|
2025-07-28 13:29:45 -03:00
|
|
|
|
<th>Valor Total</th>
|
2025-08-11 13:14:54 -03:00
|
|
|
|
<th>ICMS (%)</th>
|
|
|
|
|
|
<th>ICMS (R$)</th>
|
|
|
|
|
|
<th>PIS (R$)</th>
|
|
|
|
|
|
<th>COFINS (R$)</th>
|
|
|
|
|
|
<th>Distribuidora</th>
|
|
|
|
|
|
<th>Processado em</th>
|
2025-07-28 13:29:45 -03:00
|
|
|
|
</tr>
|
|
|
|
|
|
</thead>
|
2025-08-11 13:14:54 -03:00
|
|
|
|
<tbody id="relatorios-body">
|
2025-07-28 13:29:45 -03:00
|
|
|
|
{% for f in faturas %}
|
2025-08-11 13:14:54 -03:00
|
|
|
|
<tr>
|
|
|
|
|
|
<td>{{ f.nome }}</td>
|
|
|
|
|
|
<td class="mono">{{ f.unidade_consumidora }}</td>
|
|
|
|
|
|
<td class="mono">{{ f.referencia }}</td>
|
|
|
|
|
|
<td class="mono">{{ f.nota_fiscal }}</td>
|
|
|
|
|
|
<td>R$ {{ '%.2f'|format((f.valor_total or 0.0))|replace('.', ',') }}</td>
|
|
|
|
|
|
<td>{{ '%.2f'|format((f.icms_aliq or 0.0))|replace('.', ',') }}</td>
|
|
|
|
|
|
<td>R$ {{ '%.2f'|format((f.icms_valor or 0.0))|replace('.', ',') }}</td>
|
|
|
|
|
|
<td>R$ {{ '%.2f'|format((f.pis_valor or 0.0))|replace('.', ',') }}</td>
|
|
|
|
|
|
<td>R$ {{ '%.2f'|format((f.cofins_valor or 0.0))|replace('.', ',') }}</td>
|
|
|
|
|
|
<td>{{ f.distribuidora or '-' }}</td>
|
|
|
|
|
|
<td class="muted">{{ f.data_processamento }}</td>
|
|
|
|
|
|
</tr>
|
2025-07-28 13:29:45 -03:00
|
|
|
|
{% endfor %}
|
|
|
|
|
|
</tbody>
|
|
|
|
|
|
</table>
|
2025-08-11 13:14:54 -03:00
|
|
|
|
<div id="pager" style="display:flex; align-items:center; justify-content:space-between; gap:12px; margin-top:12px;">
|
|
|
|
|
|
<div id="range" class="muted">Mostrando 0–0 de 0</div>
|
|
|
|
|
|
<div style="display:flex; gap:8px;">
|
|
|
|
|
|
<button id="prev" class="btn btn-primary">◀ Anterior</button>
|
|
|
|
|
|
<button id="next" class="btn btn-primary">Próxima ▶</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
|
.combo {
|
|
|
|
|
|
appearance: none;
|
|
|
|
|
|
-moz-appearance: none;
|
|
|
|
|
|
-webkit-appearance: none;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
border: 1px solid #e5e7eb;
|
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
|
padding: 12px 44px 12px 14px;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
line-height: 1.2;
|
|
|
|
|
|
color: #111827;
|
|
|
|
|
|
box-shadow: 0 6px 20px rgba(0,0,0,.06);
|
|
|
|
|
|
transition: box-shadow .2s ease, border-color .2s ease, transform .2s ease;
|
|
|
|
|
|
}
|
|
|
|
|
|
.combo:focus { outline: none; border-color: #2563eb; box-shadow: 0 8px 28px rgba(37,99,235,.18); }
|
|
|
|
|
|
.combo-wrap { position: relative; display: inline-flex; align-items: center; gap: 8px; }
|
|
|
|
|
|
.combo-wrap:after {
|
|
|
|
|
|
content: "▾"; position: absolute; right: 12px; pointer-events: none; color:#6b7280; font-size: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* tabela no estilo “clientes” */
|
|
|
|
|
|
.table {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
border-collapse: collapse;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
box-shadow: 0 12px 34px rgba(0,0,0,.06);
|
|
|
|
|
|
}
|
|
|
|
|
|
.table thead th {
|
|
|
|
|
|
background: #2563eb;
|
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
letter-spacing: .04em;
|
|
|
|
|
|
padding: 12px 14px;
|
|
|
|
|
|
text-align: left;
|
|
|
|
|
|
}
|
|
|
|
|
|
.table tbody td {
|
|
|
|
|
|
border-top: 1px solid #eef2f7;
|
|
|
|
|
|
padding: 12px 14px;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
color: #374151;
|
|
|
|
|
|
}
|
|
|
|
|
|
.table tbody tr:nth-child(odd){ background:#fafafa; }
|
|
|
|
|
|
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace; }
|
|
|
|
|
|
.muted { color:#6b7280; }
|
|
|
|
|
|
|
|
|
|
|
|
#pager .btn[disabled]{ opacity:.5; cursor:not-allowed; }
|
|
|
|
|
|
</style>
|
|
|
|
|
|
<script>
|
|
|
|
|
|
let page = 1;
|
|
|
|
|
|
let pageSize = 20;
|
|
|
|
|
|
let total = 0;
|
|
|
|
|
|
|
|
|
|
|
|
function updateExcelLink() {
|
|
|
|
|
|
const cliente = document.getElementById('relatorio-cliente').value || '';
|
|
|
|
|
|
const tipo = document.getElementById('tipo-relatorio').value || 'geral';
|
|
|
|
|
|
const params = new URLSearchParams();
|
|
|
|
|
|
params.set('tipo', tipo);
|
|
|
|
|
|
if (cliente) params.set('cliente', cliente);
|
|
|
|
|
|
document.getElementById('link-excel').setAttribute('href', `/export-excel?${params.toString()}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function carregarTabela() {
|
|
|
|
|
|
const cliente = document.getElementById('relatorio-cliente').value || '';
|
|
|
|
|
|
const url = new URL('/api/relatorios', window.location.origin);
|
|
|
|
|
|
url.searchParams.set('page', page);
|
|
|
|
|
|
url.searchParams.set('page_size', pageSize);
|
|
|
|
|
|
if (cliente) url.searchParams.set('cliente', cliente);
|
|
|
|
|
|
|
|
|
|
|
|
const r = await fetch(url);
|
|
|
|
|
|
const data = await r.json();
|
|
|
|
|
|
|
|
|
|
|
|
total = data.total;
|
|
|
|
|
|
renderRows(data.items);
|
|
|
|
|
|
updatePager();
|
|
|
|
|
|
updateExcelLink();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function renderRows(items) {
|
|
|
|
|
|
const tbody = document.getElementById('relatorios-body');
|
|
|
|
|
|
if (!items.length) {
|
|
|
|
|
|
tbody.innerHTML = `<tr><td colspan="11" style="padding:14px;">Nenhum registro encontrado.</td></tr>`;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
const fmtBRL = (v) => (v || v === 0) ? Number(v).toLocaleString('pt-BR',{style:'currency',currency:'BRL'}) : '';
|
|
|
|
|
|
const fmtNum = (v) => (v || v === 0) ? Number(v).toLocaleString('pt-BR') : '';
|
|
|
|
|
|
const fmtDate = (iso) => iso ? new Date(iso).toLocaleString('pt-BR') : '';
|
|
|
|
|
|
|
|
|
|
|
|
tbody.innerHTML = items.map(f => `
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<td>${f.nome || ''}</td>
|
|
|
|
|
|
<td class="mono">${f.unidade_consumidora || ''}</td>
|
|
|
|
|
|
<td class="mono">${f.referencia || ''}</td>
|
|
|
|
|
|
<td class="mono">${f.nota_fiscal || ''}</td>
|
|
|
|
|
|
<td>${fmtBRL(f.valor_total)}</td>
|
|
|
|
|
|
<td>${fmtNum(f.icms_aliq)}</td>
|
|
|
|
|
|
<td>${fmtBRL(f.icms_valor)}</td>
|
|
|
|
|
|
<td>${fmtBRL(f.pis_valor)}</td>
|
|
|
|
|
|
<td>${fmtBRL(f.cofins_valor)}</td>
|
|
|
|
|
|
<td>${f.distribuidora || '-'}</td>
|
|
|
|
|
|
<td class="muted">${fmtDate(f.data_processamento)}</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
`).join('');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function updatePager() {
|
|
|
|
|
|
const start = total ? (page - 1) * pageSize + 1 : 0;
|
|
|
|
|
|
const end = Math.min(page * pageSize, total);
|
|
|
|
|
|
document.getElementById('range').textContent = `Mostrando ${start}–${end} de ${total}`;
|
|
|
|
|
|
document.getElementById('prev').disabled = page <= 1;
|
|
|
|
|
|
document.getElementById('next').disabled = page * pageSize >= total;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
document.getElementById('prev').addEventListener('click', () => {
|
|
|
|
|
|
if (page > 1) { page--; carregarTabela(); }
|
|
|
|
|
|
});
|
|
|
|
|
|
document.getElementById('next').addEventListener('click', () => {
|
|
|
|
|
|
if (page * pageSize < total) { page++; carregarTabela(); }
|
|
|
|
|
|
});
|
|
|
|
|
|
document.getElementById('page-size').addEventListener('change', (e) => {
|
|
|
|
|
|
pageSize = parseInt(e.target.value, 10);
|
|
|
|
|
|
page = 1;
|
|
|
|
|
|
carregarTabela();
|
|
|
|
|
|
// não precisa alterar o link aqui
|
|
|
|
|
|
});
|
|
|
|
|
|
document.getElementById('relatorio-cliente').addEventListener('change', () => {
|
|
|
|
|
|
page = 1;
|
|
|
|
|
|
carregarTabela();
|
|
|
|
|
|
updateExcelLink();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
window.addEventListener('DOMContentLoaded', () => {
|
|
|
|
|
|
const pre = "{{ cliente_selecionado or '' }}";
|
|
|
|
|
|
if (pre) document.getElementById('relatorio-cliente').value = pre;
|
|
|
|
|
|
updateExcelLink();
|
|
|
|
|
|
carregarTabela();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
document.getElementById('tipo-relatorio').addEventListener('change', () => {
|
|
|
|
|
|
updateExcelLink();
|
|
|
|
|
|
});
|
|
|
|
|
|
</script>
|
2025-07-28 13:29:45 -03:00
|
|
|
|
{% endblock %}
|