Mapa do painel — 69 fluxos
▸Começando
Do clique em "Criar conta" até estar logado no painel — direto ou por link de indicação.
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Acessa rallydevendas.com.br/registro OU chega por um link de indicação de um parceiro.
→
2Preenche e-mail, senha, nome, WhatsApp e nacionalidade (detectada pela localização).
→
3Clica em "Criar conta".
→
4Cai direto no painel já logado — sem etapa bloqueante de e-mail.
↓ o sistema reage por baixo
⚙️ O sistema faz
ValidaConfere o token de segurança e os dados; captura nacionalidade e país fiscal.
CriaGrava o usuário com senha criptografada.
VinculaSe veio por indicação, conecta vendedor e parceiro automaticamente (vira comissão pro indicador).
SessãoInicia a sessão e registra o cadastro para auditoria.
AvisaNotifica o admin do novo cadastro por Telegram.
⚠️ AtençãoE-mail duplicadoLimite de tentativas (anti-fraude)
02
🔌
Conectar conta Mercado Livre
Conectar sua conta do Mercado Livre com segurança para sincronizar anúncios e vendas. A conexão parte de /lojas; /contas/ml/connect é o endpoint OAuth interno (não é tela).
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Vai em Lojas → clica "Conectar Mercado Livre".
→
2Faz login no ML e autoriza o acesso (leitura e escrita).
→
3Volta pro Rally já com a conta conectada (nome e reputação aparecem).
↓ o sistema reage por baixo
⚙️ O sistema faz
ValidaConfere se você está logado e se o plano permite mais contas.
ProtegeGera um código de segurança único (PKCE) pra ninguém interceptar a conexão.
TrocaRecebe a autorização do ML e troca por um acesso permanente, renovado sozinho a cada 6h.
CriptografaGuarda o acesso criptografado (AES-256) — o token nunca fica em texto puro.
IndexaCria um índice rápido pra receber avisos de venda em tempo real.
🌐 Vai pro Mercado Livre / Pix
Autorização + troca de token no Mercado LivreConsulta seus dados de vendedor (nome, reputação)
⚠️ AtençãoToken expira a cada 6h (renova sozinho)Conta já conectada em outro usuário é recusada
▸Início
Fornecer ao vendedor uma visão centralizada de seu desempenho com KPIs, checklist onboarding, avisos de ação e atalhos rápidos.
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Acessa o painel pelo menu 'Início > Dashboard' ou abre /dashboard
→
2Visualiza seu progresso em cards-destaque: faturamento hoje, pedidos da semana, produtos/anúncios, faturamento do mês
→
3Verifica 3 alertas principais: pedidos Drop a pagar, anúncios sem vincular, contas ML desconectadas
→
4Completa itens do checklist onboarding (foto, WhatsApp, CPF, conexão ML, colaborador, primeiro anúncio)
→
5Consulta as notificações recentes e aciona atalhos rápidos (criar produto, novo anúncio, conectar ML, calcular preço, clonar anúncio, estoque, pedidos)
→
6Acompanha seu gráfico de receita (14 dias vs período anterior) e interage com seções colapsáveis (Gamificação, Pedidos, Gráfico, Ações Rápidas)
↓ o sistema reage por baixo
⚙️ O sistema faz
AutenticaValida que o usuário está logado e perfil preenchido. Se expirou trial gratuito, bloqueia acesso.
CarregaBusca métricas do vendedor: receita, pedidos, anúncios, saldo da carteira, contas ML vinculadas e seus status.
ComparaCalcula deltas de receita e pedidos (semana vs semana anterior, mês vs mês anterior) para mostrar crescimento.
Cria KPIsMonta 4 cards: faturamento hoje, pedidos semana, produtos/anúncios, faturamento mês com sub-métricas e progresso.
Verifica alertasIdentifica: custos Drop pendentes, anúncios desvinculados e contas com token expirado/revogado.
Monta checklistRetorna 7 etapas onboarding com status (feito/pulado): foto, WhatsApp, CPF, conexão ML, colaborador, sobre você, anúncio.
RenderizaCompõe a página com hero orientado por valor (Design Language v2), seções colapsáveis, notificações e atalhos contextuais.
⚠️ AtençãoContas ML com token expirado/revogado aparecem em alerta; vendedor deve reconectar via /contas/ml/connect para evitar pausa na sincronia de pedidos.Anúncios sem produto vinculado (unlinked) bloqueiam visibilidade completa; redirecionam para /central/anuncios.Usuários sem conta ML conectada veem CTA em destaque com wizard; sem ao menos uma loja, alguns widgets não carregam (crescimento, gráfico).
Assistente conversacional RAG em tempo real — responde perguntas sobre vendas, anúncios e reputação com dados reais do ML em segundos, usando sua chave de IA (BYOK: Claude, Gemini ou OpenAI) sem cobrar créditos do Rally.
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1No painel, clique em 'Assistente IA' no menu lateral (só aparece se configurou sua chave). A tela mostra histórico de conversas, seletor de modelo e dicas de uso.
→
2Cole sua chave em Perfil › Inteligência Artificial. Onde criar (Gemini é grátis): aistudio.google.com/apikey → 'Create API key' → chave AIza. Claude: console.anthropic.com/settings/keys · OpenAI: platform.openai.com/api-keys
→
3Selecione o modelo (Claude Sonnet, Gemini 2.5 Flash, etc.) — sistema faz fallback automático para chave do sistema se BYOK não configurada.
→
4Digite sua pergunta em português: 'Qual meu melhor anúncio?', 'Quantos pedidos tive essa semana?', 'Como criar um anúncio?' — a IA responde em segundos com contexto real dos seus dados.
→
5Para editar títulos/descrições de anúncios, use a IA de edição embutida na Central de Anúncios — também usa BYOK sem cobrar créditos.
→
6Histórico de conversas fica salvo no painel — cada sessão mostra perguntas anteriores e respostas para consulta posterior.
↓ o sistema reage por baixo
⚙️ O sistema faz
autentiaValida se o usuário está logado; bloqueia acesso a /concierge se não tiver BYOK ativo (redireciona para gate de configuração).
carregaRecupera histórico de solicitações anteriores do usuário (JSON persistente em storage/concierge_requests.json) — mais recentes primeiro.
processa-entradaRecebe mensagem do vendedor, valida tamanho (máx 2000 caracteres), anti-flood (20 segundos entre envios). Processamento é SÍNCRONO — sem fila.
RAG-contextoMonta contexto RAG em tempo real: vendas recentes, anúncios ativos, reputação atual, perguntas abertas, saldo de créditos — dados frescos por usuário.
resolve-chaveSe BYOK ativo: carrega chave criptografada (AES-256-GCM) e descriptografa. Fallback: chave do sistema (Claude/Gemini). Nenhum crédito consumido em BYOK.
gera-respostaEnvia ao Claude ou Gemini com contexto RAG completo; IA responde em streaming (tokens aparecem à medida que chegam). Sugere ações navegáveis (abrir wizard, ir para anúncio).
persisteSalva par pergunta+resposta no histórico local (IaChatHistoryService) — listado na tela em próximas visitas. Nenhum webhook ou e-mail — resposta está na tela.
🌐 Vai pro Mercado Livre / Pix
POST para Claude/Gemini/OpenAI com pergunta + contexto RAG (vendas, reputação, anúncios) — usa chave BYOK ou do sistema.Dados do ML são PRÉ-CARREGADOS via RAG antes de chamar a IA (sem chamada ML em tempo real durante a resposta).
⚠️ AtençãoSem BYOK e sem chave do sistema configurada: /concierge exibe gate de ativação. Configure em Perfil > Inteligência Artificial.RAG usa dados do cache local (ml_orders, announcements) — pedidos sincronizados há >1h podem não aparecer no contexto. Forçar sync antes de perguntas sobre dados recentes.Modelo selecionado precisa ter chave BYOK válida. Se retornar 429/401, trocar modelo (Gemini 2.5 Flash é gratuito com cota generosa).
▸Mercado Livre
O vendedor publica, edita e clona seus anúncios no Mercado Livre de forma centralizada, sincronizando dados em tempo real com a plataforma ML.
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Acessa a Central de Anúncios e vê a lista de todos seus anúncios ativos, pausados ou encerrados no Mercado Livre, filtrado por conta e status
→
2Clica em 'Criar novo anúncio' e passa por um assistente (wizard) visual: seleciona um produto do catálogo Rally, escolhe a conta ML, marca opções de frete e logística, preenche atributos obrigatórios e valida antes de publicar
→
3Ao publicar, o Rally envia o anúncio (fotos, título, descrição, preço) ao Mercado Livre via API; o anúncio fica ativo imediatamente e aparece na lista com status 'active'
→
4Clica em um anúncio para editar: ajusta preço, estoque, descrição, fotos ou status (pausar/retomar/encerrar) — o Rally sincroniza as mudanças com o ML em tempo real
→
5Usa 'Clonar' para duplicar um anúncio existente para outras contas ML, com opção de ajustar preço e saltar itens pausados
→
6Consulta o histórico de alterações de cada anúncio e verifica a saúde (score, restrições, elegibilidade para catálogo) diretamente na Central
→
7Acessa a aba 'Catálogo' para monitorar o Buy Box dos anúncios em catálogo: vê posição atual, concorrentes e preço vencedor em tempo real
→
8Clica em 'Aplicar agora' para ajustar o preço ao valor vencedor do Buy Box com 1 clique, ou usa o botão IA (ícone robô) para receber sugestão de preço com justificativa via BYOK
↓ o sistema reage por baixo
⚙️ O sistema faz
RedirecionarGET /anuncios redireciona permanentemente (301) para /central/anuncios — consolidação da listagem
CarregarCentral carrega anúncios ao vivo do Mercado Livre: faz autenticação OAuth, obtém token fresco, chama GET /users/{mluid}/items?status={filtro} para listar por status (active/paused/closed/under_review)
FiltrarAplica filtros avançados: por conta, status, score, logística (Full/Flex/Standard), catálogo vs. tradicional, tipo de listagem (premium/clássica) — tudo via JavaScript no client
CriarWizard POST /anuncios coleta produto local + conta ML + atributos obrigatórios, valida com /api/ml/rules/dry-run, sobe fotos (até 6 comprimidas + mais via segunda chamada), publica via POST /items ao ML
EditarPOST /anuncios/{id}/editar faz pre-flight (verifica se está em revisão/encerrado/com vendas), filtra campos bloqueados, envia PUT /items/{ml_id} com mudanças permitidas; se em revisão, aceita só descrição (PUT /items/{id}/description)
PublicarMlPublisher retry automático (até 4 tentativas): ajusta payload conforme erros ML, descarta campos inválidos, sobe fotos antes do POST /items, posta descrição após criação de item
ClonarPOST /anuncios/{id}/clonar-simples dispara CloneJobService para cada conta destino: cria novo anúncio com mesmo produto e dados, mantém histórico de origem em drop_origin
🌐 Vai pro Mercado Livre / Pix
POST /items — publica novo anúncio com título, descrição, preço, estoque, fotos, atributos, frete e logísticaPUT /items/{id} — atualiza preço, estoque, status ou detalhes do anúncio existente; bloqueado se anúncio está em revisão ou tem vendas em certas categoriasPUT /items/{id}/description — atualiza só descrição quando o anúncio está sob revisão ML (under_review)GET /users/{mluid}/items?status={status}&offset={offset} — lista anúncios da conta: ativos, pausados, encerrados ou em revisão; paginada com 50 itens por pagePOST /pictures — carrega fotos individuais e retorna IDs para referenciar no anúncioGET /items/{id} — sincroniza dados do anúncio (marketplace_data): status atual, vendas, visitas, saúde, logística, categoria
⚠️ AtençãoAnúncios em revisão (under_review) — só descrição é editável; tentar editar outros campos retorna erro ML 'item_status_change_not_allowed' e trava o formulárioAnúncios com vendas (has_bids=true) em categorias restritas — campo título, categoria e tipo de listagem ficam travados; ML recusa PUT. Usuário vê aviso 'anúncio com vendas' ao abrir ediçãoLimite de 6 fotos na primeira chamada — Rally faz segunda chamada POST /pictures + PUT para fotos extras (6-12); falha de upload não bloqueia publicação, mas anúncio fica sem fotos completas
Permitir que o vendedor configure NCM, CEST e origem dos produtos para destravar emissão automática de Notas Fiscais Eletrônicas.
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Acesse o menu 'Dados Fiscais' (grupo Mercado Livre) no painel
→
2Veja a lista de anúncios dividida em: ⚠ Sem NCM, ✓ Com NCM e Total
→
3Selecione um ou mais anúncios clicando nos checkboxes e clique em 'Editar Fiscais em Massa'
→
4Preencha os campos obrigatórios (NCM) e opcionais (CEST, CSOSN/CST, Origem) no modal
→
5O sistema valida o NCM em tempo real contra a tabela TIPI e mostra se é válido
→
6Clique em 'Aplicar' para enviar os dados selecionados ao Mercado Livre e gerar as NF-es
↓ o sistema reage por baixo
⚙️ O sistema faz
buscaSistema carrega todos os anúncios do vendedor e calcula totalizadores (com NCM, sem NCM, total)
filtraAgrupa anúncios por status de completude fiscal — exibe apenas os solicitados (incomplete, complete ou all)
validaQuando vendedor digita NCM, sistema faz consulta ao Brasil API para confirmar se o código existe na tabela TIPI
montaPrepara payload com dados fiscais (ncm, cest, csosn, cst, origin_type) dentro de tax_information
enviaFaz PUT para Mercado Livre em /items/fiscal_information/{item_id} com token OAuth da conta conectada
persisteSalva resposta do ML localmente em announcements.marketplace_data.tax_information com timestamp sync
notificaSe sucesso em lote, envia notificação ao vendedor via Telegram com contagem de anúncios atualizados
🌐 Vai pro Mercado Livre / Pix
GET https://brasilapi.com.br/api/ncm/v1/{ncm} — valida NCM contra TIPI (sem autenticação)PUT https://api.mercadolibre.com/items/fiscal_information/{item_id} — envia dados fiscais ao Mercado Livre (requer token OAuth do vendedor)
⚠️ AtençãoML não retorna NCM/CEST via API — dados são cadastrados apenas pelo vendedor; sem NCM correto, o anúncio fica bloqueado em 'invoice_pending' e não gera NF-eValidação NCM é apenas verificação de existência contra TIPI; dados financeiros (CSOSN/CST) ainda exigem conferência manual pela contabilidade do vendedor para consistência com regime tributárioRate limiting: aplicação em lote aguarda 150ms entre cada PUT (proteção contra throttling do ML); lotes de 500+ anúncios podem levar minutos
Visualizar, filtrar e gerenciar vendas do Mercado Livre — desde a criação do pedido até a entrega — acompanhando etiquetas, notas fiscais e rastreio de envio.
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Vendedor acessa menu Painel > Vendas (no grupo Mercado Livre) e cai em /central/vendas
→
2Vê uma lista de pedidos com filtros por data, status, conta ML e busca por produto/comprador; KPIs mostram total de vendas, faturamento total, vendas e receita de hoje
→
3Clica em um pedido para ver detalhe — status, comprador, endereço, número de rastreio; visualiza timeline com eventos (pedido criado, pagamento confirmado, postado, entregue)
→
4Após o pagamento, baixa etiqueta de envio via botão 'Etiqueta' (entra /ml/etiqueta?ship_id=X) e imprime
→
5Digita ou escaneia o número de rastreio na etiqueta no sistema do Mercado Livre para atualizar o status
→
6Acessa /ml/nf para baixar a Nota Fiscal emitida (ou /pedidos/declaracao se for CPF sem NF); acompanha envio via aba 'Envios & Rastreio' que mostra carrier, datas e status em tempo real
↓ o sistema reage por baixo
⚙️ O sistema faz
sincronizarSistema sincroniza pedidos via API do Mercado Livre a cada webhook ou cron, consultando os endpoints /orders/search (filtros: paid, confirmed, payment_in_process, cancelled) e /shipments/{id} para obter status, datas e dados do transportista.
armazenarTodos os dados de vendas são salvos em ml_orders.json local (banco de dados por usuário) com campos: order_id, status, date_created, date_closed, date_shipped, itens, comprador, envio, rastreio e valores (total_amount, comissão).
listarAo abrir /central/vendas, carrega os pedidos do JSON local, aplica filtros (data, status, conta, busca), pagina em 50 itens e calcula KPIs (total de vendas, faturamento total, hoje).
enriquecerPara cada pedido, busca a foto do produto no banco local de anúncios; conecta o account_id à conta ML (nickname) para exibir em qual loja foi vendido (suporta vendedor com múltiplas contas).
detalharAo clicar em /pedidos/{id}, carrega o registro, constrói uma timeline com 5-6 marcos (pedido criado, pagamento confirmado, envio gerado, postado, entregue, cancelado) e calcula prazos (73h para postar após pagamento, alertas de atraso em reputação).
etiquetaVia /ml/etiqueta?ship_id=X, valida o status+substatus do envio (a etiqueta só existe em ready_to_ship: ready_to_print/printed/in_hub) e baixa o PDF da ML — inclusive Flex (self_service), que sai imediato. Captura proativa: webhook + cron pré-baixam a etiqueta assim que fica pronta, então abre instantânea do cache. Suporta download em lote.
rastreioExibe número de rastreio + carrier (Correios, Loggi, etc) vindo do shipment; para Flex (LogBG/Fretecentro), integra webhook de transportista que atualiza status em tempo real com GPS.
🌐 Vai pro Mercado Livre / Pix
GET /orders/search?seller={mlUserId}&order.status=paid,confirmed,payment_in_process,cancelled — busca todas as vendas com status ativoGET /shipments/{shipmentId} — obtém detalhes do envio (status, tracking_number, carrier, logistic_type, receiver_address)GET /shipments/{shipmentId}/label — baixa etiqueta em PDF/ZPL para impressão (via LabelService)GET /invoices/{orderId} — tenta buscar NF-e emitida (se houver CNPJ no vendedor)
⚠️ AtençãoStatus pode ficar desatualizado se webhook do ML falhar — existe re-sync forçada (force=true) para pedidos parados 72h+; vendedor vê 'Enviado' local mas ML ainda diz 'Preparando'Etiqueta só existe quando o ML libera (ready_to_ship). O botão de download só aparece quando ela está REALMENTE pronta — antes disso o sistema mostra o estado certo (Aguardando ML / Aguardando NF / Despachado / Entregue) em vez de um botão que dá erro. Para o que ainda não saiu, há 'Pedir etiqueta ao vendedor' por WhatsApp. Cross-docking (xd_drop_off) pode segurar horas — um monitor diário avisa se uma etiqueta pronta parar de vir.NF-e só aparece para vendedores com CNPJ que emitiram via integração ou automático ML; para CPF ou sem integração, oferece Declaração de Conteúdo (PDF simples) como fallback
Consultar e enriquecer a base de compradores do Mercado Livre com dados pessoais (nome real, telefone, CPF) via API FonteData ou BigDataCorp, mantendo histórico de contatos (follow-ups).
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1O vendedor acessa o menu Clientes no painel e vê a lista de todos os compradores que fizeram pedidos no Mercado Livre, filtrados por nome, tag, sexo, classe social e ordenados por recência, valor ou quantidade de pedidos
→
2Clica em um comprador para ver seu perfil completo (nome, apelido, telefone, e-mail, endereço, pedidos recentes, histórico de contatos e dados enriquecidos como idade, profissão, classe econômica)
→
3Edita o perfil manualmente (nome, telefones, e-mails, tags, aniversário, notas) ou clica em Enriquecer para buscar dados reais do CPF na FonteData (R$ 1,06/consulta, descontos em volume)
→
4Consulta e seleciona o resultado encontrado para preencher automaticamente telefones, e-mails, endereço, classe social, renda, profissão e idade
→
5Registra follow-ups (ligação, mensagem, e-mail, visita) com data e nota para manter histórico de contatos com cada comprador
→
6Exporta a base inteira em CSV, XLSX ou TXT (respeitando filtros ativos) para análise em planilha ou importação em CRM externo
↓ o sistema reage por baixo
⚙️ O sistema faz
SincronizaImporta automaticamente compradores do Mercado Livre cada vez que há novo pedido (buyer_ml_id como chave de deduplicação). Campos protegidos (nome, telefone, e-mail, tags, aniversário, notas) nunca são sobrescritos pelo auto-sync.
AutenticaçãoVendedor vê só seus clientes; gerente vê dos seus vendedores; admin vê todos. Acesso por role na sessão.
FiltraBusca em tempo real por nome/apelido/e-mail/telefone, agrupa por tags, filtra por sexo/classe (dados enriquecidos) e ordena por data/valor/quantidade.
EnriqueceFonteData (CPF) ou BigDataCorp (nome+CEP) consultam base pública e retornam nome real, telefones com WhatsApp, e-mails, endereço, sexo, idade, profissão, classe social e signo. Cache local: se já enriquecido, devolve sem cobrar novamente.
EditaSalva manualmente nome, lista de telefones/e-mails, tags, aniversário (DD/MM ou DD/MM/AAAA), notas (máx 1000 chars). Registra quem editou (autor, role, timestamp) no log de auditoria (últimas 60 mudanças).
Follow-upsAdiciona notas de contato com tipo (ligação/mensagem/e-mail/visita/outro), data (padrão=hoje) e descrição. Remove por ID. Exibe mais recentes primeiro.
ExportaMonta CSV/TXT/XLSX com 20 colunas (nome, CPF, telefones, e-mails, endereço, classe, renda, profissão, idade, sexo, signo, aniversário, tags, contagem de pedidos, total gasto, última compra, loja, vendedor). Respeita todos os filtros aplicados. XLSX gerado com ZipArchive sem dependências externas. BOM UTF-8 em todos os formatos.
🌐 Vai pro Mercado Livre / Pix
POST https://app.fontedata.com/api/v1/consulta/cadastro-pf-plus (X-API-Key header) — busca por CPF (11 dígitos) e retorna nome, telefones com tipo/operadora/WhatsApp, e-mails, endereço, profissão (CBO), classe social, renda estimada, idade, sexo, aniversário, signo e saldo restante em R$POST https://plataforma.bigdatacorp.com.br/people (headers: TokenId, AccessToken) — busca por nome + CEP e retorna lista com até 5 resultados (nome, CPF, telefones, e-mails). Query format: 'name:NOME address_zipcode:CEP'
⚠️ AtençãoFonteData e BigDataCorp usam modelo de crédito (BYOK: Bring Your Own Key). Cada consulta FonteData custa ~R$ 1,06. Saldo consultado e cacheado localmente. Se saldo insuficiente, exibe URL pra top-up.Campos enriquecidos (class, renda, profissão, idade, sexo, signo) ficam no campo enriched[] e são consultados na view (show.php) — a exportação também os inclui.CPF é campo sensível (LGPD). Só é consultado se preenchido manualmente pelo vendedor; nunca é enviado a BigDataCorp (nome+CEP é alternativa). Log de auditoria (edit_log) registra todas as mudanças manuais.
Centralizar e responder perguntas, avaliações, mensagens pós-venda, reclamações e devoluções do Mercado Livre em um único painel por aba.
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Acessa a Central de Comunicação no menu lateral do painel
→
2Seleciona uma aba: Perguntas, Avaliações, Mensagens, Reclamações ou Devoluções
→
3Filtra por conta Mercado Livre conectada ou visualiza todas as contas
→
4Para Perguntas e Avaliações: responde usando templates rápidos ou texto livre; sistema marca como respondida e publica na ML
→
5Para Mensagens: abre thread conversa clicando no item, lê histórico completo, responde texto ou envia mídia (imagem/vídeo)
→
6Para Reclamações: verifica motivo e etapa, responde para resolver ou escala para mediação ML; para Devoluções: monitora status (aberta/reembolsada/fechada)
↓ o sistema reage por baixo
⚙️ O sistema faz
RenderizaCarrega 5 abas (Perguntas, Avaliações, Mensagens, Reclamações, Devoluções) conforme permissões do plano do usuário; exibe seletor de contas ML ativas
BuscaChama API Mercado Livre para sincronizar dados: perguntas não respondidas, avaliações, threads de conversa, reclamações abertas/fechadas, devoluções em processamento
FiltraAplica filtros na tela: status de resposta (perguntas/avaliações), conta selecionada, período (últimos 90 dias)
NotificaWhatsApp transacional via MlEventWaNotifier para pergunta nova, mensagem pós-venda, reclamação (afeta reputação), devolução; sincronização de digest 14h para gerentes/fornecedores/admins
RespondePOST para Mercado Livre: resposta de pergunta (até 2000 caracteres), reply em conversa (com suporte a mídia), mensagem em reclamação, resposta avaliação; sistema publica direto na ML
EscalonaPara reclamações abertas: envia mediation request para Mercado Livre; sistema rastreia estágio (buyer_response, seller_response, mediation_accepted) e deadline
ExportaCSV/TXT de cada aba contendo: perguntas (ID, texto, data, status), mensagens (pack, unread, data), reclamações (ID, motivo, etapa, prazo), devoluções (pedido, motivo, status), avaliações (rating, resposta, data)
🌐 Vai pro Mercado Livre / Pix
GET /questions/search (Perguntas não respondidas com status=UNANSWERED ou todas com status=ANSWERED)POST /questions/{question_id}/answers (Enviar resposta de pergunta)POST /questions/{question_id}/hide (Ocultar pergunta como spam)DELETE /questions/{question_id} (Deletar pergunta permanentemente)GET /reviews (Avaliações de itens publicados, com rating aggregation: positive/neutral/negative)POST /reviews/{review_id}/answer (Responder avaliação)GET /messages/packs (Threads de conversa pós-venda com histórico)POST /messages/packs/{pack_id}/sellers/{seller_id}/messages (Enviar mensagem em conversa)GET /claims/search com type=claims (Reclamações abertas com motivo, etapa, deadline)POST /claims/{claim_id}/messages (Responder reclamação)POST /claims/{claim_id}/mediation (Escalar reclamação para mediação Mercado Livre)GET /claims/search com type=returns (Devoluções em processamento com status refunded/closed)
⚠️ AtençãoSincronização: perguntas/reclamações chegam a cada ~5 min via webhook ou manual (botão 'Atualizar agora'); se muita atividade, retardo é esperado — click sync forçado para obter dados frescos imediatamenteWhatsApp: notificações transacionais dependem de Blipei configurado no /perfil; killswitch global em /logs/.broadcast_paused pausa todas as notificações; opt-in por user (wa_ml_*_notif_enabled) desativa por tipo de eventoMediação ML: reclamação escalada passa por estágios (buyer_response → seller_response → mediation_accepted); prazo rigoroso — atraso na resposta desfavorece vendedor; sistema marca deadline em vermelho; responder rápido afeta reputação
10
📣
Marketing (Mercado Ads)
Gerenciar campanhas de Product Ads (publicidade paga) no Mercado Livre para impulsionar vendas com controle de orçamento e métricas de performance (impressões, cliques, ROAS).
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Acessa Menu Lateral > Mercado Livre > Marketing (tela carrega Central de Marketing).
→
2Na aba 'Product Ads', visualiza campanhas ativas e pausadas com resumo de anúncios e orçamento/dia de cada uma.
→
3Clica em 'Nova Campanha', preenche nome e orçamento diário, e envia — a campanha aparece em tempo real na lista.
→
4Expande uma campanha e cola o ID do anúncio (MLB...) para adicionar itens — sistema sincroniza foto e título do ML automaticamente.
→
5Ativa/pausa campanhas ou anúncios individuais com clique, podendo também remover itens da campanha.
→
6Clica em 'Métricas (30d)' para visualizar impressões, cliques, custo total, CTR, vendas geradas e ROAS da campanha.
↓ o sistema reage por baixo
⚙️ O sistema faz
AutenticaValida token OAuth do vendedor com Mercado Livre e resolve advertiser_id da conta.
SincronizaBusca lista de campanhas da API v2 do ML (/marketplace/advertising/MLB/advertisers/{id}/product_ads/campaigns).
RenderizaCarrega view Alpine.js com campanhas em JSON; vendedor vê resumo: status (ativo/pausado), quantidade de anúncios, orçamento/dia.
CriaPOST para ML com nome, orçamento diário, estratégia de lucratividade e ACOS alvo (15% padrão) — sistema retorna campaign_id.
GerenciaAlterna status (pause/resume) e atualiza orçamento via PUT na API v2; remove anúncios via DELETE.
ColetaRecupera métricas dos últimos 30 dias (impressões, cliques, custo, CTR, pedidos, ROAS) e calcula índices de eficiência.
ExportaGera CSV ou TXT com relação de campanhas, status e orçamento para análise offline.
🌐 Vai pro Mercado Livre / Pix
GET /users/me — resolve ID do vendedor autenticado.GET /advertising/advertisers?product_id=PADS — obtém advertiser_id para Product Ads.GET /marketplace/advertising/MLB/advertisers/{id}/product_ads/campaigns — lista campanhas com IDs, names, budgets, status.POST /marketplace/advertising/MLB/advertisers/{id}/product_ads/campaigns — cria nova campanha (name, budget, status, strategy, roas_target — UI ainda coleta ACOS, sistema converte pra ROAS = 100/ACOS antes de enviar; ML exige roas_target desde 08/12/2025).PUT /marketplace/advertising/MLB/advertisers/{id}/product_ads/campaigns/{campaign_id} — altera status (active/paused) e budget.GET /marketplace/advertising/MLB/advertisers/{id}/product_ads/campaigns/{id}/ads — lista anúncios dentro de uma campanha.POST /marketplace/advertising/MLB/advertisers/{id}/product_ads/campaigns/{id}/ads — adiciona item (MLB) à campanha.PUT /marketplace/advertising/MLB/advertisers/{id}/product_ads/campaigns/{id}/ads/{ad_id} — pausa/ativa um anúncio.GET /marketplace/advertising/MLB/advertisers/{id}/product_ads/campaigns/{id}/metrics?date_from=X&date_to=Y — métricas de campanha (impressões, cliques, custo, ROAS).
⚠️ AtençãoToken pode expirar: sistema tenta refresh automático com Mercado Livre; se falhar, vendedor vê erro e precisa reconectar conta em /lojas.ACOS alvo (15%) é default, mas deve ser ajustado conforme margem do produto — anúncios com ACOS alto (>25%) podem não ser rentáveis; sem custo cadastrado, sistema mostra 'Receita' não 'Lucro'. Internamente o ACOS é convertido pra ROAS (100/ACOS) antes de mandar pro ML — o campo acos_target foi descontinuado na criação em 24/02/2026.Adicionar mesmo MLB em duas campanhas simultâneas pode causar conflito de orçamento no ML; sistema não previne, vendedor deve atentar-se a overlaps.
Monitorar a saúde operacional das contas Mercado Livre do vendedor visualizando reputação, histórico de 60 dias, taxas de cancelamento e atraso para identificar problemas e solicitar limpeza de vendas comprometidas.
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Acessa o painel e clica em 'Saúde da Loja' no menu do grupo 'Mercado Livre' (ícone: pacote)
→
2Visualiza um painel com 5 KPIs: contas críticas (vermelho), alertas (laranja), reconectadas (amarelo), pedidos atrasados e cancelados nos últimos 60 dias
→
3Consulta a tabela de lojas mostrando: vendedor, conta ML, reputação em termômetro de 5 barras coloridas, total de vendas, reclamações, atrasos, cancelados e taxa de problema (%)
→
4Filtra por criticidade (crítico, alerta, atenção, reconectar, OK) ou por vendedor para encontrar contas problemáticas
→
5Clica em uma linha da tabela para expandir detalhes da conta, visualizar histórico de problemas e, se necessário, solicitar remoção de venda comprometida (paga R$25 ao Rally)
→
6Exporta o relatório em CSV (Excel) ou TXT (legível por vendedor) para análise externa ou auditoria
↓ o sistema reage por baixo
⚙️ O sistema faz
RecuperaBusca todas as contas Mercado Livre vinculadas (respeitando escopo: vendedor vê só suas, gerente vê sua carteira, admin vê todas)
SincronizaConsulta dados oficiais do Mercado Livre: reputação do vendedor (nível de termômetro), transações completadas, canceladas, reclamações (claims), atrasos (handling_time), pedidos dos últimos 60 dias via API /users/{mlUserId}
ValidaVerifica se os dados estão frescos (sync < 7 dias) ou desatualizados (token expirado ou sincronização não feita); marca como 'reconectar' se precisar renovar credenciais
CalculaComputa taxa de problema: (reclamações + atrasos + cancelados) / total de pedidos, em porcentagem
ClassificaAtribui criticidade a cada conta: crítico (reputação vermelha ou >5% problemas), alerta (laranja ou 2-5%), atenção (amarela), reconectar (dados velhos), OK (verde e <2%)
ConsolidaAgrupa métricas por conta ML: uma linha por conta, mesmo que vendedor tenha múltiplas. Computa KPIs globais (total de contas críticas, alertas, pedidos atrasados em toda base)
FiltraRestringe visibilidade por multi-tenant: vendedor vê apenas seus dados; gerente vê bindings; admin vê tudo conforme permissão; remove contas sem reputação útil (termômetro vazio)
🌐 Vai pro Mercado Livre / Pix
Reputação do vendedor: GET /users/{mlUserId} para obter seller_reputation.metrics (claims, cancellations, delayed_handling_time) e nível de termômetro (level_id: red, orange, yellow, green)Programas Decola: GET /users/{mlUserId}/programs para verificar se o vendedor é elegível ao Programa Decola (acelerador para novos sellers)Sincronização de pedidos: recupera lista de vendas do Mercado Livre com status (paid, shipped, delivered, cancelled), datas e comissões armazenadas em cache local (não faz chamada direta aqui, usa dados pré-sincronizados)
⚠️ AtençãoContas sem reputação ML: lojas recém-criadas ou sem histórico não aparecem no relatório porque o termômetro está vazio — dados não são úteis para análise operacionalDesincronização: token expirado ou sincronização > 7 dias não mostra dados confiáveis; sistema avisa 'RECONECTAR' e tenta renovação automática, mas vendedor pode precisar reconectar manualmenteComissão e frete desconhecido: métricas de custo (frete, comissão real) podem ser estimadas em vez de oficiais se a sincronização não capturou todos os pedidos — recomenda-se vincular produtos aos custos unitários para precisão
Pausar todos os anúncios ativos do vendedor no Mercado Livre com um clique, preservando configurações e permitindo reativação automática na data agendada.
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Acessa o menu Mercado Livre → Modo Férias (ou /ferias direto) e visualiza o estado atual dos seus anúncios
→
2Define quando volta viajando (data e hora) ou deixa vazio para reativar manualmente — ambos opcionais
→
3Escreve mensagem automática personalizada para responder compradores durante o período (ex: 'Volto em 15/05')
→
4Clica 'Ativar Modo Férias' e confirma — todos os anúncios pausam em segundos no Mercado Livre
→
5Sistema reativa automaticamente na data escolhida (ou vendedor clica 'Reativar agora' em qualquer momento
→
6Vê resumo de quantos anúncios foram pausados e quantos voltaram ao ar
↓ o sistema reage por baixo
⚙️ O sistema faz
AutenticaVerifica login do vendedor e carrega permissões de acesso ao painel
CarregaBusca todos os anúncios ativos nas contas Mercado Livre conectadas do vendedor
PausaEnvia requisição PUT a cada anúncio ativo para o Mercado Livre (status_change=paused) agrupando por conta para reusar tokens de acesso
PersisteSalva estado atual da pausa (data, mensagem, quais anúncios foram pausados) em arquivo JSON local do usuário
ReativaAo clicar reativar ou na data agendada, envia PUT ao Mercado Livre (status_change=active) restaurando anúncios ao estado anterior
AgendaCron job a cada hora verifica se algum usuário tem data de retorno no passado e dispara reativação automática
NotificaRetorna contagem de sucesso (X pausados, Y falharam) e exibe alerta visual no painel informando quantos anúncios foram reativados
🌐 Vai pro Mercado Livre / Pix
PUT /items/{item_id} com status_change=paused (pausar anúncio no Mercado Livre)PUT /items/{item_id} com status_change=active (reativar anúncio no Mercado Livre)GET /users/{user_id}/items (listar anúncios ativos da conta para identificar quais pausar)
⚠️ AtençãoTimeout ao pausar muitos anúncios: se vendedor tiver mais de 200 anúncios ativos, o limite de 180 segundos pode ser insuficiente — considerar fila assíncronaDesincronização silenciosa: anúncio pode ficar pausado no Rally mas ativo no ML se chamada à API falhar — histórico de erro é limitado a 10 itensReativação automática não dispara: se cron job não estiver rodando ou se vendedor tiver plano expirado, anúncios não reativam mesmo após data agendada
Gerenciar parceiros transportadores para entregas Flex e despachos on-demand (Frete Center, Flex, Uber Direct).
📸 Tela real · clique pra ampliar
👤 Você faz e vê
11. Acesse o menu Mercado Livre > Transportadoras (grupo oculto, em evolução) ou direto em /painel/transportadoras.
→
22. Explore o Catálogo de transportadoras: veja regiões, preços e avaliações. Clique em uma oferta para mais detalhes.
→
33. Vincule uma transportadora: obtenha um código de 8 caracteres da transportadora parceira e cole no modal ou acesse /painel/transportadoras/vincular?code=XXXXX.
→
44. Gerencie prioridades: com múltiplas transportadoras vinculadas, o sistema pedirá que você escolha qual notificar para cada pedido em /painel/pedidos.
→
55. Acompanhe dispatches: veja status (pendente, aceito, em rota, entregue), preço cobrado e pagamentos PIX por transportadora em /painel/transportadoras/{id}/dispatches.
→
66. Declare lotes pro hub consignado: em /painel/transportadoras/lotes, crie lotes de anúncios com quantidade esperada. O operador do hub confirma recebimento no scanner.
↓ o sistema reage por baixo
⚙️ O sistema faz
listarSistema busca todas as transportadoras vinculadas do vendedor (ativas), calcula KPIs: dispatches deste mês, quantidade em aberto e valor PIX a pagar.
validarSistema valida código de convite: checa cache local (CarrierInviteCode) ou consulta Frete.Center API para confirmar se código existe e não expirou (janela 72h).
vincularSistema cria vínculo (CarrierLink) entre vendedor e transportadora. Armazena PIX, chave tipo, e marcadores de hub consignado.
despacharQuando vendedor marca pedido para dispatch, sistema consulta Frete.Center: cotação de frete, aceita/recusa, rastreia status em tempo real (pending → picked_up → in_transit → delivered).
conciliarSistema sincroniza descontos de frete ML com despachos Flex: se ML pagou frete direto, marca como pago; se Flex cobrou, gera débito PIX pendente.
receberOperador do hub scaneia etiqueta de lote (ASN/WRO). Sistema registra recebimento e move estoque do nó origin_node_id para nó do hub automaticamente.
fecharSistema agrupa dispatches pagos por transportadora, gera comprovante PIX (upload para auditoria) e marca período como fechado.
🌐 Vai pro Mercado Livre / Pix
Frete Center API: valida código de convite (GET /carriers/invite-code/{code}).Frete Center API: busca transportadora por ID ou slug para catálogo público.Frete Center API: estima frete e cria dispatch (POST /dispatches), rastreia status.Mercado Livre API: verifica se pedido tem frete cobrado ou grátis (buyer paid frete), integra em reconciliação.Webhook Frete Center: recebe eventos de dispatch (status updates: picked_up, in_transit, delivered, refused) em tempo real.
⚠️ AtençãoMenu Transportadoras está oculto no painel (comentado em _sidebar_painel.php). Acessar direto em /painel/transportadoras exige plano 'frete_center_flex_dispatch' ativado.Com 2+ transportadoras vinculadas, cada pedido Flex novo exige escolha manual em /painel/pedidos. Auto-dispatch só funciona com 1 transportadora ativa; desvincule a segunda para retomar automático.Reconciliação: se ML descontou frete (buyer paid), mas Flex tambem cobrou, há risco de dupla cobrança. Sistema marca como 'pago' somente se ambas as fontes confirmam.
Vendedor visualiza fechamentos semanais consolidados de dispatches Flex por transportadora, localiza a chave PIX, efetua o pagamento direto e comprova o envio. (grupo oculto, em evolução — rotas existem mas o item de menu está comentado em _sidebar_painel.php; acesso direto em /painel/transportadoras/fechamentos.)
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Acessa o painel e clica em 'Fechamentos PIX' no menu Mercado Livre
→
2Vê lista de fechamentos por transportadora com período, número de dispatches e valor devido
→
3Localiza a chave PIX da transportadora (CPF/CNPJ/Telefone/Email) e realiza pagamento via seu banco
→
4Anexa o comprovante do PIX (foto ou PDF) no formulário de cada fechamento
→
5Aguarda confirmação da transportadora no painel Frete.Center; seu status muda para 'Pago'
↓ o sistema reage por baixo
⚙️ O sistema faz
consolidaSistema agrupa todos os dispatches Flex com pagamento aberto por transportadora e período ISO (semanal), executado diariamente via cron
calculaPara cada grupo (transportadora + semana), soma a quantidade de deliveries e o valor total devido
persisteCria ou atualiza registro de FlexSettlement no JSON DB do vendedor com status inicial 'aberto'
exibeCarrega lista de fechamentos da tela, mapeando PIX e dados da transportadora via CarrierLink
recebeValida upload de comprovante (PDF/PNG/JPG até 5MB) e armazena em storage/uploads/flex_receipts
marcaRegistra URL do comprovante no FlexSettlement como 'parcial' até transportadora confirmar em Frete.Center
confirmaTransportadora marca como pago no seu painel externo (webhook ou integração); status muda para 'pago' com timestamp
🌐 Vai pro Mercado Livre / Pix
Nenhum — PIX é direto entre vendedor e transportadora; Rally não toca no dinheiro
⚠️ AtençãoFechamentos são calculados apenas se houver dispatches Flex com status payment.status='open'; sem Flex ativo, a tela não mostra nadaComprovante é opcional no fluxo atual (marcado como 'parcial'); apenas anexo não força status 'pago' — requer confirmação da transportadora no Frete.Center
▸Vitrine
Vendedor navega e gerencia produtos de seus fornecedores drop vinculados para publicar no Mercado Livre com precificação automática.
painel.rallydevendas.com.br/fornecedorespainel.rallydevendas.com.br/fornecedores/{id}/vitrinedrop.rallydevendas.com.br/vitrine
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Vendedor acessa 'Fornecedores' no painel e vê lista dos seus fornecedores drop vinculados (com nome, logo e pontuação)
→
2Clica em um fornecedor para entrar em 'Minha Vitrine' e visualiza o catálogo completo (fotos e nome dos produtos; preço oculto até acionar detalhes)
→
3Usa filtros (busca, categorias, estoque) e visualiza dados de custo + margem + estoque para cada produto via modal de detalhe
→
4Define preferências globais de precificação: margem (%), tipo de anúncio (bronze/ouro/premium), frete grátis e promoção De/Por
→
5Seleciona produtos (checkbox em lote) e clica 'Anunciar', OU clica botão de publicação individual (1-clique) para enviar ao ML imediatamente
→
6Vê confirmação de sucesso com URL do anúncio e feedback de erros (categoria faltante, custo zero, etc.)
↓ o sistema reage por baixo
⚙️ O sistema faz
buscarCarrega lista de fornecedores vinculados ao vendedor com status, logotipos e pontuação
filtrarAplica categorias, busca por nome/SKU e filtra produtos ativos (sem pausados/inativos da vitrine)
carregar_detalhesBusca dados completos do produto (fotos, EAN, dimensões, descrição) via endpoint /produto/{pid} ao abrir modal
ler_preferênciasRecupera configurações salvas do vendedor (margem, tipo de anúncio, frete grátis, promoção) do banco de dados
calcular_preçoAplica fórmula: Preço ML = Custo + Frete Médio, multiplicado por (1 + margem%), desconta taxa do Mercado Livre
publicar_mlEnvia anúncio ao Mercado Livre via API (fotos, dados, preço, atributos); retorna MLB (ID do item) e link permanente
persistir_prefsArmazena preferências de margem, tipo de anúncio e modo de frete para reuso em próximos anúncios deste fornecedor
🌐 Vai pro Mercado Livre / Pix
POST para Mercado Livre: envia título, descrição, categoria, preço, foto (upload base64), atributos obrigatórios; retorna MLB + permalinkGET de dados de catálogo: valida NCM, consulta elegibilidade Flex, aplica regras de atributos obrigatórios do marketplace
⚠️ AtençãoContrato não aceito: vendedor vê modal de cláusulas antes de publicar; fluxo bloqueia até aceitar digitalmente (IP + timestamp auditado)Produto com dados incompletos (categoria não atribuída, custo zero, fotos ausentes): sistema exibe aviso de 'prontidão' (contador verde) e impede publicação até completarVinculação expirada ou plano sem permissão 'view_prices': vitrine pública oculta preços; painel do painel mostra erro 'Fornecedor não vinculado' se acessar rota diretamente
Permitir que o vendedor configure e publique sua loja virtual pública com produtos curados, blog e domínio próprio, com visibilidade controlada por slug único e cidades.
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Vendedor acessa Perfil > Vitrine Pública no painel e preenche nome da loja, descrição, logo, cover e cores
→
2Configura contato (WhatsApp/telefone), horário de funcionamento, localidade e redes sociais opcionais
→
3Faz upload de domínio próprio (A+CNAME) e aguarda verificação da Hostinger/Cloudflare
→
4Curada produtos: liga/desliga anúncios do Mercado Livre visíveis e adiciona produtos próprios com fotos
→
5Publica posts no blog interno (suporta SEO score, imagens, tags)
→
6Acessa /loja/{slug} ou domínio próprio pra visualizar a vitrine ao vivo
↓ o sistema reage por baixo
⚙️ O sistema faz
GerarSistema cria slug único automático ao salvar config da vitrine (baseado em shop_display_name ou nome do user)
PersistirSalva todas as configurações (nome, descrição, logo, cover, cores, horas, categorias, contato) na tabela user com prefixo shop_*
IndexarCarrega anúncios ativos do user (ML ou vitrine própria) filtrando por status e flag vitrine_visible
BuscarAPI pública busca lojas por cidade, categoria ou termo, com facetas de filtro e pagination (24 itens/página)
RenderizarVitrine pública (VitrineController) renderiza HTML da loja com hero customizável, catálogo, blog, contactos e links legais
Verificar domínioSistema testa A+CNAME via Hostinger ou Cloudflare SaaS, aguarda SSL pronto, ativa quando verified_at não nulo
Gerar feedCria XML feed.xml para Google Shopping com todos os produtos visíveis (invalidado ao mudar config/produtos)
🌐 Vai pro Mercado Livre / Pix
GET /api/v2/items — busca anúncios ML do user para exibir na vitrine (paginado, com imagens)GET /api/v2/sites/MLB/reviews — busca avaliações do vendedor no Mercado Livre pra exibir na vitrine (reputação)GET /flex/v1/availability — verifica cobertura Flex por CEP pra avisar cliente sobre entrega express
⚠️ AtençãoDomínio próprio requer 2-3 dias após verificação da DNS para SSL ficar pronto (feedback claro no painel durante espera)Produtos ML continuam visíveis mesmo que desativados no Mercado Livre — flag vitrine_visible desacopla a curadoria da vitrine da vida real do anúncioFeed XML em cache pode ficar stale se vendedor edita produto rápido demais — cache invalidação ocorre ao salvar config
18
🖼️
Produtos da Vitrine
O vendedor seleciona e gerencia produtos para sua vitrine pública, curando anúncios do Mercado Livre e adicionando produtos próprios, que são sincronizados em um feed Google Shopping para integração com Merchant Center.
📸 Tela real · clique pra ampliar
👤 Você faz e vê
11. Acessa o painel → seção Vitrine → clica em 'Produtos da Vitrine'
→
22. Visualiza dois painéis: seus produtos próprios (criáveis via formulário) e anúncios ativos do Mercado Livre
→
33. Para cada anúncio ML, usa um toggle para ativar/desativar sua visibilidade na vitrine pública
→
44. Cria produtos próprios preenchendo nome, preço, descrição e fazendo upload de fotos
→
55. A vitrine pública em /loja/{slug} exibe os produtos selecionados em uma galeria
→
66. Periodicamente, o feed XML gerado em /loja/{slug}/feed.xml é buscado pelo Google Merchant Center para sincronizar produtos em Google Shopping
↓ o sistema reage por baixo
⚙️ O sistema faz
carregaSistema obtém lista de anúncios ativos (ML) e produtos próprios (vitrine) do vendedor, respeitando a flag vitrine_visible de cada um
exibeRenderiza dois blocos: produtos próprios com formulário de criação/edição, anúncios ML com toggle on/off para visibilidade
persisteSalva alterações em tempo real: criação/edição/exclusão de produtos próprios, ativação/desativação de visibilidade de anúncios ML na tabela Announcements
constróiGera feed XML RSS 2.0 com namespace Google Shopping (LojaFeedService), mapeando cada produto para g:id, g:title, g:price, g:link (domínio próprio ou /loja/{slug}), g:image_link, g:condition, g:availability, g:brand, GTIN/MPN
cacheiaArmazena feed em storage/cache/shopping_feed_v2_*.xml com TTL 1h; Google refaz o fetch diariamente
publicaA vitrine pública em /loja/{slug} obtém os mesmos produtos via productsForLoja() e os exibe em galeria; /loja/{slug}/p/{mlb} fornece página de produto (landing para Merchant Center)
sincronizaEnvia ao Google Merchant Center via feed XML público em /loja/{slug}/feed.xml; Google Shopping aparece como outro canal de tráfego além de Mercado Livre
🌐 Vai pro Mercado Livre / Pix
GET /loja/{slug}/feed.xml → Google Merchant Center faz fetch diário do feed (autenticação via reivindicação de domínio)PUT para Mercado Livre API (legado) → quando produto é publicado no ML, Rally captura marketplace_item_id e preço para o feedGET em Google Shopping → listagem de produtos da vitrine após sincronização bem-sucedida do feed
⚠️ AtençãoDomínio próprio: se o vendedor reivindicar um domínio customizado no Merchant Center, os links de produto (g:link) devem ser do domínio verificado, não da Rally de Vendas — a tela não avisa visualmente dessa restriçãoRequisitos de dados: produto sem preço válido, foto ou permalink não entra no feed, causando silêncio sem aviso ao vendedorFoto formato: o feed usa ml_img() para converter imagens do ML para tamanho F (full), mas produtos próprios devem usar upload manual — sem validação de resolução mínima
Permitir que o vendedor crie e publique posts no blog da sua vitrine pública com otimização SEO e monetização via AdSense.
painel.rallydevendas.com.br/perfil/loja/blogpainel.rallydevendas.com.br/perfil/loja/blog/novopainel.rallydevendas.com.br/perfil/loja/blog/{id}/loja/{slug}/blog/loja/{slug}/blog/{post}
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1O vendedor acessa o painel, clica em Vitrine > Blog da Loja e vê a lista de posts salvos (rascunhos e publicados)
→
2Clica em Novo Post ou Editar Post existente e preenche título, slug, capa, resumo, conteúdo (com editor visual WYSIWYG) e metadados SEO (descrição, palavra-chave, og:image, tags)
→
3Ao editar, vê em tempo real um score SEO (0-100) e um checklist com sugestões (título, densidade de palavras-chave, imagem, quantidade de subtítulos, etc)
→
4Faz upload de imagens inline dentro do editor (até 4MB, formatos JPG/PNG/WebP/GIF)
→
5Define status como Rascunho ou Publicado e salva; ao publicar, o post fica visível em /loja/{slug}/blog
→
6A vitrine pública do lojista mostra os posts publicados em grid (capa, título, resumo, data); clicando abre a página completa com conteúdo HTML, Open Graph, schema JSON-LD e anúncios Google AdSense
↓ o sistema reage por baixo
⚙️ O sistema faz
ListarCarrega todos os posts do vendedor (rascunhos e publicados) ordenados por data de publicação mais recente, com score SEO e tempo de leitura estimado em cache
ValidarCalcula score SEO (0-100) checando título, conteúdo (mínimo 500 palavras), densidade de keywords, meta description (50-160 chars), imagem de capa e estrutura de subtítulos (H2); retorna score + detalhes + sugestões
GuardarSalva o post (create/update) em JSON per-user com slug automático, status, timestamps (created_at/published_at/updated_at) e metadados SEO (title, meta_description, focus_keyword, og_image, canonical_url, tags)
SubirMove a imagem de capa/editor para /public/uploads/loja/{userId}/blog/ com nome aleatório (img_[hex].ext) e retorna URL relativa
Renderizar (público)Mostra grid de posts publicados em /loja/{slug}/blog com card (capa lazy-loaded, título, resumo, data); ao clicar, renderiza /loja/{slug}/blog/{post} com conteúdo HTML, Open Graph (og:title, og:image, og:description), schema JSON-LD (BlogPosting), e include do ads.txt para AdSense
IndexarInclui na sitemap.xml de cada vitrine: /loja/{slug}/blog (changefreq=weekly) e cada post publicado com slug (changefreq=monthly) e lastmod do updated_at
DeletarRemove o post da coleção JSON do usuário com confirmação CSRF
🌐 Vai pro Mercado Livre / Pix
POST /perfil/loja/blog/score: envia título, meta_description, focus_keyword, cover_url, conteúdo HTML e recebe score SEO (0-100) com checklist + sugestões em JSON
⚠️ AtençãoBlog fica oculto em /loja/{slug}/blog se shop_blog_enabled não estiver ativo no perfil do vendedor — validar que o usuário tem a flag habilitada antes de usar o editor (status 404 caso contrário)Score SEO usa throttle de 3s mínimo entre requisições para evitar WAF; se o vendedor digita rápido, pode parecer travado. Draft local salvo em localStorage (7 dias) pode conflitar se sessão expirouImagens inline no editor devem ser <4MB e formatos específicos (JPG/PNG/WebP/GIF); se usuário tentar .SVG ou HEIC, será rejeitado. URLs absolutas no canonical_url/og_image são obrigatórias para SEO funcionar (não URLs relativas)
▸Crescimento
O vendedor acessa cursos de capacitação, assistindo videoaulas, lendo materiais e respondendo quiz para elevar seu potencial de vendas.
/cursos ↗/cursos/{id}/cursos/{id}/{lesson_id}/acelera ↗/acelera/dia/{n} · clique pra abrir (já logado)
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Clica em 'Cursos' no menu de Crescimento do painel e vê a lista de treinamentos disponíveis (cursos criados pelo admin ou seu gerente).
→
2Escolhe um curso e clica para entrar; o player mostra videoaulas, PDFs, textos e quiz organizados em módulos com barra de progresso visível.
→
3Assiste cada aula, marca como concluída e navega pela próxima; se a aula tiver quiz, responde as questões (precisa passar em 70% para seguir).
→
4Acompanha seu percentual de conclusão em tempo real no topo da tela (ex: 45% completo).
→
5Opcionalmente, entra no 'Acelera' para participar de um programa de 180 dias com missões diárias, checkin de presença e prêmios por sequência.
↓ o sistema reage por baixo
⚙️ O sistema faz
ListarSistema busca cursos publicados visíveis para o vendedor (criados por admin ou seu gerente) e calcula o percentual de conclusão de cada um.
RenderizarExibe catálogo de cursos em cards com capa, título, resumo, e barra de progresso preenchida (ex: 45% — 7 de 15 aulas).
CarregarAo abrir um curso, carrega todos os módulos e aulas associados, e determina qual aula assistir (primeira não vista ou a última deixada aberta).
ReproduzirApresenta a aula no player (vídeo YouTube/Vimeo/nativo, PDF com anexos, ou texto HTML) com barra lateral mostrando todas as aulas do curso em ordem com status (concluída/pendente).
MarcarRegistra aula como concluída quando vendedor clica em 'Marcar como feito'; persiste no progresso e atualiza a porcentagem total do curso.
AvaliarSe a aula for um quiz, processa respostas, calcula nota (pontos corretos ÷ total × 100) e bloqueia avanço se < 70% (permite tentar novamente).
SincronizarAtualiza progresso do curso em tempo real (barra, % concluído, status de cada aula) sem recarregar página; salva no banco JSON global.
⚠️ AtençãoQuiz com nota < 70% bloqueia a próxima aula até ser retentado; vendedor pode ficar desmotivado se pular muitas questões.Vídeos embarcados (YouTube, Vimeo) dependem de CDN externo; em conexões lentas, o player pode travá-lo.Progresso é local (por vendedor × curso); se o vendedor trocar de conta ou dispositivo, o histórico não sincroniza entre contas.
Vendedor registra venda do dia (faturamento + quantidade) e humor, recebendo feedback comparativo e mantendo streak de consistência diária.
📸 Tela real · clique pra ampliar
👤 Você faz e vê
11. Acessa menu Crescimento → Check-in Diário (ou painel.rally.com.br/checkin).
→
22. Vê faturamento e pedidos de hoje dos marketplaces já preenchidos automaticamente; confirma ou corrige o valor de vendas fora dos marketplaces.
→
33. Seleciona seu humor (😡 😟 😐 🙂 😄) e escreve notas opcionais sobre o dia.
→
44. Clica 'Registrar check-in' e recebe página de sucesso com comparativas (vs ontem, vs média 7 dias, melhor dos 30 dias) + destaque motivacional.
→
55. Consulta histórico (últimos 60 dias) em /checkin/historico com tabela de data, faturamento, vendas, humor e notas.
→
66. Sistema mantém streak visível em ambas as telas (quantidade de dias seguidos com check-in registrado).
↓ o sistema reage por baixo
⚙️ O sistema faz
auto_fillSistema busca Orders do vendedor da data e preenche automaticamente faturamento_auto e vendas_auto a partir de marketplaces conectados.
validarValida data (Y-m-d), sentimento (um de: muito_triste, triste, neutro, feliz, muito_feliz), e valores de faturamento/vendas (não negativos).
upsertGrava ou atualiza check-in no JSON do usuário (idempotente por user_id + data). Separa auto/manual e armazena faturamento total = auto + manual.
calcular_streakComputa sequência de dias consecutivos com check-in terminando em hoje/ontem (função pura, não persiste).
gerar_insightExtrai comparativas: today (faturamento, vendas, sentimento), yesterday (delta %), avg_7d (delta %), best_30d, ticket_médio, e gera 1 frase de dopamina (recorde, streak, delta, sem venda, etc).
renderizarExibe form.php (com 4 cards: Vendas Online, Vendas Manuais, Total Ao Vivo, Humor+Notas), done.php (com hero + comparativas), ou history.php (tabela 60 dias).
integrar_gamifCheck-in contribui indiretamente ao sistema de pontos/níveis (SellerGamificationService); gamificação é cálculo em tempo real sobre ml_orders, não persistida no check-in.
🌐 Vai pro Mercado Livre / Pix
Nenhum. Check-in é ritual local: auto-fill lê Orders do banco JSON do user (not API). Streak e insights são cálculos puros sobre histórico local.
⚠️ Atenção1. Auto-fill depende de ml_orders.json sincronizado — se marketplace desconectado, faturamento_auto = 0 (vendedor deve preencher manual).2. Streak quebra se saltar 1 dia (hoje ou ontem deve ter check-in; 2+ dias sem = streak volta a 0).3. Check-in é idempotente por data (POST /checkin com mesma data atualiza o existente) — se vendedor refizer o dia, valores novos sobrescrevem (não há log de edições).
Vendedor visualiza e participa de lives institucionais (mentorias ao vivo, eventos) agendadas pela plataforma com gravações disponíveis.
📸 Tela real · clique pra ampliar
👤 Você faz e vê
11. Clica no menu Crescimento > Lives & Eventos do painel do vendedor
→
22. Vê lista de eventos futuros com data, horário, descrição e duração em minutos
→
33. Clica no botão 'Entrar' para acessar o link da live (se disponível)
→
44. Acessa a live ao vivo ou assiste à gravação posterior (se existir)
→
55. (Admin) Agenda novo evento via formulário com título, descrição, data, hora, URL da live e audiência-alvo (todos / plano acelera / enrolled)
→
66. (Admin) Envia lembretes via WhatsApp para usuários notificáveis do evento
↓ o sistema reage por baixo
⚙️ O sistema faz
GuardValida se vendedor tem permissão 'acelera.eventos' (plan guard) para acessar a tela
QueryBusca eventos institucionais futuros do banco JSON, filtrando por tenant e data agendada (scheduled_at >= agora)
RenderExibe cartão para cada evento com data destaque, título, descrição, duração e botão 'Entrar' (se live_url existe)
Admin CreateRecebe dados do formulário, valida data/hora, cria novo evento no banco de dados JSON (InstitutionalEvent)
Admin BroadcastFiltra audiência (todos / plano acelera / enrolled), valida quem já recebeu notificação (idempotência), dispara mensagem WhatsApp em lote
TrackMarca user_id como notificado no evento para evitar duplicação de lembrete
Public View(Admin) Gera link público com token de 30 min para compartilhar live/tela campeão com token temporário
🌐 Vai pro Mercado Livre / Pix
Sem chamadas de API externa nesta tela — eventos e lives são gerenciados internamente via JSON Database e WhatsApp broadcast (serviço próprio da plataforma)
⚠️ AtençãoApenas usuários com permissão 'acelera.eventos' ativa veem a seção Lives & Eventos no menu (validação por PlanGuard)Links de live (live_url) são opcionais — se vazio, o botão 'Entrar' não aparece (sem erro, apenas oculto)Idempotência: mesmo após múltiplas chamadas de broadcast do mesmo evento, cada usuário recebe WhatsApp uma só vez (controle via notified_user_ids)
Expor lista pública curada de 132 canais Telegram brasileiros (10k+ membros) em 15 nichos e capturar leads via bot deeplink para nurture.
/grupos ↗/grupos/{niche} · clique pra abrir (já logado)
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Clica em 'Grupos Telegram' (item 132) no menu Crescimento do painel ou acessa rallydevendas.com.br/grupos
→
2Navega pelo hub público: escolhe um nicho (Promoções, Shopee, Ofertas, Cupons, Amazon, etc.)
→
3Na landing SEO de cada nicho, vê 3 grupos em acesso direto + lista de outros bloqueados
→
4Preenche email OU WhatsApp na caixa de desbloqueio
→
5Recebe link de bot (t.me/RallyBot?start=grupos_niche_code) que entrega a lista completa no Telegram
→
6Clica no link, o bot valida o código e envia todos os grupos do nicho como mensagem
↓ o sistema reage por baixo
⚙️ O sistema faz
ValidaConfere se o niche existe, se email/WhatsApp é válido e aplica rate limit (3 unlocks/h por IP)
GeraCria registro de lead com unlock_code 8 chars alfanuméricos (sem 0/O/1/I para evitar confusão)
MontaConstrói deeplink do RallyBot com parâmetros: grupo_niche_codigo para validação server-side
ArmazenaPersiste lead em storage/json_db/group_unlock_leads.json com status=pending, email/WhatsApp e UTM
RetornaResponde com JSON contendo bot_url para redirecionar o browser direto para o Telegram
EntregaBot recebe /start grupos_niche_code, valida unlock_code e envia lista de grupos como inline keyboard
MarcaRegistra lead como status=delivered com tg_chat_id para cruzar em nurture sequence posterior
⚠️ AtençãoDeeplink do bot deve manter formato exato 'grupos_{niche}_{code}' — qualquer variação invalida a entregaOs 132 canais são carregados de telegram_groups.json (importado via TGStat); inatividade detectada por revalidação manual marca is_alive=falseEmail duplicado ou taxa esgotada (3 unlocks/hora) rejeitam unlock — usuário vê mensagem de erro no frontend
▸Extras
Fornecer análise financeira e validação de anúncios para otimizar a rentabilidade das vendas no Mercado Livre.
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1O vendedor clica em 'Ferramentas' na seção Extras do painel
→
2Escolhe entre: Rentabilidade (simular lucro de produtos), Relatório (análise de vendas), Checar Fotos (validar imagens contra regras ML) ou integração com Mercado Pago
→
3Insere dados como preço de venda, custo do produto e outras variáveis de custo
→
4O sistema calcula margem líquida, impostos por regime tributário e lucro esperado
→
5Visualiza relatório de P&L com devoluções separadas, saldo recebido do MP e alertas de reconciliação
→
6Exporta dados em CSV, Excel ou PDF para análise externa
↓ o sistema reage por baixo
⚙️ O sistema faz
CarregarBusca anúncios e pedidos do usuário na base de dados local (JSON).
CalcularComputa margem líquida usando preço bruto, comissão do ML (real ou estimada), frete, custo do produto, impostos e custos de publicidade.
ReconciliarCompara valor esperado do ML com saldo efetivamente recebido no Mercado Pago, identifica retenções ou divergências.
AgruparSepara vendas válidas de devoluções, soma totalizações por período e regime tributário (CPF/MEI/Simples Nacional).
ValidarAvalia fotos de anúncios contra 20+ regras de qualidade do ML (dimensões, sobreposição de texto, marca d'água).
ExportarGera arquivo (CSV, XLSX ou PDF) com colunas de receita, custos, lucro e status para auditoria fiscal/contábil.
ConectarOpcionalmente sincroniza com conta Mercado Pago (OAuth) para obter saldo oficial e status de liberação de valores.
🌐 Vai pro Mercado Livre / Pix
Valida foto contra regras JPEG/PNG (dimensões mín/máx, aspect ratio, texto em fundo, marca)Consulta saldo de conta no Mercado Pago (se conectada via OAuth)
⚠️ AtençãoComissão estimada quando falta sincronia real dos pedidos do ML — leitura desatualizada pode gerar margem incorretaCusto do produto vazio ou não sincronizado com anúncios — exibe alerta 'custo desconhecido' para que usuário vincule manualmentePeríodos de reconciliação MP podem levar 5-7 dias — nem todos os pedidos mostram saldo recebido imediatamente
Pesquisar concorrentes no Mercado Livre em tempo real, analisar preços/tendências e clonar anúncios vencedores sem sair do painel.
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Acessa Extras → Pesquisa de Mercado no menu lateral
→
2Digita uma palavra-chave (ex: airfryer) na aba Buscar no ML ou cola URL/HTML de página listada no Mercado Livre
→
3Seleciona a fonte: Rally Catalog (indexado, rápido) ou Mercado Livre direto (dados vivos)
→
4Vê resultados com preço, quantidade à venda, dias no ar e score Rally; filtra por preço e condição se quiser
→
5Clica em Clonar neste anúncio para copiar de um concorrente vencedor pra sua loja
→
6Usa abas adicionais: Analisar minha loja (comparação local vs pool), Tendências (Google Trends), Concorrência de Catálogo (repasse + buy-box)
↓ o sistema reage por baixo
⚙️ O sistema faz
BuscaConsulta palavra-chave contra o Rally Catalog (pool indexado em 6h) ou chama API pública do Mercado Livre ao vivo; se API bloqueia (403), faz scraping HTML público como fallback.
NormalizaPadroniza dados do item (id, título, preço, disponibilidade, vendas, foto, permalink) para exibição consistente.
CacheGuarda resultado por 6 horas compartilhado entre todos os vendedores (dados ML são públicos); avita chamadas repetidas à API.
AnalisaCalcula estatísticas: mediana de preço, distribuição de concorrentes, histórico de vendas, oportunidades de posicionamento (tabela ao vivo vs histórico).
Renderiza abasMonta visão em tabs: busca de palavras (Buscar no ML), self-analysis (Analisar minha loja), bypass via HTML (Colar HTML ML), Google Trends, e competição por catálogo (repasse + buy-box).
Gera insightsIA nativa sugere estratégia: gap de preço, sazonalidade, produto sem fotos (risco), itens com estoque zerado, pausa recomendada.
ClonarPrepara fluxo integrado: clica Clonar → vai pra wizard de anúncio com dados do vencedor (título, preço, fotos) pré-preenchidos.
🌐 Vai pro Mercado Livre / Pix
GET /sites/MLB/search?q=X&limit=50 (API pública Mercado Livre — busca por palavra-chave)GET /items/{MLB_ID} (API pública ML — detalhes de um anúncio)POST /pesquisa-mercado/parse-html (servidor Rally faz parsing HTML quando usuário cola página ML direto)
⚠️ AtençãoMercado Livre pode bloquear a API pública (403) em certos IPs/períodos; nesse caso o sistema fallback para scraper HTML ou oferece aba Colar HTML como workaround.Rally Catalog tem TTL 6h — dados de busca não são 100% ao vivo, mas suficientemente frescos (mostrador de age: 'dados de 2h atrás' etc.).Extensão Chrome Rally permite coleta cooperativa, mas sem ela o pool cresce mais lentamente em categorias niche; aba Colar URL/HTML sempre funciona como bypass.
Validar fotos dos anúncios contra as regras do Mercado Livre (resolução, proporção, fundo branco) e permitir download com dica para melhorar via IA generativa.
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Acessa menu Extras > Checar Fotos no painel do vendedor
→
2Visualiza lista de anúncios próprios à esquerda, cada um com thumbnail e contagem de fotos
→
3Clica em um anúncio para ver análise automática: score (0-100) em cores + checks de quantidade/resolução/proporção/fundo/texto
→
4Visualiza galeria das fotos com preview em lightbox ao clicar, e botão Baixar para salvar cada imagem
→
5Copia o prompt de IA pré-gerado que indica como melhorar a foto em Gemini/ChatGPT (remover fundo, centralizar, etc)
→
6Baixa foto via proxy do Rally, abre Gemini/ChatGPT, cola prompt, anexa foto, gera versão melhorada e reuploada no ML
↓ o sistema reage por baixo
⚙️ O sistema faz
BuscaCarrega lista de todos os anúncios do vendedor (até 400, filtrados por status ≠ deleted) com título, thumbnail e contagem de fotos
AnalisaAo clicar em anúncio, faz fetch das imagens do CDN do ML e verifica: quantidade (0 a 12), resolução (px ≥500 ok, ≥1200 recomendado), proporção da capa (1:1 ideal), cor das bordas da capa (branco ≥240 RGB)
PonturaGera score 0-100 subtraindo penalidades por erros (−30) e alertas (−12), e lista até 7 checks com status ok/warn/error/info
RenderizaMostra painel direito com score colorido, checks em 2 colunas, galeria de fotos com números, lightbox, e prompt de IA copiável
DownloadQuando vendedor clica 'Baixar', proxeia requisição para https://http2.mlstatic.com/* com curl, valida MIME type (jpg/png), e envia como attachment com nome foto_N.ext
PromptMonta texto instruindo especialista em ecommerce a melhorar a foto para regras do ML: fundo #FFFFFF, produto 80%, sem texto/preço/selo, 1:1, ≥1200px, iluminação uniforme
CópiaOferece botão 'Copiar prompt' que escreve no clipboard do vendedor via navigator.clipboard.writeText(), permitindo colar direto no chat da IA
🌐 Vai pro Mercado Livre / Pix
Valida características de imagem remota do ML CDN: detecta dimensões [w,h] via getimagesizefromstring() PHP, sensibilidade 4s por fotoDetecta cor de borda via GD Library imagecolorat() em 8 pontos (cantos + meios das bordas) para estimar fundo branco (≥240 R,G,B em ≥75% das amostras)Gera prompt textual (não API call) que o vendedor copia para Gemini ou ChatGPT passando a foto baixada, recebendo imagem melhorada manualmente
⚠️ AtençãoTimeout de 4s por imagem remota: se CDN do ML estiver lento ou foto removida, check vira 'info' (não pode certificar).Detecção de texto/selo/logo é apenas lembrete manual — o GD não identifica OCR, depende de revisão visual do vendedor.Download via proxy limita a URLs autorizadas (regex #^https://http2\.mlstatic\.com/[A-Za-z0-9/_\-\.%]+$#) — qualquer URL fora do padrão retorna erro 400.
Criar e reutilizar prompts de IA personalizados para gerar títulos, descrições, fichas técnicas, análises e respostas de anúncios.
/ia/prompts ↗/api/ia/prompts/api/ia/prompts/salvar/api/ia/prompts/excluir/api/ia/prompts/default · clique pra abrir (já logado)
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Acessa Painel > Extras > Prompts de IA (menu na barra lateral)
→
2Visualiza lista de prompts criados com filtro por tipo (Título, Descrição, Ficha técnica, Análise, Resposta)
→
3Clica em 'Novo prompt' ou edita um existente em modal com nome, tipo e corpo do prompt
→
4Insere variáveis como {produto_nome}, {categoria}, {pergunta} usando botões de atalho
→
5Marca prompt como padrão do seu tipo (estrela) — usado automaticamente em gerações
→
6Salva e vê aparecer na lista; deleta prompts se não precisar mais
↓ o sistema reage por baixo
⚙️ O sistema faz
CarregaBusca todos os prompts do vendedor (tabela ai_prompts.json) com filtro por tipo e prompts já marcados como padrão
ValidaVerifica tipo válido (Título/Descrição/Ficha/Análise/Resposta), nome entre 1-60 caracteres e corpo entre 10-4000 caracteres
ArmazenaPersiste prompt em JSON por usuário (users/{id}/ai_prompts.json) com timestamps created_at e updated_at
Marca padrãoDefine prompt como padrão do seu tipo — desmarca outros prompts do mesmo tipo automaticamente
DescartaSoft-deleta prompt (marca deleted_at) sem remover registro, para auditoria
InterpolaSubstitui placeholders {var} no corpo do prompt pelos valores reais (produto_nome, categoria, etc.) na hora da geração
ResolveNa hora de gerar IA, segue prioridade: prompt_id explícito > prompt padrão do tipo > default do sistema
🌐 Vai pro Mercado Livre / Pix
POST /api/ia/prompts/salvar: envia nome, tipo, corpo e marca padrão ao servidorGET /api/ia/prompts: recupera lista de prompts do usuário em JSONPOST /api/ia/prompts/excluir: marca prompt como deletadoPOST /api/ia/prompts/default: define prompt como padrão do seu tipo
⚠️ AtençãoPrompt precisa ter pelo menos 10 caracteres — validação no frontend e backendSó pode ter um prompt padrão por tipo — sistema desmarca os outros automaticamenteVariáveis {produto_nome}, {categoria} etc. não existem no prompt = são substituídas por string vazia na geração
Permitir vendedores gerenciar plugins de terceiros (incluindo extensão Chrome) e parceiros criar/manter integrações com acesso via Bearer token (em evolução).
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Vendedor acessa o painel e clica em 'Plugins & Extras' no menu Extras do grupo lateral.
→
2Vê a galeria de plugins (gratuitos, premium e trial) com descrição, preço e status de instalação.
→
3Clica 'Instalar' em um plugin — o sistema valida plano, cobra se necessário, ativa e retorna com flash de sucesso.
→
4Pode desativar um plugin instalado clicando 'Desativar' (desativa sem cobrar novamente).
→
5Para extensão Chrome: abre o popup → aba 'Conta' → copia token Bearer via GET /ext/token (gerado da sessão autenticada do painel).
→
6Cola o token na extensão Chrome; ela passa a fazer requisições autenticadas (GET /ext/analise, /tracking, POST /ext/import-product, /ext/clone-announcement) com Authorization Bearer.
↓ o sistema reage por baixo
⚙️ O sistema faz
BuscaLê lista global de plugins aprovados (Plugin::all) e plugins instalados do usuário (PluginService::getUserPlugins).
IndexaMonta dicionário installedIds[plugin_id] = status (active/inactive/revoked) para render rápido.
ValidaNa instalação: confere se plugin existe, está aprovado e se modelo de preço é compatível com plano do usuário (via PluginBillingService).
CobraSe plugin pago: PluginBillingService::chargeInstall incrementa wallet/créditos ou registra débito mensal.
CriaInsere registro em user_plugins (JSON local) com id, plugin_id, status, pricing_model, paid_amount, installed_at.
Gera tokenGET /ext/token cria ExtToken (Bearer) com TTL (expires_at), armazena hash em storage, devolve plain_token ao painel para vendedor copiar.
RetornaApós ação (install/uninstall), redireciona com flash (sucesso/erro) e reload do marketplace.php.
⚠️ AtençãoTokens Bearer de extensão têm TTL fixo — extensão precisa renovar periodicamente via GET /ext/token; sem login válido no painel, devolve erro 401.Instalação de plugin pago requer saldo em carteira ou plano pago ativo; cobrança falha silenciosamente (erro em flash, sem exceção).Sem dados públicos no blueprint_flows.php — tela está em evolução e não tem fluxo mapeado oficialmente para usuários finais.
Acessar o blog público oficial do Rally de Vendas a partir do painel do vendedor para ler guias, tutoriais e estratégias de vendas.
📸 Tela real · clique pra ampliar
👤 Você faz e vê
11. No painel do vendedor, clica no menu lateral no grupo 'Extras'
→
22. Clica no link 'Blog Oficial' (abre em nova aba, subdomain blog.rallydevendas.com.br)
→
33. Vê a listagem de artigos em cards com imagem, título, categoria, tempo de leitura e data
→
44. Busca por termo digitando na caixa de pesquisa OU filtra por categoria (Guia, Estratégia, Ferramenta, etc)
→
55. Clica em um card para ler o artigo completo com índice, autor, comentários e artigos relacionados
→
66. Rolando a página, vê progresso de leitura, opções de compartilhamento e captura de lead (WhatsApp)
↓ o sistema reage por baixo
⚙️ O sistema faz
carregaBlogIndexService lê índice leve (~500KB) com posts publicados, ignora o arquivo pesado blog_posts.json (13MB) na memória
renderizaLista até 12 posts por página em grid com imagem, categoria colorida, tempo estimado de leitura e data. Suporta busca por termo no título/keyword/descrição e filtragem por categoria
navegaUsuários logados veem blog integrado ao painel (com sidebar). Públicos veem versão standalone com header/footer público
exibe postBlogController::post() busca artigo por slug, valida existência (404 se não encontrado), registra visualização via BlogTrackingTrait
enriqueceAdiciona metadados SEO (JSON-LD Article + BreadcrumbList + FAQ schema), Open Graph, Twitter Card, RSS link. Integra Google AdSense (ca-pub-2442955258067136)
capturaPOST /api/lead coleta WhatsApp + nome do leitor e envia checklist automaticamente via BlogLeadService. Salva em blog_leads.json
trackedGoogle Analytics 4 (GA4) e Microsoft Clarity rastreiam visualizações. Pixel de tracking invisível registra evento de view. Meta Pixel (881126080985979) dispara PageView.
🌐 Vai pro Mercado Livre / Pix
POST /blog/api/lead — envia WhatsApp do leitor para captura de contato (resposta JSON {success: true/false})
⚠️ AtençãoBlog posts.json tem 13MB — BlogController DEVE usar BlogIndexService (índice de 500KB), nunca carregar o arquivo inteiro na RAMAdSense e Analytics disparam píxeis de rastreamento — verificar CSP nonce e GDPR ao integrar novo tracking terceiroURLs canônicas e JSON-LD são críticas para SEO — manter consistência entre og:url, canonical e sitemap.xml gerado em tempo real
▸Parceiros
O vendedor gerencia seu programa de afiliados MLM de 2 níveis para ganhar comissões de 25%/5% sobre novos clientes indicados.
📸 Tela real · clique pra ampliar
👤 Você faz e vê
11. Clica em 'Afiliados' no menu (grupo Parceiros) do painel do vendedor
→
22. Sistema exibe painel com status: inativo ou ativo com rede de indicações (L1 e L2)
→
33. Se inativo, clica em 'Quero ser afiliado' para ativar (aprovação automática para planos pagos, pendente para free)
→
44. Copia seu link de indicação único (com código referral) ou compartilha direto via WhatsApp
→
55. Visualiza a rede de indicados (Nível 1 diretos + Nível 2 indiretos) com comissões acumuladas
→
66. Acompanha ganhos pendentes, pagos e taxa de conversão dos cliques (últimos 30 dias)
↓ o sistema reage por baixo
⚙️ O sistema faz
loadSistema lê status do vendedor em users.json (affiliate_status: active/pending/blocked) e carrega árvore de indicados (L1 diretos + L2 indiretos via L1)
applyAo clicar 'Quero ser afiliado', gera código referral único, ativa imediatamente (auto_approved=true para planos pagos), cria link global de indicação no AffiliateLink
calcCalcula comissões L1 (25% do plano = R$100-500/mês) e L2 (5% do plano = R$20-100/mês) para cada indicado com first_paid_at há ≥90 dias (carência contra churn)
rankAtribui tier ao afiliado (Bronze 1-2, Prata 3-9, Ouro 10-24, Diamante 25+ indicados ativos) e bônus mensal por tier (0 a R$400/mês)
trackRegistra clicks (utm_source=affiliate) e conversões (first_sale + recurring) em AffiliateConversion pending até elegibilidade (90d) ser atingida
payoutCron mensal credita Wallet do afiliado com conversões elegíveis, respeitando cap individual (R$1.000/mês) e cap global (15% MRR da empresa)
⚠️ Atenção⚠️ Carência 90 dias: comissão só crédita se o indicado pagou há ≥90 dias (previne fraude e churn rápido)⚠️ Limite mensal R$1.000 por afiliado + 15% MRR global: payout para automaticamente se atingir cap (configurável em affiliate_config.json)⚠️ Aprovação automática mudou em 2026-05-21: agora TODOS os planos (free + paid) são auto-aprovados; admin apenas bloqueia suspeitos
O vendedor acessa seu painel de programa de afiliados para criar links de campanha com rastreamento UTM, acompanhar comissões de indicações e visualizar ofertas especiais de aquecimento de vendas.
mkt.rallydevendas.com.br/painel/indicacao?tab=afiliado (dashboard afiliado)mkt.rallydevendas.com.br/painel/afiliado/links (lista e cria campanhas)mkt.rallydevendas.com.br/painel/afiliado/relatorios (extrato de comissões)painel.rallydevendas.com.br/esquenta (ofertas aquecimento)mkt.rallydevendas.com.br/afiliados (landing pública)mkt.rallydevendas.com.br/ranking (ranking de afiliados)
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Na seção 'Parceiros' do menu, clica em 'Campanhas' para ir ao painel de afiliado
→
2Visualiza seu link principal (sem parâmetros) e cria novos links com nome, URL de destino e tags UTM (source, medium, campaign) para rastrear cada canal
→
3Copia o link gerado e compartilha via WhatsApp, email ou Instagram para trazer pessoas pro Rally
→
4Acompanha em 'Extrato' quantas vendas geraram cada campanha e sua comissão acumulada
→
5Acessa 'Esquenta' para ver ofertas especiais que pode ativar (-5% em anúncios por 14 dias) para impulsionar vendas
↓ o sistema reage por baixo
⚙️ O sistema faz
autenticaçãoValida que o usuário está logado e tem acesso ao programa de afiliados (status=active no banco)
carregaBusca link principal (kind=global) e todas as campanhas do vendedor (kind!=global) no banco de dados
renderizaExibe modal para criar nova campanha com campos: nome, URL destino, UTM source/medium/campaign
criaPersiste novo link no banco com código único, associado ao user_id do vendedor
copiaGera URL completa (domínio afiliado + código + parâmetros UTM) em campo readonly + botão copiar
rastreiaSistema de click tracking registra cada clique no link para calcular conversões e comissões no mês
🌐 Vai pro Mercado Livre / Pix
GET /api/ml/campanhas - Listar campanhas disponíveis no Mercado Livre (DEAL, Lightning, etc) que o vendedor pode participarPOST /api/ml/campanhas/participar - Adicionar produto a uma promoção ML com preço específicoPOST /api/ml/campanhas/sair - Remover produto de uma campanha ML
⚠️ AtençãoLinks criados pelo afiliado são para rastrear o próprio tráfego (ex: Instagram) - não confundir com campanhas de promoção do Mercado Livre (Ofertas do Dia, Lightning, etc)Sorteio mensal (R$500 para afiliado com mais conversões) é automático (roda 1º do mês, sem ação do usuário)Se o vendedor estiver bloqueado ou com status cancelado, não consegue acessar painel nem criar campanhas
▸Config & Conta
O vendedor acessa suas configurações de perfil pessoal, dados fiscais, nacionalidade, chaves de IA próprias (BYOK), verifica seu WhatsApp e escolhe COMO recebe cada tipo de notificação no WhatsApp.
📸 Tela real · clique pra ampliar
👤 Você faz e vê
11. Clica em 'Config & Conta' no menu lateral (ícone de engrenagem)
→
22. Vê as abas 'Meu Perfil', 'Carteira Digital', 'Contas ML' e 'Fornecedores'
→
33. Na aba 'Meu Perfil', acessa seu avatar, nome, email e plano; clica em 'Editar Perfil' ou 'Alterar Senha'
→
44. Em '/perfil', preenche dados pessoais: nome, email, WhatsApp, nacionalidade, país de operação, CPF/CNPJ, PIX
→
55. Verifica email/WhatsApp clicando no botão 'Verificar' (recebe código por email ou WhatsApp)
→
66. Na aba 'Integrações & IA', ativa suas próprias chaves de Claude, Gemini ou OpenAI; testa cada uma antes de salvar
→
77. Na aba 'Notificações', escolhe por tipo (Vendas / Perguntas & mensagens / Reclamações & devoluções) COMO receber no WhatsApp: imediato (1 mensagem por evento), resumo diário às 09:30, ou desligado
↓ o sistema reage por baixo
⚙️ O sistema faz
carregaBusca dados do usuário no banco: perfil, plano, contas Mercado Livre, preferências
renderizaExibe abas (Meu Perfil, Carteira, Contas ML, Fornecedores) conforme permissões do usuário
validaVerifica email/nome (min 3 caracteres), email único se mudar, WhatsApp com +55, nacionalidade contra lista de países
criptografaProtege chaves de IA (Claude/Gemini/OpenAI) antes de salvar no banco
enviaDispara código de verificação por email (SMTP) ou WhatsApp (API Blipei)
testaFaz chamada real à API do provedor de IA para confirmar chave válida
exportaGera JSON com todos os dados do usuário (LGPD Art. 18 V: dados pessoais, anúncios, pedidos, histórico de IA)
notificaSalva o modo de notificação por tipo. No imediato dispara 1 WhatsApp por evento; no diário enfileira o evento no mesmo resumo das 09:30 que já vai pra gerente/fornecedor/admin; desligado não envia. Default: imediato (zero mudança pra quem já usa).
🌐 Vai pro Mercado Livre / Pix
Envia SMS/WhatsApp via Blipei (serviço terceirizado de mensageria) para verificação de númeroFaz teste de autenticação contra API Claude (Anthropic), Gemini (Google AI Studio) ou OpenAI (se chaves BYOK forem preenchidas)
⚠️ AtençãoDocumento fiscal (CPF/CNPJ) é opcional, mas avisa se o formato não bater com o país de operação selecionadoSe mudar nacionalidade de BR para outro país, PIX é limpo automaticamente (PIX só funciona no Brasil)Chaves de IA não são salvas em pleno texto—são criptografadas e o sistema mostra apenas uma máscara (ex: 'sk-ant-***')
Vendedor visualiza e gerencia seu plano atual, vê período de trial, compara planos (free vs paid/managed) e realiza upgrade com checkout flexível via Mercado Pago (cartão, PIX ou boleto).
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Clica no ícone de coroa no menu lateral (seção 'Gestão') ou no drawer completo para acessar 'Meu Plano'
→
2Vê seu status atual: trial ativo com dias restantes, trial expirado, ou plano ativo (free/paid/managed)
→
3Compara lado a lado os planos: free (ilimitado ML + anúncios) vs paid (R$ 29,90/mês + R$ 1,00 por venda com vitrine, blog, shopping)
→
4Interage com simulador de custo: arrasta barra para estimar quantas vendas faria no mês e vê custo total amortizado por pedido
→
5Escolhe método de pagamento (Cartão = assinatura automática via Mercado Pago ou PIX/Boleto = pagamento único mensal)
→
6Clica 'Ativar Plano' ou 'Gerar Código PIX' e é levado ao checkout do Mercado Pago
↓ o sistema reage por baixo
⚙️ O sistema faz
CarregaBusca plano atual do usuário (free/paid/managed), verifica status do trial (ativo/expirado/sem trial), calcula dias restantes se ativo, obtém todos os planos ativos do sistema
CompõeRenderiza estado visual do trial/plano em banner destacado (aviso, relojoura de contagem regressiva ou cadeado de expiração)
ExibeMostra dois cards comparativos de plano gratuito vs plano pago com listas de features verificáveis (perguntas, mensagens, raio-X, vitrine, blog, suporte)
CalculaBusca histórico de vendas no mês atual, apresenta simulador que recalcula taxa mensal + taxa por venda conforme usuário arrasta slider
ValidaVerifica se Mercado Pago está configurado (MP_ACCESS_TOKEN em .env), se não nega acesso com mensagem de erro
DirecionaPOST /planos/checkout recebe choice de plan_id, period, payment_method; cria preference (método flex = PIX/boleto com link avulso; card = preapproval com assinatura automática) e redireciona ao initPoint do MP
RegistraSalva pref_id e billing_status (flex_pending ou preapproval_pending) para webhook processar confirmação; após sucesso, plan muda de free para paid, TrialGuardMiddleware libera acesso pleno
🌐 Vai pro Mercado Livre / Pix
Mercado Pago: createPreapproval (cartão com renovação automática) envia dados de cobrança recorrente; createPreference (PIX/boleto) gera link de checkout avulso; webhook valida approval e credita wallet + muda plan do usuário
⚠️ AtençãoTrial expirado bloqueia acesso via TrialGuardMiddleware — whitelist inclui /planos para que usuário possa ver opções de upgrade; se não chegar aqui, permanece bloqueado em redirect loopBUG-PLAN-CUSTOM-PRICE-001: administrador pode sobrescrever custom_per_order_price do usuário no admin; view respeita override individual, não preço defaultRDV-MP-FLEX-001: dois fluxos paralelos — 'card' usa Preapproval (debit automático, só cartão/conta MP) vs 'flex' usa Preference avulsa (aceita PIX, boleto, cartão mas sem renovação automática); vendedor escolhe ambos a cada mês
▸Loja com checkout
36
💳
Comprar na vitrine (checkout rápido)
O comprador fecha o pedido em 1-2 cliques, mobile-first, com o endereço já preenchido.
/loja/{slug}/loja/{slug}/checkout
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Na página do produto, clica em "Comprar agora".
→
2Se identifica em destaque com Mercado Livre (puxa nome + e-mail + telefone + endereço) — ou Google (1 toque) ou como visitante.
→
3Digita só o CEP: o endereço é preenchido sozinho (ViaCEP) e o frete é cotado na hora.
→
4Escolhe a entrega, escolhe o pagamento (PIX, Mercado Pago ou na entrega) e finaliza.
→
5Cai na página do pedido com o status e as instruções de pagamento.
↓ o sistema reage por baixo
⚙️ O sistema faz
IdentidadeLogin ML/Google/visitante cai no CRM de clientes (Buyer) — recompra fica em 1 clique.
EndereçoCEP via ViaCEP + cotação de frete em tempo real pelo gateway (Correios/Uber/Flex/retirada).
LeadCaptura o lead (nome+WhatsApp) já no passo 1 — base da recuperação de carrinho.
PedidoRecalcula tudo no servidor (nunca confia no preço do cliente) e cria o pedido da loja.
🌐 Vai pro Mercado Livre / Pix
OAuth comprador: troca o code, lê /users/me + /users/{id}/addresses pra pré-preencher o endereço (não cria conta nem marketplace_account).
⚠️ AtençãoProduto de ML não entra no carrinho — só produto próprio é vendido pelo checkout (ML é fulfilled pelo Mercado Livre).Google One Tap só renderiza em domínio autorizado; em domínio próprio do lojista cai pra ML/visitante.
37
💸
Pagamento da loja (PIX, Mercado Pago, na entrega)
Receber o pagamento do jeito que o vendedor escolheu — direto na conta dele.
/loja/{slug}/pedido/{token}
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1PIX: a página do pedido mostra QR Code + copia-e-cola com o valor já preenchido; o comprador paga e manda o comprovante.
→
2Mercado Pago: o comprador é levado ao checkout do MP do vendedor (PIX, cartão, boleto — com o parcelamento que ele liberou).
→
3Na entrega: o comprador combina e paga ao receber.
↓ o sistema reage por baixo
⚙️ O sistema faz
PIXGera o BR Code (EMV + CRC16) sem API de banco — a confirmação é o vendedor que dá (comprovante).
Mercado PagoCria a preference na conta MP do vendedor (token dele) com as opções configuradas (parcelas/cartão/boleto/PIX).
ConfirmaO webhook do MP confirma o pagamento automático (busca no token do vendedor) e marca o pedido pago.
🌐 Vai pro Mercado Livre / Pix
Mercado Pago: createPreference na conta do vendedor (collector próprio) + webhook /webhooks/pagamento/mercadopago?store_seller=X pra confirmar e dar baixa.
⚠️ AtençãoPIX da chave própria não confirma sozinho (o banco não avisa o Rally) — pra automático, use o Mercado Pago PIX.Mercado Pago só aparece no checkout se o vendedor conectou a conta dele (MP Connect).
38
🚚
Frete + Etiqueta da loja
Cotar o frete certo pelo CEP e despachar com etiqueta — mesmo quando a transportadora não fornece uma.
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1No checkout, o comprador vê as opções de entrega válidas pro CEP dele (com preço e prazo).
→
2Depois de pago, o vendedor abre o pedido em /loja/pedidos e clica em Etiqueta.
→
3Imprime a etiqueta (remetente + destinatário + QR) e cola no pacote.
↓ o sistema reage por baixo
⚙️ O sistema faz
CotaGateway de frete: Frete.center (transportadora vinculada), Uber Direct (≤5km, same-day), Flex (mesmo dia em SP), retirada, ou estimativa.
RegrasUber só aparece se o destino está na área (~5km); Flex só na região de SP e com produto elegível (peso/dimensão).
EtiquetaLoja/Uber/Flex não têm etiqueta de transportadora → o Rally gera a própria (imprimível, com QR). Correios/transportadora trazem a oficial.
⚠️ AtençãoFrete grátis acima de um valor mínimo zera a opção padrão (transportadora).Sem CEP de origem configurado, Uber e a cotação de transportadora não rodam.
39
📦
Gerenciar pedidos + recuperar carrinho
O vendedor acompanha as vendas, confirma pagamento, despacha e recupera quem deixou o carrinho.
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Em /loja/pedidos vê os pedidos por status, confirma o PIX manual, marca como enviado e abre a etiqueta.
→
2É avisado na hora de cada nova venda (WhatsApp → Telegram → e-mail); o comprador recebe e-mail em "pago" e "enviado".
→
3Em /loja/leads vê o Kanban de carrinhos abandonados e exporta o CSV pra recuperar do jeito dele.
↓ o sistema reage por baixo
⚙️ O sistema faz
ConfirmaPIX manual / na entrega vira "pago" com um clique; Mercado Pago confirma sozinho pelo webhook.
AvisaVendedor via NotifyDispatchService (cascata); comprador via e-mail transacional (zero risco de ban WhatsApp).
RecuperaO lead+carrinho fica no CRM do vendedor; a recuperação é canal DELE (Blipei/SMTP) ou export manual — o Rally não dispara nada.
⚠️ AtençãoA recuperação por WhatsApp/e-mail é responsabilidade do vendedor (integração dele) — evita risco de ban da conta do Rally.
40
⭐
Avaliações + Produto de fornecedor (drop) na loja
Ter prova social na página do produto e vender produto de fornecedor pela loja própria.
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Avaliações: o comprador avalia o produto (estrelas + texto) e aparece na hora; o vendedor esconde as ruins em /loja/avaliacoes.
→
2Pode importar as avaliações reais do mesmo produto no Mercado Livre pra ter prova social desde o dia 1.
→
3Drop na loja: no catálogo do fornecedor, clica "Adicionar à minha loja" — vira produto próprio (personalizável) com vínculo oculto ao fornecedor.
→
4Quando esse produto vende na loja, o fornecedor é acionado pra despachar e o vendedor paga o custo (fluxo drop que já existe).
↓ o sistema reage por baixo
⚙️ O sistema faz
AvaliaAvaliações entram visíveis (auto-aprova); o vendedor modera escondendo as problemáticas.
ImportaPuxa as reviews do MLB pelo token do vendedor (/reviews/item/{MLB}), com dedup.
DropVenda paga de produto com drop_origin cria um pedido drop → o fornecedor despacha e o vendedor paga (margem = preço da loja − custo).
🌐 Vai pro Mercado Livre / Pix
Avaliações: GET /reviews/item/{MLB} (token do vendedor) pra importar a prova social do anúncio do mesmo produto.
⚠️ AtençãoImportar avaliações exige uma conta Mercado Livre conectada e o MLB do mesmo produto.O pedido drop entra na pipeline de /fornecedores — o vendedor paga o fornecedor (PIX/wallet) e ele despacha.
▸Mercado Livre
41
🏆
Monitor de Catálogo + Precificação IA
Monitorar a posição no Buy Box do Mercado Livre, ajustar preços manualmente ou via sugestão de IA (BYOK) e ativar auto-repricing automático a cada ~55 min — com piso de preço, cooldown e histórico de mudanças.
/central/anuncios?tab=catalogo ↗/api/ml/catalog/monitor/status/api/ml/catalog/monitor/reprice-now/api/ml/catalog/monitor/ai-suggest · clique pra abrir (já logado)
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Acessa Central de Anúncios → aba Catálogo e vê anúncios em catálogo com status do Buy Box (vencedor/concorrente/perdendo) e preço vencedor atual
→
2Clica em "Aplicar agora" para ajustar o preço ao valor vencedor do Buy Box com 1 clique
→
3Clica no botão IA (ícone robô) para obter sugestão automática via BYOK: a IA analisa concorrentes, preço atual e piso e recomenda baixar | manter | subir com justificativa
→
4Ativa o auto-reprice nas configurações do catálogo: define piso de preço mínimo (ex: R$ 89,90) e se quer ajuste automático a cada ciclo de monitoramento
→
5Acompanha histórico de mudanças de preço por anúncio (data, preço anterior, preço novo, motivo: auto/manual/IA)
→
6Recebe alerta via Telegram quando o auto-reprice não consegue vencer o Buy Box mesmo no preço mínimo
↓ o sistema reage por baixo
⚙️ O sistema faz
MonitorCron a cada ~55 min executa CatalogMonitorService::runAll(): para cada anúncio em catálogo, consulta ML Buy Box Status e dados de concorrência
ComparaCalcula se preço atual está acima do vencedor (price_to_win). Se sim, tenta auto-reprice dentro da banda e acima do piso configurado
RepricePUT /items/{id} com novo preço via ML API; grava snapshot no histórico (timestamp, preço anterior, novo, reason: auto). Cooldown de 4h por anúncio
IA SuggestPOST /api/ml/catalog/monitor/ai-suggest usa AiRepricingService com BYOK (Claude/Gemini/OpenAI do usuário via AIService). Retorna action + preço recomendado + raciocínio. Piso sempre respeitado: max(suggested, floor_price)
AlertaQuando auto-reprice não consegue vencer Buy Box nem no piso, envia alerta Telegram ao vendedor com MLB + preço vencedor + gap de preço
🌐 Vai pro Mercado Livre / Pix
GET /items/{id}/catalog_listing_status — status do catálogo + Buy Box (posição, winner_price, price_to_win)GET /items/{id}/catalog — elegibilidade, concorrentes e preço alvoPUT /items/{id} — atualiza preço do anúncio para tentar vencer o Buy Box
⚠️ AtençãoBYOK obrigatório para sugestão IA: configure chave em Perfil > Inteligência Artificial. Sem BYOK, botão IA mostra gate de ativaçãoAuto-reprice pausável por killswitch .auto_reprice_paused — admin pode desativar globalmente em emergênciaCooldown de 4h por anúncio evita flapping de preço (ML pode penalizar mudanças muito frequentes)
▸Extras
42
💹
Relatório Financeiro
P&L por venda com reconciliação real contra Mercado Pago — vê net_received de cada pedido via API MP, desconta taxa ML e frete, e mostra lucro/prejuízo com devoluções e multi-conta.
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Acessa Ferramentas > Relatório Financeiro e seleciona período (30/60/90 dias ou personalizado)
→
2Visualiza P&L resumido: faturamento bruto, taxas ML totais, frete, devoluções e receita líquida real reconciliada com Mercado Pago
→
3Expande linha por linha: vê o que o MP realmente depositou (net_received), a taxa cobrada e o custo estimado
→
4Filtra por conta ML, status (pago/devolvido/cancelado) ou produto para análise específica
→
5Exporta CSV com todas as colunas para planilha ou contador
↓ o sistema reage por baixo
⚙️ O sistema faz
BuscaCarrega pedidos do período do JSON local (ml_orders.json), filtra por status paid/shipped/delivered e datas date_closed
ReconciliaPara cada pedido, consulta api.mercadopago.com/v1/payments (token ML) para obter net_received real — líquido após taxas ML, parcelamento e frete gratuito. Usa collector_id (underscore) como filtro
CalculaP&L por linha: receita bruta (total_amount) - taxa ML (fee_amount) - frete (shipping_cost) - devoluções = receita líquida real. Acumula totais do período
AgregaConsolida por conta ML, por produto e por dia — mostra evolução temporal da margem
ExportaCSV com colunas: data, pedido, produto, bruto, taxa, frete, net_received, status — UTF-8 com BOM para Excel
🌐 Vai pro Mercado Livre / Pix
GET api.mercadopago.com/v1/payments?collector_id={}&date_approved.range.begin={} — net_received real por pagamentoGET /orders/{order_id}/billing — detalhes de cobrança do pedido (taxa ML, breakdown)
⚠️ Atençãonet_received do MP chega em parcelas (parcelado) — relatório mostra total da venda, mas saldo MP cai gradualmentecollector.id (com ponto) causa 400 silencioso no MP; usar collector_id (com underscore) no filtroDevoluções aparecem com net_received negativo — reduz o P&L total corretamente
Emissão de Notas Fiscais Eletrônicas (NF-e) via gateway PlugNotas — por venda do vendedor e por custo do pedido drop — com suporte multi-CNPJ.
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Acessa Ferramentas > NF-e e configura CNPJ de emitente (ou cada loja drop no subdomain fornecedor)
→
2Visualiza pedidos pagos aguardando NF-e — clica em Emitir para lançar via gateway PlugNotas
→
3NF-e gerada em segundos (sandbox) ou minutos (produção); status atualiza automaticamente (autorizada/rejeitada)
→
4Baixa o DANFE (PDF) e XML da NF-e diretamente no painel
→
5Para pedidos drop, o fornecedor emite NF de custo separada — vendedor vê as duas NFs no detalhe do pedido
↓ o sistema reage por baixo
⚙️ O sistema faz
ConfiguraArmazena CNPJ, certificado digital (A1 .pfx), regime tributário e dados do emitente por conta (multi-CNPJ suportado)
EmiteChama API PlugNotas (POST /nfe) com dados do pedido: destinatário, produtos, NCM/CEST, valores, frete. Aguarda callback ou polling de status
ArmazenaSalva chave NF-e (44 dígitos), DANFE URL e XML no pedido local. NF-e de custo drop fica separada no registro do pedido drop
NotificaQuando autorizada, envia link DANFE ao comprador via mensagem pós-venda ML (opcional)
🌐 Vai pro Mercado Livre / Pix
POST PlugNotas API /nfe — emissão NF-e (gateway fiscal externo, não API ML)POST /messages/packs/{pack_id}/sellers/{seller_id}/messages — envia DANFE ao comprador via chat ML
⚠️ AtençãoCertificado A1 (.pfx) tem validade — sistema alerta 30 dias antes do vencimento. Sem certificado válido, emissão falhaProdução requer contrato com gateway fiscal (PlugNotas/Nuvem Fiscal); sandbox é gratuito. Configure em Ferramentas > NF-e > GatewayNCM obrigatório: sem NCM no produto, NF-e é rejeitada pela SEFAZ. Configure em Dados Fiscais antes de emitir
▸Mercado Livre
44
⚡
Uber Direct — Entrega Express
Despachar pedidos via Uber Direct (entrega on-demand no mesmo dia) direto do painel — com rastreamento em tempo real e cobrança automática.
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1No detalhe de um pedido, clica em "Despachar via Uber Direct" (disponível para pedidos Flex Turbo)
→
2Confirma endereço de coleta, endereço de entrega do comprador e valor do frete
→
3Sistema cria corrida no Uber Direct e exibe link de rastreamento em tempo real
→
4Comprador recebe atualização de status em tempo real via WhatsApp (quando configurado)
→
5Admin vê badge "Turbo" laranja nos pedidos despachados via Uber Direct em /admin/pedidos
↓ o sistema reage por baixo
⚙️ O sistema faz
Cria corridaPOST /deliveries na API Uber Direct (UBER_CLIENT_ID/SECRET do .env) com pickup_address, dropoff_address e manifest (descrição do item)
MonitoraWebhook Uber Direct atualiza status da corrida (enroute_to_pickup → arrived_at_pickup → enroute_to_dropoff → delivered) no pedido local
NotificaEnvia link de rastreamento ao comprador via WhatsApp transacional quando corrida inicia
RegistraGrava delivery_id, status e custo real da corrida no pedido (ultra_direct_delivery_id). Badge Turbo aparece em /admin/pedidos
🌐 Vai pro Mercado Livre / Pix
POST api.uber.com/v1/customers/{customer_id}/deliveries — cria corrida Uber DirectGET api.uber.com/v1/customers/{customer_id}/deliveries/{delivery_id} — status da corrida
⚠️ AtençãoDisponível apenas para pedidos com Flex Turbo — item precisa ter tag "Turbo" ativada no anúncioUBER_CLIENT_ID e UBER_CLIENT_SECRET devem estar configurados no .env — sem eles botão fica ocultoCobertura Uber Direct limitada a regiões metropolitanas — verificar disponibilidade antes de ativar para o vendedor
▸Fornecedor
45
🏭
Dashboard do Fornecedor
Visão consolidada das operações do fornecedor: pedidos de revenda pendentes, faturamento do mês, vendedores ativos e alertas de estoque baixo.
drop.rallydevendas.com.br/dashboard
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Fornecedor acessa drop.rallydevendas.com.br/dashboard com seu login (role=drop)
→
2Vê 4 KPIs: pedidos pendentes de pagamento, pedidos a despachar, receita do mês e vendedores ativos
→
3Verifica alertas: produtos com estoque abaixo do mínimo configurado e pagamentos em atraso
→
4Acessa atalhos rápidos: Pedidos, Catálogo, Financeiro, Integrações
→
5Usa o botão "Entrar como Vendedor" no sidebar para alternar entre a conta fornecedor e a conta vendedor (quando o fornecedor também é vendedor)
↓ o sistema reage por baixo
⚙️ O sistema faz
AutenticaValida role=drop na sessão; redireciona outros roles para seu subdomain correto (vendedor→/painel, admin→/admin)
AgregaConta pedidos drop por status (pending_payment, paid, dispatched), calcula receita do mês somando drop_orders aprovados
AlertaLista produtos com stock_quantity < min_stock_alert (configurável por produto) e pedidos com status pending_payment > 3 dias
⚠️ AtençãoVendedores vinculados ao fornecedor são gerenciados pelo admin — fornecedor não adiciona vendedores sozinhoEstoque no catálogo drop é independente do estoque ML — queda a zero não pausa anúncios automaticamente (alerta manual)
46
📦
Catálogo do Fornecedor
Fornecedor gerencia catálogo de produtos (fotos, preço de custo, estoque, descrição) visível para vendedores vinculados — edições propagam automaticamente para anúncios ML dos vendedores.
drop.rallydevendas.com.br/produtosdrop.rallydevendas.com.br/produtos/novodrop.rallydevendas.com.br/produtos/{id}
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Acessa /drop/produtos e vê lista de produtos com estoque e status de visibilidade
→
2Cria produto preenchendo nome, categoria, SKU, preço de custo, estoque, EAN e fazendo upload de até 6 fotos
→
3Define preço de custo e estoque mínimo para alertas de reposição
→
4Ativa/desativa visibilidade do produto na vitrine dos vendedores (toggle visível/oculto)
→
5Quando edita produto (título/fotos/descrição/preço), sistema propaga automaticamente para os anúncios ML dos vendedores preservando a margem de cada um
↓ o sistema reage por baixo
⚙️ O sistema faz
PersisteSalva produto em storage/json_db/users/{dropId}/products.json com foto em public/uploads
PropagaQuando produto é editado, DropPushAllService propaga para os anúncios ML dos vendedores vinculados via PUT /items/{id} — preservando margem do vendedor
DecrementaAo confirmar pedido drop (status=paid), decrementa estoque do produto no catálogo do fornecedor
🌐 Vai pro Mercado Livre / Pix
PUT /items/{id} — quando propagando edição de produto para anúncios dos vendedores (via DropPushAllService)
⚠️ AtençãoPropagar edição para produto com 100+ vendedores vinculados pode levar alguns minutos (propagação síncrona)Estoque do catálogo drop é independente do estoque ML — zerar estoque não pausa anúncios dos vendedores automaticamente
Fornecedor recebe, confere e despacha pedidos de revenda dos vendedores — com upload de etiqueta/NF, confirmação de pagamento PIX e histórico de status.
drop.rallydevendas.com.br/pedidosdrop.rallydevendas.com.br/pedidos/{id}
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Recebe notificação WhatsApp/Telegram quando vendedor faz pedido de compra novo
→
2Acessa /drop/pedidos e vê pedidos por status: aguardando pagamento, pago, despachado, entregue
→
3Verifica comprovante PIX do vendedor e confirma pagamento recebido
→
4Prepara e despacha o produto, faz upload da etiqueta de transporte e NF de custo
→
5Vendedor recebe notificação de despacho e pode acompanhar rastreio pelo painel
↓ o sistema reage por baixo
⚙️ O sistema faz
RecebeNovo pedido drop criado quando venda de anúncio de revenda entra como paga. DropOrderService::processNewMlOrders detecta via drop_origin no anúncio
NotificaEnvia alerta ao fornecedor (WhatsApp e/ou Telegram) com produto, quantidade, endereço de entrega do comprador ML e prazo de despacho
ConfirmaFornecedor confirma pagamento recebido (ou verifica comprovante PIX); status muda para paid — despacho autorizado
DespachaUpload de etiqueta PDF e NF-e de custo no pedido. Status atualiza para dispatched; vendedor recebe notificação automática
⚠️ AtençãoPrazo ML corre: fornecedor deve despachar em até 2 dias úteis após pagamento confirmado para não afetar reputação do vendedorPedidos sem drop_origin (anúncio não vinculado ao catálogo) não geram pedido drop — vendedor deve vincular o anúncio primeiro
48
💰
Financeiro do Fornecedor
P&L dos pedidos drop: receita, status de pagamento PIX dos vendedores e relatório do período — com visão de margem de cada lado (fornecedor e vendedor).
drop.rallydevendas.com.br/financeiro
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Acessa /drop/financeiro e seleciona período (30/60/90 dias)
→
2Vê resumo: total faturado, pedidos pagos, pendentes e margem média da carteira
→
3Filtra por vendedor ou produto para ver rentabilidade individual
→
4Exporta CSV com pedido, vendedor, produto, preço de custo, status pagamento, data despacho
↓ o sistema reage por baixo
⚙️ O sistema faz
AgregaCarrega drop_orders do período, cruza com products para preço de custo
CalculaMargem do fornecedor = preço de custo × quantidade. Margem do vendedor = (receita ML - taxa ML) - preço de custo. Mostra ambos os lados
Alerta pendênciasLista pedidos com payment_status=pending_payment há >48h como alertas de cobrança para o fornecedor
⚠️ AtençãoMargem do vendedor é estimada (Rally não tem acesso ao net_received real do vendedor via MP) — é indicativaPendências de pagamento PIX são cobradas diretamente — Rally não intermedia pagamentos entre vendedor e fornecedor
49
🔗
Integrações do Fornecedor
Conectar Mercado Pago via MP Connect para receber pagamentos dos vendedores diretamente, com geração automática de link de cobrança por pedido.
drop.rallydevendas.com.br/integracoesdrop.rallydevendas.com.br/integracoes/mercadopago
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Acessa /drop/integracoes e vê cartões de integrações disponíveis
→
2Clica em Conectar Mercado Pago e autoriza via OAuth MP para receber pagamentos
→
3Após conexão, pedidos com payment_method=mercado_pago geram link de pagamento automático para o vendedor
→
4Acompanha status de cada cobrança no painel de integrações
↓ o sistema reage por baixo
⚙️ O sistema faz
OAuth MPFluxo OAuth Mercado Pago: authoriza acesso, armazena collector_id e access_token criptografado (AES-256)
Link pagamentoQuando pedido criado com MP, gera preference MP (POST /checkout/preferences) com valor do custo + dados do vendedor como pagador
🌐 Vai pro Mercado Livre / Pix
POST api.mercadopago.com/checkout/preferences — gera link de pagamento MP para o vendedor pagar custo do pedido
⚠️ AtençãoMP Connect requer aprovação de aplicativo no painel MP — processo leva 1-3 dias úteisTaxas MP (3-4% por transação) são descontadas do fornecedor — preço de custo deve considerar esse custo
▸Gerente de Contas
50
📋
Dashboard do Gerente
Gerente de contas acompanha em tempo real a performance da carteira de vendedores — KPIs consolidados, alertas de saúde e acesso rápido às contas individuais.
gc.rallydevendas.com.br/dashboard
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Gerente acessa gc.rallydevendas.com.br/dashboard com login (role=gerente)
→
2Vê KPIs da carteira: total de vendedores ativos, faturamento consolidado do mês, pedidos totais e alertas críticos
→
3Identifica vendedores com queda de performance ou contas ML desconectadas (alerta em vermelho)
→
4Clica em um vendedor para ver detalhes da conta ou acionar impersonation (ver como o vendedor)
→
5Usa o switch "Entrar como Vendedor" no sidebar do GC para alternar entre as contas (vendedor → GC e vice-versa)
↓ o sistema reage por baixo
⚙️ O sistema faz
Carrega carteiraVisibilityService retorna vendedores vinculados ao gerente via bindings.json — respeitando escopo multi-tenant
Agrega KPIsPara cada vendedor, soma faturamento e pedidos do mês (usando date_closed). Calcula totais da carteira com AggregateCache
Detecta alertasMarca vendedores com: token ML expirado, reputação vermelha, pedidos atrasados >48h ou sem vendas nos últimos 7 dias
Switch sidebarNo painel vendedor (subdomain painel), gerente vê botão "Entrar como Gerente" para ir ao GC. No GC, vê "Entrar como Vendedor"
⚠️ AtençãoGerente só vê vendedores de sua carteira (bindings.json) — vínculos são criados pelo admin em /adminDados carregam com AggregateCache — podem ter delay de até 5 min vs tempo real
51
👥
Carteira de Vendedores (GC)
Gerente visualiza, monitora e acessa contas individuais da carteira — com métricas por vendedor, saúde ML, histórico de contatos e semáforo de status.
gc.rallydevendas.com.br/lojasgc.rallydevendas.com.br/lojas/{id}
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Acessa /gc/lojas e vê cartões de vendedores com: nome, plano, faturamento do mês, contas ML e status (semáforo)
→
2Filtra por status (ativo/alerta/crítico), plano ou período de inatividade
→
3Clica em um vendedor para ver perfil detalhado: contas ML, pedidos recentes, reputação e histórico de contatos
→
4Adiciona nota de acompanhamento (follow-up: ligação, mensagem, visita) na conta do vendedor para registro interno
→
5Envia notificação push ou WhatsApp diretamente ao vendedor pelo painel
↓ o sistema reage por baixo
⚙️ O sistema faz
ListaCarrega vendedores via VisibilityService + dados agregados de faturamento e reputação de cada um
ClassificaSemáforo: verde (ativo+reputação boa), amarelo (sem venda 7d ou token quase expirando), vermelho (reputação ruim ou desconectado)
Follow-upGerente registra notas de contato com vendedor (tipo + data + descrição) — salvas no histórico de follow-ups
⚠️ AtençãoDados de vendedores carregam com AggregateCache — podem ter delay de até 5 min vs dados em tempo real
Relatórios consolidados da carteira: faturamento por vendedor, evolução temporal, ranking e exportação para análise gerencial.
gc.rallydevendas.com.br/relatorios
📸 Tela real · clique pra ampliar
👤 Você faz e vê
1Acessa /gc/relatorios e seleciona período (semana/mês/trimestre)
→
2Vê ranking de vendedores por faturamento com variação vs período anterior
→
3Exporta CSV consolidado da carteira para repassar à diretoria da agência
↓ o sistema reage por baixo
⚙️ O sistema faz
ConsolidaItera sobre vendedores da carteira, agrega faturamento e pedidos por período usando date_closed para status paid/shipped/delivered
RankeiaOrdena por faturamento decrescente, calcula delta percentual vs período anterior para cada vendedor
ExportaCSV: vendedor, plano, contas_ml, faturamento, pedidos, ticket_medio, variacao_% — filtrado pelo escopo do gerente
⚠️ AtençãoRelatório respeita escopo do gerente — nunca inclui vendedores de outra carteira
▸Gestão
Central única (em abas) onde o vendedor cadastra produtos locais do Rally, controla estoque e movimentações, importa/exporta em massa via CSV e — se tiver fornecedor vinculado — navega o catálogo do fornecedor para criar anúncios. É o cadastro-base que abastece a criação de anúncios; preço de venda NÃO mora aqui, só custo e estoque.
👤 Você faz e vê
1No menu lateral, abra o grupo 'Gestão' e clique em 'Produtos & Estoque' (ícone de caixa) — a tela abre na aba Produtos com 3 KPIs: total de produtos, ativos e rascunhos.
→
2Na aba Produtos, veja a prévia dos 20 primeiros itens (foto, título, SKU, custo, estoque, status). Clique em 'Novo Produto' para abrir o cadastro (/produtos/criar) ou 'Gerenciar' para a lista completa (/produtos); clique num produto para abrir o detalhe/edição.
→
3Troque para a aba Estoque para ver as últimas 20 movimentações (entradas/saídas, quantidade, motivo). Use 'Nova Movimentação' / 'Histórico Completo' (/estoque) para registrar ajustes manuais.
→
4Na aba Importar / Exportar, baixe o modelo CSV, importe produtos em massa (/produtos/importar com preview antes de confirmar), exporte seu catálogo (/produtos/exportar) ou importe ajustes de estoque (/estoque/importar).
→
5Se você tiver fornecedor(es) drop vinculado(s) e com produtos ativos, aparece a aba 'Catálogo Fornecedor': filtre por fornecedor, busque por nome/SKU e clique em 'Anunciar' para criar um anúncio já apontando product_id + drop_id.
→
6Para estoque distribuído em vários depósitos/Full/Flex no mesmo anúncio ML, use o link Estoque Multi-Origem (/estoque/multi-origem) — só funciona em contas ML com multiwarehouse habilitado.
↓ o sistema reage por baixo
⚙️ O sistema faz
autenticaCentralProdutosController@index chama Auth::requireAuth(); resolve o user_id da sessão e monta as abas fixas (produtos, estoque, importar). A tela é o subdomínio painel.
monta-abasVerifica Binding::getDropIdsOf(userId); se houver fornecedores vinculados, carrega DropConfig::findByOwner + Product::byUser de cada drop (só status=active e sem vitrine_hidden) e injeta a aba 'Catálogo Fornecedor' dinamicamente.
renderiza-shellA view central/produtos.php é um shell Alpine.js com tablist; cada aba carrega seu partial (_tab_produtos / _tab_estoque / _tab_importar / _tab_catalogo). A aba ativa é sincronizada na URL via ?tab= sem recarregar a página.
carrega-produtos_tab_produtos lê Product::byUser(userId) do JSON DB per-user (products.json), conta total/ativos/rascunhos e lista os 20 primeiros com foto (photos[0]), SKU, custo e estoque — tudo dado LOCAL, sem chamar o Mercado Livre.
carrega-estoque_tab_estoque usa StockMovement::byUserPaginated(userId, 1, 20) para a fita de movimentações; entradas viram badge verde (+qtd) e saídas badge vermelho (-qtd). CRUD real fica no StockController (/estoque, /estoque/movimentar).
importa-exporta_tab_importar só linka os fluxos do ImportExportController: modelo CSV (/produtos/importar/modelo), preview+confirmar (/produtos/importar/preview e /confirmar), export (/produtos/exportar) e import de estoque (/estoque/importar) — o processamento real roda nesses endpoints, não na central.
multi-origemO link Estoque Multi-Origem aciona MultiOriginStockController, que para cada conta ML ativa com multiwarehouse habilitado descriptografa o token (MarketplaceAccount::decryptToken) e consulta os depósitos via MlMultiOriginService; gravação de qty por depósito é AJAX (MultiOriginApiTrait).
🌐 Vai pro Mercado Livre / Pix
A tela central em si NÃO chama o Mercado Livre — produtos, estoque e catálogo de fornecedor são todos dados LOCAIS do JSON DB (products.json, stock_movements.json, products do drop vinculado).Apenas a sub-tela Estoque Multi-Origem (/estoque/multi-origem) fala com o ML: lista depósitos/origens via MlMultiOriginService (User Products / stock locations) e grava quantidade por depósito; usa token descriptografado da conta.
⚠️ AtençãoPreço de venda NÃO existe no produto — só custo e estoque. O preço é definido no Anúncio. Quem procurar 'preço de venda' aqui não acha; cadastrar produto não publica nada no ML (status inicial é rascunho até virar anúncio).A aba 'Catálogo Fornecedor' some quando não há fornecedor drop vinculado E ativo com produtos — não é bug, é condicional (Binding + DropConfig status=active + produtos active não-hidden). Sem isso, só aparecem 3 abas.Estoque Multi-Origem só lista contas com multiwarehouse REALMENTE habilitado no ML (MlMultiOriginService::accountEnabled); contas sem User Products aparecem vazias. Cuidado: PUT /items legado em anúncio com user_product_id pode corromper o estoque multi-origem — gravar sempre pelo fluxo multi-origem, nunca pelo PUT de estoque antigo.
O vendedor junta 2+ produtos do próprio catálogo (ou 2-3 unidades do mesmo) num produto composto único — o custo e o estoque do kit são calculados sozinhos a partir dos componentes, pronto para virar anúncio depois.
👤 Você faz e vê
1Acesse 'Kits & Combos' no menu lateral (grupo Gestão). A tela mostra o formulário 'Montar kit' e a lista dos kits já criados. Se você ainda não tem produtos cadastrados, aparece um aviso para criar produto primeiro.
→
2Dê um nome ao kit (ex.: 'Kit 3 Camisetas Básicas') e, opcionalmente, defina o markup — deixe 0 para o canal de venda decidir o preço depois.
→
3Em 'Componentes', digite para buscar cada produto (autocomplete que filtra por título, ótimo para quem tem milhares de produtos), selecione-o e informe a quantidade por kit. Use 'Adicionar produto' para incluir mais itens.
→
4O botão 'Criar kit' só habilita quando o total de unidades é ≥ 2 e todos os produtos das linhas estão selecionados — o contador 'Total de itens' avisa em tempo real se falta algo.
→
5Na lista de kits, veja cada componente com foto, quantidade (ex.: '3×') e estoque individual; o sistema mostra o custo somado e o 'Estoque do kit' ao vivo (limitado pelo menor componente), com selo vermelho 'sem estoque' se algum componente zerar.
→
6Use 'Editar' (abre a edição do produto-kit em /produtos/{id}/editar) ou o lixeira para excluir o kit. O botão '→ Virar anúncio' aparece desabilitado, marcado como 'Em breve'.
↓ o sistema reage por baixo
⚙️ O sistema faz
autenticaKitController::index exige login com papel admin, gerente, drop ou cliente (Auth::requireRole). Carrega os produtos do usuário (Product::byUser) e separa em type='kit' (lista de kits) vs. demais (produtos disponíveis como componentes).
monta-formInjeta na view só id+título+custo de cada produto (prodJs) para alimentar o autocomplete Alpine 'kitForm'; pré-gera o CSRF (CSRF::field) no controller e passa como $csrfField para os forms.
valida-composicaoNo POST, CompositeProductService::createKit normaliza os componentes e exige composição válida: ≥1 componente e total de unidades ≥ 2 (cobre '2-3 unidades do mesmo produto'); título obrigatório. Falha lança InvalidArgumentException que vira flash de erro.
calcula-custokitCost soma custo_componente × quantidade de cada item (lê cost ou cost_price do produto) e arredonda — o kit nunca guarda preço de venda, só custo (regra Rally: preço só no anúncio).
calcula-estoquekitStock = mínimo de intdiv(estoque_componente, quantidade) entre todos os componentes; zera se algum componente acabar. A própria view recalcula esse estoque AO VIVO ao listar, refletindo o estoque atual de cada produto.
persisteO kit é gravado como um Product comum com type='kit', SKU auto-gerado (prefixo KIT-), components[], kit_markup, cost, stock e status=active (Product::create) — mesmo store dos produtos, fonte única marketplace-agnóstica.
excluidestroy verifica CSRF e chama Product::delete só no produto-kit; os produtos componentes NÃO são apagados (o CompositeProductService::delete reforça que só o composto sai). Após qualquer ação, redireciona para /produtos/kits com flash de sucesso/erro.
⚠️ AtençãoEsta tela é 100% local — NÃO publica nada no Mercado Livre. O botão '→ Virar anúncio' está desabilitado ('Em breve'); para anunciar o kit é preciso ir pela Central de Anúncios. Não há nenhuma chamada à API do ML aqui.Estoque do kit é derivado: é o menor intdiv(estoque, qtd) entre os componentes e zera se qualquer componente acabar. Vender o kit (deductKitSale, fase de pedido) baixa o estoque de CADA componente — não há estoque próprio do kit para ajustar manualmente.Composição inválida (menos de 2 unidades no total ou produto não selecionado em alguma linha) bloqueia o envio no front (botão desabilitado) e também no back (exceção). Se um produto componente for excluído do catálogo depois, o item aparece como 'produto (removido?)' e o custo/estoque do kit ficam distorcidos até reeditar.
Mostra, anúncio por anúncio, quanto sobra de líquido depois das taxas do Mercado Livre — ranqueado da pior margem para a melhor — cruzando o preço de venda com o custo cadastrado localmente para expor o gap de rentabilidade (headline Niada) sem nenhuma chamada ao vivo ao ML.
👤 Você faz e vê
1No painel, abra o grupo 'Gestão' no menu lateral e clique em 'Margem por SKU'
→
2No topo, leia os 6 KPIs: anúncios ativos, líquido médio %, anúncios de baixa margem, quantos têm custo cadastrado, vínculos de fornecedor e exposição de taxa por venda (R$)
→
3Se nenhum anúncio tiver custo nem vínculo de fornecedor, um alerta sugere cadastrar custos em Produtos & Estoque (link direto)
→
4Percorra a tabela já ordenada pior-margem-primeiro: cada linha traz thumbnail, título, badge da origem do custo (Produto / SKU / Fornecedor / sem custo), tipo de anúncio (Clássico/Premium/Grátis), frete grátis, preço, taxa ML, % líquido, custo/margem e estoque
→
5Clique numa linha para expandir o detalhamento (preço, taxa em R$ e %, líquido, custo, lucro, unidades vendidas, estoque, tipo e SKU)
→
6Use os botões do detalhe: 'Editar no Rally' (abre /anuncios/{id}/editar), 'Editar no ML' (deep-link para mercadolivre.com.br/anuncios/{mlb}/modificar) ou 'Cadastrar custo' (vai para /produtos) quando o anúncio não tem custo
→
7Foque nas linhas em vermelho: % líquido abaixo de 65% ou margem abaixo de 10% sinalizam anúncios que estão dando pouco (ou nenhum) lucro
↓ o sistema reage por baixo
⚙️ O sistema faz
autenticaMargemController@index chama Auth::requireAuth(); só vendedor logado acessa /margem (rota registrada em routes/painel.php, subdomínio painel)
cacheiaMargemService::forUser usa AggregateCache::remember (TTL 120s) — o agregado varre TODOS os anúncios, é pesado, então resultado fica cacheado por usuário (frescor ~2min)
indexa-custoMonta índice de custos a partir de products.json local: por product_id e por SKU (ignora produtos com custo <= 0). Nenhuma origem externa de custo
filtra-ativosLê announcements.json (cache local sincronizado do ML) e considera só status='active' com price > 0; descarta pausados, encerrados e em revisão
calcula-taxaPara cada anúncio chama MlFeeCalculatorService::calculate(price, listing_type, free_shipping) — taxa por tipo (Premium ~17%, Clássico ~13%, Grátis 0%), taxa fixa por faixa e subsídio de frete grátis. Cálculo 100% LOCAL via ml_ref_fee_structure.json, sem bater na API ML
resolve-margemResolve custo por prioridade: product_id → SKU → drop_origin (fornecedor = sem custo local). Calcula lucro (líquido − custo) e margem % sobre o preço; drop fica como 'fornecedor' sem número
ranqueiaOrdena pior-primeiro pela margem % (ou pelo % líquido quando não há custo) e gera o resumo: total, baixa margem (<65% líquido), com custo, vínculos drop, líquido médio e exposição total de taxa
🌐 Vai pro Mercado Livre / Pix
Nenhuma chamada ao vivo ao Mercado Livre nesta tela. Os anúncios vêm do cache local announcements.json (sincronizado por webhook/cron em outro fluxo) e as taxas são calculadas localmente via tabela de referência (MlFeeCalculatorService/ml_ref_fee_structure.json); os custos vêm de products.json. Os botões de ação apenas deep-linkam para o editor do Rally ou para o ML (mercadolivre.com.br/anuncios/{mlb}/modificar)
⚠️ AtençãoA taxa é uma ESTIMATIVA por tipo de anúncio (Premium/Clássico ~17%/13% + taxa fixa + subsídio de frete), não a taxa exata por categoria que o ML cobra. Para valor exato por categoria seria preciso MlPricingService (que chama a API ML), não usado aquiAnúncios de fornecedor (drop_origin) aparecem com badge 'Fornecedor' e margem em branco: o custo é externo e não fica no products.json do vendedor, então lucro/margem não são calculadosSem custo cadastrado em Produtos & Estoque, a coluna margem fica '—' e o ranking cai para o % líquido; KPIs 'com custo' e 'baixa margem' só fazem sentido depois de preencher os custos. Além disso o agregado é cacheado por 120s — mudanças de preço/custo recentes podem levar até ~2min para refletir
▸Extras
Automações por evento no estilo "quando acontecer X → me avise por Y": o vendedor cria regras que disparam uma mensagem automática no Telegram ou WhatsApp ao vender, receber pergunta ou logar no painel.
/macros ↗/macros/{id}/toggle/macros/{id}/test/macros/{id}/delete · clique pra abrir (já logado)
👤 Você faz e vê
1Acesse 'Macros' (grupo Extras) no menu lateral — na 1ª visita o sistema já semeia 4 macros de exemplo (todas pausadas): venda, pergunta, resumo ao logar e concorrente baixou preço.
→
2Confira a faixa 'Canais' no topo: ✅/⚠️ indica se Telegram e WhatsApp estão conectados — uma macro só ativa/testa se o canal dela estiver pronto (link 'conectar' leva a /telegram, 'cadastrar' leva a /perfil).
→
3No formulário, preencha Nome, escolha o Gatilho (Nova venda, Nova pergunta, Ao acessar o painel, Concorrente mudou preço), o canal (Telegram/WhatsApp) e a Mensagem.
→
4Monte a mensagem clicando nas variáveis disponíveis do gatilho ({produto}, {valor}, {qtd}, {pergunta}, {vendas_hoje}, {preco_novo}, {concorrente}…) — elas são substituídas pelos dados reais no disparo.
→
5Salve (a macro nasce PAUSADA), clique em 'Testar' para receber uma mensagem de exemplo 🧪 no seu canal e, confirmado o recebimento, clique em 'Ativar'.
→
6Use os botões Editar (lápis), Pausar/Ativar e Excluir (lixeira) em cada card para gerenciar as macros existentes.
↓ o sistema reage por baixo
⚙️ O sistema faz
autenticaindex() exige login (Auth::requireRole admin/gerente/drop/cliente) e resolve o user_id da sessão — macros são per-user (storage/json_db/users/{id}/macros.json).
semeiaMacro::ensureExamples cria 4 macros de exemplo desativadas na 1ª visita e marca users.macros_seeded para não repetir; demais visitas só carregam a lista existente.
valida-canalMacroService::channelReady checa pré-requisito: Telegram exige users.telegram_chat_id; WhatsApp exige users.whatsapp/phone com ≥10 dígitos. Sem isso, ativar/testar é bloqueado com hint.
salvastore() valida gatilho/canal contra Macro::TRIGGERS e Macro::CHANNELS, trunca nome (60) e mensagem (600), e cria/atualiza via JsonDatabase::forUser. Nova macro nasce enabled=false (testar antes de ativar).
testatest() (AJAX, CSRF::validatePersistent) chama MacroService::runTest: interpola a mensagem com dados de exemplo, prefixa 🧪 [TESTE de macro] e envia DE VERDADE via NotifyDispatchService::toUser (transactional=true, fura caps anti-ban).
dispara-eventoEm eventos reais, hooks chamam MacroService::runForEvent(trigger, userId, ctx): on_sale e on_question via webhook ML (MlWebhookTopicsTrait), on_login via PainelDashboardController (1x/dia, guarda macro_login_last). Filtra macros ativas do gatilho, interpola e despacha pelo canal.
blindarunForEvent roda dentro de try/catch que engole exceções — uma macro nunca pode quebrar o fluxo principal de venda/pergunta/login; falha de envio é silenciosa.
🌐 Vai pro Mercado Livre / Pix
Nenhuma chamada direta à API do ML. As macros on_sale/on_question são disparadas a partir do webhook do Mercado Livre (orders/questions) já processado pelo MlWebhookTopicsTrait — a macro só consome o contexto do evento.Envio das mensagens via NotifyDispatchService (Telegram Bot API / WhatsApp Blipei), modo transacional.
⚠️ AtençãoGatilho 'Concorrente mudou preço' (on_price_change) aparece no seletor e é semeado como exemplo, mas NÃO tem nenhum hook que o dispare no código — macro criada com esse gatilho nunca envia nada (só funciona no botão Testar).Canal não conectado trava tudo: sem telegram_chat_id (ou WhatsApp <10 dígitos no perfil) o botão Testar fica desabilitado e Ativar retorna erro. Conectar em /telegram ou /perfil antes.Macro de 'Ao acessar o painel' (on_login) dispara no máximo 1x por dia (trava macro_login_last com a data); não avisa a cada login do mesmo dia.
▸Gestão
Conectar (BYOK) o token da própria conta LogManager — a transportadora same-day homologada pelo ML por trás dos pedidos Flex Turbo — para a vitrine gerar etiquetas de Flex automaticamente e responder o rastreio do comprador direto nas mensagens do Mercado Livre, sem link nem dados pessoais.
👤 Você faz e vê
1No painel, abra o grupo 'Gestão' no menu lateral e clique em 'LogManager (Flex)' (ícone com o favicon da marca). A tela mostra o status da conexão (conectada/pendente) e dois cartões: conectar o token e preferências de mensagem ao comprador.
→
2Se ainda não conectou, abra 'Como pegar meu token na LogManager?' (ou o botão 'Guia passo a passo'): entre em app.logmanager.com.br › Integrações › 'Criar nova integração' › canal 'api' › Salvar › copie a 'API Key'.
→
3Cole a API Key no campo Token (input password, autocomplete off) e confirme/ajuste a Transportadora — o sistema já sugere o nome detectado pelo seu cadastro (CarrierLink).
→
4Clique em 'Testar conexão' (AJAX, throttle 3s) para confirmar que a LogManager está acessível, depois em 'Salvar token' — ele é guardado criptografado e só volta como máscara (••••1234).
→
5No cartão 'Rastreio nas mensagens do Mercado Livre', marque 'Responder rastreio automaticamente' (recomendado, default ON) e, se quiser, 'Avisar a previsão de entrega de forma proativa' (sancionada pelo ML, default OFF) e salve.
→
6Se entrou pela sua transportadora parceira, use a CTA verde '3 meses grátis' para solicitar a parceria e abrir a conversa no WhatsApp. Para desconectar, clique em 'Desconectar' (pede confirmação) — o rastreio continua funcionando mesmo sem token.
↓ o sistema reage por baixo
⚙️ O sistema faz
autenticaindex() chama Auth::requireAuth(); carrega UserLogManagerService::forView() (connected, máscara, connected_at, last_ok_at, carrier_name, prefs de mensagem) e monta CSRF::field().
sugere-transportadoraA API da LogManager não revela a transportadora pelo token, então o sistema varre CarrierLink::active() do usuário e pré-preenche o nome da transportadora vinculada no cadastro; checa referred_by_carrier e partnership_requested_at para exibir a CTA de parceria (3 meses grátis).
salva-tokenPOST /conectar valida CSRF, valida formato (não vazio, ≥16 chars, sem espaços) e grava em users.json › logmanager.token CRIPTOGRAFADO (AES-256-GCM, prefixo 'enc:') via Encryption::encrypt; registra evento em LogManagerLog (token mascarado). Token cru NUNCA volta ao front.
testa-sem-efeitoPOST /testar (AJAX, ApiAuthMiddleware, CSRF persistente) chama LogManagerClient::ping() = GET /api/v1/shipmentsV2/0 — propositalmente SEM criar envio (o callback dispara motorista real). Qualquer HTTP (200/404/422) = host no ar; marca last_ok_at.
prefs-mensagemPOST /preferencias grava {auto_reply, proactive_eta} em users.json › logmanager.msg e mantém um índice enxuto lm_proactive_optin.json (cron lê só quem ativou ETA pró-ativa — custo zero quando ninguém ligou).
gera-etiqueta-flexQuando há venda Flex/Turbo, o token BYOK habilita LogManagerClient::createShipment() (POST /api/integrations/erp/callback, Bearer) para criar a entrega e devolver label_url/tracking_url na conta do próprio vendedor; rate-limit real de 10 req/min por conta.
rastreio-publicoO rastreio NÃO usa este token: endpoints públicos (track por chave NFe e shipmentsV2 por id) alimentam a resposta automática ao comprador no ML, mapeando o status LogManager → status do shipment ML (entregue/a caminho/coletado etc.) sem incluir link, telefone ou e-mail.
🌐 Vai pro Mercado Livre / Pix
POST https://app.logmanager.com.br/api/integrations/erp/callback (Bearer token do vendedor) — cria a entrega Flex e devolve label_url/tracking_url (efeito colateral: dispara motorista; só no Flex real, nunca no Testar)GET /api/v1/shipmentsV2/{id} — consulta entrega por id (status + histórico + etiqueta); usado também no ping com id=0 para testar conectividade sem criar envio (público)GET /api/v1/shipments/track/{nfeKey} — rastreio pela chave da NF-e (44 dígitos): situação, comprovante, recebedor, data prevista (público, dispensa token)Mapeamento de status LogManager → ML (delivered/shipped/returning_to_sender/lost...) feito localmente para responder o rastreio dentro das mensagens do Mercado Livre
⚠️ AtençãoO botão 'Testar conexão' só confirma que a LogManager está no ar (formato do token + host alcançável) — NÃO valida que o token é válido para despachar. A validação definitiva só acontece no primeiro Flex realmente despachado pela vitrine, porque o /erp/callback cria um pedido real (motorista a caminho).Sem token conectado a vitrine deixa de gerar etiqueta de Flex automaticamente, mas o RASTREIO continua funcionando (endpoints públicos por NFe/shipment id) — não confunda 'sem conexão' com 'rastreio quebrado'.A mensagem pró-ativa de previsão de entrega (proactive_eta) começa DESLIGADA por design: ela usa a cota sancionada DELIVERY_PROMISE do ML; ligar e usar em escala sem validar numa conta de teste pode esgotar a cota. A resposta automática reativa só dispara quando o comprador pergunta e nunca inclui link/telefone/e-mail (regra de moderação do ML).
▸TikTok
Transformar um produto/anúncio do Rally em anúncio do TikTok Shop (Brasil e México): o vendedor escolhe um produto, vê uma pré-visualização do anúncio TikTok com avisos de pré-requisito, e ficará a 1 clique de publicar assim que o app de integração for aprovado no TikTok Shop Partner Center.
/tiktok/shop ↗/tiktok/shop?id={announcement_id}/tiktok/shop?page={n} · clique pra abrir (já logado)
👤 Você faz e vê
1Ative a integração TikTok em Configurações da Conta (/central/config?tab=lojas) — só assim a seção 'TikTok' aparece no menu lateral (com Visão geral, Conteúdo, Relatórios e TikTok Shop, este com selo 'análise')
→
2Clique em 'TikTok Shop' no menu. A tela explica que o app está em análise no TikTok Partner Center: você já pode preparar e pré-visualizar, mas o envio libera após a aprovação
→
3Veja a grade dos seus anúncios do Rally (12 por página, com foto e preço) e clique em um produto para transformá-lo em anúncio do TikTok Shop
→
4O sistema monta a pré-visualização: nome do produto (cortado em 255 caracteres), preço em R$, estoque, atributos e até 3 imagens — espelhando como ficaria no TikTok Shop
→
5Leia o bloco 'Antes de publicar' com os avisos de pré-requisito (categoria a mapear, GTIN/marca por categoria, sem imagem, estoque zerado) e ajuste o produto se preciso
→
6Use 'Escolher outro produto' para voltar à grade, ou pagine entre seus anúncios. O botão 'Clonar pro TikTok Shop' fica desabilitado (aguardando aprovação) — nenhum dado é enviado ao TikTok por enquanto
↓ o sistema reage por baixo
⚙️ O sistema faz
gate-menuA seção TikTok no sidebar só renderiza se UserIntegrations::has(userId, 'tiktok') for true — flag em users.json.integrations, ligada/desligada em /central/config?tab=lojas. Sem a flag, a tela existe mas não há link no menu
autenticaTikTokController@shop chama Auth::requireRole(admin, gerente, drop, cliente) — qualquer papel logado acessa; rota GET /tiktok/shop registrada em routes/painel.php com middleware $auth
carregaPagina os anúncios do próprio usuário via Announcement::byUserPaginated(userId, page, 12) — 100% dados locais do Rally, ZERO chamada ao TikTok no carregamento da tela
mapeiaAo selecionar (?id=), Announcement::findById carrega o anúncio e UnifiedListingMapper::fromAnnouncement o converte no formato unificado (title, price, stock, photos, attributes, category_hint, source_mlb)
previewUnifiedListingMapper::tiktokShopPreview gera a pré-visualização: corta product_name em 255 chars, monta preço/estoque/atributos/imagens — tudo calculado localmente, sem tocar a API do TikTok
valida-preflightO mapper acumula warnings de pré-requisito: título >255 chars, ausência de imagem, categoria ML a mapear para a árvore TikTok (GetCategoryTree), GTIN/marca por categoria (GetCategoryRules) e estoque zerado — exibidos no bloco 'Antes de publicar'
publish-gatedO botão 'Clonar pro TikTok Shop' renderiza com atributo disabled e title 'Disponível quando o TikTok aprovar o app'. Não existe rota de POST de publicação no TikTok Shop — o push real está desligado até a aprovação no Partner Center
🌐 Vai pro Mercado Livre / Pix
Nenhuma chamada externa em tempo real nesta tela: o picker e a pré-visualização usam só dados locais do Rally (announcements.json). O envio ao TikTok Shop (POST /products via TikTokShopClient) está implementado no service mas NÃO ligado à UI — botão desabilitado até aprovação do app
⚠️ AtençãoSe a seção TikTok não aparece no menu, a integração não foi ativada: vá em Configurações da Conta › Lojas (/central/config?tab=lojas) e ligue o TikTok (flag em users.json.integrations.tiktok)Publicação real é vaporware controlado: o botão 'Clonar pro TikTok Shop' está sempre disabled enquanto o app aguarda aprovação no TikTok Shop Partner Center. A tela só prepara e pré-visualiza — nenhum anúncio é criado no TikTok aindaA pré-visualização sai dos dados do anúncio ML local: se faltar imagem (photos[0]), categoria (category_hint) ou estoque, o produto vira avisos de pré-requisito em vez de anúncio pronto — corrija na origem (anúncio/produto) antes que o push seja liberado
▸Shopee
Hub do marketplace Shopee dentro do painel: o vendedor pega um produto do Rally e vê em tempo real como o anúncio sairia na Shopee (nome ≤120, preço, estoque, fotos, atributos + avisos de pré-requisito) antes de publicar. O envio e o painel de pedidos ficam travados ('planejado / em breve') até o conector Shopee Open Platform ser ativado.
👤 Você faz e vê
1Ativa a integração Shopee em Central de Configurações › Lojas (/central/config?tab=lojas) — só então a seção 'Shopee' aparece no menu lateral (badge 'em breve').
→
2Abre 'Shopee › Visão geral' (/shopee): vê 3 cartões — Produtos→anúncio (preview), Pedidos (planejado) e Documentação oficial — mais um aviso de que o conector ainda não está ligado.
→
3Clica em 'Produtos' (/shopee/produtos) e vê a lista paginada (12 por página) dos seus anúncios do Rally com foto e preço.
→
4Clica num produto: o sistema converte o anúncio Rally em preview Shopee e mostra nome (cortado a 120 caracteres), preço, estoque, até 3 imagens, atributos e uma lista de avisos do que falta ajustar.
→
5Lê os avisos (título longo, sem foto, categoria a mapear, marca possivelmente exigida, estoque zerado) e ajusta o produto no Rally antes de publicar.
→
6Tenta 'Publicar no Shopee' — o botão está DESABILITADO com a etiqueta 'planejado'; o envio só liga quando o app for cadastrado no Shopee Open Platform.
→
7Abre 'Pedidos' (/shopee/pedidos): vê um placeholder explicando que pedidos (status, comprador, valor, faturar, etiqueta, marcar enviado) entram aqui quando o conector ativar.
↓ o sistema reage por baixo
⚙️ O sistema faz
autorizaMarketplaceHubController::hub/produtos/pedidos roda Auth::requireRole(admin, gerente, drop, cliente) — qualquer papel logado acessa.
resolve-marketplaceDeriva o marketplace do 1º segmento da URI (/shopee) e valida contra MarketplaceCatalog (whitelist). Se não estiver no catálogo → HTTP 404 'Marketplace não encontrado'. Config 'shopee' traz label, ícone, cor #ee4d2d, status=planejado, gated=true e gated_reason.
lista-produtosEm /shopee/produtos chama Announcement::byUserPaginated(userId, page, 12) — pega anúncios reais do Rally do próprio vendedor, paginados, com foto (photos[0]) e preço.
monta-previewAo selecionar um produto (?id=), UnifiedListingMapper::fromAnnouncement normaliza o anúncio e shopeePreview gera o preview: nome=mb_substr(title,0,120), preço, estoque, fotos, atributos + avisos (título>120, sem imagem, categoria ML a mapear via get_category, marca possivelmente exigida via get_brand_list, estoque<1).
renderizaTudo é catalog-driven e usa as mesmas views genéricas (painel/marketplace/index.php, produtos.php, pedidos.php) compartilhadas com o Magalu — nenhuma tela exclusiva da Shopee.
gate-publishComo gated=true / status=planejado, o botão 'Publicar no Shopee' é renderizado disabled e a tela de pedidos é só placeholder. A UI funciona (preparar/pré-visualizar), mas o push fica travado até o conector ser ativado.
menu-condicionalA seção Shopee só aparece no sidebar se UserIntegrations::has(userId,'shopee') — flag persistida em users.json › integrations.shopee, ligada/desligada em /central/config?tab=lojas.
🌐 Vai pro Mercado Livre / Pix
Nenhuma chamada à API da Shopee é feita — o conector ainda não está ativado (status=planejado, gated). O preview é 100% local, a partir de dados do anúncio do Rally.Os anúncios usados no preview vêm do cache local do Rally (announcements.json do vendedor, populado pelo sync do Mercado Livre) — não há chamada externa em tempo real.Quando ativado, a integração deverá usar o Shopee Open Platform (get_category, get_brand_list, add_item, get_order_list) — documentação oficial linkada em open.shopee.com/developer-guide/12.
⚠️ AtençãoTela gated: o botão 'Publicar no Shopee' está sempre desabilitado e Pedidos é placeholder. Falta cadastrar o app no Shopee Open Platform Console (partner_id + partner_key) e o vendedor autorizar a loja. Hoje a tela só prepara e pré-visualiza — não publica nem sincroniza.A seção Shopee fica invisível no menu até o usuário ativar a integração em Central de Configurações › Lojas (/central/config?tab=lojas) — sem isso, /shopee até responde por URL direta mas o atalho não aparece no sidebar.O preview reaproveita as views genéricas de marketplace (compartilhadas com o Magalu) e o cache local de anúncios do ML; produtos sincronizados há muito tempo ou sem foto/atributos saem com avisos no preview. Categoria e marca da Shopee ainda precisarão de mapeamento manual (get_category/get_brand_list) quando o conector ligar.
▸Marketplaces
Hub do marketplace Magalu (Magazine Luiza) no painel: o vendedor prepara e pré-visualiza como um produto do Rally sairá como anúncio no Magalu (nome, SKU, preço, estoque, fotos, atributos + avisos fiscais/fulfillment) antes de publicar. UI 100% navegável; o envio real (publicar/sincronizar pedidos) fica travado em 'em breve' até o conector Magalu ser ativado.
👤 Você faz e vê
1Ative a integração Magalu em /central/config?tab=lojas — só então a seção 'Magalu' aparece no menu lateral (grupo Marketplaces, ícone sacola azul). Sem ativar, nada aparece.
→
2Clique em 'Magalu › Visão geral' (badge 'em breve'). A tela explica o status ('planejado'), o que falta para ligar o conector (app no ID Magalu + scopes + consentimento do seller no Portal Magalu) e mostra 3 cards: Produtos→anúncio (preview), Pedidos e link da Documentação oficial.
→
3Em 'Produtos', veja a lista paginada dos seus anúncios do Rally (12 por página, foto + título + preço) e clique em um produto para gerar o preview.
→
4Veja o preview do anúncio formatado para o Magalu: nome do produto, SKU, preço (R$), estoque, até 3 fotos (+contador) e os atributos — exatamente como o item sairia no portfólio Magalu.
→
5Leia o bloco de avisos antes de publicar (ex: 'sem SKU — o Magalu identifica pelo SKU', cadastro assíncrono HTTP 202 via webhook, NCM/dados fiscais para NF-e, definir se é fulfillment ou envio próprio).
→
6O botão 'Publicar no Magalu (planejado)' aparece desabilitado — liga só quando o conector for ativado. Em 'Pedidos', a tela é um placeholder que descreve o painel futuro (faturar/etiqueta/marcar enviado, espelhando o ML).
↓ o sistema reage por baixo
⚙️ O sistema faz
deriva-marketplaceMarketplaceHubController é genérico e catalog-driven: deriva o marketplace do 1º segmento da URI (/magalu) e valida contra MarketplaceCatalog::get('magalu') (whitelist). Se não estiver no catálogo, responde 404. As rotas são registradas em loop sobre MarketplaceCatalog::all() — adicionar marketplace não cria tela nova.
autorizaAuth::requireRole('admin','gerente','drop','cliente') nas 3 ações (hub/produtos/pedidos). A seção no sidebar é gateada por UserIntegrations::has($userId,'magalu') — flag em users.json → integrations.magalu, ligada em /central/config?tab=lojas.
carrega-configLê a config declarativa do Magalu do MarketplaceCatalog (label, icon ph-shopping-bag-open, cor #0086ff, regions=[Brasil], status='planejado', gated=true, gated_reason, capabilities=[produtos,pedidos], docs developers.magalu.com). Nenhuma chamada externa — config estática em PHP.
lista-produtosEm /magalu/produtos, Announcement::byUserPaginated($userId,$page,12) carrega os anúncios do Rally do usuário (JSON DB per-user), paginado 12 por página. Foto vem de photos[0] (campo thumbnail é morto).
mapeia-unificadoAo selecionar um produto (?id=), UnifiedListingMapper::fromAnnouncement converte o anúncio no formato canônico UnifiedProduct (title, sku, price, stock BRL, photos, attributes, category_hint, source_mlb) — o mesmo objeto que alimentaria todos os adapters (TikTok/Shopee/Magalu).
gera-previewChama UnifiedListingMapper::magaluPreview (resolvido via mk.preview_fn) que monta o preview específico do Magalu e a lista de warnings (SKU obrigatório, ≥1 imagem, cadastro assíncrono 202+webhook, NCM/fiscal NF-e, fulfillment vs envio próprio, estoque>0). Tudo local, sem rede.
trava-publicacaoComo gated=true, o botão 'Publicar' renderiza disabled e a aba Pedidos é um placeholder estático. Não há POST de publicação nem sync de pedidos — o conector Magalu ainda não está implementado (backend de publish/orders inexistente).
🌐 Vai pro Mercado Livre / Pix
Nenhuma chamada externa real nesta tela. O preview é gerado 100% local a partir dos anúncios já existentes no Rally (UnifiedListingMapper).Endpoints Magalu citados apenas como AVISO no preview (não chamados): cadastro de SKU de portfólio é assíncrono (HTTP 202 + confirmação por webhook); ref. oficial developers.magalu.com/docs/apis. A autenticação seria via app no ID Magalu (client_id+secret IDM CLI) + consentimento do seller — ainda não configurado.
⚠️ AtençãoVaporware parcial: a publicação real e o painel de pedidos NÃO existem (botão 'Publicar' fica disabled, /magalu/pedidos é placeholder). Só preview de produto funciona de verdade. Status 'planejado'/'em breve'.A seção Magalu só aparece no menu se UserIntegrations.magalu=true (ativar em /central/config?tab=lojas). Sem isso, as rotas /magalu existem mas nada no sidebar leva a elas — vendedor 'não acha'.Preview puxa anúncios do Rally; se o usuário não tem anúncios, a tela de produtos mostra empty-state ('crie ou sincronize produtos primeiro'). Sem SKU no produto, o aviso lembra que o Magalu identifica o item do portfólio pelo SKU — preview funciona, mas publicar (quando ligar) exigiria SKU.
▸Vitrine
O vendedor cria e gerencia cupons de desconto da sua loja própria (vitrine com checkout) — percentual, valor fixo em reais ou frete grátis — com regras de pedido mínimo, validade e limite de usos, que o comprador aplica no carrinho durante o checkout.
👤 Você faz e vê
1No painel, abra o grupo 'Vitrine' no menu lateral e clique em 'Cupons' (ícone de ticket). A tela mostra o formulário 'Novo cupom' no topo e a tabela com os cupons já criados (ou um estado vazio convidando a criar o primeiro)
→
2Preencha o formulário: Código (ex: BEMVINDO10, digitado em maiúsculas), Tipo (% de desconto, R$ de desconto ou Frete grátis), Valor, Pedido mínimo em R$, Máx. usos (0 = ilimitado) e Validade opcional
→
3Clique em 'Criar cupom' — ele aparece na tabela já ativo; o sistema mostra um aviso verde 'Cupom criado!' (ou 'Informe o código e um valor válido' se faltar dado)
→
4Na tabela, veja por cupom: código, tipo de desconto, mínimo, usos atuais/máximo, validade e status. Clique no botão de status (Ativo/Inativo) para ligar ou pausar o cupom sem excluí-lo
→
5Para remover de vez, clique na lixeira — pede confirmação ('Excluir este cupom?') antes de apagar
→
6O comprador, no checkout público da sua loja, digita o código do cupom no carrinho; o desconto (ou frete grátis) é aplicado em tempo real e o contador de 'usos' do cupom sobe quando o pedido é fechado
↓ o sistema reage por baixo
⚙️ O sistema faz
autenticaAuth::requireRole(...ROLES) garante que só o dono logado acessa /loja/cupons; pega o user_id da sessão como sellerId. Mesmas ROLES e guardPost() reaproveitados do PainelStoreOrdersController (CSRF obrigatório em todo POST)
listaStoreCouponService::bySeller lê a coleção user-scoped store_coupons via JsonDatabase::forUser(sellerId)->readAll — cada vendedor só vê e edita os próprios cupons (multi-tenant por sharding JSON)
criacouponCreate normaliza o código (uppercase, sem espaços), valida o tipo contra a whitelist [percent, fixed, free_ship], arredonda valor/mínimo, força max_uses>=0 (0=ilimitado), zera 'used' e grava active=true. Exige código + (valor>0 OU tipo=free_ship)
ativa-pausacouponToggle faz update do campo active no JSON (true/false) — pausa o cupom sem perder histórico de usos; couponDelete remove o registro definitivamente
valida-checkoutNo checkout público, POST /checkout/coupon chama StoreCouponService::validate(sellerId, code, subtotal): confere se existe, está ativo, não expirou (expires_at), não esgotou (used < max_uses) e atinge o pedido mínimo; calcula o desconto e devolve JSON com label amigável
aplica-no-pedidoStoreCheckoutService::place RE-valida server-side via StoreCouponService::apply: recalcula desconto/frete grátis sobre o subtotal real e chama recordUse() para incrementar o contador de usos do cupom — nunca confia só na validação do front
renderizaA view painel/loja/cupons.php monta a tabela responsiva (vira cards no mobile <=760px), sanitiza todo output com htmlspecialchars(ENT_QUOTES) e renderiza dentro de layouts/app.php; flash de sucesso/erro vem da sessão
⚠️ AtençãoSem chamadas ao Mercado Livre — esses cupons valem SÓ na vitrine/loja própria do vendedor (checkout direto). Não têm relação com os cupons do ML, que ficam em outra tela (/api/ml/coupons via MlApiExtendedController)A validação no front (POST /checkout/coupon) é só UX; a verdade é a re-validação em StoreCheckoutService::place. O contador 'usos' só sobe quando o pedido é efetivamente fechado, não quando o comprador apenas digita o códigoFrete grátis (free_ship) não exige valor, mas os tipos percent/fixed exigem valor>0 — caso contrário o couponCreate recusa com 'Informe o código e um valor válido'. Código é sempre normalizado em MAIÚSCULAS sem espaços, então 'bemvindo 10' e 'BEMVINDO10' colidem
▸Crescimento
Vitrine de gamificação do vendedor — mostra nível atual (Iniciante→Diamante), pontos acumulados, barra de progresso pro próximo nível, streak de semanas em alta e o grid das 10 conquistas (badges) desbloqueadas/bloqueadas, tudo calculado a partir da qualidade dos anúncios e do histórico de vendas no ML, sem cobrar nada nem chamar IA.
👤 Você faz e vê
1No menu lateral do painel, abra o grupo 'Crescimento' e clique em 'Meu Pódio' (ícone de troféu) — leva para /conquistas
→
2Veja no topo (hero) seu nível atual com medalha/emoji, total de pontos formatado e a barra mostrando quantos pontos faltam pro próximo nível (ou '🎉 Você chegou ao nível máximo!' no Diamante)
→
3Confira as 4 mini-estatísticas: nº de conquistas desbloqueadas, anúncios ativos, semanas consecutivas em alta (🔥 streak) e score médio dos anúncios
→
4Role até o grid de conquistas: badges desbloqueadas aparecem coloridas com ✓ e rótulo 'Desbloqueada'; as bloqueadas ficam em cinza (grayscale) com cadeado 🔒 e 'Bloqueada'
→
5Suba de nível vendendo mais e melhorando seus anúncios — a página é só leitura/consulta, não tem botões de ação nem formulários (gamificação puramente informativa)
↓ o sistema reage por baixo
⚙️ O sistema faz
autenticaAuth::requireRole('admin','gerente','drop','cliente') — qualquer papel logado acessa; pega o user_id da sessão. Sem PlanGuard: a tela não é feature paga.
cacheSellerGamificationService::forUser() serve de cache local storage/cache/gamification_{userId}.json com TTL de 30min (1800s); só recomputa se expirado ou força. Invalidate() apaga o arquivo.
pontos-qualidadePercorre Announcement::byUser, filtra só status='active' com marketplace_item_id, e soma o score (0-100) de cada um via AnnouncementDiagnosticService::analyze() — calculado LOCALMENTE da dados em cache do anúncio (descrição, fotos, atributos, logística, promo), sem chamar o ML.
pontos-vendascomputeSalesContext() lê ml_orders.json local: +10 pts por pedido em 90d e +1 pt a cada R$10 faturados em 90d. Usa date_closed quando status∈{paid,shipped,delivered}, senão date_created; ignora cancelled/invalid.
niveisSoma pontos (qualidade+vendas) e resolve o nível na escala fixa LEVELS: 🌱 Iniciante 0 · 🥉 Bronze 500 · 🥈 Prata 2.000 · 🥇 Ouro 5.000 · 💎 Platina 15.000 · 👑 Diamante 30.000; calcula próximo nível, pts_to_next e progress_pct.
conquistascomputeBadges() avalia 10 badges sobre o total histórico (lifetime): 1ª venda, 10/100 pedidos, R$1k/10k/50k faturados, ativo 90d, 30+ pedidos em 90d, 30/90 dias vendendo. Cada uma vira unlocked=true/false.
streakcomputeStreakWeeks() agrupa ml_orders por semana (ISO o-W), soma faturamento por semana e conta semanas consecutivas com receita maior que a anterior, de trás pra frente — esse é o número '🔥 semanas em alta'.
renderizaPainelConquistasController seta $g e require da view painel/conquistas.php, que monta hero+stats+grid em CSS BEM/tokens dentro do layout app.php. Empty-state amigável quando não há badges ('Conecte sua conta ML e comece!').
⚠️ AtençãoPontos/badges/streak refletem o cache de 30min (gamification_{userId}.json) E o cache de ml_orders/announcements — vendas recém-sincronizadas só aparecem após o TTL expirar ou SellerGamificationService::invalidate() ser chamado; não há refresh ao vivo na tela.Score de qualidade só conta anúncios com status='active' e marketplace_item_id preenchido — vendedor com tudo pausado/encerrado ou sem conta ML conectada vê 0 pontos de qualidade e fica no nível Iniciante mesmo tendo vendido.A escala de níveis na constante LEVELS do service (Bronze=500, Prata=2.000) diverge do comentário/docstring do topo do arquivo (que cita Bronze=2.000, Prata=5.000) — o que vale é o array LEVELS; ao mexer em thresholds, conferir o array, não o comentário.
▸Extras
O vendedor pareia sua conta Rally com o bot do Telegram (@rallydevendas_bot) para receber alertas (vendas, perguntas, lembretes) e conversar com o assistente direto no chat — gera um código de 6 caracteres, manda /link CODIGO no bot e pronto.
👤 Você faz e vê
1Acesse 'Telegram' no grupo Extras do menu lateral. A tela mostra um cartão com o status de conexão: conectado (mostra o chat_id) ou ainda não pareado
→
2Se não estiver conectado, clique no botão azul 'Abrir bot (@rallydevendas_bot)' que leva direto pro chat do bot no Telegram (https://t.me/rallydevendas_bot)
→
3Clique em 'Gerar código' — o sistema cria um código de 6 caracteres (ex: A1B2C3) e mostra na tela com botão de copiar
→
4No chat do bot, mande a mensagem /link CODIGO (substituindo CODIGO pelo gerado). O bot confirma com '✅ Conectado, [seu nome]!'
→
5De volta na tela, o cartão passa a exibir 'Conectado' com o chat_id; mande /ajuda no bot pra ver os comandos disponíveis
→
6Para desfazer, clique em 'Desconectar' (pede confirmação) — limpa o vínculo e o bot para de enviar mensagens
↓ o sistema reage por baixo
⚙️ O sistema faz
autenticaAuth::requireRole('admin','gerente','drop','cliente') — qualquer papel logado acessa /telegram (middleware $auth na rota)
carrega-estadoChatState::get(userId) lê o registro único chat_state.json do usuário (per-user shard) e extrai telegram_chat_id (vínculo ativo) e telegram_link_code (código pendente)
resolve-botLê TELEGRAM_BOT_USERNAME do .env (fallback 'rallydevendas_bot') pra montar o deep-link https://t.me/{bot} e o CTA azul de abrir o chat
gera-codigoPOST /telegram/gerar: CSRF::verify, gera código de 6 hex (strtoupper(substr(bin2hex(random_bytes(4)),0,6))) e grava via ChatState::setTelegramLinkCode; redireciona pra /telegram?code=XXXXXX
parea-no-botQuando o usuário manda /link CODIGO no Telegram, o TelegramWebhookController (trait TelegramCommandsTrait::doLink) faz ChatState::findByTelegramLinkCode (varredura linear de todos os users), e ao achar chama ChatState::linkTelegram(userId, chatId) — grava telegram_chat_id e zera o link_code
confirmaBot responde '✅ Conectado, [nome]!' no chat; na próxima visita à tela /telegram o cartão renderiza o estado conectado com o chat_id
desconectaPOST /telegram/desconectar: CSRF::verify, zera telegram_chat_id e telegram_link_code no chat_state via JsonDatabase::forUser(userId)->upsert; redireciona pra /telegram?disconnected=1 com alerta de sucesso
🌐 Vai pro Mercado Livre / Pix
Sem chamadas ao Mercado Livre — esta tela é só pareamento Rally↔Telegram.O envio efetivo de mensagens é feito pela API do Telegram (Bot API sendMessage), via TelegramNotifyService/webhook, fora desta tela. As preferências de quais notificações chegar ficam em /perfil#telegram (telegram_notif_prefs).
⚠️ AtençãoTELEGRAM_BOT_USERNAME ausente no .env: a tela cai no fallback hardcoded 'rallydevendas_bot' — se o bot real tiver outro username, o deep-link 'Abrir bot' aponta pro bot errado e o /link nunca casaO código de pareamento NÃO expira nem é de uso único no lado do bot (findByTelegramLinkCode só compara igualdade) — gerar um novo código sobrescreve o antigo, mas códigos antigos ficam válidos até serem sobrescritos; o vínculo é por chat_id (1 Telegram = 1 conta Rally via última vinculação)Existe um segundo fluxo de pareamento legado em /perfil (telegram-link/unlink via deep-link ?start=TOKEN, grava telegram_chat_id direto em users.json, com expiração de 600s) — coexiste com este (que grava em chat_state.json). São stores diferentes; desconectar numa tela não necessariamente limpa o vínculo da outra
▸Operações
O gerente (ou admin/fornecedor) acompanha num só painel TODOS os pedidos Flex e Turbo (same-day) dos seus vendedores visíveis — separados por etapa logística (a despachar, a caminho, entregue, problema) — imprime etiquetas e confere o estado da integração LogManager por vendedor, sem precisar entrar conta a conta.
gc.rallydevendas.com.br/flexgc.rallydevendas.com.br/flex?bucket=a_despachar&seller_id={id}&q=&turbo=1gc.rallydevendas.com.br/flex/ordersgc.rallydevendas.com.br/flex/transportadoresgc.rallydevendas.com.br/flex/transportadores?seller_id={id}
👤 Você faz e vê
1No menu lateral do Gerente de Contas, grupo 'Operações', clica em 'Gestão Flex' (ícone raio). Abre o painel com 5 cartões de métrica: total Flex/Turbo, a despachar, a caminho, Turbo same-day e receita Flex.
→
2Usa as abas (pílulas) para filtrar por etapa logística: Todos, A despachar, A caminho, Entregue, Problemas — cada aba mostra a contagem de pedidos entre parênteses.
→
3Refina com os filtros: escolhe um vendedor específico no seletor (só aparece se há mais de um), digita no campo de busca (pedido, comprador, item, SKU ou shipment) ou marca 'Só Turbo' para ver apenas same-day.
→
4Na tabela vê cada pedido com comprador, vendedor, selo Flex ou Turbo, valor e status logístico colorido. Para pedidos em 'A despachar' ou 'A caminho' com envio gerado, clica em 'Etiqueta' para abrir/imprimir a etiqueta do Mercado Livre em nova aba.
→
5Clica no botão 'Transportadores' (topo) para ver, por vendedor, se o token LogManager está conectado e quantas transportadoras estão vinculadas.
→
6Clica em 'Detalhe' de um vendedor para abrir o card da integração LogManager (status, token mascarado, último OK), as transportadoras vinculadas (com selo LogManager) e o log ao vivo do processo de integração (conexão de token, chamadas de API, criação de envio, rastreio, erros).
↓ o sistema reage por baixo
⚙️ O sistema faz
autorizaAuth::requireRole('admin','gerente','drop') no controller; o gate de rota /gc/flex (AuthMiddleware + RBACMiddleware) só admite admin e gerente no subdomínio gc. VisibilityService resolve QUAIS vendedores o viewer enxerga (admin=todos, gerente=vinculados) — NUNCA usa Auth::id() como dono dos pedidos.
resolve-sellerLê ?seller_id opcional e valida com VisibilityService::canSeeUser — guarda anti-IDOR: se o seller não for visível ao viewer, devolve 403 e ignora o filtro (cai para 'todos visíveis').
agregaFlexOrdersAggregator::forViewer varre o cache local ml_orders.json de cada vendedor em escopo (JsonDatabase::forUser) — não existe listagem nativa do ML filtrada por logística. Mantém só pedidos elegíveis: logistic_type self_service/flex OU tag 'turbo'.
classificabucketOf deriva a etapa logística de shipping.status/substatus: delivered=entregue; not_delivered/cancelled/returned ou substatus lost/damaged/stale/delayed=problema; shipped/in_transit/out_for_delivery=a caminho; resto (ready_to_ship/handling/pending/vazio)=a despachar. isTurbo lê a tag ML 'turbo' (NÃO o badge Uber Direct do admin).
metrificaCalcula contagens por bucket, total, Flex vs Turbo, receita (exclui cancelados) e KPIs de hoje. Ordena por data efetiva desc usando date_closed quando paid/shipped/delivered, senão date_created (regra KPI Rally). Limita a 200 pedidos.
etiquetaBotão 'Etiqueta' deep-linka para /gc/ml/etiqueta?ids={shipment_id} (PainelMlProxyController::shippingLabel reaproveitado) — só renderiza para buckets a_despachar/em_transito com shipment_id presente. Nenhuma etiqueta é gerada pelo painel em si.
transportadoresAba carriers(): sem seller_id mostra overview dos vendedores (UserLogManagerService::forView=conectado/mascarado/último OK + CarrierLink::countActive). Com seller_id mostra detalhe + CarrierLink::active + LogManagerLog::recent(60) (trilha ao vivo, read-only). Casa carrier_id contra CarrierDirectory para o selo LogManager.
🌐 Vai pro Mercado Livre / Pix
Nenhuma chamada ML direta nesta tela para listar — os pedidos vêm do cache local ml_orders.json (populado pela sincronia ML em background: webhook + cron).GET /shipments/{id}/labels — disparado apenas ao clicar 'Etiqueta', via deep-link para o proxy existente /gc/ml/etiqueta (PainelMlProxyController), que usa token OAuth fresco do vendedor dono do envio.
⚠️ AtençãoLista alimentada pelo cache ml_orders: pedido recém-pago/recém-despachado só aparece (ou muda de bucket) após a próxima sincronia ML. Forçar sync se o painel parecer atrasado em relação ao Mercado Livre.Classificação depende de logistic_type (self_service/flex) e da tag 'turbo' em shipping.tags estarem no pedido sincronizado — se a sincronia não trouxe esses campos, o pedido não conta como Flex/Turbo e some da lista. Turbo aqui é a tag ML, não o badge Uber Direct do /admin.Botão 'Etiqueta' só aparece com shipment_id e bucket a_despachar/em_transito; entregues e problemas mostram '—'. Sem o token LogManager do vendedor conectado, o rastreio básico funciona mas o fluxo same-day/etiqueta fica limitado — o card de detalhe orienta o gerente a pedir ao vendedor para conectar em painel → Integrações → LogManager.
▸Crescimento
O gerente de contas dispara um comunicado em massa (push APK + Web/PWA + WhatsApp opcional) para TODOS os vendedores vinculados a ele, com preview de quantos alvos receberão antes de enviar e histórico dos broadcasts anteriores.
gc.dominio.com.br/broadcastgc.dominio.com.br/broadcast/preview (POST AJAX)gc.dominio.com.br/broadcast/create (POST)
👤 Você faz e vê
1No menu lateral, abre o grupo 'Crescimento' e clica em 'Broadcast' (ícone avião de papel). A tela tem dois cards lado a lado: formulário à esquerda, histórico à direita com badge de quantos broadcasts já existem.
→
2Preenche Título (máx 60 caracteres, contador ao vivo) e Mensagem (máx 200 caracteres, contador ao vivo) — ambos obrigatórios.
→
3Define a URL de destino do clique (padrão /painel/dashboard) e o Tipo: Marketing (respeita opt-out LGPD), Transacional ou Sistema (ignoram opt-out).
→
4Marca os canais: APK Android e Web/PWA vêm marcados por padrão; WhatsApp é opt-in. Opcionalmente agenda data/hora (datetime-local).
→
5Clica em 'Pré-visualizar' — sem disparar nada, o sistema retorna quantos vendedores vinculados receberão e a quebra por canal (com APK / com Web). O número aparece num alerta azul.
→
6Clica em 'Enviar' — um confirm() repete o total estimado antes do POST. O broadcast é gravado e aparece no histórico com status; o disparo real (APK/Web/WA/email/Telegram) acontece depois via cron, não na hora do clique.
↓ o sistema reage por baixo
⚙️ O sistema faz
autorizaConstrutor de GCBroadcastController chama Auth::requireRole('admin','gerente') — vendedor/drop tomam 403. Rotas /broadcast* usam AuthMiddleware + RBACMiddleware::roles('admin','gerente').
escopa-vinculadosindex() resolve os alvos com Binding::getUserIdsOf($gerenteId, 'gerente') — SÓ os vendedores vinculados àquele gerente. O histórico é filtrado por created_by === gerenteId (admin vê todos).
previewPOST /broadcast/preview (AJAX, valida CSRF) chama BroadcastService::preview com user_ids = vínculos do gerente. Para cada alvo conta FcmToken::countForUser (APK), NotificationPreference::isEnabled web_push e whatsapp. Retorna JSON {total, with_apk, with_web, with_wa} — nenhum envio.
criaPOST /broadcast/create valida CSRF, exige clientIds não-vazio (senão flash 'Nenhum vendedor vinculado'), injeta filter_user_ids = vínculos e chama BroadcastService::create. Grava em storage/json_db/broadcasts.json (global) com status 'pending' (ou 'scheduled' se scheduled_at preenchido) e created_by = gerente.
segmentaBroadcastService::resolveTargets dá precedência total a filters.user_ids: como o gerente sempre injeta a lista de vínculos, o alvo fica travado nos vendedores dele — os demais filtros (plano/role/ativo_7d) não são expostos no form do gerente.
dispara-assincronoO envio real ocorre em BroadcastService::dispatch (chamado pelo cron de comunicação), não no clique. Honra killswitch storage/logs/.broadcast_paused, respeita NotificationPreference por canal/usuário, opt-out de marketing (blog_email_optout), cap MAX_WA_PER_RUN=40 com usleep(2s) entre WhatsApps (anti-ban WA3).
registra-statsAo despachar, atualiza status para 'sent' + sent_at e grava stats por canal (sent_fcm, sent_web, sent_wa, sent_email, sent_telegram, errors). O histórico na tela mostra Título, status (badge sent/warning), enviados APK, enviados Web e data.
🌐 Vai pro Mercado Livre / Pix
Nenhuma chamada ao Mercado Livre — broadcast é notificação interna do Rally.FCM (Firebase Cloud Messaging) via FcmPushService::sendBroadcast — push para o app Android.Web Push (VAPID) via WebPushService::notifyBroadcast — push no navegador/PWA.WhatsApp via BlipeiApiClient::sendMessage (transactional=true) — só se canal WA marcado e usuário com whatsapp + opt-in.SmtpMailer e TelegramChannelService existem no dispatch (channel_email/channel_telegram), mas o formulário do gerente só expõe APK/Web/WhatsApp.
⚠️ AtençãoBroadcast do gerente SEM agendamento é salvo com status 'pending', mas o cron jobBroadcastScheduled só processa allScheduledDue() (status 'scheduled' com scheduled_at vencido). Resultado: broadcasts 'enviar agora' do GC ficam pendentes sem dispatch automático — só saem se agendados ou disparados por outro caminho. Validar o ciclo de cron antes de prometer entrega imediata.Alvo é travado nos vendedores vinculados (Binding::getUserIdsOf gerente). Sem vínculos, create() bloqueia com 'Nenhum vendedor vinculado' e o preview retorna total=0 — não é bug, é gerente sem carteira.Entrega por canal depende de NotificationPreference por usuário + token: vendedor sem FcmToken não conta em with_apk, sem opt-in WhatsApp não recebe WA, e tipo 'marketing' pula quem tem blog_email_optout. O 'total' do preview é universo de alvos, não garantia de entrega em todos os canais.
▸Carteira
O gerente de contas cria um cupom de desconto idêntico (percentual, valor fixo ou frete grátis) em várias lojas de vendedores vinculados de uma só vez — porém a criação está temporariamente desativada porque o endpoint de cupom usado antes não existe na API do ML.
👤 Você faz e vê
1No subdomínio do gerente (gc.), abra o grupo 'Carteira' no menu lateral e clique em 'Cupons em Massa' (ícone de ticket). O cabeçalho mostra um selo com a contagem de lojas vinculadas.
→
2Escolha o tipo de desconto: 📊 Percentual (%), 💰 Valor fixo (R$) ou 🚚 Frete grátis — o campo de valor aparece conforme a opção (percentual 1–50% ou valor em reais).
→
3Defina validade e regras: data de início e término (padrão hoje + 30 dias), valor mínimo de compra, máximo de usos e a opção 'Uso único por comprador'.
→
4Na seção 'Selecionar lojas', marque as contas ML ativas dos vendedores (botões 'Selecionar todas' / 'Limpar'); um contador mostra quantas estão marcadas.
→
5Clique em 'Criar cupom em N lojas' e confirme o aviso. O botão exibe progresso (Criando X/N...) e há throttle de 3s entre submissões (anti-WAF).
→
6Veja o painel de Resultado: quantos cupons foram criados e a lista dos que falharam, com o apelido da loja e o motivo do erro.
↓ o sistema reage por baixo
⚙️ O sistema faz
autenticaGCCuponsController@index chama Auth::requireRole('admin','gerente') — aborta 403 se o papel não for gerente ou admin. Pega o gerenteId via Auth::id().
carrega-lojasResolve os vendedores vinculados com Binding::getUserIdsOf(gerenteId,'gerente'), busca cada User e suas MarketplaceAccount::byUserAndType(sid,'mercadolivre'), filtra só as com status='active' e descarta vendedores sem conta ativa. Ordena por nome (usort/strcasecmp).
monta-viewDefine title 'Cupons em Massa', viewAtiva='gc-cupons', pre-gera csrfField=CSRF::field() e renderiza resources/views/gc/cupons/index.php (form Alpine bulkCouponForm). Se não houver vendedor com conta ML ativa, mostra empty-state em vez do formulário.
valida-entradaPOST /cupons/bulk (bulkCreate) lê JSON do corpo, valida CSRF::validate (token no body ou header X-CSRF-Token) → 403 se inválido; exige 'type' e array 'accounts' não-vazio → 422 caso contrário.
blinda-vinculoReconstrói o mapa de contas permitidas a partir dos bindings do gerente (só contas ML ativas dos vendedores vinculados). Cada account_id enviado que não pertença a esse conjunto é marcado como falha 'Conta sem vínculo' (proteção IDOR).
stub-desativadoRDV-FULL-FIX-001 (2026-06-25): a criação real foi DESATIVADA. Para cada conta selecionada o controller NÃO chama o ML — apenas registra falha com 'Cupom em massa temporariamente indisponível'. O antigo POST /coupons era um endpoint inventado que não existe na API do ML.
respondeDevolve JSON { ok, created:[], failed:[...] } com apelido e motivo por conta. A view exibe quantos criados (sempre 0 no estado atual) e a lista de falhas. Nenhum texto cru de erro do ML é vazado na tela.
🌐 Vai pro Mercado Livre / Pix
NENHUMA chamada ao ML acontece no estado atual — a criação está desativada (RDV-FULL-FIX-001). O endpoint POST /coupons usado antes era inventado (não existe na API do ML).Canônico para reimplementar (TODO no código): POST /seller-promotions/promotions (promotion_type=SELLER_COUPON_CAMPAIGN; sub_type=FIXED_AMOUNT|FIXED_PERCENTAGE, só MLB) + vínculo de itens via POST /seller-promotions/promotions/{PROMOTION_ID}/items — sempre com MarketplaceAccount::decryptToken(access_token) antes do Bearer.
⚠️ AtençãoFuncionalidade desativada: a tela renderiza e o formulário funciona, mas TODA submissão volta com falha 'Cupom em massa temporariamente indisponível'. Nenhum cupom é criado de fato até a reimplementação via API de seller-promotions.Cupom de vendedor no ML só existe para o site MLB e via campanha (SELLER_COUPON_CAMPAIGN) — não pelo endpoint /coupons. Reimplementar exige shape de campanha + vínculo de itens, não o payload simples de cupom que a UI sugere.Só aparecem vendedores com pelo menos uma conta ML ativa vinculada ao gerente; lojas sem conta ativa somem da lista. Sem nenhuma, a tela mostra apenas o aviso de 'nenhum vendedor com conta ML ativa vinculado a você'.
67
📈
Evolução dos Anúncios
O gerente de contas acompanha a evolução de score, vendas e visitas dos anúncios da sua carteira ao longo do tempo, comparando anúncios melhorados pela ferramenta Rally contra anúncios sem melhoria (grupo de controle) para provar o impacto — tudo a partir de snapshots diários capturados por cron, sem nenhuma chamada ao Mercado Livre no momento da visualização.
gc.dominio/evolucaogc.dominio/evolucao?days=7|30|90gc.dominio/api/anuncios/{ann_id}/evolution?seller_id=&days=
👤 Você faz e vê
1No painel do gerente, abre o grupo 'Carteira' no menu lateral e clica em 'Evolução dos Anúncios' (ícone de gráfico subindo). A tela só lista anúncios dos vendedores vinculados a esse gerente.
→
2Escolhe a janela de tempo nos botões do topo: 7 dias, 30 dias ou 90 dias (padrão 30). Trocar o período recarrega a página via ?days= e recalcula tudo.
→
3Lê os 3 cards do comparativo: 'Anúncios melhorados pela ferramenta' (verde), 'Anúncios sem melhoria (controle)' e 'Impacto estimado da ferramenta' (vendas extras e Δ de score do grupo Rally vs o baseline).
→
4Percorre a lista 'Top Movers' — os anúncios que mais subiram no período — com foto, título, vendedor, MLB, evolução de score (de X → Y com o delta colorido) e vendas extras (+N). Anúncios tocados pela ferramenta levam o selo 'RALLY' e barra verde à esquerda.
→
5Se ainda não há dados suficientes (menos de 2 snapshots no período), vê um aviso informando que o sistema captura snapshots diariamente e que deve voltar no dia seguinte.
↓ o sistema reage por baixo
⚙️ O sistema faz
autorizaGCEvolucaoController@index exige role admin ou gerente (Auth::requireRole) e resolve os vendedores visíveis via Binding::getUserIdsOf(gerenteId, 'gerente') — gerente só enxerga a própria carteira.
janelaLê ?days e trava entre 7 e 90 dias (max(7, min(90, days))), default 30. Esse período define o cutoff usado em todos os agregados.
top-moversAnnouncementSnapshotService::topMovers varre announcement_snapshots.json de cada vendedor, exige ≥2 snapshots no período, calcula score_diff e sold_diff (último menos primeiro), descarta quem não subiu e ordena por (score_diff + sold_diff*5). Retorna os 15 maiores.
enriquecePara cada top mover, busca o anúncio (Announcement::findById) e o vendedor (User::findById) para preencher título, MLB (marketplace_item_id), foto (photos[0]) e nome do vendedor; marca rally_improved a partir de marketplace_data.rally_improved.
comparativoReabre os snapshots de todos os vendedores e separa cada anúncio em dois baldes — 'melhorado pela ferramenta' (rally_improved) vs 'controle' — somando contagem, diferença média de score e total de vendas extras de cada grupo.
impactoNa view, calcula o impacto estimado da ferramenta como a diferença entre o grupo Rally e o baseline (vendas extras e Δ de score), exibido no terceiro card.
captura-cronA FONTE dos dados é um job diário (jobAnnouncementSnapshots, throttle 23h via stamp .ann_snapshot_last_run) que chama captureAll: para cada anúncio ATIVO de cada usuário grava 1 snapshot/dia (score via calcQuality, preço, visitas/vendas via AnnouncementDiagnosticService, status, rally_improved), idempotente por dia e auto-prune de 90 dias.
api-timeseriesGET /api/anuncios/{ann_id}/evolution retorna JSON com a série temporal de um anúncio (snapshots + deltas first→last), validando visibilidade do seller_id contra os bindings do gerente (403 sem visibilidade, 422 sem ann_id).
🌐 Vai pro Mercado Livre / Pix
NENHUMA chamada ao Mercado Livre nesta tela. Os dados vêm 100% dos snapshots locais (announcement_snapshots.json) capturados pelo cron diário — a página é leitura pura de JSON, sem token ML nem requisição externa em tempo de visualização.
⚠️ AtençãoSem histórico = tela vazia. Precisa de ≥2 snapshots dentro do período para qualquer anúncio aparecer; conta/vendedor recém-vinculado mostra o empty-state ('volte amanhã') até o cron rodar pelo menos 2 dias. Se o cron diário (jobAnnouncementSnapshots) não estiver rodando, a tela nunca enche.Só anúncios ATIVOS entram no snapshot — captureForUser pula status != 'active' e itens sem marketplace_item_id. Anúncio pausado/encerrado some da série e seu histórico para de crescer; nada é capturado retroativamente.O flag has_promo do snapshot usa announcement.original_price, que o ML descontinuou (PUT bloqueado) — esse campo tende a ficar sempre falso/vazio e não deve ser tratado como indicador confiável de promoção. O comparativo Rally×baseline depende inteiramente do flag marketplace_data.rally_improved estar corretamente marcado pela ferramenta de melhoria.
▸Financeiro & Conta
68
📊
Financeiro do Gerente
O gerente de contas vê em um só painel a saúde financeira da sua carteira de vendedores — assinantes ativos, inadimplentes, MRR e a receita do seu próprio plano (wallet-to-wallet) — e pode atribuir/trocar o plano de qualquer vendedor vinculado direto da tabela.
gc:/financeirogc:/financeiro/atribuir (POST)
👤 Você faz e vê
1No menu lateral, abra o grupo 'Financeiro & Conta' e clique em 'Financeiro' (ícone de gráfico de pizza). A tela só lista vendedores vinculados a você (multi-tenant via bindings).
→
2Veja os 4 cartões de topo: Assinantes Ativos (planos pagos), Inadimplentes (overdue), MRR Mensal (soma dos planos ativos mensais) e Meus Vendedores (total da carteira).
→
3Se você tiver um plano próprio de gerente, aparece o bloco roxo 'Receita do meu plano (wallet-to-wallet)': assinantes do seu plano, inadimplentes, MRR e total histórico — com link para 'Gerenciar planos'.
→
4Na tabela 'Assinaturas e Planos', consulte por vendedor: plano, ciclo, valor, status (badge colorido), próxima cobrança e saldo da carteira. Clique no nome para abrir o usuário no admin.
→
5Para trocar o plano de um vendedor, clique no lápis na linha dele: abre um dropdown com seletor de plano e de ciclo (Mensal/Trimestral/Semestral/Anual/Vitalício).
→
6Escolha o plano e o ciclo e clique 'Salvar' — o sistema cancela a assinatura antiga, cria a nova (se não for grátis) e atualiza o plano do usuário, voltando para a tela com mensagem de sucesso.
↓ o sistema reage por baixo
⚙️ O sistema faz
autenticaAuth::requireRole('admin','gerente') — só admin ou gerente acessam; rota protegida por AuthMiddleware + RBACMiddleware (admin/gerente).
escopaBinding::getUserIdsOf(gerenteId, 'gerente') resolve os vendedores vinculados ao gerente; só esses IDs entram na tela (blindagem multi-tenant).
monta-linhasPara cada vendedor com role 'cliente', junta User + Plan (via planMap por slug/id) + Subscription (via subMap por user_id) + Wallet::getBalance(uid); ordena por próxima cobrança (mais recente primeiro).
agrega-kpiCalcula sobre as assinaturas escopadas: totalActive (active e plano != free), totalOverdue (status overdue) e MRR somando price das assinaturas active de ciclo monthly.
agrega-plano-gerenteGerenteSubscription::byGerente + mrrFor (RDV-GERENTE-BILLING-001): conta assinantes ativos/past_due do plano próprio do gerente e o MRR wallet-to-wallet — bloco só renderiza se houver assinaturas.
atribui-planoPOST /financeiro/atribuir valida CSRF e checa que user_id está nos vínculos do gerente (anti-IDOR); cancela Subscription existente, cria nova se plan != free (com price do ciclo e next_billing +1 mês) e faz User::update(plan).
flashMensagens de sucesso/erro via $_SESSION['_flash'] (sessão expirada, acesso negado, plano não encontrado, plano atualizado); redireciona de volta para /financeiro. Sem webhook, e-mail ou chamada externa.
⚠️ AtençãoNÃO consome API do Mercado Livre — é 100% JSON DB local (Wallet, Subscription, GerenteSubscription, Plan, User, Binding). Os números refletem o estado interno de billing, não vendas do ML.MRR conta apenas assinaturas com ciclo 'monthly'; planos trimestrais/anuais/vitalícios não entram no número de MRR mensal (subdimensiona a receita real se a carteira tiver ciclos longos).Atribuir plano aqui NÃO cobra nem debita wallet na hora: só cria a Subscription com next_billing +1 mês e troca o plano do usuário — a cobrança efetiva (wallet-to-wallet, past_due 30d → volta a free) é feita pelo cron de billing do GerenteSubscription, não por esta tela.Só lista usuários com role 'cliente' vinculados ao gerente; vendedor sem binding ativo (ou com role diferente) simplesmente não aparece, mesmo que tenha assinatura.
▸Global Administrador
69
📡
Vendas ao Vivo (Live)
Painel-estádio em tempo real (estilo Shopee Live) que soma o faturamento de HOJE de todos os vendedores, compara com ontem no mesmo horário, e exibe ranking de vendedores, feed das últimas vendas e ticker rotativo de campeões — ideal para projetar numa TV durante campanhas e gerar competição saudável.
👤 Você faz e vê
1No menu lateral (seção Global Administrador), clique em 'Vendas ao Vivo' — abre uma tela cheia escura, sem o layout normal do painel, com badge pulsante 'AO VIVO', relógio e o faturamento gigante do dia no centro.
→
2Acompanhe o número de faturamento subir em tempo real: o valor faz animação de 'bump' a cada nova venda, com confete e som (botão de mudo no topo), e mostra o delta percentual vs. ontem no mesmo horário.
→
3Leia os painéis: mini-gráfico SVG de tendência (hoje verde vs. ontem cinza tracejado), KPIs de visitas/pedidos/ticket médio, ranking 'Top Vendedores' com medalhas e feed das 3 últimas vendas com foto, vendedor e horário.
→
4Use o botão tela cheia (canto superior) para projetar numa TV; o ticker inferior rotaciona campeão do dia, top vendedor, top 10 produtos mais vendidos e última venda.
→
5Clique no ícone de compartilhar para gerar um LINK PÚBLICO de 30 minutos — copie e envie para alguém ver a tela sem login (ideal para sócios/equipe externa durante a live).
→
6Clique no ícone de troféu para abrir a tela permanente do 'Campeão do Dia' (/live/campeao) numa aba/TV separada — versão pública e sempre acessível focada no recordista.
↓ o sistema reage por baixo
⚙️ O sistema faz
autoriza-redirecionaGET /live exige role admin (Auth::requireRole). Se gerente ou fornecedor cair na URL do admin (link compartilhado), resolve a role no DB e redireciona para o /live do subdomínio dele (gc ou drop) em vez de 403.
resolve-escoporesolveUserIds() lista TODOS os usuários não-admin e não-deletados (User::all filtrado) — o admin vê a soma da 'Equipe Rally' inteira; gerente/fornecedor veem só seus vinculados nos seus próprios controllers.
agrega-snapshotLiveSalesAggregatorService::snapshot lê ml_orders.json de cada shard de usuário, filtra status válidos (paid/confirmed/shipped/delivered), e soma faturamento, pedidos e unidades para HOJE e ONTEM (mesmo recorte de horário) no fuso America/Sao_Paulo.
data-efetivaPara 'hoje' usa date_closed (data do pagamento) quando o pedido está paid/shipped/delivered, com fallback para date_created — evita o bug de venda paga hoje mas criada ontem cair fora do dia (RDV date_closed).
rankeiaMonta top 5 vendedores por faturamento, item-recorde (mais unidades hoje), top 10 produtos mais vendidos e buckets de faturamento por hora (0-23) hoje vs. ontem para o mini-gráfico de tendência.
cache-30sO snapshot inteiro é cacheado em arquivo por 30s (chave = md5 dos userIds) para não martelar os shards quando vários navegadores fazem polling do feed simultaneamente; visitas ML têm cache próprio de 5min.
feed-pollO componente Alpine liveStadium() faz polling de GET /api/live/feed (JSON, Cache-Control: no-store), detecta IDs de vendas novas, dispara confete/som e atualiza o feed; a versão pública usa /api/live/feed/publico?token=XYZ validado por token de 30min em storage/cache/.live_public_tokens.json.
🌐 Vai pro Mercado Livre / Pix
GET /users/{ml_uid}/items_visits/time_window?last=1&unit=day — total de visitas únicas de hoje, agregado por curl_multi paralelo de todas as contas ML ativas (cache 5min). Token descriptografado obrigatório.GET /users/{ml_uid}/items_visits?ids={ids}&date_from={hoje}&date_to={agora} — visitas de hoje POR item das vendas recentes, para exibir contador de visitas em cada card do feed.
⚠️ AtençãoNÃO confundir com 'Lives & Eventos' (/eventos, subdomínio painel) — aquela é o módulo institucional de transmissões/eventos do vendedor; esta /live é o estádio de KPIs de venda em tempo real (admin/gerente/fornecedor).Cache de 30s no snapshot: uma venda recém-paga pode levar até meio minuto para aparecer no placar; e os dados dependem de ml_orders.json já sincronizado — pedidos ainda não sincronizados do ML não entram no faturamento.Link público vale só 30 minutos e a tela do campeão (/live/campeao) é totalmente pública (sem login) — não compartilhar com quem não deveria ver o faturamento agregado da equipe; tokens expirados mostram tela '🔒 Link expirado'.