ET-LIC-2026-001 Fase 1/3
⚙ ET-LIC-2026-001 · Fase 1 de 3

Especificação Técnica
Sistema Pipeline
Módulo de Licenciamento

Arquitetura, stack tecnológica e modelagem completa de banco de dados
Documento
ET-LIC-2026-001
Fase
1 de 3
Frontend
Vanilla JS SPA
Backend
Supabase (PostgreSQL)
Cloud
GitHub Pages + Supabase Cloud
Data
Maio 2026

Sumário Executivo Técnico

Este documento constitui a Especificação Técnica Completa do Módulo de Licenciamento integrado ao sistema Pipeline da Vendemmia. O documento abrange toda a arquitetura de software, modelagem de banco de dados, APIs, segurança, infraestrutura e estratégias de evolução técnica.

🏗

Arquitetura atual: O sistema foi implementado como uma Single Page Application (SPA) em Vanilla JavaScript servida via GitHub Pages (CDN global), com backend 100% gerenciado pela plataforma Supabase (PostgreSQL + PostgREST + Auth + Storage). Esta escolha arquitetural prioriza velocidade de entrega, custo zero de infraestrutura e ausência de complexidade operacional — adequada para o estágio atual do produto.

Frontend
Vanilla JS ES2022+
  • SPA em HTML/CSS/JS puro
  • Chart.js 4.4.1 (visualizações)
  • Leaflet.js 1.9.4 (mapas)
  • jsPDF 2.5.1 + html2canvas 1.4.1
  • Tabler Icons 2.44 (ícones)
  • Google Fonts: Inter + JetBrains Mono
🗄
Backend / BaaS
Supabase (PostgreSQL 17)
  • PostgreSQL 17.6.1 (sa-east-1)
  • PostgREST — REST API automática
  • Supabase Auth (JWT)
  • Supabase Storage (S3-compatible)
  • Row Level Security (RLS)
  • SDK JS: @supabase/supabase-js@2
Infraestrutura
Serverless / PaaS
  • GitHub Pages (CDN Akamai)
  • Supabase Cloud (AWS sa-east-1)
  • GitHub Actions (CI/CD)
  • BrasilAPI (CEP, geocodificação)
  • ViaCEP (fallback de CEP)
  • QR Server API (geração de QR Code)

1. Visão Geral Técnica Seção 1

1.1 Stack Tecnológica Detalhada

CamadaTecnologiaVersãoPropósitoCDN / Package
LinguagemJavaScript (ES2022+)Lógica de UI, manipulação DOM, state managementNativo do browser
Markup / StylingHTML5 + CSS3Estrutura e estilização — single-file architecture
Database client@supabase/supabase-js2.xComunicação com Supabase (Auth, DB, Storage)cdn.jsdelivr.net
GráficosChart.js4.4.1Todos os gráficos do sistema (line, bar, doughnut)cdnjs.cloudflare.com
MapasLeaflet.js1.9.4Mapa interativo do Brasil com marcadores e choroplethunpkg.com
PDF generationjsPDF2.5.1Geração de PDFs (Board Report + PDF individual do lead)cdnjs.cloudflare.com
Canvas capturehtml2canvas1.4.1Captura de HTML renderizado para conversão em PDFcdnjs.cloudflare.com
ÍconesTabler Icons (webfont)2.44.0Sistema de ícones SVG via CSS classcdn.jsdelivr.net
TipografiaGoogle FontsInter (UI) + JetBrains Mono (code/dados)fonts.googleapis.com
DatabasePostgreSQL17.6.1Persistência de todos os dados do sistemaSupabase managed
AuthSupabase AuthJWT-based authentication com sessão persistenteSupabase managed
StorageSupabase StorageArmazenamento de arquivos (documentos, fotos, PDFs)S3-compatible
GeoJSON (BR)giuliano-macedo/geodata-br-statesPolígonos dos estados brasileiros para choroplethraw.githubusercontent.com
CEP lookupBrasilAPIv2Geocodificação por CEP (retorna lat/lng)brasilapi.com.br
CEP fallbackViaCEPFallback de CEP quando BrasilAPI falhaviacep.com.br
QR CodeQR Server APIGeração de QR Code para link do portalapi.qrserver.com
Tile mapsCartoDB VoyagerTiles do mapa (estradas, parques, relevo)basemaps.cartocdn.com
HostingGitHub PagesHospedagem estática com CDN Akamai globalpipelinevendemmia.com.br
CI/CDGitHub ActionsDeploy automático via git push para branch maingithub.com

1.2 Arquitetura Geral do Sistema

O sistema adota uma arquitetura JAMstack (JavaScript + APIs + Markup) com backend como serviço (BaaS). Esta abordagem elimina a necessidade de servidores de aplicação próprios, reduz a superfície de ataque e minimiza custos operacionais.

Camadas da Arquitetura
🌐 Akamai CDN (Global)
⚡ GitHub Pages (Static Hosting)
📄 index.html (SPA)
↕ HTTPS REST (Supabase JS SDK v2)
🔐 Supabase Auth (JWT)
📡 PostgREST (REST API)
🗄 PostgreSQL 17
📦 Supabase Storage (S3)
←→
🛡 RLS Policies
←→
📊 5 Tabelas Licenciamento
APIs Externas: BrasilAPI (CEP) · ViaCEP (fallback) · CartoDB (tiles) · QR Server · GitHub GeoJSON

1.3 Decisões Arquiteturais e Justificativas

DecisãoAlternativas consideradasEscolhaJustificativa
Single-file SPAReact, Vue, AngularVanilla JS (index.html)Zero dependências de build pipeline; deploy via git push; equipe sem expertise em frameworks; MVP rápido
Backend como serviçoNode.js + Express, .NET, FastAPISupabase (BaaS)PostgreSQL gerenciado, Auth, Storage e API REST prontos; sem custo de servidor; SA-East-1 (baixa latência para o Brasil)
AutenticaçãoAuth0, Firebase Auth, JWT próprioSupabase AuthIntegração nativa com o banco; RLS por usuário; sessão persistente; custo zero no tier atual
Controle de acessoRBAC no backend, middlewareWhitelist de e-mail + RLSSimplicidade para o estágio atual; RLS no PostgreSQL garante isolamento de dados; whitelist no frontend filtra acesso
StorageAWS S3 direto, CloudinarySupabase StorageJá incluído no Supabase; políticas RLS aplicáveis; SDK unificado; sem custo adicional
HospedagemVercel, Netlify, AWS S3GitHub PagesIntegrado ao repositório existente; CDN Akamai global; domínio customizado; custo zero
Geração de PDFPuppeteer server-side, wkhtmltopdfjsPDF + html2canvas (client-side)Zero infraestrutura de servidor; funciona offline; menor latência percebida pelo usuário
MapasGoogle Maps, Mapbox, Deck.glLeaflet.js + CartoDBOpen source; sem API key; tiles CartoDB gratuitos com qualidade visual adequada; leve (≈150KB)

Limitação conhecida — single-file architecture: A ausência de bundler (Webpack/Vite) e de gerenciamento de módulos ES significa que todo o código JS está em um único arquivo index.html com aproximadamente 11.000+ linhas. Esta abordagem funciona para o estágio atual mas representa débito técnico que deve ser endereçado na evolução para arquitetura modular (ver Seção 12 na Fase 3).

2. Arquitetura do Sistema Seção 2

2.1 Frontend — Single Page Application (SPA)

O frontend é uma SPA implementada em um único arquivo HTML (index.html) servida via GitHub Pages no domínio pipelinevendemmia.com.br. Toda a lógica de UI, state management e comunicação com APIs está contida neste arquivo.

Estrutura lógica do frontend

// Organização lógica do código em index.html (~11.000+ linhas)
// ─────────────────────────────────────────────────────────

// [1] HEAD: CDNs carregados em ordem de dependência
Leaflet CSSTabler Icons CSSjsPDFhtml2canvasLeaflet JSChart.js@supabase/supabase-js

// [2] STYLE: CSS global (~700 linhas)
- Variáveis CSS (design tokens)
- Componentes reutilizáveis (cards, buttons, modals)
- Módulos específicos (lic-*, fin-*, kb-*)

// [3] HTML: Estrutura de telas (SPA)
- #login-overlay      → tela de autenticação
- #main-system        → container principal (hidden até login)
  - header (nav global)
  - #page-dashboard   → tela Home/Pipeline
  - #page-kanban      → Kanban de oportunidades
  - #page-financeiro  → Financeiro
  - #page-licenciamento → Módulo Licenciamento
    - #lic-view-tabela       → dashboard + tabela
    - #lic-view-form         → formulário 11 seções
    - #lic-view-mapa         → mapa interativo
    - #lic-view-kanban       → kanban de leads
    - #lic-view-scorecard    → scorecard KPIs
    - #lic-view-auditoria    → pipeline auditoria
    - #lic-view-capacidade   → painel capacidade
    - #lic-view-disponibilidade → disponibilidade
  - Modais globais (fixed position)

// [4] SCRIPT: JavaScript (~9.000+ linhas)
- Inicialização e autenticação
- Módulos do sistema principal (Pipeline, Financeiro...)
- Módulo Licenciamento (funções lic*, lc*, aud*, sc*...)
  - State: baseLicLeads, _licPerfData, _audData, _licHistData
  - Funções de renderização: renderizarLicenciamento()
  - Funções de mapa: _licInicializarMapa(), _licAtualizarMarkers()
  - Funções de scorecard: calcLicScore(), getLicTier()
  - Funções de auditoria: audAvancarEtapa(), emitirCertificado()
  - Funções de histórico: registrarLicHistorico()
  - Funções de PDF: gerarLicPDF(), gerarBoardReport()

State Management

O sistema não utiliza biblioteca de state management. O estado é gerenciado via variáveis globais JavaScript com padrão de nomenclatura prefixada:

VariávelTipoPropósitoCarga
baseLicLeadsArray<Object>Todos os leads de licenciamentoSupabase lic_leads
_licPerfDataObject<id, Array>KPIs por lead (indexado por lead_id)Supabase lic_performance
_audDataObject<id, Object>Dados de auditoria por leadSupabase lic_auditoria
_licHistDataObject<id, Array>Histórico de eventos por leadSupabase lic_historico
_licGeoJsonDataGeoJSON ObjectPolígonos dos estados do BrasilGitHub CDN (uma vez)
_licMapPhotoCacheObject<id, URL>Cache de URLs de fotos por leadSupabase lic_anexos
_licDChartsObject<key, Chart>Instâncias Chart.js para destroy antes de recriarRuntime
LIC_TIERSArray<Object>Configuração dos tiers (Platina/Ouro/Prata/Bronze)Constante em memória
LIC_AUDIT_STAGESArray<Object>Configuração das 6 etapas de auditoriaConstante em memória
LIC_KPI_METAObjectConfiguração dos 6 KPIs com thresholdsConstante em memória

2.2 Backend — Supabase (BaaS)

O backend é inteiramente gerenciado pelo Supabase (projeto geojgcncmtbjebftosor, região sa-east-1 — São Paulo). O Supabase expõe automaticamente uma API REST via PostgREST sobre o PostgreSQL, com autenticação via JWT e controle de acesso por Row Level Security (RLS).

Componentes Supabase em uso

ComponenteEndpoint baseFunção
PostgREST (REST API)https://geojgcncmtbjebftosor.supabase.co/rest/v1/CRUD automático de todas as tabelas; filtros, ordenação e seleção de campos via query params
Auth/auth/v1/Login com e-mail/senha, geração de JWT, refresh token, sessão persistente no localStorage
Storage/storage/v1/Upload, download e listagem de arquivos; buckets com políticas RLS; URLs públicas
Realtimewss://Não utilizado atualmente — disponível para evolução futura (live updates)

Padrão de chamada à API

// Inicialização do cliente Supabase (chamada única no carregamento)
const SUPABASE_URL = "https://geojgcncmtbjebftosor.supabase.co";
const SUPABASE_KEY = "eyJ..."; // anon key (safe para expor no client)
const { createClient } = supabase;
const supabaseClient = createClient(SUPABASE_URL, SUPABASE_KEY);

// Padrão de leitura (SELECT)
const { data, error } = await supabaseClient
  .from('lic_leads')
  .select('*')
  .eq('status', 'Convertido')
  .order('created_at', { ascending: false });

// Padrão de escrita (INSERT)
const { data: novo, error } = await supabaseClient
  .from('lic_leads')
  .insert([{ razao_social: '...', status: 'Ativo' }])
  .select('id')
  .single();

// Padrão de atualização (UPDATE)
await supabaseClient
  .from('lic_leads')
  .update({ status: 'Convertido', updated_at: new Date().toISOString() })
  .eq('id', leadId);

// Upload para Storage
await supabaseClient.storage
  .from('lic-anexos')
  .upload(path, file, { cacheControl: '3600', upsert: false });

2.3 Storage e Estrutura de Arquivos

O Supabase Storage é usado para persistir todos os arquivos do módulo Licenciamento no bucket lic-anexos. A estrutura de paths garante isolamento por lead e rastreabilidade por tipo.

Estrutura de paths no bucket lic-anexos

lic-anexos/
├── leads/
│   └── {lead_id}/
│       ├── contrato_social/
│       │   └── {timestamp}_{nome_arquivo}     ← Contrato Social (PDF/DOC)
│       ├── foto_armazem_1/
│       │   └── {timestamp}_{nome_arquivo}     ← Fotos do Armazém 1 (JPG/PNG)
│       ├── foto_armazem_2/
│       │   └── {timestamp}_{nome_arquivo}     ← Fotos do Armazém 2
│       ├── certificado/
│       │   └── {timestamp}_{nome_arquivo}     ← Certificações (PDF/JPG)
│       ├── relatorio_visita/
│       │   └── {timestamp}_{nome_arquivo}     ← Relatório de visita técnica (PDF)
│       └── pdf_lead/
│           └── {timestamp}_lead_{id}_AAAA-MM.pdf  ← PDF gerado do lead

Políticas de Storage (RLS em storage.objects)

PolíticaOperaçãoUsuáriosCondição
lic-anexos uploadINSERTauthenticatedbucket_id = 'lic-anexos'
lic-anexos anon uploadINSERTanon (portal público)bucket_id = 'lic-anexos'
lic-anexos leituraSELECTpublicbucket_id = 'lic-anexos'
lic-anexos deleteDELETEauthenticatedbucket_id = 'lic-anexos'

2.4 Autenticação e Sessão

Fluxo de autenticação

// 1. Login — Supabase Auth (e-mail + senha)
const { data, error } = await supabaseClient.auth
  .signInWithPassword({ email, password });

// 2. JWT obtido → armazenado no localStorage pelo SDK
//    Chave: 'sb-geojgcncmtbjebftosor-auth-token'

// 3. Todas as chamadas subsequentes incluem automaticamente
//    o header: Authorization: Bearer {jwt_token}

// 4. Controle de acesso ao módulo Licenciamento
const LICENCIAMENTO_WHITELIST = [
  'gppereira@vendemmia.com.br',
  'jprado@vendemmia.com.br'
];
// Verificação via EMAIL_USUARIO_LOGADO após login

// 5. Sessão persistente: SDK recupera sessão do localStorage
//    automaticamente no reload da página
//    JWT expira em ~3600s; SDK faz refresh automático

2.5 Comunicação entre Componentes

OrigemDestinoProtocoloFormato
Browser (SPA)Supabase PostgRESTHTTPS RESTJSON (application/json)
Browser (SPA)Supabase StorageHTTPS multipartFormData (files)
Browser (SPA)Supabase AuthHTTPS RESTJSON
Browser (SPA)BrasilAPIHTTPS RESTJSON (GeoJSON v2)
Browser (SPA)CartoDB CDNHTTPSPNG (map tiles 256×256)
Browser (SPA)GitHub CDNHTTPSJSON (GeoJSON estados BR)
Browser (SPA)QR Server APIHTTPSPNG (imagem do QR Code)
Portal (portal.html)Supabase PostgRESTHTTPS RESTJSON
Portal (portal.html)Supabase StorageHTTPS multipartFormData
GitHub ActionsGitHub PagesGit (HTTPS)Static files

3. Banco de Dados — PostgreSQL 17 Seção 3

3.1 Modelo Lógico de Dados

O módulo Licenciamento utiliza 5 tabelas próprias no banco Supabase, além de referenciar a tabela auth.users do Supabase Auth. Todas as tabelas têm Row Level Security (RLS) habilitado.

Relacionamentos entre tabelas
lic_leads (id PK)
    │
    ├──── lic_auditoria (lead_id FK UNIQUE) — 1:1 por lead
    │
    ├──── lic_performance (lead_id FK) — 1:N por lead (múltiplos períodos)
    │
    ├──── lic_historico (lead_id FK) — 1:N por lead (linha do tempo)
    │
    └──── lic_anexos (lead_id FK) — 1:N por lead (múltiplos arquivos)
  

3.2 Tabela: lic_leads

Tabela principal. Armazena todos os dados cadastrais de cada lead/licenciado da rede.

lic_leads
Leads e licenciados da rede · Criada: Fev 2026 · ~50 registros esperados inicialmente
Campo
Tipo
Nulo
Descrição
Chave
id
BIGSERIAL
NOT NULL
Identificador único autoincremental
PK
razao_social
TEXT
NULL
Razão social completa da empresa
cnpj
TEXT
NULL
CNPJ formatado (00.000.000/0001-00)
website
TEXT
NULL
URL do site da empresa
ano_fundacao
INTEGER
NULL
Ano de fundação da empresa (1800–atual)
faturamento_faixa
TEXT
NULL
Faixa de faturamento anual (enum textual)
segmentos
TEXT
NULL
Segmentos atendidos (CSV: "Armazenagem, Transporte")
contato_nome
TEXT
NULL
Nome completo do contato principal
contato_cargo
TEXT
NULL
Cargo do contato
contato_email
TEXT
NULL
E-mail do contato principal
contato_telefone
TEXT
NULL
WhatsApp/telefone do contato
decisor
TEXT
NULL
"Este contato" ou "Outro"
decisor_info
TEXT
NULL
Nome e cargo do decisor (se diferente)
uf
TEXT
NULL
UF principal (sigla de 2 chars, ex: "SP")
IDX
polo_logistico
TEXT
NULL
Cidade/polo principal de operação
regiao_exclusividade
TEXT
NULL
Região pretendida para exclusividade territorial
outras_ufs
TEXT
NULL
Outras UFs de atuação (texto livre)
cep
TEXT
NULL
CEP principal (formato 00000-000)
lat
NUMERIC
NULL
Latitude GPS obtida via BrasilAPI CEP
lng
NUMERIC
NULL
Longitude GPS obtida via BrasilAPI CEP
endereco_logradouro
TEXT
NULL
Rua/Avenida (preenchido pelo CEP)
endereco_bairro
TEXT
NULL
Bairro (preenchido pelo CEP)
armazens
JSONB
DEFAULT '[]'
Array de armazéns [{nome, uf, cidade, area_m2, docas, ocupacao, paletes, pe_direito}]
area_total_m2
NUMERIC
DEFAULT 0
Soma de area_m2 de todos os armazéns (calculado no frontend)
cargas_geral
TEXT
NULL
Tipos de carga geral habilitados (CSV)
cargas_perigosas
TEXT
NULL
Classes IMDG habilitadas (CSV)
cargas_especiais
TEXT
NULL
Cargas de temperatura controlada (CSV)
licencas
TEXT
NULL
Licenças ativas (CSV: "ANTT, ANVISA")
obs_cargas
TEXT
NULL
Observações sobre restrições de carga
wms_atual
TEXT
NULL
Sistema WMS em uso
erp_atual
TEXT
NULL
Sistema ERP em uso
sistemas_integrados
TEXT
NULL
Sistemas integrados (CSV: "TMS, BI, API REST")
nivel_maturidade
TEXT
NULL
Maturidade tecnológica (Básico/Em desenvolvimento/Intermediário/Avançado)
certificacoes
TEXT
NULL
Certificações ativas (CSV: "ISO 9001, ANVISA")
otif_pct
NUMERIC
NULL
OTIF médio atual (%); espelhado de lic_performance
acuracidade_pct
NUMERIC
NULL
Acuracidade de inventário (%); espelhado de lic_performance
plano
TEXT
NULL
Plano selecionado (Starter/Pro/Enterprise/Custom)
implantacao_wms
TEXT
NULL
"Sim" ou "Não" — implantação de WMS prevista
wms_opcao
TEXT
NULL
Opção de WMS selecionada (Senior, API Pronta, etc.)
etapa
TEXT
DEFAULT 'Prospecção'
Etapa atual no kanban comercial
IDX
origem
TEXT
NULL
Canal de origem do lead
data_primeiro_contato
DATE
NULL
Data do primeiro contato com o candidato
data_proxima_acao
DATE
NULL
Data da próxima ação planejada
proxima_acao
TEXT
NULL
Descrição da próxima ação planejada
observacoes
TEXT
NULL
Observações livres da negociação
responsavel
TEXT
NULL
Nome do vendedor/comercial responsável
status
TEXT
DEFAULT 'Ativo'
Status do lead (Ativo/Convertido/Perdido)
IDX
motivo_perda
TEXT
NULL
Motivo da perda (obrigatório quando status=Perdido)
fee_mensal
NUMERIC
DEFAULT 0
Fee mensal estimado/contratado (R$)
form_token
TEXT
NULL
Token único de acesso ao portal público
UNIQUE
form_status
TEXT
DEFAULT 'pendente'
Status do portal (pendente/preenchendo/concluido)
form_filled_at
TIMESTAMPTZ
NULL
Data/hora do submit final do portal
criado_por
TEXT
NULL
E-mail do usuário que criou o lead
created_at
TIMESTAMPTZ
DEFAULT NOW()
Timestamp de criação do registro
IDX
updated_at
TIMESTAMPTZ
DEFAULT NOW()
Timestamp da última atualização

3.3 Tabela: lic_auditoria

lic_auditoria
Pipeline de homologação · Relação 1:1 com lic_leads (UNIQUE por lead_id)
Campo
Tipo
Nulo
Descrição
Chave
id
BIGSERIAL
NOT NULL
PK autoincremental
PK
lead_id
BIGINT
NOT NULL
Referência ao lead (ON DELETE CASCADE)
FKUNIQUE
estagio_atual
TEXT
DEFAULT 'prospectado'
Etapa atual (prospectado/formulario/documental/visita/aprovacao/ativo)
IDX
checklists
JSONB
DEFAULT '{}'
Estado dos checklists por etapa: {"prospectado": [true, false, true, true], ...}
prazos
JSONB
DEFAULT '{}'
Prazos por etapa: {"prospectado": "2026-06-01", ...}
historico
JSONB
DEFAULT '[]'
Registro de conclusões: [{estagio, iniciado_em, concluido_em}]
etapa_iniciada_em
TIMESTAMPTZ
DEFAULT NOW()
Quando o lead entrou na etapa atual (base do cálculo de SLA)
notas
TEXT
NULL
Observações da etapa atual (apagado ao avançar de etapa)
relatorio_visita_url
TEXT
NULL
URL pública do relatório de visita técnica no Storage
relatorio_visita_nome
TEXT
NULL
Nome original do arquivo do relatório
relatorio_visita_path
TEXT
NULL
Path no Storage (para operações de delete)
certificado_numero
TEXT
NULL
Número do certificado emitido (ex: VDM-2026-001)
certificado_emitido_em
DATE
NULL
Data de emissão do certificado
certificado_validade
DATE
NULL
Data de validade do certificado
IDX
auditor
TEXT
NULL
Nome do auditor responsável pelo processo
criado_por
TEXT
NULL
E-mail do usuário que iniciou a auditoria
created_at
TIMESTAMPTZ
DEFAULT NOW()
Timestamp de criação
updated_at
TIMESTAMPTZ
DEFAULT NOW()
Timestamp da última atualização

3.4 Tabela: lic_performance

lic_performance
KPIs trimestrais por licenciado · Permite múltiplos registros por período (sem UNIQUE)
Campo
Tipo
Nulo
Descrição
Chave
id
BIGSERIAL
NOT NULL
PK autoincremental
PK
lead_id
BIGINT
NOT NULL
Referência ao lead (ON DELETE CASCADE)
FKIDX
periodo
TEXT
NULL
Período textual (ex: "Q1 2026", "Q2 2026")
periodo_ref
DATE
NULL
Data de referência do período (1º dia do trimestre)
IDX
otif_pct
NUMERIC
NULL
OTIF — On-Time In-Full (%)
acuracidade_inventario
NUMERIC
NULL
Acuracidade de inventário (%)
dock_to_stock_hrs
NUMERIC
NULL
Dock-to-Stock Time (horas)
utilizacao_capacidade
NUMERIC
NULL
Utilização de capacidade (% de m² ocupado)
nps_score
NUMERIC
NULL
Net Promoter Score (-100 a +100)
tempo_onboarding_dias
NUMERIC
NULL
Tempo médio de onboarding de novos clientes (dias)
em_remediacao
BOOLEAN
DEFAULT FALSE
Flag indicando que este registro tem plano de remediação ativo
plano_remediacao
TEXT
NULL
Ações corretivas descritas no plano de remediação
data_inicio_remediacao
DATE
NULL
Data de início do plano de remediação
data_prevista_resolucao
DATE
NULL
Data prevista de conclusão do plano
criado_por
TEXT
NULL
E-mail do usuário que lançou os KPIs
created_at
TIMESTAMPTZ
DEFAULT NOW()
Timestamp de criação do registro
IDX

3.5 Tabela: lic_historico

lic_historico
Timeline de eventos CRM · Append-only (sem UPDATE, apenas INSERT e DELETE)
Campo
Tipo
Nulo
Descrição
Chave
id
BIGSERIAL
NOT NULL
PK autoincremental
PK
lead_id
BIGINT
NOT NULL
Referência ao lead (ON DELETE CASCADE)
FKIDX
tipo
TEXT
NULL
Tipo do evento (ver tabela de tipos na Especificação Funcional Anexo B)
IDX
titulo
TEXT
NULL
Título descritivo do evento
descricao
TEXT
NULL
Detalhes adicionais do evento
etapa_anterior
TEXT
NULL
Etapa anterior (para eventos de mudança de etapa)
etapa_nova
TEXT
NULL
Nova etapa (para eventos de mudança de etapa)
autor
TEXT
NULL
Nome ou e-mail do responsável pelo evento
criado_por
TEXT
NULL
E-mail do usuário autenticado que criou o registro
created_at
TIMESTAMPTZ
DEFAULT NOW()
Timestamp do evento (usado para ordenação da timeline)
IDX

3.6 Tabela: lic_anexos

lic_anexos
Metadados de arquivos · Armazenamento físico no bucket Supabase Storage 'lic-anexos'
Campo
Tipo
Nulo
Descrição
Chave
id
BIGSERIAL
NOT NULL
PK autoincremental
PK
lead_id
BIGINT
NOT NULL
Referência ao lead (ON DELETE CASCADE)
FKIDX
tipo
TEXT
NULL
Tipo do arquivo (contrato_social/foto_armazem_N/certificado/relatorio_visita/pdf_lead)
IDX
nome_arquivo
TEXT
NULL
Nome original do arquivo com extensão
url
TEXT
NULL
URL pública permanente para acesso/download
storage_path
TEXT
NULL
Path no bucket (para operações de delete no Storage)
tamanho_bytes
BIGINT
NULL
Tamanho do arquivo em bytes
criado_por
TEXT
NULL
E-mail do usuário ou "Portal" para uploads do portal público
created_at
TIMESTAMPTZ
DEFAULT NOW()
Timestamp de upload
IDX

3.7 Índices Recomendados

-- lic_leads: queries mais frequentes
CREATE INDEX idx_lic_leads_status ON lic_leads(status);
CREATE INDEX idx_lic_leads_uf ON lic_leads(uf);
CREATE INDEX idx_lic_leads_etapa ON lic_leads(etapa);
CREATE INDEX idx_lic_leads_created_at ON lic_leads(created_at DESC);
CREATE UNIQUE INDEX idx_lic_leads_form_token ON lic_leads(form_token)
  WHERE form_token IS NOT NULL; -- partial unique index

-- lic_auditoria: lookup por lead
CREATE UNIQUE INDEX idx_lic_auditoria_lead ON lic_auditoria(lead_id);
CREATE INDEX idx_lic_auditoria_estagio ON lic_auditoria(estagio_atual);
CREATE INDEX idx_lic_auditoria_cert_val ON lic_auditoria(certificado_validade)
  WHERE certificado_validade IS NOT NULL; -- para alertas de renovação

-- lic_performance: queries por lead + ordenação por período
CREATE INDEX idx_lic_perf_lead_periodo ON lic_performance(lead_id, periodo_ref DESC);

-- lic_historico: timeline ordenada por lead
CREATE INDEX idx_lic_hist_lead_created ON lic_historico(lead_id, created_at DESC);

-- lic_anexos: lookup por lead e tipo
CREATE INDEX idx_lic_anx_lead_tipo ON lic_anexos(lead_id, tipo);

3.8 Row Level Security (RLS)

Todas as tabelas do módulo Licenciamento têm RLS habilitado com política "Allow all" para usuários autenticados. O controle de acesso granular é feito na camada de aplicação (whitelist no frontend).

-- Padrão aplicado em todas as 5 tabelas
ALTER TABLE lic_leads ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Allow all lic_leads" ON lic_leads
  FOR ALL USING (true) WITH CHECK (true);

-- Nota: política permissiva necessária pois o portal público
-- (usuário anônimo) precisa ler/escrever dados de leads via token.
-- Evolução: implementar RLS por token (ver Fase 3 - evolução técnica)

Débito técnico — RLS permissivo: A política atual USING (true) WITH CHECK (true) permite que qualquer usuário autenticado (ou anônimo, no caso do portal) leia e escreva qualquer registro. Isso é adequado para o MVP mas deve ser endurecido na evolução: restringir acesso anônimo apenas a registros cujo form_token corresponda ao token na requisição, e restringir autenticados ao tenant Vendemmia.

⚙ Especificação Técnica — Parte 1 de 3 concluída
Esta é a Parte 1. Acesse as demais partes pelos links da barra lateral.
✅ Documento aprovado
PIPELINE by Vendemmia
ET-LIC-2026-001 · Versão 1.0 · Fase 1/3 · Arquitetura e Banco de Dados
Confidencial — Vendemmia Comércio Internacional Ltda.
pipelinevendemmia.com.br · Maio 2026