feat(web+api): ficha do cliente — contatos, CTR, NF-e, pedidos e produtos
- Contatos: grid de cards 3 colunas, botão "Novo Contato" no topo da página - Sync ERP↔SAR corrigido: vw_contatos aceita formato COR#<id>, trigger normaliza id_empresa (9001→1) - CTR + NF-e: layout 50/50 — lista de títulos abertos com badge vencido/a vencer e lista de notas com botão copiar chave NF-e - Histórico de pedidos: UNION SAR+ERP, top 5 + modal "Ver todos" - Produtos mais comprados: top 5 com último preço + modal "Ver todos" - Novos endpoints: ctr-list, notas, orders-history, top-produtos - AppShell: overflow-x travado, sem scroll horizontal na aplicação Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
265
docs/plano-migracao-modelo-canonico.md
Normal file
265
docs/plano-migracao-modelo-canonico.md
Normal file
@@ -0,0 +1,265 @@
|
||||
# Plano de Migração — Modelo de Dados Canônico SAR
|
||||
|
||||
**Data:** 2026-06-22
|
||||
**Autor:** Julian (Product Owner SAR)
|
||||
**Status:** Proposta para avaliação
|
||||
|
||||
---
|
||||
|
||||
## 1. Contexto e Problema
|
||||
|
||||
### Situação atual
|
||||
|
||||
O SAR foi iniciado consultando diretamente as **views do ERP JCS** (`vw_clientes`, `vw_pedidos_erp`, `vw_produtos`, etc.) que existem dentro do banco de dados do cliente (`libreplast@192.168.0.43`). Isso funcionou para o cliente piloto, mas cria uma dependência estrutural que inviabiliza a expansão do produto.
|
||||
|
||||
### Problema identificado
|
||||
|
||||
| Sintoma | Consequência |
|
||||
|---|---|
|
||||
| SAR acessa views do ERP via SQL raw | Cada novo ERP exige reescrever queries |
|
||||
| Schema `sar` vive *dentro* do banco ERP | Sem isolamento; SAR depende de acesso ao servidor do cliente |
|
||||
| Views são específicas do ERP JCS | ERP diferente = produto diferente |
|
||||
| Sem camada de abstração | Impossível oferecer o SAR como SaaS multi-tenant real |
|
||||
|
||||
### Diagnóstico técnico (levantamento 2026-06-22)
|
||||
|
||||
```
|
||||
Módulos 100% acoplados ao ERP: auth, catalog, clients
|
||||
Módulos parcialmente acoplados: orders (leitura), dashboard
|
||||
Módulos já independentes: notifications, workspace
|
||||
Views ERP consumidas: 11 views + 1 tabela direta
|
||||
Tabelas próprias SAR existentes: 6 models Prisma
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Solução Proposta
|
||||
|
||||
### Modelo Canônico + Connectors
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ APP SAR │
|
||||
│ (frontend + API NestJS) │
|
||||
│ Lê APENAS tabelas próprias do SAR │
|
||||
└──────────────────────┬──────────────────────────────┘
|
||||
│ Prisma ORM
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Banco de Dados SAR │
|
||||
│ (PostgreSQL isolado por workspace) │
|
||||
│ tabelas canônicas: clientes, produtos, pedidos... │
|
||||
└──────────┬──────────────────────┬───────────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────────┐ ┌──────────────────────────────┐
|
||||
│ Connector │ │ Connector │
|
||||
│ ERP JCS │ │ ERP X (futuro) │
|
||||
│ (PostgreSQL) │ │ (REST API / CSV / outro) │
|
||||
└──────────────────┘ └──────────────────────────────┘
|
||||
```
|
||||
|
||||
**Princípio:** O ERP é uma **fonte de dados**, não o sistema de registro do SAR. Cada cliente tem um connector específico para seu ERP. O SAR opera exclusivamente sobre seu próprio modelo de dados.
|
||||
|
||||
### O que não muda
|
||||
|
||||
Os dados transacionais do SAR já estão corretos e **permanecem sem alteração**:
|
||||
- `pedidos`, `pedido_itens`, `historico_pedido`
|
||||
- `alcada_desconto`, `meta_representante`
|
||||
- `push_subscription`
|
||||
|
||||
---
|
||||
|
||||
## 3. Tabelas Canônicas a Criar
|
||||
|
||||
### 3.1 Entidades mestres (sincronizadas do ERP)
|
||||
|
||||
| Tabela SAR | Substitui | Campos principais |
|
||||
|---|---|---|
|
||||
| `sar.clientes` | `vw_clientes` | codigo, razao_social, nome_fantasia, cnpj, cidade, uf, ativo, representante_codigo |
|
||||
| `sar.representantes` | `vw_representantes` | codigo, nome, email, senha_hash, tipo (rep/supervisor), ativo |
|
||||
| `sar.produtos` | `vw_produtos` | codigo, descricao, unidade, ncm, ativo |
|
||||
| `sar.estoques` | `vw_estoque` | produto_codigo, deposito, saldo (snapshot com timestamp) |
|
||||
| `sar.pautas` | `vw_pautas` | codigo, descricao, ativa |
|
||||
| `sar.pauta_produtos` | `vw_pauta_produtos` | pauta_codigo, produto_codigo, preco, desconto_max |
|
||||
| `sar.formas_pagamento` | `vw_formas_pagamento` | codigo, descricao, parcelas, ativa |
|
||||
| `sar.municipios` | `vw_municipios` | ibge, nome, uf |
|
||||
| `sar.empresa` | `gestao.empresa` | cnpj, razao_social, nome_fantasia, endereco, logo_url |
|
||||
|
||||
### 3.2 Histórico ERP (read-only, sincronizado)
|
||||
|
||||
| Tabela SAR | Substitui | Propósito |
|
||||
|---|---|---|
|
||||
| `sar.pedidos_erp` | `vw_pedidos_erp` | Histórico de pedidos anteriores ao SAR |
|
||||
| `sar.pedido_itens_erp` | `vw_peditens_erp` | Itens dos pedidos ERP |
|
||||
|
||||
### 3.3 Controle de sincronização
|
||||
|
||||
| Tabela SAR | Propósito |
|
||||
|---|---|
|
||||
| `sar.sync_log` | Registro de cada execução de sync (início, fim, erros, registros processados) |
|
||||
| `sar.sync_config` | Configuração do connector por workspace (tipo ERP, credenciais, frequência) |
|
||||
|
||||
---
|
||||
|
||||
## 4. Plano de Execução por Fases
|
||||
|
||||
### Fase 0 — Banco de Dados Isolado `[Fundação]`
|
||||
|
||||
**Objetivo:** Separar o banco SAR do banco ERP do cliente.
|
||||
|
||||
**Ações:**
|
||||
- Provisionar banco PostgreSQL exclusivo para o SAR (`sar_db`)
|
||||
- Configurar variável `DATABASE_URL` apontando para o novo banco
|
||||
- Manter `ERP_DB_*` apenas no connector (não no app principal)
|
||||
- Estrutura multi-tenant: um schema por workspace ou banco por workspace (já previsto na stack)
|
||||
|
||||
**Resultado:** SAR pode funcionar mesmo sem acesso ao ERP; connector roda separado.
|
||||
|
||||
**Estimativa:** 2–3 dias
|
||||
|
||||
---
|
||||
|
||||
### Fase 1 — Schema Canônico `[Estrutura]`
|
||||
|
||||
**Objetivo:** Criar todos os models Prisma das tabelas canônicas.
|
||||
|
||||
**Ações:**
|
||||
- Adicionar 11 models ao `schema.prisma` (seção 3.1 e 3.2)
|
||||
- Adicionar 2 models de controle de sync (seção 3.3)
|
||||
- Gerar e aplicar migrations
|
||||
- Nenhuma mudança no código da API ainda
|
||||
|
||||
**Resultado:** Tabelas existem no banco, prontas para receber dados.
|
||||
|
||||
**Estimativa:** 3–4 dias
|
||||
|
||||
---
|
||||
|
||||
### Fase 2 — Sync Service (Connector ERP JCS) `[Motor]`
|
||||
|
||||
**Objetivo:** Criar a camada que lê o ERP e popula as tabelas canônicas.
|
||||
|
||||
**Ações:**
|
||||
- Criar módulo `SyncModule` no NestJS
|
||||
- Implementar `ErpJcsConnector` com leitura das 11 views existentes
|
||||
- Sync completo na inicialização do workspace
|
||||
- Sync incremental por cron (ex: a cada 5 minutos para estoque, 30 min para cadastros)
|
||||
- Registrar execuções em `sync_log`
|
||||
- Endpoint admin `/api/v1/sync/trigger` para forçar sync manual
|
||||
|
||||
**Estratégia de sync:**
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────┐
|
||||
│ Frequências recomendadas │
|
||||
│ │
|
||||
│ A cada 5 min: estoques (alta volatilidade) │
|
||||
│ A cada 30 min: produtos, pautas, preços │
|
||||
│ A cada 60 min: clientes, representantes │
|
||||
│ A cada 6h: pedidos_erp (histórico) │
|
||||
│ 1x por dia: municipios, empresa, formas_pgto │
|
||||
└────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Resultado:** Tabelas SAR ficam populadas e atualizadas automaticamente.
|
||||
|
||||
**Estimativa:** 5–8 dias
|
||||
|
||||
---
|
||||
|
||||
### Fase 3 — Migração dos Módulos `[Execução]`
|
||||
|
||||
**Objetivo:** Substituir todos os `$queryRaw` por queries Prisma nas tabelas canônicas.
|
||||
|
||||
**Prioridade e ordem:**
|
||||
|
||||
| Ordem | Módulo | Complexidade | Motivo da prioridade |
|
||||
|---|---|---|---|
|
||||
| 1 | `catalog` | Baixa | Somente leitura, sem lógica de negócio complexa |
|
||||
| 2 | `clients` | Média | Base do dashboard e pedidos |
|
||||
| 3 | `auth` | Baixa | Bem delimitado; lê apenas `representantes` |
|
||||
| 4 | `dashboard` | Média | Depende de clients migrado |
|
||||
| 5 | `orders` (leitura) | Média | Parte escrita já usa Prisma corretamente |
|
||||
|
||||
**Processo por módulo:**
|
||||
1. Com o sync rodando, os dados já estão nas tabelas canônicas
|
||||
2. Substituir `$queryRaw` por `prisma.cliente.findMany(...)` etc.
|
||||
3. Manter o contrato de resposta da API (frontend não muda)
|
||||
4. Teste comparativo: mesmo resultado via ERP vs. via tabela SAR
|
||||
5. Remover a query antiga após validação
|
||||
|
||||
**Resultado:** Zero dependência de views ERP no código da API.
|
||||
|
||||
**Estimativa:** 5–8 dias
|
||||
|
||||
---
|
||||
|
||||
### Fase 4 — Segundo Connector (Validação da Abstração) `[Prova]`
|
||||
|
||||
**Objetivo:** Onboarding de um cliente com ERP diferente do JCS.
|
||||
|
||||
**Ações:**
|
||||
- Identificar segundo ERP (ex: TOTVS, Sankhya, outro)
|
||||
- Criar `ErpXConnector` implementando a mesma interface do `ErpJcsConnector`
|
||||
- Mapear campos do ERP X para o schema canônico SAR
|
||||
- Sem nenhuma mudança no app SAR
|
||||
|
||||
**Resultado:** Prova de conceito real de que a arquitetura é ERP-agnóstica.
|
||||
|
||||
**Estimativa:** 4–6 dias (variável conforme ERP)
|
||||
|
||||
---
|
||||
|
||||
## 5. Cronograma Estimado
|
||||
|
||||
```
|
||||
Semana 1: Fase 0 (banco isolado) + Fase 1 (schema Prisma)
|
||||
Semana 2: Fase 2 (Sync Service — maior esforço)
|
||||
Semana 3: Fase 3 (migração dos módulos)
|
||||
Semana 4: Fase 4 (segundo connector) + testes + documentação
|
||||
|
||||
Total estimado: 4 semanas (19–25 dias úteis)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Riscos e Mitigações
|
||||
|
||||
| Risco | Probabilidade | Impacto | Mitigação |
|
||||
|---|---|---|---|
|
||||
| Divergência de dados entre ERP e tabela SAR durante sync | Médio | Alto | Sync comparativo com log de diferenças; alertas |
|
||||
| Latência do sync afeta experiência do rep | Baixo | Médio | Sync de estoque a cada 5 min é suficiente para força de vendas |
|
||||
| ERP do cliente sem conectividade temporária | Médio | Baixo | SAR continua funcionando com dados do último sync |
|
||||
| Campos do segundo ERP sem equivalência no schema canônico | Médio | Médio | Schema canônico flexível com campo `metadata jsonb` por entidade |
|
||||
| Custo de manutenção de dois bancos | Baixo | Baixo | Banco SAR é pequeno; ERP continua como está |
|
||||
|
||||
---
|
||||
|
||||
## 7. Benefícios Esperados
|
||||
|
||||
| Benefício | Impacto |
|
||||
|---|---|
|
||||
| **Multi-ERP real** | Onboarding de qualquer cliente independente do ERP |
|
||||
| **Isolamento de falhas** | Queda do ERP não derruba o SAR |
|
||||
| **Performance** | Queries no banco SAR são mais rápidas (schema otimizado) |
|
||||
| **Funciona offline** | Rep consulta dados mesmo sem VPN/internet no cliente |
|
||||
| **Multi-tenant limpo** | Banco SAR separado por workspace desde o início |
|
||||
| **Evolução independente** | SAR pode adicionar campos sem depender do ERP |
|
||||
|
||||
---
|
||||
|
||||
## 8. Decisão
|
||||
|
||||
O projeto SAR **não precisa ser refeito do zero**. A infraestrutura (NestJS, autenticação, frontend React, ciclo de pedidos, alçadas, metas, push notifications) está correta e é reaproveitada integralmente.
|
||||
|
||||
A mudança é **cirúrgica e incremental**:
|
||||
- Adicionar banco e tabelas canônicas (Fases 0 e 1)
|
||||
- Construir o motor de sync (Fase 2)
|
||||
- Substituir queries raw módulo a módulo (Fase 3)
|
||||
|
||||
O risco de **não fazer** essa migração agora é maior: quanto mais o produto crescer sobre as views do ERP JCS, mais caro fica refatorar depois.
|
||||
|
||||
---
|
||||
|
||||
*Documento gerado em 2026-06-22. Para dúvidas: jcsinfo@gmail.com*
|
||||
Reference in New Issue
Block a user