Importação de Dados — Smart Migration Tool
Visão Geral
O Smart Migration Tool permite importar dados de sistemas legados (planilhas Excel, exports de PDV antigos, ERPs genéricos) para o PopinaFlow com zero retrabalho manual e 100% de integridade de dados.
| Característica | Detalhe |
|---|---|
| Rota | /t/:slug/admin/migration |
| View | frontend-react/src/views/admin/MigrationView.tsx |
| Backend | backend/src/migration/ |
| Formatos suportados | CSV, Excel (.xlsx), JSON |
| Tamanho máximo | 50 MB por arquivo |
| Processamento | Assíncrono via BullMQ (concorrência 2) |
| Atomicidade | MongoDB withTransaction() — tudo ou nada por domínio |
| TTL da sessão | 48h (arquivo S3 + documento MongoDB) |
| Permissão | Admin ou Superadmin |
Domínios Suportados
| Domínio | O que importa | Chave de deduplicação |
|---|---|---|
| Cardápio | Itens de menu, categorias, preços | nome + categoria (case-insensitive) |
| Estoque | Itens de estoque, quantidades iniciais | SKU único por tenant |
| Fornecedores | Cadastro de fornecedores | CNPJ (14 dígitos) ou nome |
| Pedidos | Histórico de vendas (status delivered) | Gerado automaticamente |
| Contabilidade | Lançamentos contábeis e DRE | entryNumber único por tenant |
Ordem de dependência: Quando múltiplos domínios são selecionados, a importação respeita a ordem topológica obrigatória:
Fornecedores → Estoque → Cardápio → Pedidos → ContabilidadeFluxo do Wizard (5 Etapas)
Etapa 1 — Selecionar Domínios
Selecione quais domínios importar. Múltiplos domínios podem ser selecionados simultaneamente — o sistema enfileira automaticamente na ordem correta.
Etapa 2 — Enviar Arquivo
Arraste ou selecione um arquivo por domínio. O sistema valida:
- Formato (CSV/Excel/JSON via magic bytes)
- Tamanho (≤ 50 MB)
- Estrutura básica (pelo menos 1 linha de dados)
O arquivo é enviado para o S3 (armazenamento privado) e o processamento inicia imediatamente em background.
Etapa 3 — Mapear Campos
O FieldMapper analisa os cabeçalhos do arquivo e sugere automaticamente o mapeamento para os campos internos do PopinaFlow.
Algoritmo de mapeamento (duas fases):
| Fase | Condição | Fonte |
|---|---|---|
| Levenshtein | Confiança ≥ 70% | Baseado em tabelas de aliases por domínio |
| Fallback IA | Confiança < 70% | AiProviderService com prompt estruturado |
| Cache | Tenant já importou antes | Redis (TTL 30 dias) — zero custo de IA |
Indicador de confiança:
| Barra | Confiança | Ação do usuário |
|---|---|---|
| Verde sólido | ≥ 70% | Mapeamento automático — clique "Editar" para sobrescrever |
| Âmbar | < 70% | Dropdown de seleção obrigatório |
Exemplos de aliases reconhecidos para Cardápio:
| Campo interno | Aliases aceitos |
|---|---|
name | nome, produto, item, descricao, item_name |
price | preco, valor, preco_venda, sale_price |
category | categoria, grupo, section |
sku | cod, code, ref, codigo |
Etapa 4 — Validar & Aplicar
Migration Pulse
Barra de progresso em tempo real com 5 estágios:
[ Enviando ] → [ Analisando ] → [ Mapeando ] → [ Validando ] → [ Pronto ]- Ativo: animação de carregamento
- Concluído: ✓ verde
- Falha: ✗ vermelho com mensagem de erro
Painel de Conflitos (Quiet Error Console)
Exibido quando há conflitos a resolver antes de aplicar. Um card por tipo de conflito:
| Tipo | Descrição | Ações disponíveis |
|---|---|---|
missing_required | Campo obrigatório ausente | Campo de valor padrão + "Usar padrão" / "Ignorar" |
duplicate | Registro já existe | Radio: Substituir / Manter ambos / Ignorar |
unresolved_fk | Entidade relacionada não encontrada | Link para criar a entidade / Ignorar |
invalid_unit | Unidade de medida não reconhecida | Selecionar unidade equivalente |
currency_parse_fail | Valor monetário não parseável | Campo de valor padrão |
Preview de Impacto
| Contador | Descrição |
|---|---|
| Criar | Registros novos que serão criados |
| Atualizar | Registros existentes que serão atualizados |
| Ignorar | Linhas puladas (campos obrigatórios vazios) |
| Conflitos | Linhas que requerem resolução manual |
O botão "Aplicar importação" fica disponível somente quando todos os conflitos estão resolvidos.
Etapa 5 — Ledger de Sucesso
Resumo da importação aplicada com contadores finais, domínio, data e botão para nova importação.
Integração com Onboarding
O Smart Migration Tool está integrado ao Wizard de Onboarding, na Etapa 4 (Monte seu Cardápio):
Aba "🚀 Importar arquivo" → importa cardápio de um arquivo legado → avança automaticamente o onboarding ao concluir.
Isso permite que novos clientes migrem seus cardápios existentes sem sair do fluxo de configuração inicial.
API Endpoints
Guards: JwtAuthGuard → TenantGuard → RolesGuard(Admin, Superadmin)
| Método | Rota | Descrição |
|---|---|---|
POST | /t/:slug/migration | Upload de arquivo + criação de sessão + enfileiramento |
GET | /t/:slug/migration/history | Histórico de sessões do tenant |
GET | /t/:slug/migration/:id | Polling de status da sessão (2s no frontend) |
PATCH | /t/:slug/migration/:id/mapping | Atualizar mapeamento de campos após edição |
PATCH | /t/:slug/migration/:id/conflicts | Submeter resoluções de conflito |
POST | /t/:slug/migration/:id/apply | Aplicar sessão pronta ao banco de dados |
DELETE | /t/:slug/migration/:id | Cancelar sessão + remover arquivo do S3 |
Estados da Sessão
uploading → parsing → mapping → validating → ready → applying → completed
↓
failed| Status | Descrição |
|---|---|
uploading | Arquivo sendo enviado ao S3 |
parsing | Parser lendo o arquivo (CSV/Excel/JSON) |
mapping | FieldMapper inferindo cabeçalhos |
validating | Domain service validando linhas (dry-run) |
ready | Pronto para aplicar (sem conflitos, ou todos resolvidos) |
applying | Transação MongoDB em andamento |
completed | Importação aplicada com sucesso |
failed | Erro irrecuperável — errorMessage disponível |
Segurança e Integridade
| Garantia | Implementação |
|---|---|
| Isolamento de tenant | Toda query inclui { tenant: tenantId } |
| Atomicidade | session.withTransaction() — falha parcial = zero escrita |
| Duplo-apply | CAS atômico: findOneAndUpdate({ status: 'ready' }) impede concorrência |
| PATCH em sessão ativa | Status guard: rejeita mutação em sessões applying/completed |
| Tamanho de arquivo | Multer limits.fileSize = 50 MB — rejeita antes do upload ao S3 |
| Limpeza de S3 | Arquivo removido ao cancelar ou ao expirar (TTL 48h via job separado) |
| Idempotência de retry | BullMQ reprocessa com segurança — $setOnInsert em contabilidade, SKU upsert em estoque |
Comportamento por Domínio
Cardápio
- Cria categorias inexistentes automaticamente dentro da mesma transação
- Converte preço para centavos (
parseCurrency / 100) - Deduplicação por
nome + categoria(case-insensitive) - Resolve
inventoryItemvia SKU se coluna presente
Estoque
- Cria
InventoryMovementde entrada apenas para registros novos (não atualizações) - Resolve nome do fornecedor →
ObjectIdde Supplier (requer importação de fornecedores primeiro) - SKU único por tenant como chave de upsert
Fornecedores
- CNPJ normalizado para 14 dígitos (remove
./-) - Deduplicação por CNPJ ou nome quando CNPJ ausente
Pedidos (Histórico)
- Criados com
status: 'delivered'(histórico) orderNumbergerado comcrypto.randomUUID()(sem colisão)createdAtlegado preservado viacollection.insertOne()(bypassatimestamps: truedo Mongoose)
Contabilidade
- Lançamentos marcados com
{ _legacyImport: true, _legacyFiscalNote: taxasOriginais } createdAtlegado preservado viacollection.insertOne()entryNumbergerado comoIMPORT-{domain}-{timestamp}-{index}
Backend — Arquivos
backend/src/migration/
├── migration.module.ts
├── migration.controller.ts
├── migration-orchestrator.service.ts
├── migration.processor.ts ← BullMQ WorkerHost, concorrência 2
├── migration.queue.ts ← MIGRATION_QUEUE + MigrationJobData
├── schemas/
│ └── migration-session.schema.ts ← TTL 48h via índice MongoDB
├── parsers/
│ ├── csv-parser.service.ts ← papaparse
│ ├── excel-parser.service.ts ← xlsx
│ └── json-parser.service.ts ← array/envelope/objeto único
├── fuzzy/
│ └── field-mapper.service.ts ← Levenshtein + IA + cache Redis
└── domains/
├── menu-import.domain.ts
├── inventory-import.domain.ts
├── suppliers-import.domain.ts
├── orders-import.domain.ts
└── accounting-import.domain.ts