13 KiB
P00 — Discovery técnico e auditoria do módulo existente
Data: 2026-10-02
Escopo auditado: c:\Users\Usuário\Documents\crm (package crm-2.0, Next.js 16.0.10).
Workspace desta sessão: gerador-contratos (repositório Gitea vazio, sem código de produto).
Cópia irmã Documents\CRM-2.0 existe e contém o mesmo módulo antigo de contratos, sem as migrations mais novas da calculadora. A fonte usada aqui é crm.
Nenhum código de produto, banco ou UI foi alterado.
Achado de localização
O Gerador de Contratos não pode nascer neste repositório. gerador-contratos só tem git e remote https://gitea.fatorxtech.com/cferraz/gerador-contratos.git. Auth, RLS, feature flag, AppShell, Sentry, Redis e Design System estão em crm.
A spec e a regra do módulo proíbem arquitetura paralela. DT-CTR-007 foi aprovado: implementar dentro de crm, preservando /dashboard/contracts.
Arquitetura atual
Sidebar (lib/nav.ts, flag contracts)
|
v
app/dashboard/contracts/layout.tsx
requireFeature('contracts') --> fail-closed, redirect /dashboard
|
+-- page.tsx lista
+-- new/page.tsx ContractWizard (passos fixos)
+-- [id]/page.tsx ContractEditor
+-- actions.ts Server Actions (CRUD, PDF, deal)
|
v
Supabase Auth/SSR + getCurrentWorkspaceId()
cookie active_workspace_id validado por membership
|
v
Postgres
contracts + contract_commercial + contract_sla_items
+ contract_modules + contract_clauses
+ contract_sla_defaults + contract_clause_defaults
RLS: is_workspace_member (qualquer papel)
|
v
PDF @react-pdf/renderer (utils/contractPdf.tsx)
persistido no bucket privado Storage `contracts`
caminho {workspace_id}/{contract_id}.pdf
pdf_url gravado na linha
API externa paralela (Bearer API key, service role):
POST/GET /api/v1/contracts
/api/v1/contracts/defaults
/api/v1/contracts/:id
POST /api/v1/contracts/:id/generate-pdf
Fora deste fluxo, e não é o gerador:
calculator_contracts(migration 156) confirma uma versão imutável de proposta da calculadora. Dono, time e hash. Não usar essas tabelas para templates.
Inventário
| Caminho | Papel | Classe |
|---|---|---|
lib/nav.ts |
Item Contratos, flag contracts |
REUTILIZAR |
utils/featureFlags.ts |
isFeatureEnabled / requireFeature, sem linha = desligado |
REUTILIZAR |
app/dashboard/contracts/layout.tsx |
Guard da flag | REUTILIZAR |
utils/supabase/server.ts, utils/supabase/user.ts |
Auth SSR | REUTILIZAR |
utils/workspace.ts |
getCurrentWorkspaceId, isWorkspaceAdmin, isWorkspaceManager, getManagedTeamIds |
REUTILIZAR |
utils/workspaceAdmin.ts |
Decisões puras de papel | REUTILIZAR |
migrations/038_contracts.sql |
Schema do contrato comercial Fly Apex | MANTER como legado; não é o gerador |
app/dashboard/contracts/actions.ts |
CRUD do contrato fixo | ADAPTAR só depois do DT-CTR-007; hoje qualquer membro edita |
components/Contracts/ContractWizard.tsx |
Wizard de campos fixos + hex antigo | ADAPTAR ou conviver; não é wizard de template |
components/Contracts/ContractEditor.tsx |
Editor de cláusulas/SLA/módulos | idem |
components/Contracts/DealContractsTab.tsx |
Contratos no card do deal | REUTILIZAR o gancho de deal; domínio novo é outro |
components/Contracts/ContractStatusBadge.tsx |
Badge de status comercial | REUTILIZAR se o status legado permanecer |
utils/contractPdf.tsx |
PDF do schema fixo + persistência | ADAPTAR o renderer; o modelo de documento é CRIAR |
app/api/v1/contracts/** |
API externa do schema antigo | MANTER para o legado; API nova só se DT-CTR-006 exigir |
utils/sanitize.ts |
stripHtml / sanitizeText (remove tags) |
ADAPTAR; não é sanitizador HTML com allowlist |
utils/auditLog.ts |
Auditoria de plataforma (workspace/user/org) | ADAPTAR o padrão; eventos de contrato são CRIAR |
utils/rateLimit.ts, utils/cache.ts |
Redis/ioredis, rate limit fail-open | REUTILIZAR |
sentry.*.config.ts, instrumentation-client.ts |
Sentry, sendDefaultPii: false, replay em erro |
ADAPTAR (falta scrub de conteúdo contratual) |
app/globals.css tokens --bc-* |
Traxion DS implementado no app | REUTILIZAR; ver conflito visual abaixo |
scripts/db/migrate.ts + migrations/*.sql |
Migrations numeradas, npm run migrate |
REUTILIZAR |
.github/workflows/ci.yml |
npm ci, Node 20, tsc, build | REUTILIZAR |
| Famílias/versões de template, placeholders, drafts 24 h, Contract Document Model | inexistentes | CRIAR |
Não há parser DOCX, upload de .docx/.txt de modelo, autosave de rascunho de formulário, nem cota de 10 modelos.
Schema encontrado
Migration 038_contracts.sql:
contracts: workspace, deal opcional, título, cliente, statusrascunho|enviado|assinado|ativo|expirado|cancelado,pdf_url.contract_commercial: limites, setup, mensalidade, vencimento, pagamento.contract_sla_items,contract_modules,contract_clauses.contract_sla_defaultsecontract_clause_defaults: seed global (workspace_idnulo) ou por workspace. Escrita de global exigeis_superadmin().- Flag
contractsinserida comenabled_globally = false. - RLS de membro em todas as tabelas de contrato. Sem divisão owner/admin vs gestor/agent.
- Nenhuma migration posterior altera
public.contracts.
calculator_contracts é outro bounded context (proposta da calculadora).
Feature flag e guards
- Flag
contracts, default global desligado. Override por workspace vence o global (utils/featureFlags.ts). - Ausência da linha retorna
false(fail-closed). - Layout chama
requireFeature('contracts')e redireciona para/dashboard. - Server Actions repetem
guardFeature(). - API v1 repete a checagem e devolve 403.
- Sidebar só mostra o item com a flag ligada.
- Não há guard de papel nas actions de contrato.
isWorkspaceAdmin()não é usado emapp/dashboard/contracts.
Tenant: getCurrentWorkspaceId() lê o cookie active_workspace_id e confirma membership. As queries filtram workspace_id no servidor. A API v1 usa o workspace da API key, não um id solto do body.
Gaps contra o Gerador de Contratos
| Requisito | Estado |
|---|---|
| Famílias e versões; mesmo nome cria versão; sem overwrite | Inexistente. Defaults globais são seed fixo. |
Upload .docx/.txt, 5 MB, 50 {{chave}} |
Inexistente. |
Tipos data_* date, valor_*/preco_* number, resto text |
Inexistente. Campos comerciais são colunas fixas. |
| Todos os campos obrigatórios | Wizard só exige título. |
| Máx. 10 modelos-base ativos; versões não contam | Inexistente. |
| Draft autosave ~15 s, TTL 24 h | Inexistente. Status rascunho é o contrato persistido. |
| Draft ativo bloqueia exclusão da versão | Delete bloqueia só status assinado e ativo. |
| Preview e PDF no mesmo Contract Document Model | PDF lê o schema relacional direto. Não há preview compartilhado. |
| PDF não é repositório permanente no MVP | O fluxo atual grava o PDF no Storage e pdf_url. |
| owner/admin gerenciam templates; gestor/agent operam | Qualquer membro do workspace faz CRUD. |
| Superadmin não edita documento privado no fluxo comum | Superadmin entra em isWorkspaceAdmin (plataforma). Defaults globais usam is_superadmin(). Não misturar com o fluxo operacional novo. |
| Sentry sem conteúdo contratual | sendDefaultPii: false. Sem beforeSend que raspe corpo de contrato, formulário, token ou cookie. Replay de erro está ligado no client (replaysOnErrorSampleRate: 1). |
| UI no Design System, sem tokens do protocolo antigo | Wizard/editor usam hex #001A3B, #00478F, #00AEEF. O app vivo usa --bc-* (Traxion). |
Conflitos que não devem ser resolvidos em silêncio:
- O módulo em produção é um contrato comercial de plataforma (SLA, módulos, cláusulas markdown), ligado a deal e funil (
proposal_created/proposal_sent). O gerador novo é template com placeholders. Substituir o schema atual quebra deal, API v1 e PDF armazenado. - A spec chama o visual de EloPro Design System V2.1. No
crmo handoff implementado é Traxion DS, tokens--bc-*emapp/globals.cssedocs/design/redesign-traxion-ds1.md. Não há artefato nomeado V2.1 neste repo. Os hex do wizard são o visual antigo e não devem ser copiados. audit_logda plataforma não tem ações de contrato e otarget_typenão inclui contrato. A calculadora temcalculator_audit_logpróprio. O gerador precisa de trilha nova, no mesmo estilo, sem enfiar conteúdo do documento nometa.
Estratégia recomendada
Não apagar o módulo atual nesta fase.
- Implementar o gerador dentro de
crm, na flagcontractsjá existente. - Conviver com o contrato comercial legado até uma decisão de produto (aba, rota irmã ou substituição). Não reutilizar
contracts/contract_clausescomo tabela de template. - Criar tabelas novas de família, versão, campo, draft e auditoria do gerador, com RLS por
workspace_ide checagem de papel no servidor (isWorkspaceAdminpara gestão). - Reusar Auth SSR,
getCurrentWorkspaceId, feature flag, nav, Redis,@react-pdf/renderer, runner de migration e CI. - PDF novo: gerar sob demanda com o Contract Document Model e não gravar como acervo. O bucket
contractscontinua válido para o PDF legado. - UI nova com tokens
--bc-*já presentes, até alguém apontar o arquivo V2.1 se ele for diferente do Traxion.
Decisões aprovadas em 2026-10-02 (encerram o P00)
- Implementar dentro de
Documents\crm, ao lado do módulo antigo. Não substituir, migrar nem alterar o contrato comercial ligado ao deal. - Parser DOCX: Mammoth. Sanitização HTML:
sanitize-htmlno servidor, obrigatória antes de persistir ou renderizar, preservando{{chave}}. Sem troca de biblioteca; Mammoth fica no Node (não no client e não no Edge). Cabeçalho, rodapé e caixa de texto seguem fora até a matriz DT-CTR-003. - Sem endpoint público
/api/v1para o gerador. A API antiga permanece só no domínio legado.
Os dez achados deste relatório continuam vinculantes. workspace_id não vem do browser. Tabela do módulo antigo não é reaproveitada por semelhança de nome.
Decision gates
| Gate | Status | Evidência |
|---|---|---|
| DT-CTR-001 parser DOCX | RESOLVIDO | Mammoth 1.13 no crm. Conversão do upload fica no P03. Roda só no Node. |
| DT-CTR-002 sanitização HTML | RESOLVIDO | sanitize-html no servidor (lib/gerador-contratos/sanitize.ts), antes de persistir ou renderizar. {{chave}} é preservado fora de tag removida. |
| DT-CTR-003 matriz DOCX | PENDENTE | Cabeçalho, rodapé, caixa de texto e estilo não estão prometidos. |
| DT-CTR-004 Storage | RESOLVIDO POR EVIDÊNCIA | Supabase Storage. Bucket privado contracts, service role, URL assinada. Templates devem usar esse mecanismo; o path/bucket exato do arquivo-fonte fica para a implementação, sem trocar de produto. |
| DT-CTR-005 deploy/CI/package | RESOLVIDO POR EVIDÊNCIA | npm (package-lock.json), Node 20, npm run migrate via postgres+tsx, CI em .github/workflows/ci.yml (tsc + build; lint não bloqueia). |
DT-CTR-006 API /api/v1 |
RESOLVIDO | Sem API pública nova no gerador. /api/v1/contracts permanece só no legado. |
DT-CTR-007 migração de /dashboard/contracts |
RESOLVIDO | Convive. Gerador em /dashboard/gerador-contratos. Contrato comercial do deal intocado. |
Plano P01–P12 (só depois do DT-CTR-007)
Trabalho no repo crm, branch dedicada. Este repo gerador-contratos não recebe o app.
- P01 Fundação: tipos de domínio, guards de flag e de admin, esqueleto de telas (lista, novo, modelos admin, histórico) sem parser nem PDF novo.
- P02 Migrations novas (próximo número após a última em
crm/migrations), RLS por workspace e papel, índices. Não alterar o significado das tabelas da 038. - P03 Upload e parser com Mammoth e
sanitizeContractHtml. Validação 5 MB, 50 chaves, tipos por prefixo, cota de 10 bases, versão por nome. A matriz DT-CTR-003 continua em aberto. - P04 Wizard operacional: escolha, revisão, formulário dinâmico, tudo obrigatório.
- P05 Draft autosave ~15 s, TTL 24 h. Redis só como apoio; Postgres é a fonte se o draft precisar sobreviver. Bloqueio de exclusão com draft ativo.
- P06 Contract Document Model único para preview e PDF.
@react-pdf/renderer. Sem persistir o PDF novo. - P07 Admin de templates, versões, exclusão protegida, auditoria sem conteúdo sensível no log.
- P08 Trocar hex do fluxo novo pelos tokens
--bc-*. Não criar design system. - P09 Hardening: papel no servidor, scrub Sentry (corpo, formulário, token, cookie, replay), upload, XSS, rate limit reusando
utils/rateLimit.ts. - P10 Vitest + critérios de aceite. E2E no Playwright já existente se o fluxo estiver estável.
- P11 Performance, WCAG AA, responsivo.
- P12 Rollout pela flag
contracts(continua fail-closed, default desligada).
P13 e P14 ficam para revisão e correção, fora deste plano de construção.