feat(platform): Billing > Fechamentos e Relatórios

GET /billing/periods e /billing/statements só serviam o próprio tenant do
JWT — sem uso pra um platform admin escolhendo um tenant arbitrário.
Adicionado GET .../by-tenant/:tenantId nos dois (mesmo padrão já usado em
Subscriptions), e GET /billing/statements/:id ganhou um ?tenantId=
opcional só aceito de quem tem role de plataforma.

Frontend: /platform/billing/fechamentos (fecha/reabre período por
tenant, seletor via querystring pra não duplicar rota) e /relatorios
(statements por tenant, detalhe com itens por categoria).

Bug real achado testando o fluxo: fechar um período de 01/08 a 31/08
mostrava "31 de jul." a "30 de ago." — meia-noite UTC de uma data-only
vira o dia anterior no timezone local do servidor. Corrigido com
formatDateUTC novo, usado só em fronteiras de calendário (não em
timestamps de verdade, que continuam com formatDate local).

Testado ponta a ponta contra a API real: período fechado, statement
gerado (R$ 0,00 honesto — Acme sem assinatura/price book ainda), detalhe
correto. Smoke test nas 19 telas do tenant + 8 telas platform, todas 200.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BFaBaBSQGhyXGEgtTYZGV8
This commit is contained in:
2026-08-29 20:31:12 -03:00
parent a23e68b011
commit c95c6805fb
18 changed files with 670 additions and 6 deletions

View File

@@ -44,8 +44,16 @@ export const PLATFORM_NAV: NavSection[] = [
href: "/platform/billing/tarifas",
description: "Price books e rate decks — catálogos globais de preço",
},
{ label: "Fechamentos" },
{ label: "Relatórios" },
{
label: "Fechamentos",
href: "/platform/billing/fechamentos",
description: "Fechar/reabrir período de billing por tenant",
},
{
label: "Relatórios",
href: "/platform/billing/relatorios",
description: "Statements gerados por tenant",
},
],
},
{