Multi-Tenant - 15 - Exemplo de NFSe para o Ambiente Nacional em Golang
Boa, vamos fechar com a parte “observabilidade” da NFSe. Vou montar:
-
Uma view principal diária por tenant.
-
Uma view filtrando últimos 30 dias.
-
Alguns exemplos de consultas pra dashboard.
Vou assumir que sua tabela nfse tem pelo menos:
-
id BIGINT -
tenant_id BIGINT -
situacao TEXT -
substatus_tecnico TEXT -
valor_servicos NUMERIC -
data_emissao TIMESTAMPTZ(oucreated_at— se for outro nome, é só ajustar).
1. View principal: vw_nfse_dashboard_diario
Essa view consolida por dia, tenant, situação e substatus técnico:
CREATE OR REPLACE VIEW public.vw_nfse_dashboard_diario AS
SELECT
n.tenant_id,
date_trunc('day', n.data_emissao)::date AS dia,
n.situacao,
COALESCE(n.substatus_tecnico, '') AS substatus_tecnico,
COUNT(*) AS total_notas,
SUM(n.valor_servicos) AS total_servicos,
-- métricas auxiliares por estado
COUNT(*) FILTER (WHERE n.situacao = 'autorizada') AS qtd_autorizadas,
COUNT(*) FILTER (WHERE n.situacao = 'rejeitada') AS qtd_rejeitadas,
COUNT(*) FILTER (WHERE n.situacao = 'erro_interno') AS qtd_erro_interno,
COUNT(*) FILTER (WHERE n.situacao = 'pendente_transmissao') AS qtd_pendente_transmissao,
-- taxa de erro (rejeitada + erro_interno) / total
CASE
WHEN COUNT(*) = 0 THEN 0
ELSE ROUND(
(COUNT(*) FILTER (WHERE n.situacao IN ('rejeitada','erro_interno'))::numeric
/ COUNTnumeric * 100
, 2)
END AS taxa_erro_percent
FROM
public.nfse n
GROUP BY
n.tenant_id,
date_trunc('day', n.data_emissao)::date,
n.situacao,
COALESCE(n.substatus_tecnico, '');
O que você ganha aqui:
-
Linha por tenant + dia + situação + substatus.
-
Já traz:
-
total de notas;
-
total de serviços no dia;
-
quantidades por estado;
-
taxa de erro do dia.
-
No dashboard você pode:
-
agregar mais (somar por dia, ignorando substatus);
-
filtrar por
situacao,substatus_tecnico,tenant_id, etc.
2. View de “últimos 30 dias”: vw_nfse_dashboard_30d
Se quiser algo já pronto pra painel de monitoramento:
CREATE OR REPLACE VIEW public.vw_nfse_dashboard_30d AS
SELECT *
FROM public.vw_nfse_dashboard_diario
WHERE dia >= (current_date - INTERVAL '30 days');
Você pode trocar para 7 dias, 90 dias, etc. Ou criar múltiplas views (_7d, _30d).
3. Exemplos de consultas pro dashboard
3.1. Resumo por dia (todas as situações somadas)
SELECT
dia,
tenant_id,
SUM(total_notas) AS total_notas,
SUM(total_servicos) AS total_servicos,
SUM(qtd_autorizadas) AS qtd_autorizadas,
SUM(qtd_rejeitadas) AS qtd_rejeitadas,
SUM(qtd_erro_interno) AS qtd_erro_interno,
SUM(qtd_pendente_transmissao) AS qtd_pendentes,
ROUND(
(SUM(qtd_rejeitadas + qtd_erro_interno)::numeric
/ NULLIFnumeric * 100
, 2) AS taxa_erro_percent
FROM public.vw_nfse_dashboard_30d
GROUP BY dia, tenant_id
ORDER BY dia, tenant_id;
Esse é ótimo pra gráfico de linha/barra por dia no Grafana/Metabase.
3.2. Top tenants com mais erro técnico nos últimos 30 dias
SELECT
tenant_id,
SUM(qtd_erro_interno) AS erros_internos,
SUM(qtd_rejeitadas) AS rejeitadas,
SUM(total_notas) AS total_notas,
ROUND(
(SUM(qtd_erro_interno + qtd_rejeitadas)::numeric
/ NULLIFnumeric * 100
, 2) AS taxa_erro_percent
FROM public.vw_nfse_dashboard_30d
GROUP BY tenant_id
ORDER BY taxa_erro_percent DESC
LIMIT 10;
3.3. Quebra por substatus_tecnico (para investigar integração)
SELECT
tenant_id,
substatus_tecnico,
SUM(total_notas) AS total_notas,
SUM(qtd_erro_interno) AS erros_internos
FROM public.vw_nfse_dashboard_30d
WHERE situacao = 'erro_interno'
GROUP BY tenant_id, substatus_tecnico
ORDER BY erros_internos DESC;
Aqui você vê, por exemplo:
-
timeout_wsexplodindo em certo tenant → problema de rede/provedor/cidade. -
config_invalidaconcentrado em poucos tenants → onboarding/config.