From ec173da8447277f46f4d7f434a58de50c27c971e Mon Sep 17 00:00:00 2001 From: jonaspachecoometas Date: Thu, 26 Feb 2026 11:38:24 -0300 Subject: [PATCH] =?UTF-8?q?feat:=20Arc=C3=A1dia=20Suite=20v2.0=20-=20Siste?= =?UTF-8?q?ma=20completo=20com=2066=20p=C3=A1ginas,=206=20motores,=20m?= =?UTF-8?q?=C3=B3dulo=20Retail?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 65 +- MAPA_GERAL_RETAIL.md | 705 +++++++++++ MAPA_SISTEMA_ARCADIA.md | 469 ++++++++ README.md | 47 + RELATORIO_TECNICO_RETAIL.md | 1261 ++++++++++++++++++++ client/src/App.tsx | 135 ++- client/src/pages/ArcadiaRetail.tsx | 1776 +++++++++++++++++++++++----- client/src/pages/Home.tsx | 12 +- server/index.ts | 27 +- server/retail/routes.ts | 620 +++++++++- server/routes.ts | 8 +- shared/schema.ts | 112 ++ 12 files changed, 4878 insertions(+), 359 deletions(-) create mode 100644 MAPA_GERAL_RETAIL.md create mode 100644 MAPA_SISTEMA_ARCADIA.md create mode 100644 README.md create mode 100644 RELATORIO_TECNICO_RETAIL.md diff --git a/.gitignore b/.gitignore index 95d7cc2..a657c64 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,65 @@ -node_modules -dist +node_modules/ +dist/ .DS_Store -server/public +.env +.env.local +.env.production + +# Replit specific +.cache/ +.upm/ +.config/ +.local/ +.pythonlibs/ +.replit +replit.nix + +# Build artifacts +server/public/ vite.config.ts.* + +# Backups and archives *.tar.gz +*.zip +plus_backup_*/ + +# Metabase +metabase/metabase.jar metabase/metabase-data.* -metabase/plugins/ \ No newline at end of file +metabase/plugins/ + +# Python +__pycache__/ +*.pyc +*.pyo +.venv/ +uv.lock + +# WhatsApp sessions +whatsapp-sessions/ + +# Uploads (user-generated content) +uploads/ +plus/public/uploads/ +plus/storage/logs/ + +# PHP dependencies (install via composer) +plus/vendor/ + +# Logs +*.log + +# IDE +.idea/ +.vscode/ +*.swp +*.swo + +# OS files +Thumbs.db + +# Attached assets +attached_assets/ + +# Package lock (regenerated on install) +package-lock.json diff --git a/MAPA_GERAL_RETAIL.md b/MAPA_GERAL_RETAIL.md new file mode 100644 index 0000000..f9e61ed --- /dev/null +++ b/MAPA_GERAL_RETAIL.md @@ -0,0 +1,705 @@ +# MAPA GERAL DO SISTEMA RETAIL - Arcádia Suite + +--- + +## 1. VISÃO MACRO DO MÓDULO + +``` +╔══════════════════════════════════════════════════════════════════════════════════╗ +║ ARCÁDIA RETAIL - MAPA GERAL ║ +║ Loja de Celulares e Assistência Técnica ║ +╠══════════════════════════════════════════════════════════════════════════════════╣ +║ ║ +║ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ║ +║ │DASHBOARD│ │ PDV │ │ PESSOAS │ │ ESTOQUE │ │SERVIÇOS │ ║ +║ │ (KPIs) │ │ (Caixa) │ │(Cadastro│ │(Depósito│ │ (O.S.) │ ║ +║ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ ║ +║ │ │ │ │ │ ║ +║ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ ║ +║ │TRADE-IN │ │ COMPRAS │ │CADASTROS│ │RELATÓRIO│ │COMISSÕES│ ║ +║ │(Aval.) │ │(Aquis.) │ │ (Config)│ │(Reports)│ │ (Metas) │ ║ +║ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ ║ +║ │ │ │ │ │ ║ +║ └────────────┴────────────┴─────┬──────┴────────────┘ ║ +║ │ ║ +║ ┌─────────┴─────────┐ ║ +║ │ CONFIGURAÇÃO │ ║ +║ │ Plus / Empresas │ ║ +║ └───────────────────┘ ║ +╚══════════════════════════════════════════════════════════════════════════════════╝ +``` + +--- + +## 2. FLUXO PRINCIPAL DE OPERAÇÕES + +``` + ┌──────────────────────────────────────────────┐ + │ ENTRADA DE MERCADORIA │ + └──────────────────────┬───────────────────────┘ + │ + ┌──────────────────────┬┴──────────────────────┐ + ▼ ▼ ▼ + ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ + │ COMPRA │ │ TRADE-IN │ │ CONSIGNAÇÃO │ + │ Fornecedor │ │ Cliente │ │ Parceiro │ + └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ + │ │ │ + │ ┌──────┴───────┐ │ + │ │ Avaliação │ │ + │ │ 19 itens │ │ + │ │ checklist │ │ + │ └──────┬───────┘ │ + │ │ │ + │ ┌──────┴───────┐ │ + │ │ Aprovação │ │ + │ │ Gera Crédito│ │ + │ │ Cria O.S. │ │ + │ └──────┬───────┘ │ + │ │ │ + │ ┌──────┴───────┐ │ + │ │ Revisão │ │ + │ │ O.S. Interna│ │ + │ │ Manutenção │ │ + │ └──────┬───────┘ │ + │ │ │ + ▼ ▼ ▼ + ┌─────────────────────────────────────────────────────┐ + │ ESTOQUE │ + │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ + │ │Depósito │ │Depósito │ │Depósito │ │ + │ │ Loja │ │ Central │ │Trânsito │ │ + │ └────┬────┘ └────┬────┘ └────┬────┘ │ + │ └────────────┼────────────┘ │ + │ │ │ + │ ┌────────────────┬┴──────────────────┐ │ + │ │ Dispositivos │ Produtos │ │ + │ │ (por IMEI) │ (por qtd/série) │ │ + │ └────────────────┴───────────────────┘ │ + └────────────────────┬────────────────────────────────┘ + │ + ▼ + ┌─────────────────────────────────────────────────────┐ + │ PDV │ + │ │ + │ ┌────────────┐ ┌────────────┐ ┌──────────────┐ │ + │ │Dispositivos│ │ Produtos │ │ Faturar O.S. │ │ + │ │ (IMEI) │ │(Acessórios)│ │ (Serviços) │ │ + │ └─────┬──────┘ └─────┬──────┘ └──────┬───────┘ │ + │ └───────────────┼────────────────┘ │ + │ ▼ │ + │ ┌──────────────────┐ │ + │ │ CARRINHO │ │ + │ │ │ │ + │ │ Subtotal │ │ + │ │ - Desconto │ │ + │ │ - Trade-In │ │ + │ │ - Crédito │ │ + │ │ = TOTAL │ │ + │ └────────┬─────────┘ │ + │ ▼ │ + │ ┌──────────────────┐ │ + │ │ PAGAMENTO │ │ + │ │ 💵 Dinheiro │ │ + │ │ 💳 Débito │ │ + │ │ 💳 Crédito (Nx) │ │ + │ │ 📱 PIX │ │ + │ │ 🔀 Combinado │ │ + │ └────────┬─────────┘ │ + │ ▼ │ + │ ┌──────────────────┐ │ + │ │ VENDA │ │ + │ │ Impressão A4 │ │ + │ │ Sync Plus │ │ + │ │ NF-e/NFC-e │ │ + │ └──────────────────┘ │ + └─────────────────────────────────────────────────────┘ + │ + ┌────────────┼────────────────┐ + ▼ ▼ ▼ + ┌────────────┐ ┌─────────┐ ┌────────────┐ + │ COMISSÃO │ │RELATÓRIO│ │ DEVOLUÇÃO │ + │ Vendedor │ │Caixa │ │ Troca │ + │ Metas │ │Diário │ │ Crédito │ + └────────────┘ └─────────┘ └────────────┘ +``` + +--- + +## 3. CICLO DE VIDA DO DISPOSITIVO (IMEI) + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ JORNADA DO DISPOSITIVO POR IMEI │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ENTRADA │ +│ ═══════ │ +│ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ COMPRA │ │ TRADE-IN │ │ CONSIGNAÇÃO │ │ +│ │ Fornecedor │ │ Cliente │ │ Parceiro │ │ +│ │ condition: │ │ condition: │ │ condition: │ │ +│ │ new │ │ used/refurb │ │ varies │ │ +│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ +│ │ │ │ │ +│ │ ┌──────┴───────┐ │ │ +│ │ │ AVALIAÇÃO │ │ │ +│ │ │ Checklist │ │ │ +│ │ │ 19 itens │ │ │ +│ │ └──────┬───────┘ │ │ +│ │ │ │ │ +│ │ ┌──────┴───────┐ │ │ +│ │ │ O.S. INT. │ │ │ +│ │ │ Revisão │ │ │ +│ │ │ Manutenção │ │ │ +│ │ └──────┬───────┘ │ │ +│ │ │ │ │ +│ ▼ ▼ ▼ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ ESTOQUE (status: in_stock) │ │ +│ │ │ │ +│ │ IMEI: 35XXXXXXXXXXXXX │ │ +│ │ Marca: Samsung | Modelo: S24 Ultra │ │ +│ │ Cor: Preto | Storage: 256GB | RAM: 12GB │ │ +│ │ Condição: refurbished │ │ +│ │ Preço Compra: R$ 2.500 | Preço Venda: R$ 3.500 │ │ +│ │ Depósito: Loja Centro │ │ +│ └──────────────────────┬──────────────────────────────┘ │ +│ │ │ +│ MOVIMENTAÇÕES │ │ +│ ═══════════════ │ │ +│ │ │ +│ ┌──────────┐ ┌──────┴──────┐ ┌──────────────┐ │ +│ │TRANSFER. │◄───│ DISPONÍVEL │───►│ ASSISTÊNCIA │ │ +│ │Entre lojas│ │ para venda │ │ O.S. cliente│ │ +│ │in_transit │ │ in_stock │ │ in_service │ │ +│ └──────────┘ └──────┬──────┘ └──────┬───────┘ │ +│ │ │ │ +│ │ │ (retorna após reparo) │ +│ │ └──────────┐ │ +│ │ │ │ +│ SAÍDA ▼ ▼ │ +│ ═════ ┌──────────────┐ ┌──────────────┐ │ +│ │ VENDA │ │ DE VOLTA AO │ │ +│ │ PDV + NF-e │ │ ESTOQUE │ │ +│ │ status:sold │ │ in_stock │ │ +│ └──────┬───────┘ └──────────────┘ │ +│ │ │ +│ ┌──────┴───────┐ │ +│ │ DEVOLUÇÃO? │ │ +│ │ returned │──────────► Volta ao estoque │ +│ └──────────────┘ │ +│ │ +│ RASTREAMENTO: device_history + imei_history (kardex completo) │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 4. FLUXO DO TRADE-IN (4 ETAPAS DETALHADAS) + +``` +╔═══════════════════════════════════════════════════════════════════════════╗ +║ TRADE-IN - FLUXO COMPLETO ║ +╠═══════════════════════════════════════════════════════════════════════════╣ +║ ║ +║ ETAPA 1: AVALIAÇÃO ║ +║ ══════════════════ ║ +║ ║ +║ ┌────────────────────────────────────────────────────────────┐ ║ +║ │ Cliente chega com dispositivo usado │ ║ +║ │ │ ║ +║ │ 1. Identificar cliente (busca por nome/CPF) │ ║ +║ │ 2. Registrar IMEI, Marca, Modelo │ ║ +║ │ 3. Preencher Checklist (19 itens): │ ║ +║ │ │ ║ +║ │ ┌──────────────────────────────────────────────────────┐ │ ║ +║ │ │ ☐ Liga corretamente ☐ Sensores/NFC │ │ ║ +║ │ │ ☐ Sem avarias/fantasma ☐ Face ID/Touch ID │ │ ║ +║ │ │ ☐ Sem manchas na tela ☐ Microfones │ │ ║ +║ │ │ ☐ Botões funcionando ☐ Áudio auricular │ │ ║ +║ │ │ ☐ Marcas de uso ☐ Alto-falante │ │ ║ +║ │ │ ☐ Wi-Fi ☐ Carregamento │ │ ║ +║ │ │ ☐ Chip ☐ Câmeras │ │ ║ +║ │ │ ☐ 4G/5G ☐ Flash │ │ ║ +║ │ │ ☐ Possui carregador ☐ 3uTools OK │ │ ║ +║ │ │ 🔋 Bateria: ____% │ │ ║ +║ │ └──────────────────────────────────────────────────────┘ │ ║ +║ │ │ ║ +║ │ 4. Listar peças necessárias (se houver) │ ║ +║ │ 5. Definir valor estimado │ ║ +║ │ 6. Assinatura digital do cliente │ ║ +║ │ 7. Imprimir comprovante │ ║ +║ │ │ ║ +║ │ Status: PENDING │ ║ +║ └────────────────────────────────────────────┬───────────────┘ ║ +║ │ ║ +║ ▼ ║ +║ ETAPA 2: APROVAÇÃO ║ +║ ══════════════════ ║ +║ ║ +║ ┌────────────────────────────────────────────────────────────┐ ║ +║ │ Gerente revisa avaliação e decide: │ ║ +║ │ │ ║ +║ │ [APROVAR] [REJEITAR] │ ║ +║ │ │ │ │ ║ +║ │ ▼ ▼ │ ║ +║ │ Automaticamente: Avaliação │ ║ +║ │ ✅ Gera CRÉDITO para cliente rejeitada. │ ║ +║ │ (R$ do valor estimado) Sem ações. │ ║ +║ │ ✅ Cria O.S. INTERNA │ ║ +║ │ (INT2602XXXXXX) │ ║ +║ │ ✅ Registra no IMEI History │ ║ +║ │ │ ║ +║ │ Status: APPROVED │ ║ +║ └────────────────────────────────────────────┬───────────────┘ ║ +║ │ ║ +║ ▼ ║ +║ ETAPA 3: REVISÃO / MANUTENÇÃO ║ +║ ═════════════════════════════ ║ +║ ║ +║ ┌────────────────────────────────────────────────────────────┐ ║ +║ │ Técnico trabalha na O.S. Interna: │ ║ +║ │ │ ║ +║ │ 1. Diagnóstico técnico │ ║ +║ │ 2. Troca de peças (registra peças + custos) │ ║ +║ │ 3. Limpeza e preparação │ ║ +║ │ 4. Teste de qualidade │ ║ +║ │ 5. Preenche checklist de conclusão │ ║ +║ │ 6. Finaliza O.S. Interna │ ║ +║ │ │ ║ +║ │ Custos: │ ║ +║ │ ├── Peças utilizadas: R$ XXX │ ║ +║ │ ├── Mão de obra: R$ XXX │ ║ +║ │ └── Total reparo: R$ XXX │ ║ +║ │ │ ║ +║ │ Status O.S.: COMPLETED │ ║ +║ └────────────────────────────────────────────┬───────────────┘ ║ +║ │ ║ +║ ▼ ║ +║ ETAPA 4: ENTRADA NO ESTOQUE ║ +║ ═══════════════════════════ ║ +║ ║ +║ ┌────────────────────────────────────────────────────────────┐ ║ +║ │ Ao finalizar O.S. Interna: │ ║ +║ │ │ ║ +║ │ ✅ Cria dispositivo no estoque (mobile_devices) │ ║ +║ │ condition: "refurbished" │ ║ +║ │ acquisitionType: "trade_in" │ ║ +║ │ │ ║ +║ │ 💰 Cálculo do preço sugerido: │ ║ +║ │ Custo aquisição = Valor Trade-In + Custo Reparo │ ║ +║ │ Preço sugerido = Custo × (1 + Margem%) │ ║ +║ │ Ex: R$1500 + R$300 = R$1800 × 1.30 = R$ 2.340 │ ║ +║ │ │ ║ +║ │ ✅ Registra no IMEI History │ ║ +║ │ ✅ Registra no Activity Feed │ ║ +║ │ ✅ Disponível no PDV para venda │ ║ +║ │ │ ║ +║ │ Status: IN_STOCK (pronto para venda) │ ║ +║ └────────────────────────────────────────────────────────────┘ ║ +╚═══════════════════════════════════════════════════════════════════════════╝ +``` + +--- + +## 5. MAPA DO PDV (PONTO DE VENDA) + +``` +╔═══════════════════════════════════════════════════════════════════════╗ +║ PDV - PONTO DE VENDA ║ +╠═══════════════════════════════════════════════════════════════════════╣ +║ ║ +║ BARRA SUPERIOR ║ +║ ┌──────────────────────────────────────────────────────────────────┐ ║ +║ │ 🏪 Empresa: Loja Centro 👤 Vendedor: João Silva │ ║ +║ │ │ ║ +║ │ [Sangria] [Reforço] [Devolução] [Selecionar Cliente] [Limpar] │ ║ +║ └──────────────────────────────────────────────────────────────────┘ ║ +║ ║ +║ ┌───────────────────────────────────┬──────────────────────────────┐ ║ +║ │ CATÁLOGO (3 colunas) │ CARRINHO (2 colunas) │ ║ +║ │ │ │ ║ +║ │ ┌──────────┬──────────┬────────┐ │ ┌──────────────────────────┐│ ║ +║ │ │📱Disposit│📦Produtos│🔧Fat.OS│ │ │ Samsung S24 Ultra ││ ║ +║ │ └──────────┴──────────┴────────┘ │ │ IMEI: 35XXX 📱Celular ││ ║ +║ │ │ │ R$ 3.500,00 ││ ║ +║ │ [🔍 Buscar... ] │ ├──────────────────────────┤│ ║ +║ │ │ │ Capinha Silicone ││ ║ +║ │ ┌──────────────────────────────┐ │ │ 2x R$ 45,00 ││ ║ +║ │ │ Samsung S24 Ultra │ │ │ R$ 90,00 ││ ║ +║ │ │ 256GB | Preto | IMEI: 35XX │ │ ├──────────────────────────┤│ ║ +║ │ │ 🟢 Novo 📦 Loja Centro │ │ │ O.S. #OS2602ABC ││ ║ +║ │ │ R$ 3.500,00 │ │ │ Troca de tela 🔧Serviço ││ ║ +║ │ │ [+ Adicionar] │ │ │ R$ 450,00 ││ ║ +║ │ ├──────────────────────────────┤ │ ╞══════════════════════════╡│ ║ +║ │ │ iPhone 15 Pro │ │ │ Subtotal R$ 4.040,00 ││ ║ +║ │ │ 128GB | Branco | IMEI: 86XX │ │ │ Desconto -R$ 40,00 ││ ║ +║ │ │ 🔵 Recond 📦 Loja Centro │ │ │ Trade-In -R$ 1.500,00 ││ ║ +║ │ │ R$ 4.200,00 │ │ │ Crédito -R$ 200,00 ││ ║ +║ │ │ [+ Adicionar] │ │ │─────────────────────────││ ║ +║ │ ├──────────────────────────────┤ │ │ TOTAL R$ 2.300,00 ││ ║ +║ │ │ Xiaomi 14 │ │ │ ││ ║ +║ │ │ 512GB | Azul | IMEI: 86XX │ │ │ 💙 Crédito: R$ 200,00 ││ ║ +║ │ │ 🟡 Usado 📦 Loja Centro │ │ │ ││ ║ +║ │ │ R$ 2.800,00 │ │ │ [↕️ Trade-In] ││ ║ +║ │ │ [+ Adicionar] │ │ │ [💰 FINALIZAR VENDA] ││ ║ +║ │ └──────────────────────────────┘ │ └──────────────────────────┘│ ║ +║ └───────────────────────────────────┴──────────────────────────────┘ ║ +║ ║ +║ MODAL DE PAGAMENTO ║ +║ ┌──────────────────────────────────────────────────────────────────┐ ║ +║ │ Total: R$ 2.300,00 │ ║ +║ │ │ ║ +║ │ [Dinheiro ✓] [Débito] [Crédito] [PIX] [Combinado] │ ║ +║ │ │ ║ +║ │ Valor: [R$ 2.300,00] Parcelas: [1x] │ ║ +║ │ │ ║ +║ │ Desconto: [___]% ou [R$ ___] │ ║ +║ │ │ ║ +║ │ [CONFIRMAR PAGAMENTO] │ ║ +║ └──────────────────────────────────────────────────────────────────┘ ║ +╚═══════════════════════════════════════════════════════════════════════╝ +``` + +--- + +## 6. FLUXO DA ORDEM DE SERVIÇO (12 ESTADOS) + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ ORDEM DE SERVIÇO - FLUXO DE STATUS │ +├─────────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌──────────┐ │ +│ │ ABERTA │ ◄── Cliente entrega dispositivo │ +│ │ open │ │ +│ └────┬─────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────┐ │ +│ │ DIAGNÓSTICO │ ◄── Técnico analisa problema │ +│ │ diagnosis │ │ +│ └──────┬──────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────┐ │ +│ │ ORÇAMENTO │ ◄── Técnico elabora orçamento │ +│ │ quote │ │ +│ └──────┬──────┘ │ +│ │ │ +│ ▼ │ +│ ┌────────────────────┐ │ +│ │ AGUARD. APROVAÇÃO │ ◄── Enviado para cliente │ +│ │ pending_approval │ │ +│ └─────┬────────┬─────┘ │ +│ │ │ │ +│ APROVOU │ │ REJEITOU │ +│ ▼ ▼ │ +│ ┌──────────┐ ┌──────────┐ │ +│ │ APROVADA │ │ REJEITADA│ ──► FIM │ +│ │ approved │ │ rejected │ │ +│ └────┬─────┘ └──────────┘ │ +│ │ │ +│ ▼ │ +│ ┌──────────┐ │ +│ │ EM REPARO│ ◄── Técnico inicia trabalho │ +│ │in_repair │ │ +│ └────┬─────┘ │ +│ │ │ +│ ┌──────────┼──────────┐ │ +│ │ │ │ +│ ▼ │ │ +│ ┌──────────────┐ │ │ +│ │ AGUARD. PEÇAS│ │ ◄── Se precisar de peça │ +│ │waiting_parts │───────────────┘ │ +│ └──────────────┘ (peça chegou, volta para reparo) │ +│ │ +│ │ │ +│ ▼ │ +│ ┌──────────────┐ │ +│ │ QUALIDADE │ ◄── Verificação pós-reparo │ +│ │quality_check │ │ +│ └──────┬───────┘ │ +│ │ │ +│ ▼ │ +│ ┌──────────────┐ │ +│ │ PRONTO PARA │ ◄── Disponível para retirada │ +│ │ RETIRADA │ (aparece no PDV p/ faturar) │ +│ │ ready_pickup │ │ +│ └──────┬───────┘ │ +│ │ │ +│ ▼ │ +│ ┌──────────────┐ │ +│ │ CONCLUÍDA │ ◄── Cliente retirou + pagou │ +│ │ completed │ (faturada no PDV) │ +│ └──────────────┘ │ +│ │ +│ Em qualquer momento: ──────────► ┌──────────────┐ │ +│ │ CANCELADA │ │ +│ │ cancelled │ │ +│ └──────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 7. MAPA DE RELATÓRIOS + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ RELATÓRIOS DO RETAIL │ +├──────────────┬──────────────────────────────────────────────────────┤ +│ │ │ +│ OS POR │ Status │ Qtd │ Valor Total │ +│ STATUS │ open │ 12 │ R$ 5.400 │ +│ │ in_repair │ 8 │ R$ 3.200 │ +│ │ completed │ 45 │ R$ 22.500 │ +│ │ │ +├──────────────┼──────────────────────────────────────────────────────┤ +│ │ │ +│ OS POR │ Técnico │ Total │ Concl. │ Andamento │ Receita │ +│ TÉCNICO │ Carlos │ 15 │ 12 │ 3 │ R$8.500 │ +│ │ Pedro │ 10 │ 8 │ 2 │ R$5.200 │ +│ │ │ +├──────────────┼──────────────────────────────────────────────────────┤ +│ │ │ +│ VENDAS POR │ Vendedor │ Vendas │ Receita │ Ticket │ Dias │ +│ VENDEDOR │ João │ 28 │ R$45.000 │ R$1607 │ 22 │ +│ │ Maria │ 22 │ R$38.000 │ R$1727 │ 20 │ +│ │ │ +├──────────────┼──────────────────────────────────────────────────────┤ +│ │ │ +│ MARGEM │ Dispositivo │ IMEI │ Custo │ Venda │ Margem │ +│ POR IMEI │ S24 Ultra │ 35XXX │ R$2500 │ R$3500 │ 40.0% │ +│ │ iPhone 15 │ 86XXX │ R$3800 │ R$4200 │ 10.5% │ +│ │ │ +├──────────────┼──────────────────────────────────────────────────────┤ +│ │ │ +│ CAIXA │ ┌────────────────────────────────────────────┐ │ +│ DIÁRIO │ │ Total Vendas: R$ 12.500 (8 vendas) │ │ +│ │ │ Dinheiro: R$ 4.200 │ Cartão: R$ 5.800 │ │ +│ │ │ PIX: R$ 2.500 │ Combinado: R$ 0 │ │ +│ (4 queries) │ │ Sangrias: -R$ 500 │ Reforços: +R$ 200 │ │ +│ │ │ Saldo Caixa: R$ 3.900 │ │ +│ │ └────────────────────────────────────────────┘ │ +│ │ │ +│ │ POR VENDEDOR: │ +│ │ ┌──────┬──────┬──────┬──────┬──────┬──────┬──────┐ │ +│ │ │Vendedor│Vendas│Dinh. │Déb. │Créd.│ PIX │TOTAL │ │ +│ │ │João │ 5 │R$2100│R$1500│R$800│R$1200│R$5600│ │ +│ │ │Maria │ 3 │R$2100│R$1300│R$600│R$1300│R$6900│ │ +│ │ │TOTAL │ 8 │R$4200│R$2800│R$1400│R$2500│R$12.5│ │ +│ │ └──────┴──────┴──────┴──────┴──────┴──────┴──────┘ │ +│ │ │ +├──────────────┼──────────────────────────────────────────────────────┤ +│ │ │ +│ GIRO DE │ Produto │ Estoque │ Vendas 30d │ Turnover │ +│ ESTOQUE │ Capinha │ 50 │ 35 │ 0.70 │ +│ │ Película │ 80 │ 42 │ 0.53 │ +│ │ │ +└──────────────┴──────────────────────────────────────────────────────┘ +``` + +--- + +## 8. ARQUITETURA TÉCNICA + +``` +╔═══════════════════════════════════════════════════════════════════════╗ +║ ARQUITETURA DO MÓDULO RETAIL ║ +╠═══════════════════════════════════════════════════════════════════════╣ +║ ║ +║ FRONTEND (React + TypeScript + Tailwind + shadcn/ui) ║ +║ ═══════════════════════════════════════════════════ ║ +║ ║ +║ client/src/pages/ArcadiaRetail.tsx ──── 10.067 linhas ║ +║ │ ║ +║ ├── Dashboard (KPIs, Feed, Alertas) ║ +║ ├── PDV (Carrinho, Catálogo, Pagamento) ║ +║ ├── Pessoas (CRUD, Papéis, Histórico) ║ +║ ├── Estoque (Depósitos, Séries, Inventário) ║ +║ ├── Serviços (O.S. CRUD, Checklist) ║ +║ ├── Trade-In (Avaliações, Fluxo 4 etapas) ║ +║ ├── Compras (Pedidos, Recebimento) ║ +║ ├── Cadastros (Pagamento, Vendedores, Promoções) ║ +║ ├── Relatórios (6 sub-abas) ║ +║ ├── Comissões (Dashboard, Metas, Fechamento) ║ +║ └── Configuração (Plus, Empresas, Sync) ║ +║ ║ +║ client/src/components/TradeInForm.tsx ─── 988 linhas ║ +║ └── Formulário completo c/ checklist + assinatura ║ +║ ║ +║ ───────────────────────────────────────────────────────────── ║ +║ API (fetch → /api/retail/*) ║ +║ ───────────────────────────────────────────────────────────── ║ +║ ║ +║ BACKEND (Express.js + Drizzle ORM) ║ +║ ══════════════════════════════════ ║ +║ ║ +║ server/retail/routes.ts ──── 5.218 linhas (~130 endpoints) ║ +║ │ ║ +║ ├── CRUD: Devices, Evaluations, ServiceOrders, ║ +║ │ Persons, Products, Warehouses, etc. ║ +║ ├── PDV: Sessions, Sales, Payments, CashMovements ║ +║ ├── Trade-In: Approve, Process, FullFlow, Stock ║ +║ ├── Reports: OsStatus, OsTech, SalesSeller, ║ +║ │ MarginIMEI, DailyCash, StockTurnover ║ +║ ├── Returns: Search, Process, Credits ║ +║ └── Commissions: Plans, Goals, Calculate, Close ║ +║ ║ +║ server/retail/plus-sync.ts ──── 542 linhas ║ +║ └── Sync: Customers, Sales+Items, NF-e ║ +║ ║ +║ ───────────────────────────────────────────────────────────── ║ +║ Drizzle ORM → PostgreSQL ║ +║ ───────────────────────────────────────────────────────────── ║ +║ ║ +║ BANCO DE DADOS (PostgreSQL + 40 tabelas) ║ +║ ════════════════════════════════════════ ║ +║ ║ +║ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ║ +║ │ Dispositivos │ │ Vendas │ │ Estoque │ ║ +║ │ mobile_devices│ │ pos_sales │ │ warehouses │ ║ +║ │ device_history│ │pos_sale_items│ │warehouse_stk │ ║ +║ │ imei_history │ │pos_sessions │ │stock_movem. │ ║ +║ └──────────────┘ │cash_movements│ │transfers │ ║ +║ └──────────────┘ │serials │ ║ +║ ┌──────────────┐ │inventories │ ║ +║ │ Trade-In │ ┌──────────────┐ └──────────────┘ ║ +║ │ evaluations │ │ Serviços │ ║ +║ │ checklist_* │ │service_orders│ ┌──────────────┐ ║ +║ │ acquisitions │ │ so_items │ │ Cadastros │ ║ +║ │ transfer_doc │ │ warranties │ │payment_meth. │ ║ +║ └──────────────┘ └──────────────┘ │ sellers │ ║ +║ │ comm_plans │ ║ +║ ┌──────────────┐ ┌──────────────┐ │ promotions │ ║ +║ │ Créditos │ │ Devoluções │ │ price_tables │ ║ +║ │customer_cred.│ │return_exch. │ │product_types │ ║ +║ └──────────────┘ │return_items │ └──────────────┘ ║ +║ └──────────────┘ ║ +║ ║ +║ ───────────────────────────────────────────────────────────── ║ +║ ║ +║ INTEGRAÇÃO PLUS (Laravel ERP - Porta 8080) ║ +║ ══════════════════════════════════════════ ║ +║ ║ +║ ┌───────────┐ ┌───────────┐ ┌───────────┐ ║ +║ │Proxy Rev. │ │ SSO │ │Auto-Start │ ║ +║ │/plus/* → │ │HMAC-SHA256│ │php artisan│ ║ +║ │:8080 │ │Token │ │serve │ ║ +║ └───────────┘ └───────────┘ └───────────┘ ║ +║ │ ║ +║ ▼ ║ +║ ┌─────────────────────────────────────────────────┐ ║ +║ │ Plus (Laravel) │ ║ +║ │ ├── NF-e / NFC-e (SEFAZ) │ ║ +║ │ ├── Clientes / Fornecedores │ ║ +║ │ ├── Vendas / Faturamento │ ║ +║ │ └── Fiscal completo │ ║ +║ └─────────────────────────────────────────────────┘ ║ +║ ║ +╚═══════════════════════════════════════════════════════════════════════╝ +``` + +--- + +## 9. SEGURANÇA MULTI-TENANT + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ CAMADAS DE SEGURANÇA │ +├─────────────────────────────────────────────────────────────────────┤ +│ │ +│ CAMADA 1: AUTENTICAÇÃO │ +│ ┌──────────────────────────────────────────────────────────────┐ │ +│ │ Passport.js Session → req.isAuthenticated() │ │ +│ │ Todas as rotas /api/retail/* exigem login │ │ +│ └──────────────────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ CAMADA 2: TENANT SCOPING │ +│ ┌──────────────────────────────────────────────────────────────┐ │ +│ │ req.user.tenantId → obrigatório │ │ +│ │ Se ausente → 403 "Tenant not identified" │ │ +│ │ WHERE tenant_id = $tenantId em TODAS as queries │ │ +│ │ Dados de um tenant NUNCA vazam para outro │ │ +│ └──────────────────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ CAMADA 3: MODULE GATING │ +│ ┌──────────────────────────────────────────────────────────────┐ │ +│ │ requireModule('retail') → verifica tenants.features.retail │ │ +│ │ Se módulo inativo → 403 "Módulo não habilitado" │ │ +│ └──────────────────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ CAMADA 4: OPERAÇÕES SENSÍVEIS │ +│ ┌──────────────────────────────────────────────────────────────┐ │ +│ │ Devoluções → Exige senha do GERENTE │ │ +│ │ Sangrias → Registra responsável + autorizador │ │ +│ │ Exclusões → Soft delete quando possível │ │ +│ └──────────────────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ CAMADA 5: AUDITORIA │ +│ ┌──────────────────────────────────────────────────────────────┐ │ +│ │ retail_activity_feed → Todas as operações relevantes │ │ +│ │ device_history → Movimentação de IMEI │ │ +│ │ imei_history → Kardex completo por IMEI │ │ +│ └──────────────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 10. MAPA DE INTEGRAÇÕES + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ INTEGRAÇÕES DO RETAIL │ +├─────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌──────────────────┐ │ +│ │ ARCÁDIA RETAIL │ │ +│ │ (Express) │ │ +│ └────────┬─────────┘ │ +│ │ │ +│ ┌───────────────────┼───────────────────┐ │ +│ │ │ │ │ +│ ▼ ▼ ▼ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ PLUS (PHP) │ │ ERPNEXT │ │ POSTGRESQL │ │ +│ │ :8080 │ │ (API ext.) │ │ (Drizzle) │ │ +│ │ │ │ │ │ │ │ +│ │ ▸ NF-e/NFC-e│ │ ▸ Clientes │ │ ▸ 40+ tabelas│ │ +│ │ ▸ Clientes │ │ ▸ Fornecedor │ │ ▸ Retail │ │ +│ │ ▸ Vendas │ │ ▸ Estoque │ │ ▸ Multi-ten. │ │ +│ │ ▸ Fiscal │ │ ▸ Financeiro │ │ │ │ +│ └──────────────┘ └──────────────┘ └──────────────┘ │ +│ │ │ │ +│ │ │ │ +│ ▼ ▼ │ +│ ┌──────────────────────────────────────────────────┐ │ +│ │ SEFAZ │ │ +│ │ NF-e / NFC-e (via Plus Laravel + nfelib) │ │ +│ └──────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────┘ +``` + +--- + +**Métricas Finais:** +- **16.800+ linhas** de código +- **40+ tabelas** no banco +- **~130 endpoints** REST +- **11 abas** na interface +- **19 itens** no checklist de avaliação +- **12 estados** de O.S. +- **4 etapas** no fluxo de Trade-In +- **5 formas** de pagamento +- **4 queries** no caixa diário diff --git a/MAPA_SISTEMA_ARCADIA.md b/MAPA_SISTEMA_ARCADIA.md new file mode 100644 index 0000000..a11bd81 --- /dev/null +++ b/MAPA_SISTEMA_ARCADIA.md @@ -0,0 +1,469 @@ +# Arcádia Suite - Mapa Geral do Sistema + +## Visão Geral + +**Arcádia Suite** é o Escritório Estratégico para a Empresa Moderna. Uma plataforma que centraliza produtividade, inteligência, tomada de decisão e governança, orquestrando ERPs, pessoas e dados. + +**Princípio Central:** Separação absoluta entre decisão e execução. +- Arcádia **pensa, governa e orienta** +- ERPs **executam, registram e obedecem** + +--- + +## Arquitetura de 4 Camadas + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ CAMADA DE APRESENTAÇÃO │ +│ React 18 + TypeScript + Tailwind CSS + shadcn/ui │ +│ Interface tipo browser com abas + omnibox │ +│ 66 páginas/módulos │ +└─────────────────────────────────────────────────────────────────────┘ + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ CAMADA DE ORQUESTRAÇÃO │ +│ Express.js + Socket.IO + Manus Agent │ +│ Porta 5000 (API + WebSocket) │ +│ 38 arquivos de rotas / 23 ferramentas registradas │ +└─────────────────────────────────────────────────────────────────────┘ + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ CAMADA DE INTELIGÊNCIA │ +│ FastAPI (Contábil 8003, BI 8004, Automação 8005) │ +│ Communication Engine (Node 8006) │ +│ OpenAI GPT-4o (Manus/Dev Center) + GPT-4o-mini (WhatsApp) │ +└─────────────────────────────────────────────────────────────────────┘ + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ CAMADA DE DADOS │ +│ PostgreSQL + Drizzle ORM │ +│ Knowledge Graph + ChromaDB (embeddings) │ +│ Session Store + Multi-tenant │ +└─────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Mapa de Portas + +| Porta | Serviço | Tecnologia | +|-------|---------|-----------| +| 5000 | API Principal + Frontend | Express.js + React | +| 8002 | Motor Fiscal (Fisco) | FastAPI (Python) | +| 8003 | Motor Contábil | FastAPI (Python) | +| 8004 | Motor BI (Insights) | FastAPI (Python) | +| 8005 | Motor Automação | FastAPI (Python) | +| 8006 | Motor Comunicação | Node.js/Express | +| 8080 | Arcádia Plus (Laravel) | PHP/Laravel | + +--- + +## Módulos do Frontend (66 páginas) + +### Núcleo & Administração +| Página | Rota | Descrição | +|--------|------|-----------| +| Home | `/` | Dashboard principal | +| SOE | `/soe` | Sistema Operacional Empresarial | +| ERP | `/erp` | Módulo ERP legado | +| Admin | `/admin` | Administração do sistema | +| SuperAdmin | `/super-admin` | Gestão multi-tenant | + +### Módulos de Negócio +| Página | Rota | Descrição | +|--------|------|-----------| +| Financeiro | `/financeiro` | Contas a pagar/receber, fluxo de caixa | +| Contábil | `/contabil` | Contabilidade, DRE, balancetes | +| Fiscal | `/fisco` | NF-e, NFC-e, CFOP, NCM, CEST | +| CRM | `/crm` | Gestão de relacionamento com cliente | +| People | `/people` | RH, colaboradores, folha | +| Production | `/production` | Ordens de produção | +| Quality | `/quality` | Controle de qualidade | + +### Varejo & Comércio +| Página | Rota | Descrição | +|--------|------|-----------| +| Retail | `/retail` | Varejo (celulares, assistência técnica) | +| RetailReports | `/retail-reports` | Relatórios do varejo | +| Marketplace | `/marketplace` | Marketplace integrado | +| Valuation | `/valuation` | Avaliação de trade-in | + +### Comunicação +| Página | Rota | Descrição | +|--------|------|-----------| +| WhatsApp | `/whatsapp` | Multi-sessão WhatsApp | +| Chat | `/chat` | Chat interno | +| XOS Inbox | `/xos/inbox` | Caixa de entrada unificada | +| XOS CRM | `/xos/crm` | CRM unificado | +| XOS Campaigns | `/xos/campaigns` | Campanhas de marketing | +| XOS Tickets | `/xos/tickets` | Sistema de tickets | + +### Inteligência & IA +| Página | Rota | Descrição | +|--------|------|-----------| +| Scientist | `/scientist` | Auto-programação com IA | +| Knowledge | `/knowledge` | Base de conhecimento/grafo | +| BI Workspace | `/bi` | Business Intelligence | +| Manus | `/agent` | Agente autônomo central | + +### Desenvolvimento & DevOps +| Página | Rota | Descrição | +|--------|------|-----------| +| IDE | `/ide` | Editor Monaco + Terminal | +| Dev Center | `/dev-center` | Centro de desenvolvimento XOS | +| XOS Pipeline | `/xos/pipeline` | Pipeline autônomo de código | +| XOS Governance | `/xos/governance` | Governança e políticas | +| API Hub | `/api-hub` | Central de APIs | +| API Tester | `/api-tester` | Testador de APIs | +| DocType Builder | `/doctype-builder` | Construtor de tipos | +| Page Builder | `/page-builder` | Construtor de páginas | +| Workflow Builder | `/workflow-builder` | Construtor de workflows | + +### Operações & Engenharia +| Página | Rota | Descrição | +|--------|------|-----------| +| Engineering Hub | `/engineering` | Hub de engenharia | +| Field Operations | `/field-ops` | Operações de campo | +| Process Compass | `/compass` | Bússola de processos | +| Suppliers Portal | `/suppliers` | Portal de fornecedores | + +### Plataforma +| Página | Rota | Descrição | +|--------|------|-----------| +| Engine Room | `/engine-room` | Casa de Máquinas (status dos motores) | +| Automations | `/automations` | Motor de automações | +| Plus | `/plus` | ERP Laravel (proxy) | +| LMS | `/lms` | Sistema de aprendizagem | +| Communities | `/communities` | Comunidades | +| Support | `/support` | Central de suporte | +| Migration | `/migration` | Migração de dados | +| Central APIs | `/central-apis` | APIs centrais | + +--- + +## APIs do Backend (38 grupos de rotas) + +### Core +| Rota Base | Arquivo | Descrição | +|-----------|---------|-----------| +| `/api/login`, `/api/register` | `server/auth.ts` | Autenticação | +| `/api/admin/*` | `server/admin/routes.ts` | Administração | +| `/api/erp/*` | `server/erp/routes.ts` | ERP principal | +| `/api/soe/*` | `server/erp/routes.ts` | SOE (alias) | +| `/api/users/*` | `server/routes.ts` | Gestão de usuários | + +### Negócio +| Rota Base | Arquivo | Descrição | +|-----------|---------|-----------| +| `/api/financeiro/*` | `server/financeiro/routes.ts` | Financeiro | +| `/api/contabil/*` | `server/contabil/routes.ts` | Contabilidade | +| `/api/fisco/*` | `server/fisco/routes.ts` | Fiscal | +| `/api/crm/*` | `server/crm/routes.ts` | CRM | +| `/api/people/*` | `server/people/routes.ts` | RH/Pessoas | +| `/api/production/*` | `server/production/routes.ts` | Produção | +| `/api/quality/*` | `server/quality/routes.ts` | Qualidade | +| `/api/retail/*` | `server/retail/routes.ts` | Varejo | +| `/api/valuation/*` | `server/valuation/routes.ts` | Avaliação trade-in | +| `/api/marketplace/*` | `server/marketplace/routes.ts` | Marketplace | + +### Comunicação +| Rota Base | Arquivo | Descrição | +|-----------|---------|-----------| +| `/api/whatsapp/*` | `server/whatsapp/routes.ts` | WhatsApp multi-sessão | +| `/api/chat/*` | `server/chat/routes.ts` | Chat interno | +| `/api/email/*` | `server/email/routes.ts` | E-mail | +| `/api/comm/*` | proxy | Motor de Comunicação | +| `/api/xos/*` | `server/xos/routes.ts` | XOS CRM unificado | + +### Inteligência +| Rota Base | Arquivo | Descrição | +|-----------|---------|-----------| +| `/api/manus/*` | `server/manus/routes.ts` | Agente Manus IA | +| `/api/knowledge/*` | `server/learning/routes.ts` | Knowledge Graph | +| `/api/bi/*` | `server/bi/routes.ts` | Business Intelligence | +| `/api/bi/metaset/*` | `server/metaset/routes.ts` | Motor BI MetaSet | +| `/api/scientist/*` | `server/routes.ts` | Cientista de dados | + +### Desenvolvimento +| Rota Base | Arquivo | Descrição | +|-----------|---------|-----------| +| `/api/ide/*` | `server/ide/routes.ts` | IDE integrada | +| `/api/dev-center/*` | `server/blackboard/routes.ts` | Dev Center/Blackboard | +| `/api/xos/pipeline` | `server/blackboard/routes.ts` | Pipeline autônomo | +| `/api/governance/*` | `server/governance/routes.ts` | Governança | +| `/api/lowcode/*` | `server/lowcode/routes.ts` | Low-code engine | + +### Protocolos de Interoperabilidade +| Rota Base | Arquivo | Descrição | +|-----------|---------|-----------| +| `/api/mcp/v1/*` | `server/mcp/routes.ts` | Model Context Protocol | +| `/api/a2a/v1/*` | `server/routes.ts` | Agent to Agent Protocol | +| `/api/api-central/*` | `server/api-central/routes.ts` | Central de APIs | + +### Infraestrutura +| Rota Base | Arquivo | Descrição | +|-----------|---------|-----------| +| `/api/engine-room/*` | `server/engine-room/routes.ts` | Casa de Máquinas | +| `/api/automations/*` | `server/automations/routes.ts` | Motor de Automação | +| `/api/modules/*` | `server/modules/loader.ts` | Módulos dinâmicos | +| `/api/login-bridge/*` | `server/login-bridge/routes.ts` | SSO Bridge | +| `/api/migration/*` | `server/migration/routes.ts` | Migração | + +--- + +## Motores (Engines) + +### Motor IA - Manus (Node.js, porta 5000) +- **Modelo:** GPT-4o (Dev Center), GPT-4o-mini (WhatsApp) +- **Agentes:** 6 agentes autônomos (Architect, Generator, Validator, Executor, Researcher, Evolution) +- **Ferramentas:** 23 ferramentas registradas (GitHub, filesystem, BI, git) +- **Pipeline:** Design → Codegen → Validation → Staging → Evolution + +### Motor Fiscal - Fisco (Python, porta 8002) +- NF-e / NFC-e via nfelib +- NCMs, CFOPs, CESTs, grupos tributários +- Certificados digitais +- Comunicação com SEFAZ + +### Motor Contábil (Python, porta 8003) +- Plano de contas +- Lançamentos contábeis +- DRE, Balanço Patrimonial + +### Motor BI - Insights (Python, porta 8004) +- Execução SQL +- Geração de gráficos +- Análise com Pandas +- Cache inteligente + +### Motor Automação (Python, porta 8005) +- Cron scheduler +- Event bus +- Executor de workflows + +### Motor Comunicação (Node.js, porta 8006) +- Unifica XOS CRM + WhatsApp + Email +- Contatos, threads, mensagens unificados +- Filas de atendimento +- Eventos para Knowledge Graph + +### Arcádia Plus - ERP Laravel (PHP, porta 8080) +- NF-e/NFC-e/CT-e/MDF-e +- PDV (ponto de venda) +- Cardápio digital +- Ordens de serviço +- Estoque com rastreabilidade +- Integrações e-commerce (WooCommerce, Mercado Livre, NuvemShop) +- Integrações delivery (iFood) + +--- + +## Dev Center XOS - 6 Agentes Autônomos + +``` +Prompt em Português + │ + ▼ +┌──────────────┐ ┌──────────────┐ ┌──────────────┐ +│ Architect │ ──▶ │ Generator │ ──▶ │ Validator │ +│ (Design) │ │ (Codegen) │ │ (Typecheck) │ +└──────────────┘ └──────────────┘ └──────────────┘ + │ + ▼ +┌──────────────┐ ┌──────────────┐ ┌──────────────┐ +│ Evolution │ ◀── │ Researcher │ ◀── │ Executor │ +│ (Aprende) │ │ (Pesquisa) │ │ (Staging) │ +└──────────────┘ └──────────────┘ └──────────────┘ +``` + +--- + +## Módulo Retail (Varejo de Celulares) + +### Funcionalidades Core +- **Vendas com IMEI:** Rastreamento individual de aparelhos +- **Trade-in:** Avaliação com checklist de 19 itens +- **Ordens de Serviço:** Gestão completa de assistência técnica +- **Garantia:** Controle de garantias por IMEI +- **Caixa Diário:** Reconciliação de caixa com fechamento +- **Comissões:** Cálculo automático por vendedor + +### Checklist Trade-in (19 itens) +1. Liga normalmente +2. Problemas na tela +3. WiFi funcionando +4. Bluetooth funcionando +5. Câmera frontal +6. Câmera traseira +7. Microfone +8. Alto-falante +9. Botões físicos +10. Sensor biométrico +11. Carregamento +12. Bateria saudável +13. GPS funcionando +14. Giroscópio +15. Acelerômetro +16. NFC +17. Resistência à água +18. Face ID / reconhecimento facial +19. Vibração + +--- + +## Banco de Dados (PostgreSQL + Drizzle ORM) + +### Tabelas Principais +| Grupo | Tabelas | +|-------|---------| +| **Identidade** | `users`, `profiles`, `tenants` | +| **Produtividade** | `workspace_pages`, `page_blocks`, `dashboard_widgets`, `quick_notes` | +| **Comunicação** | `conversations`, `messages`, `chat_threads`, `chat_messages` | +| **WhatsApp** | `whatsapp_sessions`, `whatsapp_contacts`, `whatsapp_messages`, `whatsapp_tickets` | +| **ERP Core** | `applications`, `erp_connections`, `agent_tasks`, `task_executions` | +| **Conhecimento** | `knowledge_base`, `knowledge_graph_nodes`, `knowledge_graph_edges` | +| **Governança** | `xos_governance_*`, `xos_job_queue`, `xos_agent_metrics` | +| **Pipeline** | `xos_staging_changes`, `xos_dev_pipelines` | +| **Comunicação Unificada** | `comm_contacts`, `comm_threads`, `comm_messages`, `comm_channels` | +| **Varejo** | Via módulos dinâmicos (`/api/modules/retail-reports`) | +| **Financeiro** | Contas, lançamentos, conciliação | +| **Fiscal** | NCMs, CFOPs, notas fiscais | + +--- + +## Integrações Externas + +| Serviço | Uso | +|---------|-----| +| **OpenAI** | GPT-4o (Manus, Dev Center), GPT-4o-mini (WhatsApp) | +| **GitHub** | Commits automáticos, análise de repositórios | +| **ERPNext** | Integração com ERP externo (clientes, produtos, vendas) | +| **WhatsApp/Baileys** | Multi-sessão de atendimento | +| **SEFAZ** | NF-e/NFC-e via nfelib | +| **Cloud-DFE** | SDK fiscal (NF-e, NFC-e, CT-e, MDF-e) | +| **WooCommerce** | E-commerce integration | +| **Mercado Livre** | Marketplace | +| **NuvemShop** | E-commerce | +| **iFood** | Delivery (pedidos, cardápio) | +| **Asaas** | Pagamentos, boletos | + +--- + +## Protocolos de Interoperabilidade + +| Protocolo | Rota | Descrição | +|-----------|------|-----------| +| **MCP** | `/api/mcp/v1/` | Model Context Protocol - exposição de ferramentas | +| **A2A** | `/api/a2a/v1/` | Agent to Agent - comunicação bidirecional | +| **AP2** | Planejado | Agent Payment Protocol | +| **UCP** | Planejado | Unified Commerce Protocol | + +--- + +## Como Rodar Localmente + +### Pré-requisitos +- Node.js 20+ +- Python 3.11+ +- PostgreSQL 16+ +- PHP 8.2+ (opcional, para Arcádia Plus) + +### Instalação + +```bash +# 1. Extrair o backup +tar xzf arcadia-suite-backup.tar.gz + +# 2. Instalar dependências Node +npm install + +# 3. Instalar dependências Python +pip install fastapi uvicorn pandas numpy psycopg2-binary nfelib lxml cryptography + +# 4. Configurar variáveis de ambiente +cp .env.example .env +# Editar .env com suas credenciais: +# DATABASE_URL=postgresql://user:pass@localhost:5432/arcadia +# OPENAI_API_KEY=sk-... +# GITHUB_TOKEN=ghp_... + +# 5. Criar banco de dados +createdb arcadia + +# 6. Executar migrations +npx drizzle-kit push + +# 7. Iniciar em desenvolvimento +npm run dev +``` + +### Variáveis de Ambiente Necessárias +| Variável | Descrição | +|----------|-----------| +| `DATABASE_URL` | URL de conexão PostgreSQL | +| `OPENAI_API_KEY` | Chave da API OpenAI | +| `GITHUB_TOKEN` | Token GitHub para integrações | +| `ERPNEXT_URL` | URL do ERPNext (opcional) | +| `ERPNEXT_API_KEY` | Chave API ERPNext (opcional) | +| `ERPNEXT_API_SECRET` | Segredo API ERPNext (opcional) | +| `SESSION_SECRET` | Segredo para sessões Express | + +### Credenciais Padrão +- **Usuário:** admin +- **Senha:** admin +- **Role:** master + +--- + +## Estrutura de Diretórios + +``` +arcadia-suite/ +├── client/ # Frontend React +│ ├── src/ +│ │ ├── pages/ # 66 páginas +│ │ ├── components/ # Componentes reutilizáveis +│ │ ├── hooks/ # Custom hooks +│ │ └── lib/ # Utilitários +│ └── public/ # Assets estáticos +├── server/ # Backend Express +│ ├── admin/ # Administração +│ ├── autonomous/ # Ferramentas autônomas +│ ├── bi/ # Business Intelligence +│ ├── blackboard/ # Dev Center (6 agentes) +│ ├── chat/ # Chat interno +│ ├── communication/ # Motor de comunicação +│ ├── contabil/ # Motor contábil +│ ├── crm/ # CRM +│ ├── engine-room/ # Casa de Máquinas +│ ├── erp/ # ERP/SOE +│ ├── financeiro/ # Financeiro +│ ├── fisco/ # Fiscal +│ ├── governance/ # Governança XOS +│ ├── ide/ # IDE integrada +│ ├── integrations/ # Integrações externas +│ ├── learning/ # Knowledge Graph +│ ├── manus/ # Agente Manus +│ ├── mcp/ # Model Context Protocol +│ ├── modules/ # Módulos dinâmicos +│ ├── people/ # RH +│ ├── plus/ # Proxy Laravel +│ ├── production/ # Produção +│ ├── python/ # Scripts Python +│ ├── quality/ # Qualidade +│ ├── retail/ # Varejo +│ ├── whatsapp/ # WhatsApp +│ └── xos/ # XOS unificado +├── shared/ # Código compartilhado +│ ├── schema.ts # Schema principal (Drizzle) +│ └── schemas/ # Schemas modulares +├── plus/ # ERP Laravel (PHP) +├── python-service/ # Serviço Python +├── db/ # Configuração do banco +├── migrations/ # Migrations Drizzle +└── docs/ # Documentação +``` + +--- + +*Arcádia Suite v2.0 - O Escritório Estratégico para a Empresa Moderna* diff --git a/README.md b/README.md new file mode 100644 index 0000000..7a3f83d --- /dev/null +++ b/README.md @@ -0,0 +1,47 @@ +# Arcádia Suite + +O Escritório Estratégico para a Empresa Moderna. + +Plataforma que centraliza produtividade, inteligência, tomada de decisão e governança, orquestrando ERPs, pessoas e dados. + +## Arquitetura + +- **Frontend:** React 18 + TypeScript + Tailwind CSS + shadcn/ui (66 páginas) +- **Backend:** Express.js + Socket.IO (38 grupos de rotas API) +- **Inteligência:** OpenAI GPT-4o + 6 Agentes Autônomos (Dev Center XOS) +- **Motores:** Fiscal (8002), Contábil (8003), BI (8004), Automação (8005), Comunicação (8006) +- **ERP Plus:** Laravel/PHP (8080) com NF-e/NFC-e/CT-e/MDF-e +- **Banco:** PostgreSQL 16 + Drizzle ORM + ChromaDB + +## Módulos Principais + +- SOE (Sistema Operacional Empresarial) +- Financeiro, Contábil, Fiscal +- CRM + WhatsApp Multi-sessão +- Varejo (Celulares + Assistência Técnica) +- Business Intelligence +- Dev Center com Pipeline Autônomo +- Casa de Máquinas (Engine Room) +- Governança XOS +- Knowledge Graph + IA + +## Quick Start + +```bash +npm install +cp .env.example .env # Configure DATABASE_URL, OPENAI_API_KEY +npx drizzle-kit push +npm run dev +``` + +**Login padrão:** admin / admin + +## Documentação + +- [Mapa Geral do Sistema](MAPA_SISTEMA_ARCADIA.md) +- [Relatório Técnico Retail](RELATORIO_TECNICO_RETAIL.md) +- [Mapa Retail](MAPA_GERAL_RETAIL.md) + +## Licença + +Proprietário - Arcádia Suite diff --git a/RELATORIO_TECNICO_RETAIL.md b/RELATORIO_TECNICO_RETAIL.md new file mode 100644 index 0000000..2efa44a --- /dev/null +++ b/RELATORIO_TECNICO_RETAIL.md @@ -0,0 +1,1261 @@ +# Relatório Técnico Completo - Módulo Retail (Arcádia Suite) +## Versão 1.0 - Fevereiro 2026 + +--- + +## SUMÁRIO + +1. [Visão Geral do Módulo](#1-visão-geral) +2. [Banco de Dados - Schema Completo](#2-banco-de-dados) +3. [Dashboard - Painel Principal](#3-dashboard) +4. [PDV - Ponto de Venda](#4-pdv) +5. [Trade-In - Gestão de Troca](#5-trade-in) +6. [Checklist de Avaliação](#6-checklist) +7. [Ordens de Serviço](#7-ordens-de-serviço) +8. [Devoluções e Trocas](#8-devoluções-e-trocas) +9. [Estoque e Depósitos](#9-estoque-e-depósitos) +10. [Comissões e Metas](#10-comissões-e-metas) +11. [Relatórios](#11-relatórios) +12. [Cadastros](#12-cadastros) +13. [Compras e Aquisições](#13-compras-e-aquisições) +14. [Créditos de Cliente](#14-créditos-de-cliente) +15. [Integração com Plus (Laravel)](#15-integração-plus) +16. [API - Endpoints Completos](#16-api-endpoints) +17. [Segurança e Multi-Tenant](#17-segurança) + +--- + +## 1. VISÃO GERAL + +### 1.1 Propósito +O módulo **Arcádia Retail** é o **front-office** da Suite, projetado especificamente para o segmento de **Loja de Celulares e Assistência Técnica**. Ele cobre todo o ciclo operacional: venda de dispositivos, avaliação de trade-in, ordens de serviço, gestão de estoque por IMEI, comissões de vendedores, e fechamento de caixa diário. + +### 1.2 Métricas do Módulo + +| Métrica | Valor | +|---------|-------| +| **Frontend** | 10.067 linhas (ArcadiaRetail.tsx) | +| **Backend** | 5.218 linhas (routes.ts) | +| **Sync Plus** | 542 linhas (plus-sync.ts) | +| **TradeIn Form** | 988 linhas (TradeInForm.tsx) | +| **Total** | ~16.815 linhas de código | +| **Tabelas no banco** | 40+ tabelas | +| **Endpoints de API** | ~130 endpoints REST | +| **Abas da interface** | 11 abas principais | + +### 1.3 Abas da Interface + +| # | Aba | Descrição | +|---|-----|-----------| +| 1 | **Dashboard** | Visão geral com KPIs, feed de atividades e alertas | +| 2 | **PDV** | Ponto de Venda com carrinho, pagamento múltiplo e trade-in | +| 3 | **Pessoas** | Cadastro unificado (clientes, fornecedores, técnicos) | +| 4 | **Estoque** | Depósitos, saldos, movimentações, inventários e séries | +| 5 | **Serviços** | Ordens de Serviço (externas e internas) | +| 6 | **Trade-In** | Avaliações de dispositivos usados | +| 7 | **Compras** | Pedidos de compra e recebimento de mercadoria | +| 8 | **Cadastros** | Formas de pagamento, vendedores, planos, promoções, tipos | +| 9 | **Relatórios** | 6 relatórios operacionais + detalhamento de vendas | +| 10 | **Comissões** | Dashboard de comissões, metas e fechamento | +| 11 | **Configuração** | Integração Plus, empresas, sincronização | + +--- + +## 2. BANCO DE DADOS - SCHEMA COMPLETO + +### 2.1 Tabelas do Módulo Retail (40 tabelas) + +#### Estrutura de Lojas e Depósitos + +| Tabela | Campos-chave | Descrição | +|--------|-------------|-----------| +| `retail_stores` | id, tenantId, code, name, storeType (holding/distributor/store), parentStoreId, cnpj, posEnabled, serviceEnabled, leaseEnabled | Lojas e filiais na rede | +| `retail_warehouses` | id, tenantId, storeId, code, name, type (store/central/transit/virtual), isMainWarehouse, isDefault, allowNegativeStock, visibleToAllCompanies | Depósitos por loja ou central | +| `retail_warehouse_stock` | warehouseId, productId, quantity, reservedQuantity, availableQuantity, minStock, maxStock | Saldos por produto/depósito | +| `retail_stock_movements` | warehouseId, productId, movementType (entry/exit/transfer/adjustment/return), operationType, quantity, previousStock, newStock, unitCost | Movimentações detalhadas | +| `retail_stock_transfers` | sourceWarehouseId, destinationWarehouseId, status (pending/in_transit/completed/cancelled) | Transferências entre depósitos | +| `retail_stock_transfer_items` | transferId, productId, requestedQuantity, transferredQuantity, receivedQuantity | Itens da transferência | +| `retail_transfer_serials` | transferItemId, serialId | Séries/IMEIs vinculados | +| `retail_product_serials` | productId, warehouseId, serialNumber, imei, imei2, status (in_stock/reserved/sold/returned/defective/in_transit), acquisitionCost, salePrice | Números de série e IMEI | +| `retail_inventories` | warehouseId, inventoryNumber, type (full/partial/cyclic), status (open/counting/adjusting/completed) | Inventários | +| `retail_inventory_items` | inventoryId, productId, systemQuantity, countedQuantity, difference, adjustmentApplied | Itens do inventário | + +#### Dispositivos Móveis + +| Tabela | Campos-chave | Descrição | +|--------|-------------|-----------| +| `mobile_devices` | imei, imei2, brand, model, color, storage, ram, condition (new/refurbished/used), purchasePrice, sellingPrice, acquisitionType (trade_in/purchase/consignment), relatedEvaluationId, relatedServiceOrderId, personId, suggestedPrice, profitMargin, status (in_stock/sold/in_service/returned/damaged/leased) | Celulares com IMEI | +| `device_history` | deviceId, imei, eventType (received/transferred/sold/returned/service_in/service_out/leased/purchased), fromLocation, toLocation, referenceType, referenceId | Histórico de movimentação por IMEI | + +#### PDV e Vendas + +| Tabela | Campos-chave | Descrição | +|--------|-------------|-----------| +| `pos_sessions` | storeId, cashierId, cashierName, openingBalance, closingBalance, totalSales, totalRefunds, netSales, cashPayments, cardPayments, pixPayments, otherPayments, transactionCount, status (open/closed/reconciled) | Sessões de caixa | +| `pos_sales` | saleNumber, saleType (direct_sale/lease_to_own), customerId, customerName, subtotal, discountAmount, discountPercent, tradeInValue, tradeInEvaluationId, totalAmount, paymentMethod (cash/debit/credit/pix/combined), paymentDetails (JSONB), soldBy, plusVendaId, plusSyncStatus, empresaId | Vendas no PDV | +| `pos_sale_items` | saleId, itemType (product/device/accessory/service), itemName, imei, deviceId, quantity, unitPrice, discountAmount, totalPrice | Itens da venda | +| `pos_cash_movements` | sessionId, storeId, type (withdrawal/reinforcement), amount, reason, performedBy, authorizedBy | Sangria e reforço de caixa | + +#### Pagamentos e Parcelamento + +| Tabela | Campos-chave | Descrição | +|--------|-------------|-----------| +| `payment_plans` | saleId, customerId, totalAmount, downPayment, remainingAmount, numberOfInstallments, installmentAmount, interestRate, firstInstallmentDate, paidInstallments, status (active/completed/defaulted/cancelled) | Planos de parcelamento | +| `payment_plan_installments` | planId, installmentNumber, dueDate, amount, paidAmount, paidDate, paymentMethod, status (pending/paid/overdue/cancelled) | Parcelas do plano | + +#### Locação com Opção de Compra + +| Tabela | Campos-chave | Descrição | +|--------|-------------|-----------| +| `lease_agreements` | agreementNumber, deviceId, imei, leaseStartDate, leaseEndDate, monthlyPayment, totalLeaseCost, purchaseOptionAvailable, purchasePrice, rentCreditPercent | Contratos de locação | +| `lease_payments` | leaseId, paymentNumber, dueDate, amount, paidAmount, status (pending/paid/overdue) | Pagamentos da locação | + +#### Trade-In e Avaliações + +| Tabela | Campos-chave | Descrição | +|--------|-------------|-----------| +| `device_evaluations` | imei, brand, model, personId, 19 campos de checklist booleano (cada com campo de notas), batteryHealth, estimatedValue, acquisitionValue, status (pending/analyzing/approved/rejected), linkedServiceOrderId, maintenanceOrderId, creditGenerated, creditId | Avaliações de Trade-In | +| `trade_in_checklist_templates` | name, deviceCategory (smartphone/tablet/laptop/smartwatch), isActive | Templates de checklist customizáveis | +| `trade_in_checklist_items` | templateId, category (visual/funcional/acessorios/documentacao), itemName, evaluationType (condition/boolean/percentage/text), options (JSON), impactOnValue (% impacto), isRequired, displayOrder | Itens do template | +| `trade_in_evaluation_results` | evaluationId, checklistItemId, result, percentValue, notes | Resultados por item | +| `trade_in_transfer_documents` | evaluationId, documentNumber, customerName/CPF/RG/address, deviceBrand/Model/IMEI/IMEI2, agreedValue, customerSignature (Base64), termsAccepted, status (draft/pending_signature/signed/completed) | Documento de transferência de posse | +| `retail_acquisitions` | type (trade_in/purchase/consignment), sourceEvaluationId, sourceServiceOrderId, deviceId, acquisitionCost, repairCost, totalCost, suggestedPrice, finalPrice, status (pending/ready_for_stock/in_stock/sold) | Aquisições processadas | + +#### Ordens de Serviço + +| Tabela | Campos-chave | Descrição | +|--------|-------------|-----------| +| `service_orders` | orderNumber, deviceId, imei, customerName, serviceType (repair/maintenance/internal_review/diagnostic), origin (customer_request/device_acquisition/warranty), assignedTo, technicianName, partsCost, laborCost, totalCost, isInternal, internalType (revision/cleaning/maintenance/quality_check/trade_in_diagnosis/trade_in_maintenance), sourceEvaluationId, checklistData (JSONB), status (12 estados) | Ordens de Serviço | +| `service_order_items` | serviceOrderId, productId, itemType (part/labor/accessory), itemName, quantity, unitPrice, totalPrice | Peças e serviços da O.S. | +| `service_warranties` | serviceOrderId, deviceId, imei, serviceType, warrantyDays, startDate, endDate, status (active/expired/claimed/voided) | Garantias vinculadas | + +#### Devoluções + +| Tabela | Campos-chave | Descrição | +|--------|-------------|-----------| +| `return_exchanges` | returnNumber, originalSaleId, returnType (return/exchange), reason, refundAmount, refundMethod, status (pending/approved/rejected/processed) | Devoluções e trocas | +| `return_exchange_items` | returnId, itemName, imei, deviceId, reason, refundAmount | Itens devolvidos | + +#### Cadastros e Configurações + +| Tabela | Campos-chave | Descrição | +|--------|-------------|-----------| +| `retail_payment_methods` | name, type (cash/debit/credit/pix/boleto/financing), brand (visa/mastercard/elo/amex/hipercard), feePercent, fixedFee, installmentsMax, installmentFees (JSONB), daysToReceive | Formas de pagamento | +| `retail_sellers` | personId, code, name, storeId, commissionPlanId, hireDate, isActive | Vendedores | +| `retail_commission_plans` | name, type (fixed/percent/tiered/per_product), baseValue, basePercent, rules (JSONB) | Planos de comissão | +| `retail_seller_goals` | sellerId, month, year, goalAmount, goalType (sales/units/margin), achievedAmount, achievedPercent, bonus | Metas de vendedor | +| `retail_store_goals` | storeId, month, year, goalAmount, achievedAmount, achievedPercent | Metas da loja | +| `retail_commission_closures` | sellerId, periodType (daily/monthly/custom), periodStart, periodEnd, totalSales, totalReturns, netSales, commissionRate, commissionAmount, bonusAmount, status (open/closed/paid) | Fechamento de comissão | +| `retail_commission_closure_items` | closureId, saleId, returnId, itemType (sale/return), amount, commission | Vendas do fechamento | +| `retail_price_tables` | name, customerType (retail/wholesale/vip/employee), discountPercent, markupPercent, validFrom, validTo | Tabelas de preço | +| `retail_price_table_items` | priceTableId, productId, deviceId, customPrice, discountPercent | Preços por produto | +| `retail_promotions` | name, type (percent_off/fixed_off/buy_x_get_y/bundle), discountValue, discountPercent, applyTo (all/category/product/brand), validFrom, validTo | Promoções | +| `retail_product_types` | name, category (device/accessory/part/service), requiresImei, requiresSerial, ncm, cest, origem, cstIcms, csosn, cfops (4 tipos), alíquotas (ICMS/PIS/COFINS/IPI), aliqIbs, aliqCbs | Tipos com atributos fiscais | +| `retail_activity_feed` | activityType, entityType, entityId, title, description, metadata (JSONB), severity (info/success/warning/error) | Feed de atividades | +| `retail_reports` | name, type (sales/inventory/commissions/financial/custom), query, filters (JSONB), columns (JSONB) | Relatórios customizáveis | + +#### Créditos + +| Tabela | Campos-chave | Descrição | +|--------|-------------|-----------| +| `customer_credits` | personId, customerName, amount, remainingAmount, origin (trade_in/return/manual/promotion), status (active/used/expired/cancelled), expiresAt | Créditos de cliente | + +--- + +## 3. DASHBOARD - PAINEL PRINCIPAL + +### 3.1 KPIs em Tempo Real (5 Cards) + +| Card | Fonte de Dados | Cálculo | +|------|---------------|---------| +| **Dispositivos em Estoque** | `mobile_devices` WHERE status = 'in_stock' | COUNT total | +| **Vendas Hoje** | `pos_sales` WHERE DATE(created_at) = hoje AND status = 'completed' | SUM(total_amount) + COUNT | +| **OS Abertas** | `service_orders` WHERE status NOT IN ('completed','cancelled') | COUNT | +| **Trade-In Pendentes** | `device_evaluations` WHERE status = 'pending' | COUNT | +| **Ticket Médio** | Derivado | todaySalesTotal / todaySalesCount | + +### 3.2 Componentes do Dashboard + +| Componente | Descrição | +|------------|-----------| +| **Últimas Ordens de Serviço** | Lista das 5 O.S. mais recentes com número, dispositivo, cliente, status e valor | +| **Avaliações de Trade-In** | Lista das 5 avaliações mais recentes com IMEI, dispositivo, cliente, condição, valor e status | +| **Produtos com Estoque Baixo** | Alertas de produtos com `stockQty < minStock`, exibidos com badge vermelho | +| **Feed de Atividades** | Timeline em tempo real (atualiza a cada 30s) com ícones por tipo (venda/serviço/avaliação), severidade colorida (info/success/warning/error), indicador de não-lido | + +### 3.3 Endpoint do Dashboard Stats + +``` +GET /api/retail/dashboard-stats +``` + +**Queries executadas:** +1. COUNT de dispositivos em estoque +2. SUM e COUNT de vendas do dia +3. COUNT de O.S. abertas +4. COUNT de avaliações pendentes + +--- + +## 4. PDV - PONTO DE VENDA + +### 4.1 Pré-requisitos para Usar o PDV + +1. **Sessão de Caixa ativa** (abertura obrigatória com saldo inicial) +2. **Empresa selecionada** (seletor de empresa na barra superior) +3. **Vendedor selecionado** (obrigatório para registrar vendas) + +### 4.2 Layout do PDV (Grid 5 Colunas) + +``` +┌─────────────────────────────────────────┬───────────────────────┐ +│ CATÁLOGO DE ITENS (3 colunas) │ CARRINHO (2 colunas)│ +│ │ │ +│ [Dispositivos] [Produtos] [Faturar OS]│ Lista de itens │ +│ │ Subtotal │ +│ Busca + Filtro │ - Desconto │ +│ Lista de itens disponíveis │ - Trade-In │ +│ com preço e botão "Adicionar" │ - Crédito │ +│ │ = TOTAL │ +│ │ │ +│ │ [Trade-In] │ +│ │ [FINALIZAR VENDA] │ +└─────────────────────────────────────────┴───────────────────────┘ +``` + +### 4.3 Três Abas de Catálogo + +#### 4.3.1 Dispositivos (Celulares) +- **Busca:** IMEI, marca, modelo, cor, armazenamento, preço +- **Filtro:** Somente status "in_stock" +- **Exibição:** Marca + Modelo, Storage | Cor | IMEI, Badge de condição (new/refurbished/used), Badge do depósito, Preço de venda +- **Ação:** Botão "Adicionar" (desabilitado se IMEI já está no carrinho) +- **Tipo no carrinho:** `device` com IMEI vinculado + +#### 4.3.2 Produtos (Acessórios, Peças) +- **Busca:** Nome, código, descrição, código de barras, NCM +- **Filtro:** Somente status "active" +- **Exibição:** Nome, Código | Categoria, Badge de estoque (quantidade + unidade), Preço de venda +- **Ação:** Botão "Adicionar" com quantidade editável +- **Tipo no carrinho:** `product` + +#### 4.3.3 Faturar O.S. (Ordens de Serviço) +- **Busca:** Número da O.S. ou nome do cliente +- **Filtro:** O.S. com status "ready_pickup" (aguardando faturamento) +- **Exibição:** Número da O.S., Cliente, Marca + Modelo, Descrição, Valor total +- **Ação:** Botão "Faturar" (vincula a O.S. à venda) +- **Tipo no carrinho:** `service` com serviceOrderId vinculado + +### 4.4 Carrinho de Compras + +| Campo | Descrição | +|-------|-----------| +| **Itens** | Lista com nome, descrição, badge de tipo (Celular/Serviço), preço unitário, botão remover | +| **Subtotal** | Soma de todos os itens | +| **Desconto** | Desconto em R$ ou % (configurável) | +| **Trade-In** | Valor de abatimento de avaliação aprovada | +| **Crédito** | Crédito disponível do cliente (trade-in anterior, devolução) | +| **Total** | Subtotal - Desconto - TradeIn - Crédito | + +### 4.5 Ações do PDV + +| Botão | Função | +|-------|--------| +| **Sangria** | Retirada de dinheiro do caixa (exige motivo) | +| **Reforço** | Adição de dinheiro ao caixa (exige motivo) | +| **Devolução** | Abre fluxo de devolução/troca | +| **Selecionar Cliente** | Busca/cadastro de pessoa unificada | +| **Limpar** | Remove todos os itens do carrinho | +| **Trade-In** | Inicia avaliação de dispositivo do cliente | +| **Finalizar Venda** | Abre modal de pagamento | + +### 4.6 Fluxo de Finalização de Venda + +``` +1. Usuário clica "Finalizar Venda" +2. Modal de pagamento abre com: + - Forma de pagamento padrão: Dinheiro (valor total) + - Opções: Cash, Débito, Crédito, PIX, Combinado + - Para Crédito: campo de parcelas + - Para Combinado: múltiplas linhas de pagamento + - Desconto: campo de % e R$ +3. Ao confirmar: + a. Cria registro em pos_sales + b. Cria registros em pos_sale_items + c. Atualiza status dos dispositivos para "sold" + d. Atualiza estoque dos produtos + e. Registra no activity_feed + f. Gera impressão da venda (PDF A4) + g. Sincroniza com Plus (se configurado) + h. Limpa o carrinho +``` + +### 4.7 Impressão da Venda (PDF A4) + +A impressão é chamada **ANTES** de limpar o carrinho para preservar os dados: +- Formato: A4 (595 x 842pt) +- Conteúdo: Número da venda, data/hora, dados da empresa, cliente, vendedor, lista de itens (produto, quantidade, unitário, total), subtotal, desconto, trade-in, total, forma de pagamento +- Dispositivos: exibe IMEI +- Produtos: exibe NCM (quando disponível) +- Geração: via `jspdf` no navegador + +### 4.8 Formas de Pagamento Suportadas + +| Método | Campos | +|--------|--------| +| **Dinheiro (cash)** | Valor | +| **Débito (debit)** | Valor | +| **Crédito (credit)** | Valor + Parcelas | +| **PIX (pix)** | Valor | +| **Combinado (combined)** | Múltiplas linhas (ex: R$ 500 PIX + R$ 300 Crédito 3x) | + +--- + +## 5. TRADE-IN - GESTÃO DE TROCA + +### 5.1 Fluxo Completo do Trade-In (4 Etapas) + +``` +┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ +│ ETAPA 1 │ │ ETAPA 2 │ │ ETAPA 3 │ │ ETAPA 4 │ +│ Avaliação │───>│ Aprovação │───>│ Revisão │───>│ Estoque │ +│ │ │ │ │ (O.S. Int.) │ │ │ +│ Checklist │ │ Gera crédito │ │ Manutenção │ │ Cria device │ +│ IMEI/Cliente │ │ Cria O.S. │ │ Checklist │ │ Preço suger. │ +│ Valor estim. │ │ Registro │ │ Peças/custos │ │ Disponível │ +│ │ │ IMEI history │ │ │ │ para venda │ +│ Status: │ │ Status: │ │ Status: │ │ Status: │ +│ pending │ │ approved │ │ completed │ │ in_stock │ +└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ +``` + +### 5.2 Etapa 1 - Avaliação + +Dois modos de criação: + +#### Avaliação Rápida (Dialog simples) +- Campos: IMEI, Marca, Modelo, Cor, Cliente, Condição Geral, Valor Estimado +- Uso: avaliação expressa sem checklist detalhado + +#### Formulário Completo (TradeInForm - 988 linhas) +- **Dados do Cliente:** Busca por nome/CPF na base de pessoas, cadastro rápido +- **Dados do Dispositivo:** IMEI, Marca, Modelo, Cor, Armazenamento +- **Checklist Completo:** 19 itens de verificação (ver seção 6) +- **Peças Necessárias:** Lista de peças com valores +- **Valor de Avaliação:** Calculado ou manual +- **Assinatura Digital:** Canvas para assinatura do cliente (touchscreen/mouse) +- **Ações:** Salvar, Imprimir PDF, Limpar + +### 5.3 Etapa 2 - Aprovação + +**Endpoint:** `POST /api/retail/evaluations/:id/approve-and-process` + +O que acontece na aprovação: +1. Status da avaliação muda para `approved` +2. **Cria O.S. Interna** (`INT${YYMM}RANDOM`) com: + - `serviceType: "internal_review"` + - `isInternal: true` + - `internalType: "revision"` + - `sourceEvaluationId: evaluationId` +3. **Registra no IMEI History** (ação: `trade_in_approved`, status: `in_revision`) +4. **Gera Crédito para o Cliente:** + - Busca pessoa pelo `personId` + - Cria registro em `customer_credits` + - Origem: `trade_in` + - Status: `active` + - Valor = estimatedValue da avaliação + +### 5.4 Etapa 3 - Revisão (O.S. Interna) + +A O.S. Interna segue o mesmo fluxo de uma O.S. normal mas com marcação `isInternal = true`: +- Técnico pode preencher checklist de revisão +- Adicionar peças utilizadas (custo de reparo) +- Registrar mão de obra +- Preencher diagnóstico + +**Endpoint de finalização:** `POST /api/retail/service-orders/:id/finalize-internal` + +### 5.5 Etapa 4 - Entrada no Estoque + +Ao finalizar a O.S. Interna: +1. **Cria dispositivo** em `mobile_devices`: + - `condition: "refurbished"` (ou conforme avaliação) + - `acquisitionType: "trade_in"` + - `acquisitionCost: estimatedValue + repairCost` + - `relatedEvaluationId: evaluationId` + - `relatedServiceOrderId: serviceOrderId` + - `suggestedPrice: totalCost * (1 + profitMargin/100)` + - `profitMargin: margem configurada (padrão 30%)` + - `status: "in_stock"` +2. **Registra no IMEI History** (ação: `entered_stock`) +3. **Registra no Activity Feed** (tipo: `stock_in`) + +### 5.6 Consulta do Fluxo Completo + +**Endpoint:** `GET /api/retail/evaluations/:id/full-flow` + +Retorna: +```json +{ + "evaluation": { ... }, + "serviceOrder": { ... }, + "device": { ... }, + "history": [ ... ], + "flowStatus": "in_stock", + "flowStep": 4, + "steps": [ + { "step": 1, "name": "Avaliação", "status": "completed" }, + { "step": 2, "name": "Aprovação", "status": "completed" }, + { "step": 3, "name": "Revisão (O.S.)", "status": "completed" }, + { "step": 4, "name": "Estoque", "status": "completed" } + ] +} +``` + +### 5.7 Uso do Crédito Trade-In no PDV + +Quando o cliente com crédito ativo é selecionado no PDV: +- Um banner azul aparece: "Crédito disponível: R$ X.XXX,XX" +- O operador pode ativar "Usar crédito" como abatimento na compra +- O crédito é deduzido automaticamente do total +- Múltiplos créditos são somados (trade-in + devolução + manual) + +--- + +## 6. CHECKLIST DE AVALIAÇÃO + +### 6.1 Checklist Fixo (19 Itens Built-in) + +O `deviceEvaluations` possui 19 campos booleanos fixos (cada um com campo de notas/observações): + +| # | Campo | Pergunta | Tipo | Impacto | +|---|-------|----------|------|---------| +| 1 | `powerOn` | Aparelho liga corretamente? | Sim/Não | Crítico | +| 2 | `screenIssues` | Avarias, travamentos ou toque fantasma? | Sim/Não | Alto | +| 3 | `screenSpots` | Manchas na tela? | Sim/Não | Médio | +| 4 | `buttonsWorking` | Botões funcionando? | Sim/Não | Médio | +| 5 | `wearMarks` | Marcas de uso? | Sim/Não | Baixo | +| 6 | `wifiWorking` | Wi-Fi funcionando? | Sim/Não | Alto | +| 7 | `simWorking` | Chip funcionando? | Sim/Não | Alto | +| 8 | `mobileDataWorking` | 4G/5G funcionando? | Sim/Não | Alto | +| 9 | `sensorsNfcWorking` | Sensores funcionando / NFC? | Sim/Não | Médio | +| 10 | `biometricWorking` | Face ID / Touch ID funcionando? | Sim/Não | Alto | +| 11 | `microphonesWorking` | Microfones funcionando? | Sim/Não | Alto | +| 12 | `earSpeakerWorking` | Áudio auricular funcionando? | Sim/Não | Médio | +| 13 | `loudspeakerWorking` | Áudio alto-falante funcionando? | Sim/Não | Médio | +| 14 | `chargingPortWorking` | Entrada de carregamento funcionando? | Sim/Não | Alto | +| 15 | `camerasWorking` | Câmeras funcionando / Manchas? | Sim/Não | Alto | +| 16 | `flashWorking` | Flash funcionando? | Sim/Não | Baixo | +| 17 | `hasCharger` | Possui carregador? | Sim/Não | Baixo | +| 18 | `toolsAnalysisOk` | Análise pelo 3uTools OK? | Sim/Não | Alto | +| 19 | `batteryHealth` | Saúde da Bateria | 0-100% | Alto | + +Cada campo possui um campo paralelo `*Notes` (texto) para observações adicionais. + +### 6.2 Checklist do TradeInForm (Frontend - 19 Itens) + +O componente `TradeInForm.tsx` implementa o checklist como array com: +- **ID único** por item +- **Descrição** da verificação +- **Valor:** `"sim"` | `"nao"` | `""` (não avaliado) +- **Observação:** campo de texto livre + +``` +DEFAULT_CHECKLIST = [ + "Aparelho liga corretamente" + "Avarias, travamentos ou toque fantasma" + "Manchas na tela" + "Botões funcionando" + "Marcas de uso" + "Wi-Fi funcionando" + "Chip funcionando" + "4G/5G funcionando" + "Sensores funcionando / NFC" + "Face ID / Touch ID funcionando" + "Microfones funcionando" + "Áudio auricular funcionando" + "Áudio alto-falante funcionando" + "Entrada de carregamento funcionando" + "Câmeras funcionando / Manchas" + "Flash funcionando" + "Possui carregador" + "Análise pelo 3uTools OK" + "Saúde da Bateria (%)" +] +``` + +### 6.3 Sistema de Templates Customizáveis + +Para além do checklist fixo, existe um sistema de templates: + +| Tabela | Função | +|--------|--------| +| `trade_in_checklist_templates` | Templates por categoria (smartphone, tablet, laptop, smartwatch) | +| `trade_in_checklist_items` | Itens do template com: categoria (visual/funcional/acessorios/documentacao), tipo de avaliação (condition/boolean/percentage/text), opções JSON, % de impacto no valor, ordenação | +| `trade_in_evaluation_results` | Resultados vinculados à avaliação e item do checklist | + +**Tipos de avaliação:** +- `condition`: opções pré-definidas (ex: "perfeito", "bom", "regular", "ruim") +- `boolean`: sim/não +- `percentage`: valor de 0 a 100 +- `text`: texto livre + +### 6.4 Checklist na O.S. (Diagnóstico) + +As Ordens de Serviço possuem campo `checklistData` (JSONB) que armazena dados de checklist de forma flexível: +- Preenchido pelo técnico durante diagnóstico +- Endpoint: `PUT /api/retail/service-orders/:id/checklist` +- Timestamp de conclusão: `checklistCompletedAt` +- Autor: `checklistCompletedBy` + +--- + +## 7. ORDENS DE SERVIÇO + +### 7.1 Tipos de O.S. + +| Tipo | serviceType | Origem | Descrição | +|------|------------|--------|-----------| +| **Reparo (cliente)** | `repair` | `customer_request` | Cliente traz dispositivo para conserto | +| **Manutenção** | `maintenance` | `customer_request` | Manutenção preventiva | +| **Diagnóstico** | `diagnostic` | `customer_request` | Apenas diagnóstico/orçamento | +| **Revisão Interna** | `internal_review` | `device_acquisition` | Revisão de dispositivo Trade-In | +| **Garantia** | `repair` | `warranty` | Reparo coberto por garantia | + +### 7.2 Fluxo de Status da O.S. (12 Estados) + +``` +open → diagnosis → quote → pending_approval → approved → in_repair → waiting_parts → quality_check → ready_pickup → completed + ↓ ↓ + rejected cancelled +``` + +| Status | Descrição | +|--------|-----------| +| `open` | Recém-criada, aguardando atendimento | +| `diagnosis` | Em diagnóstico pelo técnico | +| `quote` | Orçamento elaborado, aguardando aprovação | +| `pending_approval` | Enviado para aprovação do cliente | +| `approved` | Cliente aprovou o orçamento | +| `rejected` | Cliente rejeitou o orçamento | +| `in_repair` | Em reparo ativo | +| `waiting_parts` | Aguardando peças/componentes | +| `quality_check` | Verificação de qualidade pós-reparo | +| `ready_pickup` | Pronto para retirada pelo cliente | +| `completed` | Concluída e entregue | +| `cancelled` | Cancelada | + +### 7.3 Campos da O.S. + +- **Identificação:** Número (auto-gerado), Loja, Prioridade (low/normal/high/urgent) +- **Dispositivo:** IMEI, Marca, Modelo, deviceId +- **Cliente:** Nome, Telefone, Email, personId (pessoa unificada) +- **Técnico:** Nome, personId do técnico +- **Custos:** Peças (partsCost), Mão de obra (laborCost), Total (totalCost) +- **Datas:** Data esperada, Data real de conclusão +- **Pagamento:** Status (pending/paid/partial) +- **O.S. Interna:** isInternal, internalType, sourceEvaluationId + +### 7.4 Itens da O.S. + +Cada O.S. pode ter múltiplos itens: +- **Peça (part):** Componente utilizado no reparo +- **Mão de obra (labor):** Serviço técnico +- **Acessório (accessory):** Acessório incluído + +### 7.5 Garantias de Serviço + +Ao concluir uma O.S., pode-se criar uma garantia vinculada: +- Vincula à O.S. e ao IMEI +- Define prazo em dias (ex: 90 dias) +- Status: active → expired (automático) ou claimed (cliente acionou) +- Consulta de garantia por IMEI: `GET /api/retail/warranties/check/:imei` + +### 7.6 Faturamento de O.S. no PDV + +Quando uma O.S. está com status `ready_pickup`: +1. Aparece na aba "Faturar O.S." do PDV +2. O operador adiciona ao carrinho como item de serviço +3. A venda é registrada normalmente +4. A O.S. é marcada como `completed` + +--- + +## 8. DEVOLUÇÕES E TROCAS + +### 8.1 Fluxo de Devolução + +``` +1. Operador clica "Devolução" no PDV +2. Busca venda original por número ou cliente +3. Seleciona itens para devolver +4. Define motivo e método de reembolso +5. Verificação de senha do gerente (MANAGER_PASSWORD) +6. Processamento: + a. Cria registro em return_exchanges + b. Cria itens em return_exchange_items + c. Para dispositivos: reverte status do mobile_device para "returned" + d. Para produtos: ajusta estoque + e. Gera crédito para o cliente (customer_credits) + f. Registra no IMEI history (para dispositivos) + g. Registra no activity feed +``` + +### 8.2 Tipos + +| Tipo | Descrição | +|------|-----------| +| `return` | Devolução pura (reembolso) | +| `exchange` | Troca por outro produto | + +### 8.3 Métodos de Reembolso + +- Crédito na loja (gera `customer_credits`) +- Dinheiro +- Estorno no cartão +- PIX + +### 8.4 Busca de Vendas para Devolução + +**Endpoint:** `GET /api/retail/sales-for-return?search=...` + +Busca por: +- Número da venda (saleNumber) +- Nome do cliente +- CPF do cliente + +Retorna vendas com status "completed" + itens detalhados. + +### 8.5 Segurança + +A devolução exige **senha do gerente** (`MANAGER_PASSWORD`) para ser processada. Endpoint de verificação: `POST /api/retail/verify-manager-password`. + +--- + +## 9. ESTOQUE E DEPÓSITOS + +### 9.1 Tipos de Depósito + +| Tipo | Descrição | +|------|-----------| +| `store` | Depósito vinculado a uma loja | +| `central` | Depósito central (distribuição) | +| `transit` | Em trânsito entre depósitos | +| `virtual` | Depósito virtual (ex: consignação) | + +### 9.2 Movimentações de Estoque + +| Tipo de Movimento | Operação | Descrição | +|-------------------|----------|-----------| +| `entry` | `purchase` | Entrada por compra | +| `entry` | `trade_in` | Entrada por trade-in | +| `entry` | `manual_entry` | Entrada manual | +| `entry` | `devolution` | Entrada por devolução | +| `exit` | `sale` | Saída por venda | +| `transfer_in` | - | Recebimento de transferência | +| `transfer_out` | - | Envio para transferência | +| `adjustment` | `inventory_adjustment` | Ajuste de inventário | +| `return` | - | Retorno (devolução) | + +### 9.3 Transferências entre Depósitos + +Fluxo: `pending → in_transit → completed` + +Cada transferência registra: +- Depósito de origem e destino +- Itens com quantidade solicitada, transferida e recebida +- Números de série/IMEI vinculados +- Responsáveis (solicitante, aprovador, recebedor) + +### 9.4 Inventário + +Tipos: `full` (completo), `partial` (parcial), `cyclic` (cíclico) + +Fluxo: `open → counting → adjusting → completed` + +Para cada item do inventário: +- Sistema mostra quantidade no sistema +- Operador informa quantidade contada +- Sistema calcula diferença +- Ao aplicar ajuste: cria movimentação automática + +--- + +## 10. COMISSÕES E METAS + +### 10.1 Dashboard de Comissões + +O dashboard exibe para cada vendedor: +- Total de vendas no período +- Número de vendas +- Ticket médio +- Comissão calculada +- Meta vs Realizado (progress bar) +- Comparativo entre vendedores + +### 10.2 Planos de Comissão + +| Tipo | Descrição | +|------|-----------| +| `fixed` | Valor fixo por venda | +| `percent` | Percentual sobre o valor da venda | +| `tiered` | Faixas progressivas (rules JSONB) | +| `per_product` | Comissão específica por produto/categoria | + +### 10.3 Metas + +- **Por Vendedor:** goalAmount, goalType (sales/units/margin), achievedAmount, achievedPercent, bonus +- **Por Loja:** goalAmount, achievedAmount, achievedPercent + +### 10.4 Fechamento de Comissão + +O fechamento consolida vendas e devoluções em um período: + +``` +POST /api/retail/commission-closures/calculate + +Input: sellerId, periodStart, periodEnd, commissionRate +Output: { + totalSales, + totalReturns (devoluções deduzidas), + netSales (vendas líquidas), + salesCount, + returnsCount, + commissionAmount, + items: [ { saleId, amount, commission } ] +} +``` + +Status do fechamento: `open → closed → paid` + +--- + +## 11. RELATÓRIOS + +### 11.1 Relatórios Disponíveis (6 + 1) + +| # | Relatório | Endpoint | Descrição | +|---|-----------|----------|-----------| +| 1 | **OS por Status** | `GET /reports/os-by-status` | Quantidade e valor total por status | +| 2 | **OS por Técnico** | `GET /reports/os-by-technician` | Total de O.S., concluídas, em andamento, receita por técnico | +| 3 | **Vendas por Vendedor** | `GET /reports/sales-by-seller` | Total vendas, receita, ticket médio, dias ativos | +| 4 | **Margem por IMEI** | `GET /reports/margin-by-imei` | Custo, venda, margem absoluta e %, status | +| 5 | **Caixa Diário** | `GET /reports/daily-cash` | Fechamento completo do dia | +| 6 | **Giro de Estoque** | `GET /reports/stock-turnover` | Estoque atual, vendas 30d, turnover ratio | +| 7 | **Detalhamento de Vendas** | `GET /sales?detailed=true` | Lista completa com filtros | + +### 11.2 Caixa Diário (Detalhamento) + +O relatório de caixa diário é o mais completo, executando **4 queries:** + +**Query 1 - Resumo:** +- Total de vendas (R$) +- Quantidade de vendas +- Total em dinheiro +- Total em cartão (débito + crédito) +- Total em PIX +- Total combinado + +**Query 2 - Movimentações:** +- Total de sangrias +- Total de reforços + +**Query 3 - Listagem de Vendas:** +- Cada venda com: número, hora, cliente, vendedor, subtotal, desconto, total, forma de pagamento + +**Query 4 - Vendas por Vendedor:** +Para cada vendedor, breakdown completo: + +| Coluna | Descrição | +|--------|-----------| +| Vendedor | Nome | +| Vendas | Quantidade | +| Dinheiro | Total em cash | +| Débito | Total em débito | +| Crédito | Total em crédito | +| PIX | Total em PIX | +| Combinado | Total combinado | +| Descontos | Total de descontos concedidos | +| **Total** | **Receita líquida** | + +Linha de rodapé com totalizadores gerais. + +**Cálculo do Saldo do Caixa:** +``` +Saldo = Dinheiro + Reforços - Sangrias +``` + +--- + +## 12. CADASTROS + +### 12.1 Formas de Pagamento + +CRUD completo com campos: +- Nome, Tipo (cash/debit/credit/pix/boleto/financing) +- Bandeira (visa/mastercard/elo/amex/hipercard) +- Taxa (%), Taxa fixa (R$) +- Máximo de parcelas +- Taxas por parcela (JSONB: [{installments, feePercent}]) +- Dias para recebimento + +### 12.2 Vendedores + +CRUD com vínculo a: +- Pessoa unificada (personId) +- Loja (storeId) +- Plano de comissão (commissionPlanId) +- Data de contratação + +### 12.3 Planos de Comissão + +Tipos: fixed, percent, tiered, per_product +Regras customizáveis via JSONB + +### 12.4 Tabelas de Preço + +- Tipos de cliente: retail, wholesale, vip, employee +- Desconto ou markup percentual +- Validade (de/até) +- Itens específicos com preço customizado + +### 12.5 Promoções + +- Tipos: percent_off, fixed_off, buy_x_get_y, bundle +- Aplicação: all, category, product, brand +- Quantidade mínima/máxima +- Período de validade + +### 12.6 Tipos de Produto + +Com atributos fiscais completos: +- Categoria: device, accessory, part, service +- Controle: requiresImei, requiresSerial +- Fiscal: NCM, CEST, Origem, CST ICMS, CSOSN, CFOPs (4 tipos), Alíquotas (ICMS, PIS, COFINS, IPI) +- Reforma Tributária: IBS e CBS (preparado) +- Unidade de medida + +--- + +## 13. COMPRAS E AQUISIÇÕES + +### 13.1 Pedidos de Compra + +Fluxo: `draft → submitted → approved → partially_received → received → cancelled` + +Campos: fornecedor, depósito destino, itens (produto, quantidade, preço), condições de pagamento, frete, desconto, totais. + +### 13.2 Recebimento de Mercadoria + +**Endpoint:** `POST /api/retail/purchase-orders/:id/receive` + +Ao receber: +1. Atualiza quantidades recebidas por item +2. Cria movimentações de estoque (entry/purchase) +3. Atualiza saldos no depósito destino +4. Registra números de série quando aplicável +5. Atualiza status do pedido + +--- + +## 14. CRÉDITOS DE CLIENTE + +### 14.1 Origens de Crédito + +| Origem | Quando é gerado | +|--------|----------------| +| `trade_in` | Aprovação de avaliação de Trade-In | +| `return` | Devolução com reembolso em crédito | +| `manual` | Criação manual pelo operador | +| `promotion` | Promoção que gera crédito | + +### 14.2 Ciclo de Vida + +``` +active → used (parcial ou total) → expired → cancelled +``` + +### 14.3 Uso no PDV + +Quando o cliente é selecionado: +1. Sistema busca `customer_credits` com status `active` e `remainingAmount > 0` +2. Soma total de créditos disponíveis +3. Exibe banner: "Crédito disponível: R$ X.XXX,XX" +4. Operador pode ativar abatimento +5. Na finalização, deduz do crédito mais antigo primeiro (FIFO) +6. Registra uso parcial (atualiza remainingAmount) ou total (status → used) + +### 14.4 Recibo de Crédito + +**Endpoint:** `GET /api/retail/customer-credits/:creditId/receipt` + +Gera dados para impressão do comprovante de crédito. + +--- + +## 15. INTEGRAÇÃO COM PLUS (LARAVEL) + +### 15.1 Componentes da Integração + +| Arquivo | Função | +|---------|--------| +| `server/plus/proxy.ts` | Proxy reverso `/plus/*` → Laravel :8080 | +| `server/plus/sso.ts` | SSO via token HMAC-SHA256 | +| `server/plus/launcher.ts` | Auto-start do Laravel (php artisan serve) | +| `server/plus/client.ts` | Cliente REST para API do Plus | +| `server/retail/plus-sync.ts` | Sincronização Retail → Plus | + +### 15.2 Sincronização Retail → Plus + +| Entidade Suite | Entidade Plus | Endpoint | +|---------------|--------------|----------| +| Persons (clientes) | Clientes | `POST /api/retail/plus/sync/customers` | +| POS Sales + Items | Vendas + Itens + Faturamento | `POST /api/retail/plus/sync/sales` | +| POS Sales | NF-e/NFC-e | `POST /api/retail/plus/sync/nfe` | + +### 15.3 Campos de Sync na pos_sales + +| Campo | Descrição | +|-------|-----------| +| `plusVendaId` | ID da venda criada no Plus | +| `plusNfeChave` | Chave de acesso da NF-e emitida | +| `plusSyncStatus` | pending → synced → error → not_applicable | +| `plusSyncError` | Mensagem de erro (se houver) | +| `plusSyncedAt` | Timestamp da sincronização | +| `empresaId` | ID da empresa (tenant_empresas) vinculada | + +--- + +## 16. API - ENDPOINTS COMPLETOS + +### 16.1 Listagem por Domínio + +Todos os endpoints estão sob `/api/retail/*` e requerem autenticação. + +#### Activity Feed (2) +``` +GET /activity-feed +POST /activity-feed/mark-read +``` + +#### Formas de Pagamento (4) +``` +GET /payment-methods +POST /payment-methods +PUT /payment-methods/:id +DELETE /payment-methods/:id +``` + +#### Vendedores (4) +``` +GET /sellers +POST /sellers +PUT /sellers/:id +DELETE /sellers/:id +``` + +#### Planos de Comissão (4) +``` +GET /commission-plans +POST /commission-plans +PUT /commission-plans/:id +DELETE /commission-plans/:id +``` + +#### Tabelas de Preço (4) +``` +GET /price-tables +POST /price-tables +PUT /price-tables/:id +DELETE /price-tables/:id +``` + +#### Promoções (4) +``` +GET /promotions +POST /promotions +PUT /promotions/:id +DELETE /promotions/:id +``` + +#### Tipos de Produto (5) +``` +GET /product-types +GET /product-types/:id +POST /product-types +PUT /product-types/:id +DELETE /product-types/:id +``` + +#### Lojas (3) +``` +GET /stores +POST /stores +PUT /stores/:id +``` + +#### Depósitos (4) +``` +GET /warehouses +POST /warehouses +PUT /warehouses/:id +DELETE /warehouses/:id +``` + +#### Saldos de Estoque (2) +``` +GET /warehouse-stock +GET /warehouse-stock/:warehouseId/summary +``` + +#### Movimentações de Estoque (2) +``` +GET /stock-movements +POST /stock-movements +``` + +#### Números de Série (3) +``` +GET /product-serials +POST /product-serials +PUT /product-serials/:id +``` + +#### Transferências (4) +``` +GET /stock-transfers +GET /stock-transfers/:id +POST /stock-transfers +PUT /stock-transfers/:id/status +``` + +#### Inventários (5) +``` +GET /inventories +POST /inventories +GET /inventories/:id +PUT /inventories/:id/count +PUT /inventories/:id/apply +``` + +#### Dispositivos (5) +``` +GET /devices +GET /devices/:id +GET /devices/imei/:imei +POST /devices +PUT /devices/:id +``` + +#### Avaliações / Trade-In (10) +``` +GET /evaluations +GET /evaluations/:id +POST /evaluations +PUT /evaluations/:id +PUT /evaluations/:id/approve +PUT /evaluations/:id/reject +PUT /evaluations/:id/start-analysis +POST /evaluations/:id/approve-and-process +GET /evaluations/:id/full-flow +GET /evaluations/:id/service-order +``` + +#### Ordens de Serviço (8) +``` +GET /service-orders +GET /service-orders/:id +POST /service-orders +PUT /service-orders/:id +PUT /service-orders/:id/complete-preparation +POST /service-orders/:id/items +GET /service-orders/:id/items +DELETE /service-orders/:id/items/:itemId +PUT /service-orders/:id/checklist +POST /service-orders/:id/finalize-internal +``` + +#### Sessões de Caixa (3) +``` +GET /pos-sessions +POST /pos-sessions/open +PUT /pos-sessions/:id/close +``` + +#### Movimentações de Caixa (2) +``` +GET /cash-movements +POST /cash-movements +``` + +#### Garantias (4) +``` +GET /warranties +POST /warranties +GET /warranties/check/:imei +PUT /warranties/:id/claim +``` + +#### Alertas de Estoque (1) +``` +GET /stock-alerts +``` + +#### Relatórios (7) +``` +GET /reports/os-by-status +GET /reports/os-by-technician +GET /reports/sales-by-seller +GET /reports/margin-by-imei +GET /reports/daily-cash +GET /reports/stock-turnover +GET /dashboard-stats +``` + +#### Vendas (1) +``` +GET /sales +``` + +#### Pessoas (7) +``` +GET /persons +GET /persons/:id +POST /persons +PUT /persons/:id +POST /persons/:id/roles +PUT /persons/:id/roles/:roleId +DELETE /persons/:id/roles/:roleType +``` + +#### Histórico por Pessoa (4) +``` +GET /persons/:id/sales +GET /persons/:id/services +GET /persons/:id/trade-ins +GET /persons/:id/credits +``` + +#### Histórico por IMEI (3) +``` +GET /devices/:imei/history +GET /devices/by-origin/:originType +GET /inventory/by-origin +``` + +#### Trade-In Workflow (2) +``` +GET /customer-trade-ins/:personId +``` + +#### Segurança (1) +``` +POST /verify-manager-password +``` + +#### Devoluções (5) +``` +GET /sales-for-return +GET /returns +POST /returns +GET /customer-credits/:personId +GET /customer-credits/:creditId/receipt +POST /customer-credits/:creditId/use +``` + +#### Metas de Vendedor (4) +``` +GET /seller-goals +POST /seller-goals +PUT /seller-goals/:id +DELETE /seller-goals/:id +``` + +#### Metas da Loja (3) +``` +GET /store-goals +POST /store-goals +PUT /store-goals/:id +``` + +#### Fechamento de Comissão (4) +``` +GET /commission-closures +POST /commission-closures +PUT /commission-closures/:id +POST /commission-closures/calculate +GET /commission-dashboard +``` + +#### Compras (5) +``` +GET /purchase-orders +GET /purchase-orders/:id +POST /purchase-orders +PATCH /purchase-orders/:id/status +POST /purchase-orders/:id/receive +DELETE /purchase-orders/:id +``` + +#### Plus Sync (8) +``` +GET /plus/status +POST /plus/sync/customers +POST /plus/sync/sales +POST /plus/sync/nfe +POST /plus/import/customers +POST /plus/import/products +GET /plus/empresas +POST /plus/empresas +POST /plus/empresas/:id/bind +``` + +#### ERPNext Sync (8) +``` +GET /sync/status +POST /sync/persons/:id +POST /sync/persons +POST /sync/devices/:id +POST /sync/service-orders/:id +POST /sync/import/customers +POST /sync/import/suppliers +POST /sync/full +POST /sync/stock-entry +POST /sync/sales-invoice +``` + +**Total: ~130 endpoints** + +--- + +## 17. SEGURANÇA E MULTI-TENANT + +### 17.1 Autenticação +- Todas as rotas exigem `requireAuth` (Passport.js session) +- Verificação: `req.isAuthenticated()` + +### 17.2 Tenant Scoping +- Todos os relatórios e consultas filtram por `tenantId` +- Se `tenantId` estiver ausente, retorna 403 +- Dados de um tenant nunca vazam para outro + +### 17.3 Module Gating +- Middleware `requireModule(key)` verifica se módulo está ativo no tenant +- Verifica campo JSONB `tenants.features[key]` +- Retorna 403 com ação sugerida se módulo inativo + +### 17.4 Operações Sensíveis +- Devoluções exigem senha do gerente (MANAGER_PASSWORD) +- Verificação via endpoint dedicado +- Sangrias de caixa registram responsável e autorizador + +### 17.5 Auditoria +- Todas as operações relevantes registram no `retail_activity_feed` +- IMEI history rastreia toda movimentação de dispositivos +- Device history registra eventos por IMEI + +--- + +## CONCLUSÃO + +O módulo Retail da Arcádia Suite é uma solução completa e verticalmente integrada para lojas de celulares e assistência técnica, com **~16.800 linhas de código**, **40+ tabelas**, e **~130 endpoints REST**. Ele cobre todo o ciclo operacional desde a aquisição de dispositivos (compra ou trade-in), passando pela gestão de estoque por IMEI, ordens de serviço com checklist detalhado, PDV com múltiplas formas de pagamento, até o fechamento de caixa com breakdown por vendedor e tipo de pagamento. A integração com o Plus (Laravel) permite emissão de NF-e/NFC-e e sincronização completa de dados fiscais. diff --git a/client/src/App.tsx b/client/src/App.tsx index cd2629a..ea75205 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,74 +1,89 @@ import { Switch, Route } from "wouter"; +import { lazy, Suspense } from "react"; import { queryClient } from "./lib/queryClient"; import { QueryClientProvider } from "@tanstack/react-query"; import { Toaster } from "@/components/ui/toaster"; import { TooltipProvider } from "@/components/ui/tooltip"; import { AuthProvider } from "@/hooks/use-auth"; -import { ErpProfileProvider } from "@/contexts/ErpProfileContext"; +import { SoeMotorProvider } from "@/contexts/SoeMotorContext"; import { ProtectedRoute } from "@/lib/protected-route"; import { CommandPalette } from "@/components/CommandPalette"; import { KnowledgeCollectorInit } from "@/components/KnowledgeCollectorInit"; import NotFound from "@/pages/not-found"; import AuthPage from "@/pages/auth-page"; -import Agent from "@/pages/Agent"; -import Admin from "@/pages/Admin"; -import Chat from "@/pages/Chat"; -import WhatsApp from "@/pages/WhatsApp"; -import Automations from "@/pages/Automations"; -import BiWorkspace from "@/pages/BiWorkspace"; -import ProcessCompass from "@/pages/ProcessCompass"; -import WorkspacePage from "@/pages/WorkspacePage"; -import AppViewer from "@/pages/AppViewer"; -import Crm from "@/pages/Crm"; -import Production from "@/pages/Production"; -import Support from "@/pages/Support"; -import Valuation from "@/pages/Valuation"; -import Canvas from "@/pages/Canvas"; -import IDE from "@/pages/IDE"; -import Scientist from "@/pages/Scientist"; -import Knowledge from "@/pages/Knowledge"; -import CentralApis from "@/pages/CentralApis"; -import ApiTesterPage from "@/pages/ApiTesterPage"; -import ApiHub from "@/pages/ApiHub"; -import Cockpit from "@/pages/Cockpit"; -import Fisco from "@/pages/Fisco"; -import People from "@/pages/People"; -import Contabil from "@/pages/Contabil"; -import ERP from "@/pages/ERP"; -import Financeiro from "@/pages/Financeiro"; -import Communities from "@/pages/Communities"; -import ArcadiaNext from "@/pages/ArcadiaNext"; -import QualityModule from "@/pages/QualityModule"; -import CommercialEnv from "@/pages/CommercialEnv"; -import FieldOperations from "@/pages/FieldOperations"; -import TechnicalModule from "@/pages/TechnicalModule"; -import SuppliersPortal from "@/pages/SuppliersPortal"; -import NPSSurvey from "@/pages/NPSSurvey"; -import EngineeringHub from "@/pages/EngineeringHub"; -import DocTypeBuilder from "@/pages/DocTypeBuilder"; -import PageBuilder from "@/pages/PageBuilder"; -import DevelopmentModule from "@/pages/DevelopmentModule"; -import ArcadiaRetail from "@/pages/ArcadiaRetail"; -import Plus from "@/pages/Plus"; -import SuperAdmin from "@/pages/SuperAdmin"; -import Marketplace from "@/pages/Marketplace"; -import LMS from "@/pages/LMS"; -import AppCenter from "@/pages/AppCenter"; -import XosCentral from "@/pages/XosCentral"; -import XosCrm from "@/pages/XosCrm"; -import XosInbox from "@/pages/XosInbox"; -import XosTickets from "@/pages/XosTickets"; -import Migration from "@/pages/Migration"; -import DevCenter from "@/pages/DevCenter"; -import XosCampaigns from "@/pages/XosCampaigns"; -import XosAutomations from "@/pages/XosAutomations"; -import XosSites from "@/pages/XosSites"; -import XosGovernance from "@/pages/XosGovernance"; -import XosPipeline from "@/pages/XosPipeline"; +const Agent = lazy(() => import("@/pages/Agent")); +const Admin = lazy(() => import("@/pages/Admin")); +const Chat = lazy(() => import("@/pages/Chat")); +const WhatsApp = lazy(() => import("@/pages/WhatsApp")); +const Automations = lazy(() => import("@/pages/Automations")); +const BiWorkspace = lazy(() => import("@/pages/BiWorkspace")); +const ProcessCompass = lazy(() => import("@/pages/ProcessCompass")); +const WorkspacePage = lazy(() => import("@/pages/WorkspacePage")); +const AppViewer = lazy(() => import("@/pages/AppViewer")); +const Crm = lazy(() => import("@/pages/Crm")); +const Production = lazy(() => import("@/pages/Production")); +const Support = lazy(() => import("@/pages/Support")); +const Valuation = lazy(() => import("@/pages/Valuation")); +const Canvas = lazy(() => import("@/pages/Canvas")); +const IDE = lazy(() => import("@/pages/IDE")); +const Scientist = lazy(() => import("@/pages/Scientist")); +const Knowledge = lazy(() => import("@/pages/Knowledge")); +const CentralApis = lazy(() => import("@/pages/CentralApis")); +const ApiTesterPage = lazy(() => import("@/pages/ApiTesterPage")); +const ApiHub = lazy(() => import("@/pages/ApiHub")); +const Cockpit = lazy(() => import("@/pages/Cockpit")); +const Fisco = lazy(() => import("@/pages/Fisco")); +const People = lazy(() => import("@/pages/People")); +const Contabil = lazy(() => import("@/pages/Contabil")); +const SOE = lazy(() => import("@/pages/SOE")); +const Financeiro = lazy(() => import("@/pages/Financeiro")); +const Communities = lazy(() => import("@/pages/Communities")); +const ArcadiaNext = lazy(() => import("@/pages/ArcadiaNext")); +const QualityModule = lazy(() => import("@/pages/QualityModule")); +const CommercialEnv = lazy(() => import("@/pages/CommercialEnv")); +const FieldOperations = lazy(() => import("@/pages/FieldOperations")); +const TechnicalModule = lazy(() => import("@/pages/TechnicalModule")); +const SuppliersPortal = lazy(() => import("@/pages/SuppliersPortal")); +const NPSSurvey = lazy(() => import("@/pages/NPSSurvey")); +const EngineeringHub = lazy(() => import("@/pages/EngineeringHub")); +const DocTypeBuilder = lazy(() => import("@/pages/DocTypeBuilder")); +const PageBuilder = lazy(() => import("@/pages/PageBuilder")); +const DevelopmentModule = lazy(() => import("@/pages/DevelopmentModule")); +const ArcadiaRetail = lazy(() => import("@/pages/ArcadiaRetail")); +const Plus = lazy(() => import("@/pages/Plus")); +const SuperAdmin = lazy(() => import("@/pages/SuperAdmin")); +const Marketplace = lazy(() => import("@/pages/Marketplace")); +const LMS = lazy(() => import("@/pages/LMS")); +const AppCenter = lazy(() => import("@/pages/AppCenter")); +const XosCentral = lazy(() => import("@/pages/XosCentral")); +const XosCrm = lazy(() => import("@/pages/XosCrm")); +const XosInbox = lazy(() => import("@/pages/XosInbox")); +const XosTickets = lazy(() => import("@/pages/XosTickets")); +const Migration = lazy(() => import("@/pages/Migration")); +const DevCenter = lazy(() => import("@/pages/DevCenter")); +const XosCampaigns = lazy(() => import("@/pages/XosCampaigns")); +const XosAutomations = lazy(() => import("@/pages/XosAutomations")); +const XosSites = lazy(() => import("@/pages/XosSites")); +const XosGovernance = lazy(() => import("@/pages/XosGovernance")); +const XosPipeline = lazy(() => import("@/pages/XosPipeline")); + + +function LoadingFallback() { + return ( +
+
+
+

Carregando...

+
+ +
+ ); +} function Router() { return ( + }> @@ -93,7 +108,8 @@ function Router() { - + + @@ -129,6 +145,7 @@ function Router() { + ); } @@ -136,14 +153,14 @@ function App() { return ( - + - + ); diff --git a/client/src/pages/ArcadiaRetail.tsx b/client/src/pages/ArcadiaRetail.tsx index 661d249..b585207 100644 --- a/client/src/pages/ArcadiaRetail.tsx +++ b/client/src/pages/ArcadiaRetail.tsx @@ -17,7 +17,8 @@ import { Truck, FileText, Settings, RefreshCw, Trash2, Edit, GripVertical, ListChecks, FileCheck, ExternalLink, UserPlus, User, CreditCard, Lock, RefreshCcw, X, Loader2, ShoppingBag, FileBarChart, Percent, Tag, Banknote, Gift, Printer, - Target, Calendar, Headphones, Upload, List, FileSpreadsheet + Target, Calendar, Headphones, Upload, List, FileSpreadsheet, + ClipboardCheck, Circle } from "lucide-react"; import { Progress } from "@/components/ui/progress"; import { Checkbox } from "@/components/ui/checkbox"; @@ -26,6 +27,7 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@ import { Pencil } from "lucide-react"; import TradeInForm from "@/components/TradeInForm"; + interface DashboardStats { devicesInStock: number; openServiceOrders: number; @@ -189,6 +191,18 @@ export default function ArcadiaRetail() { const [evaluations, setEvaluations] = useState([]); const [activities, setActivities] = useState([]); const [searchTerm, setSearchTerm] = useState(""); + + const [empresas, setEmpresas] = useState([]); + const [sellers, setSellers] = useState([]); + const [selectedEmpresaId, setSelectedEmpresaId] = useState(() => { + const stored = localStorage.getItem("retail_empresa_id"); + return stored ? parseInt(stored) : null; + }); + const [selectedSellerId, setSelectedSellerId] = useState(() => { + const stored = localStorage.getItem("retail_seller_id"); + return stored ? parseInt(stored) : null; + }); + const [showSessionRequired, setShowSessionRequired] = useState(false); const [showNewDeviceDialog, setShowNewDeviceDialog] = useState(false); const [showNewServiceDialog, setShowNewServiceDialog] = useState(false); const [showEvaluationDialog, setShowEvaluationDialog] = useState(false); @@ -273,6 +287,8 @@ export default function ArcadiaRetail() { imei?: string; name: string; description?: string; + code?: string; + ncm?: string; quantity: number; unitPrice: number; discount: number; @@ -300,6 +316,12 @@ export default function ArcadiaRetail() { const [useCredit, setUseCredit] = useState(false); const [creditAmountToUse, setCreditAmountToUse] = useState(0); + const [showCashMovementDialog, setShowCashMovementDialog] = useState(false); + const [cashMovementType, setCashMovementType] = useState<"withdrawal"|"reinforcement">("withdrawal"); + const [cashMovements, setCashMovements] = useState([]); + const [cashMovementAmount, setCashMovementAmount] = useState(""); + const [cashMovementReason, setCashMovementReason] = useState(""); + // Trade-In Alerts & Status const [customerTradeIns, setCustomerTradeIns] = useState([]); const [showTradeInAlert, setShowTradeInAlert] = useState(false); @@ -330,6 +352,12 @@ export default function ArcadiaRetail() { const [osStatus, setOsStatus] = useState(""); const [osEvaluationStatus, setOsEvaluationStatus] = useState(""); const [osNotes, setOsNotes] = useState(""); + const [osChecklistData, setOsChecklistData] = useState>({}); + const [osItems, setOsItems] = useState([]); + const [osItemSearch, setOsItemSearch] = useState(""); + const [osItemResults, setOsItemResults] = useState([]); + const [osItemQuantity, setOsItemQuantity] = useState(1); + const [loadingOsItems, setLoadingOsItems] = useState(false); // Estados para Cadastros const [cadastroPaymentMethods, setCadastroPaymentMethods] = useState([]); @@ -367,7 +395,6 @@ export default function ArcadiaRetail() { supplierId: "", supplierName: "", invoiceNumber: "", invoiceDate: "", warehouseId: "", notes: "", items: [] }); - const [purchaseOrders, setPurchaseOrders] = useState([]); const [showPaymentMethodsDialog, setShowPaymentMethodsDialog] = useState(false); const [showSellersDialog, setShowSellersDialog] = useState(false); @@ -379,8 +406,20 @@ export default function ArcadiaRetail() { const [salesDetailLoading, setSalesDetailLoading] = useState(false); const [reportDateFrom, setReportDateFrom] = useState(new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0]); const [reportDateTo, setReportDateTo] = useState(new Date().toISOString().split('T')[0]); + const [dailyCashDate, setDailyCashDate] = useState(new Date().toISOString().split('T')[0]); const [reportSellerId, setReportSellerId] = useState("all"); + const [stockAlerts, setStockAlerts] = useState([]); + const [reportSubTab, setReportSubTab] = useState("os-status"); + const [reportOsByStatus, setReportOsByStatus] = useState([]); + const [reportOsByTech, setReportOsByTech] = useState([]); + const [reportSalesBySeller, setReportSalesBySeller] = useState([]); + const [reportMarginByImei, setReportMarginByImei] = useState([]); + const [reportDailyCash, setReportDailyCash] = useState(null); + const [reportStockTurnover, setReportStockTurnover] = useState([]); + const [reportLoading, setReportLoading] = useState(false); + const [warrantyCache, setWarrantyCache] = useState>({}); + // Estados para Comissões const [commissionDashboard, setCommissionDashboard] = useState(null); const [commissionDashboardLoading, setCommissionDashboardLoading] = useState(false); @@ -415,6 +454,8 @@ export default function ArcadiaRetail() { loadChecklistTemplates(); loadPersons(); loadActivities(); + loadEmpresas(); + loadSellers(); }, []); const loadActivities = async () => { @@ -429,6 +470,186 @@ export default function ArcadiaRetail() { } }; + const loadStockAlerts = async () => { + try { + const res = await fetch("/api/retail/stock-alerts", { credentials: "include" }); + if (res.ok) setStockAlerts(await res.json()); + } catch (error) { + console.error("Error loading stock alerts:", error); + } + }; + + const loadCashMovements = async () => { + try { + const res = await fetch("/api/retail/cash-movements", { credentials: "include" }); + if (res.ok) setCashMovements(await res.json()); + } catch (error) { + console.error("Error loading cash movements:", error); + } + }; + + const handleCreateCashMovement = async () => { + if (!cashMovementAmount || parseFloat(cashMovementAmount) <= 0) { + toast({ title: "Informe um valor válido", variant: "destructive" }); + return; + } + try { + const res = await fetch("/api/retail/cash-movements", { + method: "POST", + headers: { "Content-Type": "application/json" }, + credentials: "include", + body: JSON.stringify({ + sessionId: null, + storeId: selectedEmpresaId || 1, + type: cashMovementType, + amount: cashMovementAmount, + reason: cashMovementReason + }) + }); + if (res.ok) { + toast({ title: cashMovementType === "withdrawal" ? "Sangria registrada!" : "Reforço registrado!" }); + setShowCashMovementDialog(false); + setCashMovementAmount(""); + setCashMovementReason(""); + loadCashMovements(); + } + } catch (error) { + toast({ title: "Erro ao registrar movimentação", variant: "destructive" }); + } + }; + + const loadReportData = async (reportType: string) => { + setReportLoading(true); + try { + const params = new URLSearchParams(); + if (reportDateFrom) params.append("dateFrom", reportDateFrom); + if (reportDateTo) params.append("dateTo", reportDateTo); + if (reportType === "daily-cash") params.append("date", dailyCashDate); + + const res = await fetch(`/api/retail/reports/${reportType}?${params}`, { credentials: "include" }); + if (res.ok) { + const data = await res.json(); + switch (reportType) { + case "os-by-status": setReportOsByStatus(data); break; + case "os-by-technician": setReportOsByTech(data); break; + case "sales-by-seller": setReportSalesBySeller(data); break; + case "margin-by-imei": setReportMarginByImei(data); break; + case "daily-cash": setReportDailyCash(data); break; + case "stock-turnover": setReportStockTurnover(data); break; + } + } + } catch (error) { + console.error("Error loading report:", error); + } + setReportLoading(false); + }; + + const checkWarranty = async (imei: string) => { + if (warrantyCache[imei] !== undefined) return; + try { + const res = await fetch(`/api/retail/warranties/check/${imei}`, { credentials: "include" }); + if (res.ok) { + const data = await res.json(); + setWarrantyCache(prev => ({ ...prev, [imei]: data.hasActiveWarranty })); + } + } catch (error) { + console.error("Error checking warranty:", error); + } + }; + + const loadOsItems = async (orderId: number) => { + try { + setLoadingOsItems(true); + const res = await fetch(`/api/retail/service-orders/${orderId}/items`, { credentials: "include" }); + if (res.ok) { + const data = await res.json(); + setOsItems(Array.isArray(data) ? data : []); + } + } catch (e) { console.error(e); } + finally { setLoadingOsItems(false); } + }; + + const searchOsProducts = async (term: string) => { + if (term.length < 2) { setOsItemResults([]); return; } + try { + const res = await fetch(`/api/soe/products?search=${encodeURIComponent(term)}`, { credentials: "include" }); + if (res.ok) { + const data = await res.json(); + setOsItemResults(Array.isArray(data) ? data.slice(0, 10) : []); + } + } catch (e) { console.error(e); } + }; + + const addOsItem = async (product: any) => { + if (!editingServiceOrder) return; + try { + const res = await fetch(`/api/retail/service-orders/${editingServiceOrder.id}/items`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + credentials: "include", + body: JSON.stringify({ + productId: product.id, + itemType: "part", + itemCode: product.code, + itemName: product.name, + quantity: osItemQuantity, + unitPrice: parseFloat(product.costPrice || product.salePrice || 0), + }) + }); + if (res.ok) { + await loadOsItems(editingServiceOrder.id); + setOsItemSearch(""); + setOsItemResults([]); + setOsItemQuantity(1); + toast({ title: "Peça adicionada!", description: product.name }); + } + } catch (e) { toast({ title: "Erro ao adicionar peça", variant: "destructive" }); } + }; + + const removeOsItem = async (itemId: number) => { + if (!editingServiceOrder) return; + try { + const res = await fetch(`/api/retail/service-orders/${editingServiceOrder.id}/items/${itemId}`, { + method: "DELETE", + credentials: "include" + }); + if (res.ok) { + await loadOsItems(editingServiceOrder.id); + toast({ title: "Peça removida" }); + } + } catch (e) { toast({ title: "Erro ao remover peça", variant: "destructive" }); } + }; + + useEffect(() => { + if (activeTab === "dashboard") { + loadStockAlerts(); + } + }, [activeTab]); + + useEffect(() => { + if (activeTab === "relatorios") { + loadReportData(reportSubTab === "os-status" ? "os-by-status" : + reportSubTab === "os-tech" ? "os-by-technician" : + reportSubTab === "sales-seller" ? "sales-by-seller" : + reportSubTab === "margin-imei" ? "margin-by-imei" : + reportSubTab === "daily-cash" ? "daily-cash" : "stock-turnover"); + } + }, [activeTab, reportSubTab]); + + useEffect(() => { + if (activeTab === "servicos") { + serviceOrders.forEach(order => { + if (order.imei) checkWarranty(order.imei); + }); + } + }, [activeTab, serviceOrders]); + + useEffect(() => { + if (activeTab === "pdv") { + loadCashMovements(); + } + }, [activeTab]); + // Auto-refresh activity feed every 30 seconds useEffect(() => { const interval = setInterval(() => { @@ -594,7 +815,7 @@ export default function ArcadiaRetail() { const loadAllProducts = async () => { try { - const res = await fetch("/api/erp/products", { credentials: "include" }); + const res = await fetch("/api/soe/products", { credentials: "include" }); if (res.ok) { const data = await res.json(); setAllProducts(data); @@ -859,7 +1080,7 @@ export default function ArcadiaRetail() { const loadPersons = async () => { try { - let url = "/api/erp/persons"; + let url = "/api/soe/persons"; const params = new URLSearchParams(); if (personSearch) params.append("search", personSearch); if (personRoleFilter && personRoleFilter !== "all") params.append("role", personRoleFilter); @@ -956,7 +1177,7 @@ export default function ArcadiaRetail() { const handleQuickPersonSave = async () => { try { - const res = await fetch("/api/erp/persons", { + const res = await fetch("/api/soe/persons", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ...quickPerson, roles: ["customer"] }), @@ -1169,14 +1390,14 @@ export default function ArcadiaRetail() { let res; if (editingPerson) { - res = await fetch(`/api/erp/persons/${editingPerson.id}`, { + res = await fetch(`/api/soe/persons/${editingPerson.id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(personData), credentials: "include" }); } else { - res = await fetch("/api/erp/persons", { + res = await fetch("/api/soe/persons", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(personData), @@ -1294,7 +1515,7 @@ export default function ArcadiaRetail() { const handleTogglePersonActive = async (personId: number, isActive: boolean) => { try { - const res = await fetch(`/api/erp/persons/${personId}`, { + const res = await fetch(`/api/soe/persons/${personId}`, { method: "PUT", headers: { "Content-Type": "application/json" }, credentials: "include", @@ -1317,7 +1538,7 @@ export default function ArcadiaRetail() { return; } try { - const res = await fetch(`/api/erp/persons/${personId}`, { + const res = await fetch(`/api/soe/persons/${personId}`, { method: "DELETE", credentials: "include" }); @@ -1515,6 +1736,58 @@ export default function ArcadiaRetail() { return labels[type] || type; }; + const loadEmpresas = async () => { + try { + const res = await fetch("/api/soe/empresas", { credentials: "include" }); + if (res.ok) { + const data = await res.json(); + setEmpresas(data); + if (data.length > 0 && !selectedEmpresaId) { + const firstId = data[0].id; + setSelectedEmpresaId(firstId); + localStorage.setItem("retail_empresa_id", String(firstId)); + } + } + } catch (error) { + console.error("Error loading empresas:", error); + } + }; + + const loadSellers = async () => { + try { + const res = await fetch("/api/retail/sellers", { credentials: "include" }); + if (res.ok) { + const data = await res.json(); + setSellers(data.filter((s: any) => s.isActive !== false)); + } + } catch (error) { + console.error("Error loading sellers:", error); + } + }; + + const handleSelectEmpresa = (id: string) => { + const numId = parseInt(id); + setSelectedEmpresaId(numId); + localStorage.setItem("retail_empresa_id", String(numId)); + }; + + const handleSelectSeller = (id: string) => { + const numId = parseInt(id); + setSelectedSellerId(numId); + localStorage.setItem("retail_seller_id", String(numId)); + }; + + const requireSession = (): boolean => { + if (!selectedEmpresaId || !selectedSellerId) { + setShowSessionRequired(true); + return false; + } + return true; + }; + + const selectedEmpresa = empresas.find(e => e.id === selectedEmpresaId); + const selectedSeller = sellers.find(s => s.id === selectedSellerId); + const loadDashboardStats = async () => { try { const res = await fetch("/api/retail/dashboard/stats", { credentials: "include" }); @@ -1684,6 +1957,8 @@ export default function ArcadiaRetail() { type: "product", productId: product.id, name: product.name, + code: product.code || "", + ncm: product.ncm || "", description: `${product.code} - ${product.category || ""}`, quantity: qty, unitPrice: parseFloat(product.salePrice || product.sale_price || "0"), @@ -1952,10 +2227,178 @@ export default function ArcadiaRetail() { if (activeTab === "pdv") { loadDevices(); loadAvailableOs(); + fetchPdvProducts(); } }, [activeTab]); + const printSaleOrder = (saleResult: any, cartItems: CartItem[], customer: any, empresa: any, seller: any, payments: {method: string; amount: number}[]) => { + const now = new Date(); + const dateStr = now.toLocaleDateString('pt-BR'); + const paymentLabels: Record = { + cash: "Dinheiro", credit: "Crédito Loja", credit_card: "Cartão de Crédito", + debit: "Débito", debit_card: "Cartão de Débito", pix: "PIX", + combined: "Combinado", customer_credit: "Crédito Cliente" + }; + + const itemsRows = cartItems.map((item, i) => ` + + ${item.code || item.productId || item.deviceId || i + 1} + ${item.ncm || '-'} + ${item.name} + ${item.imei || '-'} + ${item.quantity} + UN + ${formatCurrency(item.unitPrice)} + ${formatCurrency((item.unitPrice * item.quantity) - item.discount)} + + `).join(''); + + const subtotal = cartItems.reduce((s, item) => s + (item.unitPrice * item.quantity), 0); + const totalDiscount = cartItems.reduce((s, item) => s + item.discount, 0); + const totalFinal = parseFloat(saleResult.totalAmount || "0"); + + const paymentRows = payments.length > 0 ? payments.map((pm, i) => ` + + ${i + 1} + ${paymentLabels[pm.method] || pm.method} + ${dateStr} + ${formatCurrency(pm.amount)} + + `).join('') : ` + + 1 + ${paymentLabels[saleResult.paymentMethod] || saleResult.paymentMethod || 'Dinheiro'} + ${dateStr} + ${formatCurrency(totalFinal)} + + `; + + const html = ` +Pedido ${saleResult.saleNumber} + + +
+

${empresa?.nomeFantasia || empresa?.razaoSocial || 'Loja'}

+

${empresa?.razaoSocial || ''}

+

CNPJ ${empresa?.cnpj || ''}

+

${empresa?.logradouro || ''}${empresa?.numero ? ', ' + empresa.numero : ''} ${empresa?.bairro || ''} - ${empresa?.cidade || ''}${empresa?.uf ? ' - ' + empresa.uf : ''}

+

Email: ${empresa?.email || ''}    Fone: ${empresa?.phone || ''}

+
+ +
+
${saleResult.saleNumber}
+
${dateStr}
+
${dateStr}
+
+ +
+
${customer?.fullName || customer?.name || 'Consumidor'}
+
${customer?.cpfCnpj || '-'}
+
${customer?.phone || customer?.celular || '-'}
+
${customer?.email || '-'}
+
${customer?.address || customer?.logradouro || '-'}
+
${customer?.city || customer?.cidade || '-'}${customer?.state || customer?.uf ? ' - ' + (customer.state || customer.uf) : ''}
+
${customer?.neighborhood || customer?.bairro || '-'}
+
+ +
+
${payments.length > 1 ? 'Combinado' : paymentLabels[payments[0]?.method || saleResult.paymentMethod] || 'A Vista'}
+
${seller?.name || '-'}
+
Do Emitente
+
${customer?.cep || customer?.zipCode || '-'}
+
+ +
Produtos e Serviços
+ + + + + + + + + + + + + + + ${itemsRows} + + + + + +
CódigoNCMDescriçãoN. SérieQtd.UnValor UnitárioValor Total
Total${formatCurrency(subtotal - totalDiscount)}
+ +
Totais
+
+
${formatCurrency(0)}
+
${formatCurrency(totalDiscount)}
+
${formatCurrency(subtotal)}
+
${formatCurrency(totalFinal)}
+
+ +
Condições de Vendas
+ + + + + + + + + + ${paymentRows} +
Nº PagtoForma de PagamentoVencimentoValor
+ +
Termos de Garantia
+
+ SOBRE A GARANTIA: Garantia de acessórios de 90 dias a contar a partir da data de compra. Garantia de aparelhos Seminovos a partir da data de compra tem prazo de 3 meses para modelos abaixo do iPhone X e de 12 meses para modelos acima do iPhone 11, para aparelhos Novos 1 ano de garantia pela Apple. GARANTIA É CANCELADA automaticamente nos seguintes casos: queda, esmagamentos, sobrecargas elétricas, exposição a altas temperaturas, umidade ou líquidos, exposição a poeira ou limalha de metais ou em casos que seja constatado mau uso do aparelho, instalações, modificações ou atualizações no sistema operacional; Abertura do equipamento ou tentativa de conserto destes por terceiros que não sejam os técnicos autorizados, mesmo que para realizações de outros serviços, bem como a violação do selo/lacre de garantia. AGRADECEMOS PELA CONFIANÇA. +
+ +
Observações
+
Gerado automaticamente através do módulo PDV - Arcádia Retail
+ + + +`; + + const printWindow = window.open('', '_blank'); + if (printWindow) { + printWindow.document.write(html); + printWindow.document.close(); + setTimeout(() => printWindow.print(), 500); + } + }; + const finalizeSale = async () => { + if (!requireSession()) return; if (!pdvCustomer) { toast({ title: "Selecione um cliente", variant: "destructive" }); return; @@ -1991,6 +2434,9 @@ export default function ArcadiaRetail() { const saleData = { sessionId: null, storeId: 1, + empresaId: selectedEmpresaId, + sellerId: selectedSellerId, + sellerName: selectedSeller?.name || "", customerId: String(pdvCustomer.id), personId: pdvCustomer.id, customerName: pdvCustomer.fullName, @@ -2038,6 +2484,7 @@ export default function ArcadiaRetail() { }); } } + printSaleOrder(saleResult, cart, pdvCustomer, selectedEmpresa, selectedSeller, paymentMethods); toast({ title: "Venda finalizada com sucesso!" }); clearCart(); setShowPaymentModal(false); @@ -2066,7 +2513,7 @@ export default function ArcadiaRetail() { return (
-
+
@@ -2077,22 +2524,73 @@ export default function ArcadiaRetail() {

Loja e Assistência Técnica de Celulares

-
+
+
+
+ + +
+
+
+ + +
+
-
+ {(!selectedEmpresaId || !selectedSellerId) && ( +
+ + Selecione a empresa e o vendedor para iniciar as operações. +
+ )}
{ + const operationalTabs = ["pdv", "servicos", "tradein", "compras"]; + if (operationalTabs.includes(tab) && (!selectedEmpresaId || !selectedSellerId)) { + if (!requireSession()) return; + } setActiveTab(tab); if (tab === "estoque") { loadWarehouses(); @@ -2277,6 +2775,38 @@ export default function ArcadiaRetail() {
+ {stockAlerts.length > 0 && ( + + + + + Produtos com Estoque Baixo + {stockAlerts.length} + + + +
+ {stockAlerts.map((product: any) => ( +
+
+

{product.name}

+

{product.code} | {product.category || "Sem categoria"}

+
+
+ + Estoque: {parseFloat(product.stockQty || product.stock_qty || "0").toFixed(0)} + +

+ Mín: {parseFloat(product.minStock || product.min_stock || "0").toFixed(0)} +

+
+
+ ))} +
+
+
+ )} +
@@ -2349,11 +2879,30 @@ export default function ArcadiaRetail() { Ponto de Venda - - Realize vendas de dispositivos, acessórios e serviços + + {selectedEmpresa && ( + + + {selectedEmpresa.nomeFantasia || selectedEmpresa.razaoSocial} + + )} + {selectedSeller && ( + + + Vendedor: {selectedSeller.name} + + )}
+ +
@@ -2705,11 +3275,11 @@ export default function ArcadiaRetail() {
-
- - -
- - - - Data - Venda # - Cliente - Vendedor - Itens - Pagamento - Valor - Créditos - Status - - - - {salesDetailData.length === 0 ? ( + + + +
+ OS por Status + +
+
+ +
+ - - {salesDetailLoading ? "Carregando..." : "Clique em 'Atualizar' acima para visualizar as vendas"} - + Status + Quantidade + Valor Total - ) : ( - salesDetailData.map((sale: any) => ( - - {new Date(sale.createdAt).toLocaleDateString("pt-BR")} - {sale.saleNumber} - {sale.customerName || "Consumidor"} - {sale.soldBy || "-"} - {sale.itemCount || 1} - -
- {sale.paymentMethods?.map((pm: any, idx: number) => ( - - {pm.method === "cash" ? "Dinheiro" : - pm.method === "credit_card" ? "Crédito" : - pm.method === "debit_card" ? "Débito" : - pm.method === "pix" ? "PIX" : pm.method} - - )) || -} -
-
- - {formatCurrency(sale.totalAmount)} - - - {parseFloat(sale.creditUsed || "0") > 0 ? ( - - -{formatCurrency(sale.creditUsed)} - - ) : "-"} - - - - {sale.status === "completed" ? "Concluída" : sale.status === "cancelled" ? "Cancelada" : sale.status} - - +
+ + {reportOsByStatus.length === 0 ? ( + {reportLoading ? "Carregando..." : "Sem dados"} + ) : reportOsByStatus.map((row: any, i: number) => ( + + {getStatusBadge(row.status)} + {row.count} + {formatCurrency(row.total_value)} - )) - )} - -
-
- - {salesDetailData.length > 0 && ( -
-
- Total: {salesDetailData.length} vendas + ))} + + + + + + + + + +
+ OS por Técnico +
-
-
- Total Vendido: - - {formatCurrency(salesDetailData.reduce((sum, s) => sum + parseFloat(s.totalAmount || "0"), 0))} - + + + + + + Técnico + Total OS + Concluídas + Em Andamento + Receita + + + + {reportOsByTech.length === 0 ? ( + {reportLoading ? "Carregando..." : "Sem dados"} + ) : reportOsByTech.map((row: any, i: number) => ( + + {row.technician_name || "Não atribuído"} + {row.total_os} + {row.completed} + {row.in_progress} + {formatCurrency(row.total_revenue)} + + ))} + +
+
+ + + + + + +
+ Vendas por Vendedor + +
+
+ + + + + Vendedor + Total Vendas + Receita Total + Ticket Médio + Dias Ativos + + + + {reportSalesBySeller.length === 0 ? ( + {reportLoading ? "Carregando..." : "Sem dados"} + ) : reportSalesBySeller.map((row: any, i: number) => ( + + {row.sold_by || "Sem vendedor"} + {row.total_sales} + {formatCurrency(row.total_revenue)} + {formatCurrency(row.avg_ticket)} + {row.active_days} + + ))} + +
+
+
+
+ + + + +
+ Margem por IMEI + +
+
+ +
+ + + + Dispositivo + IMEI + Condição + Custo + Venda + Margem + % + Status + + + + {reportMarginByImei.length === 0 ? ( + {reportLoading ? "Carregando..." : "Sem dados"} + ) : reportMarginByImei.map((row: any, i: number) => ( + + {row.brand} {row.model} + {row.imei} + {getConditionBadge(row.condition)} + {formatCurrency(row.cost)} + {formatCurrency(row.sale_price)} + = 0 ? 'text-green-600' : 'text-red-600'}`}>{formatCurrency(row.margin)} + {parseFloat(row.margin_percent || 0).toFixed(1)}% + {getStatusBadge(row.status)} + + ))} + +
+
+
+
+
+ + + + +
+
+ + setDailyCashDate(e.target.value)} + className="w-44" + data-testid="input-daily-cash-date" + />
-
- Créditos Usados: - - {formatCurrency(salesDetailData.reduce((sum, s) => sum + parseFloat(s.creditUsed || "0"), 0))} - + +
+ {reportDailyCash?.date && `Exibindo: ${new Date(reportDailyCash.date + 'T12:00:00').toLocaleDateString('pt-BR')}`}
-
+
+
+ + {reportDailyCash ? ( + <> +
+ + +

Total Vendas

+

{formatCurrency(reportDailyCash.total_sales)}

+

{reportDailyCash.sale_count} vendas

+
+
+ + +

Dinheiro

+

{formatCurrency(reportDailyCash.cash_total)}

+
+
+ + +

Cartão

+

{formatCurrency(reportDailyCash.card_total)}

+
+
+ + +

PIX

+

{formatCurrency(reportDailyCash.pix_total)}

+
+
+
+ +
+ + +

Sangrias

+

-{formatCurrency(reportDailyCash.withdrawals)}

+
+
+ + +

Reforços

+

+{formatCurrency(reportDailyCash.reinforcements)}

+
+
+ + +

Saldo Caixa (Dinheiro + Reforços - Sangrias)

+

{formatCurrency( + parseFloat(reportDailyCash.cash_total || 0) + + parseFloat(reportDailyCash.reinforcements || 0) - + parseFloat(reportDailyCash.withdrawals || 0) + )}

+
+
+
+ + + + + + Vendas por Vendedor + + + + {reportDailyCash.bySeller?.length > 0 ? ( +
+ + + + Vendedor + Vendas + Dinheiro + Débito + Crédito + PIX + Combinado + Descontos + Total + + + + {reportDailyCash.bySeller.map((seller: any, i: number) => ( + + {seller.sold_by || "Sem vendedor"} + {seller.sale_count} + {formatCurrency(seller.cash_total)} + {formatCurrency(seller.debit_total)} + {formatCurrency(seller.credit_total)} + {formatCurrency(seller.pix_total)} + {formatCurrency(seller.combined_total)} + {parseFloat(seller.total_discount) > 0 ? `-${formatCurrency(seller.total_discount)}` : '-'} + {formatCurrency(seller.total)} + + ))} + + + + TOTAL + {reportDailyCash.sale_count} + {formatCurrency(reportDailyCash.cash_total)} + {formatCurrency(reportDailyCash.bySeller?.reduce((s: number, r: any) => s + parseFloat(r.debit_total || 0), 0))} + {formatCurrency(reportDailyCash.bySeller?.reduce((s: number, r: any) => s + parseFloat(r.credit_total || 0), 0))} + {formatCurrency(reportDailyCash.pix_total)} + {formatCurrency(reportDailyCash.combined_total || 0)} + {formatCurrency(reportDailyCash.bySeller?.reduce((s: number, r: any) => s + parseFloat(r.total_discount || 0), 0))} + {formatCurrency(reportDailyCash.total_sales)} + + +
+
+ ) : ( +

Nenhuma venda no período

+ )} +
+
+ + + + + + Listagem de Vendas + + + + {reportDailyCash.sales?.length > 0 ? ( +
+ + + + Venda + Hora + Cliente + Vendedor + Subtotal + Desconto + Total + Pagamento + + + + {reportDailyCash.sales.map((sale: any, i: number) => ( + + {sale.sale_number} + {sale.created_at ? new Date(sale.created_at).toLocaleTimeString('pt-BR', {hour: '2-digit', minute: '2-digit'}) : '-'} + {sale.customer_name || "Consumidor"} + {sale.sold_by || "-"} + {formatCurrency(sale.subtotal)} + {parseFloat(sale.discount_amount || 0) > 0 ? `-${formatCurrency(sale.discount_amount)}` : '-'} + {formatCurrency(sale.total_amount)} + + + {sale.payment_method === 'cash' ? 'Dinheiro' : + sale.payment_method === 'credit' ? 'Crédito' : + sale.payment_method === 'debit' ? 'Débito' : + sale.payment_method === 'pix' ? 'PIX' : + sale.payment_method === 'combined' ? 'Combinado' : + sale.payment_method || '-'} + + + + ))} + +
+
+ ) : ( +

Nenhuma venda registrada nesta data

+ )} +
+
+ + ) : ( + + +

{reportLoading ? "Carregando..." : "Selecione uma data e clique em Carregar para ver o movimento do dia"}

+
+
)} - - +
+ + + + +
+ Giro de Estoque + +
+
+ +
+ + + + Código + Produto + Categoria + Estoque Atual + Estoque Mínimo + Vendas 30d + Giro + + + + {reportStockTurnover.length === 0 ? ( + {reportLoading ? "Carregando..." : "Sem dados"} + ) : reportStockTurnover.map((row: any, i: number) => ( + + {row.code} + {row.name} + {row.category || "-"} + {parseFloat(row.current_stock || 0).toFixed(0)} + {parseFloat(row.min_stock || 0).toFixed(0)} + {row.sales_30d} + {parseFloat(row.turnover_ratio || 0).toFixed(2)} + + ))} + +
+
+
+
+
+ {/* ========== ABA COMISSÕES ========== */} @@ -4489,6 +5244,7 @@ export default function ArcadiaRetail() {
+
@@ -5675,12 +6431,24 @@ export default function ArcadiaRetail() { id="use-credit" checked={useCredit} onChange={(e) => { - setUseCredit(e.target.checked); - if (e.target.checked) { + const checked = e.target.checked; + setUseCredit(checked); + if (checked) { const maxCredit = Math.min(customerTotalCredit, cartSubtotal - cartDiscountTotal - tradeInCredit); setCreditAmountToUse(maxCredit); + if (paymentMethods.length > 0) { + const newTotal = cartSubtotal - cartDiscountTotal - tradeInCredit - maxCredit; + const totalPaid = paymentMethods.reduce((s, p) => s + p.amount, 0); + if (totalPaid > newTotal && paymentMethods.length === 1) { + setPaymentMethods([{ ...paymentMethods[0], amount: Math.max(0, newTotal) }]); + } + } } else { setCreditAmountToUse(0); + if (paymentMethods.length === 1) { + const newTotal = cartSubtotal - cartDiscountTotal - tradeInCredit; + setPaymentMethods([{ ...paymentMethods[0], amount: newTotal }]); + } } }} className="h-4 w-4" @@ -5704,6 +6472,10 @@ export default function ArcadiaRetail() { cartSubtotal - cartDiscountTotal - tradeInCredit ); setCreditAmountToUse(val); + if (paymentMethods.length === 1) { + const newTotal = cartSubtotal - cartDiscountTotal - tradeInCredit - val; + setPaymentMethods([{ ...paymentMethods[0], amount: Math.max(0, newTotal) }]); + } }} className="w-32" step="0.01" @@ -5722,6 +6494,19 @@ export default function ArcadiaRetail() {
+ {useCredit && creditAmountToUse > 0 && paymentMethods.length === 0 && ( +
+
+ Crédito aplicado: + - {formatCurrency(creditAmountToUse)} +
+
+ Saldo a receber: + {formatCurrency(cartTotal > 0 ? cartTotal : 0)} +
+
+ )} + {paymentMethods.length > 0 && (
{paymentMethods.map((pm, idx) => ( @@ -5760,24 +6545,45 @@ export default function ArcadiaRetail() {
))} -
- Total informado: - s + p.amount, 0) >= cartTotal ? "text-green-600 font-medium" : "text-orange-600 font-medium"}> + {useCredit && creditAmountToUse > 0 && ( +
+ Crédito aplicado: + - {formatCurrency(creditAmountToUse)} +
+ )} +
0) ? 'pt-2 border-t' : ''}`}> + Total em pagamentos: + s + p.amount, 0) >= (cartTotal > 0 ? cartTotal : 0) ? "text-green-600 font-medium" : "text-orange-600 font-medium"}> {formatCurrency(paymentMethods.reduce((s, p) => s + p.amount, 0))}
- {paymentMethods.reduce((s, p) => s + p.amount, 0) < cartTotal && ( -
- Falta: - {formatCurrency(cartTotal - paymentMethods.reduce((s, p) => s + p.amount, 0))} -
- )} - {paymentMethods.reduce((s, p) => s + p.amount, 0) > cartTotal && ( -
- Troco: - {formatCurrency(paymentMethods.reduce((s, p) => s + p.amount, 0) - cartTotal)} -
- )} + {(() => { + const totalEffective = cartTotal > 0 ? cartTotal : 0; + const totalPaid = paymentMethods.reduce((s, p) => s + p.amount, 0); + const remaining = totalEffective - totalPaid; + if (remaining > 0) { + return ( +
+ Saldo a receber: + {formatCurrency(remaining)} +
+ ); + } else if (remaining < 0) { + return ( +
+ Troco: + {formatCurrency(Math.abs(remaining))} +
+ ); + } else { + return ( +
+ Pagamento completo + +
+ ); + } + })()}
)} @@ -5837,7 +6643,7 @@ export default function ArcadiaRetail() { + + + + + @@ -6492,6 +7355,60 @@ export default function ArcadiaRetail() {
)} + {/* Checklist de Avaliação */} +
+ +
+ {[ + { key: "powerOn", label: "Liga normalmente" }, + { key: "screenOk", label: "Tela sem defeitos" }, + { key: "touchOk", label: "Touch funcionando" }, + { key: "buttonsOk", label: "Botões funcionando" }, + { key: "camerasOk", label: "Câmeras funcionando" }, + { key: "speakerOk", label: "Alto-falante OK" }, + { key: "microphoneOk", label: "Microfone OK" }, + { key: "chargingOk", label: "Carregamento OK" }, + { key: "wifiOk", label: "Wi-Fi funcionando" }, + { key: "bluetoothOk", label: "Bluetooth OK" }, + { key: "simOk", label: "Chip funcionando" }, + { key: "biometricOk", label: "Biometria OK" }, + { key: "batteryOk", label: "Bateria em bom estado" }, + { key: "sensorsOk", label: "Sensores funcionando" }, + ].map((item) => ( +
{ + setOsChecklistData(prev => ({ + ...prev, + [item.key]: prev[item.key] === true ? false : prev[item.key] === false ? undefined : true + })); + }} + data-testid={`checklist-${item.key}`} + > + {osChecklistData[item.key] === true ? ( + + ) : osChecklistData[item.key] === false ? ( + + ) : ( + + )} + {item.label} +
+ ))} +
+

Clique para alternar: sem avaliação → OK → com defeito

+
+ {/* Valores */}
@@ -6528,6 +7445,116 @@ export default function ArcadiaRetail() { rows={3} />
+ + {/* Peças Utilizadas */} +
+ + +
+
+ { + setOsItemSearch(e.target.value); + searchOsProducts(e.target.value); + }} + data-testid="input-os-part-search" + /> + {osItemResults.length > 0 && ( +
+ {osItemResults.map((product) => ( +
addOsItem(product)} + data-testid={`os-part-result-${product.id}`} + > +
+ {product.name} + ({product.code}) +
+
+ Estoque: {product.stockQty} + R$ {parseFloat(product.costPrice || 0).toFixed(2)} +
+
+ ))} +
+ )} +
+ setOsItemQuantity(parseInt(e.target.value) || 1)} + className="w-20" + placeholder="Qtd" + data-testid="input-os-part-qty" + /> +
+ + {loadingOsItems ? ( +
+ + Carregando peças... +
+ ) : osItems.length > 0 ? ( +
+ + + + + + + + + + + + + {osItems.map((item) => ( + + + + + + + + + ))} + + + + + + + + +
PeçaQtdUnitárioTotalStatus
+ {item.itemName} + {item.itemCode && ({item.itemCode})} + {item.quantity}R$ {parseFloat(item.unitPrice || 0).toFixed(2)}R$ {parseFloat(item.totalPrice || 0).toFixed(2)} + + {item.status === 'applied' ? 'Aplicada' : item.status === 'removed' ? 'Removida' : 'Pendente'} + + + {item.status === 'pending' && ( + + )} +
Total Peças:R$ {osItems.reduce((sum, i) => sum + parseFloat(i.totalPrice || 0), 0).toFixed(2)}
+
+ ) : ( +

+ Nenhuma peça adicionada. Use a busca acima para adicionar peças. +

+ )} +
)} @@ -6545,13 +7572,16 @@ export default function ArcadiaRetail() { if (!editingServiceOrder) return; try { + const hasChecklistEntries = Object.keys(osChecklistData).some(k => osChecklistData[k] !== undefined); const updateData: any = { status: osStatus, diagnosisNotes: osNotes, estimatedValue: osEstimatedValue || "0", evaluatedValue: osEvaluatedValue || "0", laborCost: osEvaluatedValue || osEstimatedValue || "0", - partsCost: "0", + partsCost: String(osItems.reduce((sum, i) => sum + parseFloat(i.totalPrice || 0), 0)), + checklistData: osChecklistData, + checklistCompletedBy: hasChecklistEntries ? "user" : null, }; if (editingServiceOrder.isInternal) { @@ -6586,6 +7616,133 @@ export default function ArcadiaRetail() { + {/* Modal Criar OS Manutenção a partir de Avaliação Aprovada */} + + + + + + Criar Ordem de Serviço + + + Criar OS de manutenção para o dispositivo avaliado + + + + {selectedEvaluation && ( +
+
+
+

IMEI

+

{selectedEvaluation.imei}

+
+
+

Dispositivo

+

{selectedEvaluation.brand} {selectedEvaluation.model}

+
+
+

Cliente

+

{selectedEvaluation.customerName}

+
+
+

Valor Avaliado

+

{formatCurrency(selectedEvaluation.estimatedValue || 0)}

+
+
+ +
+ + +
+ +
+ +