feat(web): telas de Estoque e Faturamento no EDEN Core

Continuação direta, sem parar para autorização (a pedido do usuário).
2 subagentes eden-frontend em paralelo.

- Estoque: armazéns (CRUD), ativos com ações contextuais por status
  (só oferece Reservar/Instalar/Devolver quando válido para o status
  atual), entrada de ativo, detalhe com histórico de movimentos, ledger
  só-leitura, reservas genéricas. Achado real documentado no código:
  install/return gravam o movimento com reference_type='contract', não
  'serialized_asset' — filtrar o ledger só pelo id do ativo nunca traz
  esse histórico; contornado buscando também pelos movimentos do
  contrato atual do ativo, registrado como pendência de backend.
- Faturamento: assinaturas (valor mensal pré-preenchido do contrato,
  editável com aviso), fechamentos (aviso de idempotência ao regerar),
  detalhe do fechamento com tabela de faturas + consolidação com
  confirmação em duas etapas (irreversível), lista de faturas.

Reconciliação manual de App.tsx/AuthenticatedLayout.tsx entre os 2
agentes, confirmando typecheck limpo a cada mesclagem. Um dos agentes
restaurou a base do zero (sem git merge) — comparação byte a byte
(diff -rq) contra main confirmou zero desvio antes de aceitar qualquer
arquivo como genuinamente novo.

Verificado: pnpm turbo run typecheck build (28/28); containers
eden-api/eden-core reconstruídos; testado ao vivo através do proxy
same-origin (porta 3001); trilha de auditoria conferida no Postgres.
Os próprios agentes já haviam confirmado ao vivo contra a API real:
invariante de serial duplicado (409), ciclo completo de ativo, e o
fluxo idempotente de billing (gerar→regenerar→consolidar→409).

Claude-Session: https://claude.ai/code/session_01BcWWuKfykGtUweXmHD6cKx

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-03 23:32:48 -03:00
parent 19e67dbe63
commit 7021994d8e
23 changed files with 3078 additions and 0 deletions

View File

@@ -0,0 +1,37 @@
import { Link, useLocation } from "react-router-dom";
/**
* Only one top-level nav slot is allocated for Billing ("Faturamento" →
* /billing/subscriptions, per AuthenticatedLayout.tsx) but the module has
* three screens (Assinaturas, Fechamentos, Faturas) — same problem CRM
* solved with `CrmSubNav` (src/routes/crm/CrmSubNav.tsx), replicated here:
* an in-page toggle instead of three top-level nav entries.
*/
const TABS = [
{ key: "subscriptions", label: "Assinaturas", href: "/billing/subscriptions" },
{ key: "runs", label: "Fechamentos", href: "/billing/runs" },
{ key: "invoices", label: "Faturas", href: "/billing/invoices" },
] as const;
export function BillingSubNav() {
const location = useLocation();
return (
<div className="mb-4 inline-flex rounded-md border border-border-color bg-white p-1">
{TABS.map((tab) => {
const active = location.pathname.startsWith(tab.href);
return (
<Link
key={tab.key}
to={tab.href}
className={`rounded px-3 py-1.5 text-sm font-semibold ${
active ? "bg-dark text-white" : "text-default hover:bg-light"
}`}
>
{tab.label}
</Link>
);
})}
</div>
);
}