Auditoria de linhagem · Time de TI
Rastreamento elemento por elemento — card, gráfico, filtro e tabela — até a fonte original do dado. Os dois dashboards leem o mesmo banco, mas por caminhos diferentes: é aí que nascem a duplicação e a defasagem.
Não há um problema de coleta. Há um problema de troca de fonte pela metade.
Em vários pontos alguém trocou a origem de um número — normalmente por um motivo bom, como sair do banco intermediário e ir direto na fonte — mas não trocou junto o que orbita esse número: o filtro que o recorta, o menu que lista as opções, o cálculo auxiliar que aparece na mesma linha da tabela.
O resultado não é um erro que salta aos olhos. É pior: é um número que muda de significado conforme você mexe na tela. Você filtra por propriedade e o total vira zero. Você abre dois dashboards e o mesmo indicador dá 6 e 34. A taxa de cancelamento é 7,7% num caminho e 17,3% no outro. Tudo isso está acontecendo agora, em produção.
Todo dado percorre quatro etapas até virar número na tela. Onde cada etapa roda importa mais do que parece.
Leia o risco na coluna 2: cinco dos sete transportadores rodam na sua máquina, via crontab. Mac desligado, dormindo ou sem rede = dashboard congelado sem nenhum aviso na tela — ele continua mostrando o último número que conseguiu buscar, com o carimbo "Atualizado: agora".
Todos verificados chamando a API de produção hoje. Os números abaixo são reais, não estimativas.
Nas abas Atendimento e Contatos, escolher Brasil, Argentina ou qualquer hotel faz o número virar zero — não "poucos", zero. A causa é estrutural: a tabela cs_opportunities, que virou a fonte desses números, guarda a origem como texto ("Rio"/"BA") e não tem coluna de hotel. O código contorna escrevendo SELECT NULL as hotel_id, e NULL nunca casa com filtro nenhum.
Sem filtro ......... 1.306 atendimentos · 217 pré · 1.038 suporte Filtro Brasil ..... 0 atendimentos · 0 pré · 0 suporte Filtro Argentina .. 0 atendimentos · 0 pré · 0 suporte Filtro Ipanema .... 0 atendimentos · 0 pré · 0 suporte
cs-dashboard/functions/api/cs.js:107 · buildWhere()
Existem dois caminhos vivos para a mesma pergunta. A tela usa o do Cloudbeds; o outro continua sendo calculado a cada chamada e fica disponível na API para quem consumir.
cloudbeds_bookings (na tela) .. 24.403 reservas · 4.213 cancelados · 17,3% csd_bookings (via MySQL) ...... 20.540 reservas · 1.580 cancelados · 7,7% ───────────────────────────────────────── diferença ..................... 3.863 reservas · 2.633 cancelamentos
Vale investigar qual está certa antes de aposentar a outra: a diferença de 3.863 reservas sugere que o MySQL não recebe tudo, mas pode também ser recorte de período ou de propriedade.
No Sales, o cálculo é honesto: percorre hora a hora, respeitando o turno individual de cada vendedor (Fred 8h–16h, Clara 14h–22h, e assim por diante) e conta só o tempo dentro do expediente.
No CS, não existe cálculo de horário útil. Pega-se a média corrida e multiplica-se por uma regra de três — 5 dias em 7, 6 horas em 24. É uma estimativa de fator fixo, não tempo útil medido.
CS · média corrida 32,5 min → × (5/7) × (6/24) → mostra "6 min" Sales · cálculo hora a hora dentro do turno → mostra "34,4 min"
Os dois números medem populações diferentes (CS e vendedores), então não deveriam ser iguais — mas hoje eles também não são comparáveis, e o card do CS promete "útil" no rótulo sem entregar.
A tabela de vendas é montada filtrando por booking_source nos códigos Central 1, 2, 9, 10 e 13 — que são os vendedores do Rio. Nenhuma reserva de Buenos Aires entra. O menu de propriedade, porém, lista BA Centro, BA Obelisco e Suites BA.
Propriedades com dado .. Ipanema (1.091) · Suites Copa (22) Filtro Argentina ....... 0 reservas · 0 propriedades
O menu "Status Reserva" oferece Cancelada e No Show. Mas a carga que monta a tabela exclui esses dois status na origem — eles nunca chegam a ser gravados. Escolher qualquer um dos dois devolve zero para sempre.
canceled → 0 · no_show → 0 · all → 1.113 · checked_out → 845
sales-dashboard/functions/api/sales.js:643 · WHERE status NOT IN ('canceled','no_show')
O dropdown de membro é montado a partir de csd_opportunities (caminho antigo, via MySQL, 15 min). Os números que ele filtra vêm de cs_opportunities (caminho novo, GHL direto, 5 min). São dois espelhos diferentes do mesmo CRM, com atrasos diferentes.
Na prática: o menu pode listar alguém que sumiu dos números, ou omitir alguém que está lá.
Sete campos são calculados toda vez que uma aba abre e nenhum deles aparece na tela: os contadores de IA e Humano, o total de reservas e os dois campos de cancelamento do caminho antigo.
Isso custa duas varreduras nas 184 mil linhas de mensagens e uma na tabela de reservas, a cada carregamento, para dado que é descartado no caminho. E é justamente esse cálculo que mantém csd_bookings — a versão duplicada do achado 2 — viva no fluxo principal.
Sobra um risco de fora: quem consumir a API diretamente (Power BI, n8n, planilha) vai ler atendimentos_ia = 36.902 como atendimento, quando são linhas de mensagem — 28× o número real de 1.306.
Calculados e descartados: atendimentos_ia · atendimentos_humano · total_bookings total_cancelamentos · taxa_cancelamento · by_mode · by_mode_month
Filtrar por Ipanema não muda o contador de mensagens humanas: 6.745 com filtro e sem filtro. Ou seja, 100% das mensagens que conseguimos atribuir a uma pessoa estão em Ipanema — as outras quatro propriedades aparecem com zero.
O motivo é o método de atribuição: o nome do atendente só é preenchido quando o telefone da conversa casa com um contato no MySQL, comparando os últimos 8 dígitos. Onde o cadastro de telefone é ruim, o nome não resolve — e isso bate com o que já sabemos sobre os números quebrados em BA.
csd_session_types — nenhum script a alimenta. Os dados param em junho/2026, dois meses atrás. Ainda responde pela rota interacoes_mensal, hoje não usada pela tela.
sd_seller_activity — completamente vazia. Alimentava a aba "Atividade" do Sales, que foi removida da interface mas continua respondendo pela API com campos nulos.
interacoes_mensal → último mês com dado: 2026-06 (hoje: 2026-08) atividade → by_day: 0 linhas · avg_hours: null para os 5 vendedores
"Ativos" e "Ociosos" são calculados com date('now') — sempre os últimos 6 meses a partir de hoje, independente das datas que você selecionou na barra. Os outros cards da mesma aba respeitam o filtro. Some-se a isso que a tabela csd_fidelimax é carregada à mão, sem agendamento.
cs-elmisti.pages.dev · 8 abas. A coluna "Estado" marca o que precisa de decisão.
O mesmo menu "Propriedade" usa quatro chaves diferentes conforme a aba — e uma delas não funciona.
| Filtro | De onde saem as opções | O que ele manda pra API | Vale em quais abas | Estado |
|---|---|---|---|---|
| Propriedade (modo região) | Lista fixa no código | hotel_ids=1,4 (BR) ou 307,310,317 (AR) | Atendimento · Interações · FAQ · Contatos | Zera Atendimento e Contatos |
| Propriedade (modo hotel) | Lista fixa no código | hotel_id = 1, 4, 307, 310, 317 | Reviews · Fidelidade | Funciona |
| Propriedade (modo Cloudbeds) | Lista fixa no código | property_id = 186910, 186883… | Cancelamentos | Funciona |
| De / Até | Entrada manual | start / end | Todas | Cada aba aplica numa coluna diferente: criação, avaliação, check-in, resposta ou cadastro |
| Membro | csd_opportunities (MySQL) | member | Atendimento · Contatos | Fonte diferente da dos números (cs_opportunities) |
| Status | Fixo: won / open | status | Atendimento · Contatos | Funciona |
| Estágio | Fixo: pré / pós / in-house / suporte | stage | Atendimento · Contatos | Nomes de etapa escritos à mão no código, com e sem acento |
| Interação | Fixo: IA→Humano / Direto | mode | Interações | Também troca a fonte do gráfico da aba Atendimento |
| FAQ | 15 tags fixas no código | faq_tag | Atendimento · FAQ · Contatos | Tag nova no GHL não aparece até alguém editar o código |
| OTA | Fixo: 4 OTAs | ota | Reviews | Funciona |
| Origem | cloudbeds_bookings.booking_source | source | Cancelamentos | Único filtro montado a partir do dado real |
| O que você vê | Tabela que alimenta | Fonte original | Atualiza | Observação |
|---|---|---|---|---|
| Total Atendimentos | cs_opportunities | GHL Rio + BA (API direta) | 5 min · nuvem | Zera com qualquer filtro de propriedade |
| Tempo Resposta (útil) | csd_response_times | MySQL HD · sessões e mensagens do chatbot | 15 min · Mac | Média corrida × 5/7 × 6/24 no navegador — não é horário útil medido |
| Tempo Atendimento | cs_opportunities | GHL · criação → última alteração dos "ganhos" | 5 min · nuvem | "Última alteração" não é "momento do ganho": qualquer edição posterior infla o tempo |
| CSAT CS | csd_contacts.tags | GHL · tags csat-cs-1..5 | manual | Depende de um script rodado à mão; hoje 7 avaliações no total |
| Gráfico · Atendimentos por Mês | cs_opportunities ou csd_sessions | GHL ou MySQL HD | 5 ou 15 min | Troca de fonte quando o filtro "Interação" está ativo — mesmo gráfico, universo diferente |
| Tabela · colunas Total / Abertos / Ganhos | cs_opportunities | GHL | 5 min · nuvem | — |
| Tabela · colunas Pré / In-House / Pós / Suporte | cs_opportunities | GHL · nome da etapa | 5 min · nuvem | Depende de a etapa estar escrita exatamente como no código |
| Tabela · coluna T. Resposta | csd_response_times | MySQL HD | 15 min · Mac | Mostra a média da região do membro, não a dele — Micaela e Tayla exibem sempre o mesmo valor |
| Tabela · coluna T. Atendimento | cs_opportunities | GHL | 5 min · nuvem | Esta sim é individual |
| Tabela · coluna CSAT | csd_contacts.tags | GHL | manual | — |
| Gráficos + tabela CSAT | csd_contacts.tags | GHL | manual | — |
| O que você vê | Tabela que alimenta | Fonte original | Atualiza | Observação |
|---|---|---|---|---|
| Total Sessões CS | csd_messages | MySQL HD · guest_messages | 15 min · Mac | Aqui sim é sessão: as mensagens são agrupadas por conversa antes de contar |
| IA → Humano CS | csd_messages | MySQL HD | 15 min · Mac | Conversa que o bot iniciou e uma pessoa assumiu |
| Direto Humano CS | csd_messages | MySQL HD | 15 min · Mac | — |
| Gráfico · IA vs Humano por Mês | csd_messages | MySQL HD | 15 min · Mac | — |
| Tabela · Interações por Membro | csd_messages.member_name | MySQL HD · casamento por telefone | 15 min · Mac | Só resolve quando o telefone casa nos últimos 8 dígitos — na prática só Ipanema |
— (rota interacoes_mensal, fora do ar) | csd_session_types | ninguém escreve | parado em jun/26 | Tabela órfã: não aparece na tela, mas responde pela API com dado velho |
| O que você vê | Tabela que alimenta | Fonte original | Atualiza | Observação |
|---|---|---|---|---|
| Consultas FAQ (tags) | csd_contacts.tags | MySQL HD · contacts (espelho do GHL) | 15 min · Mac | Conta contato que tem a tag, não consulta feita. Tag não expira, então o número só sobe |
| Gráfico · Consultas por Tag | csd_contacts.tags | MySQL HD | 15 min · Mac | Só conta as 15 tags escritas no código; tag nova no GHL fica invisível |
| O que você vê | Tabela que alimenta | Fonte original | Atualiza | Observação |
|---|---|---|---|---|
| Cancelamentos · Taxa · Total Reservas | cloudbeds_bookings | Cloudbeds · webhook | tempo real · nuvem | A aba inteira usa Cloudbeds — é o caminho mais confiável dos dois dashboards |
| Booking.com · Reserva Direta · Outras OTAs | cloudbeds_bookings.booking_source | Cloudbeds | tempo real · nuvem | — |
| Gráfico por Mês · Tabelas por Hotel e Origem | cloudbeds_bookings | Cloudbeds | tempo real · nuvem | Recorta por data de check-in, não de reserva |
— (rota cancelamentos, fora da tela) | csd_bookings | MySQL HD · bookings | 15 min · Mac | Caminho paralelo: dá 7,7% contra 17,3%. Continua sendo calculado e servido pela API |
| O que você vê | Tabela que alimenta | Fonte original | Atualiza | Observação |
|---|---|---|---|---|
| Total Reviews · Nota Média | csd_reviews | MySQL HD · reviews | 15 min · Mac | Quem coleta das OTAs é a Hotelaria Digital — não temos controle nem visibilidade dessa etapa |
| Booking · Google · HostelWorld · TripAdvisor | csd_reviews por ota | MySQL HD | 15 min · Mac | — |
| Gráfico e tabela por OTA | csd_reviews | MySQL HD | 15 min · Mac | Dado presente e contínuo de jan a ago/2026 |
| O que você vê | Tabela que alimenta | Fonte original | Atualiza | Observação |
|---|---|---|---|---|
| Pré · In-House · Pós · Suporte | cs_opportunities | GHL · nome da etapa | 5 min · nuvem | Zera com filtro de propriedade, igual à aba Atendimento |
| Gráfico e tabela por membro | cs_opportunities | GHL | 5 min · nuvem | A classificação depende de 12 nomes de etapa escritos no código; renomear uma etapa no GHL some com o dado sem erro |
| O que você vê | Tabela que alimenta | Fonte original | Atualiza | Observação |
|---|---|---|---|---|
| % Recorrentes · Total Check-ins | csd_checkins | MySQL HD · checkins (check-in online) | 15 min · Mac | Recorrente = times_stayed ≥ 1, campo que a Hotelaria Digital calcula — não conferimos o critério dela |
| Nota Avaliação | csd_checkin_reviews | MySQL HD · checkin_reviews | 15 min · Mac | Avaliação do check-in, diferente do review de OTA e diferente do CSAT |
| Gráficos e tabela por hotel | csd_checkins · csd_checkin_reviews | MySQL HD | 15 min · Mac | Só conta quem fez check-in online — não é o total de hóspedes |
| O que você vê | Tabela que alimenta | Fonte original | Atualiza | Observação |
|---|---|---|---|---|
| Clientes · Taxa de Retenção · Com Saldo | csd_fidelimax | Fidelimax · carga manual | sem agendamento | Nenhum cron alimenta esta tabela — envelhece até alguém rodar o script à mão |
| Ociosos | csd_fidelimax | Fidelimax | sem agendamento | Usa "hoje menos 6 meses", ignorando o período que você escolheu na barra |
| (cálculo de Ativos, usado na Retenção) | csd_fidelimax | Fidelimax | sem agendamento | Mesmo problema: contamina a Taxa de Retenção, que aparenta respeitar o filtro |
| Indicações · gráfico · Top Indicadores | csd_indicacoes | Fidelimax · webhook | tempo real · nuvem | Este caminho é sólido: chega direto na nuvem, sem intermediário |
sales-elmisti.pages.dev · 4 abas. Cobertura real: Rio apenas.
| Filtro | De onde saem as opções | O que ele manda pra API | Vale em quais abas | Estado |
|---|---|---|---|---|
| Propriedade | Lista fixa no HTML | property_id / property_ids | Todas | As 3 opções da Argentina retornam zero — não há dado de BA |
| De / Até | Entrada manual | start / end | Todas | A aba CRM vs Vendas sobrescreve a data inicial (ver abaixo) |
| Vendedor | 5 nomes fixos no código | seller | Todas | Vendedor novo exige mexer no código em 3 lugares |
| Status | Fixo: won / lost / open | opp_status | CRM | Funciona |
| Status Reserva | 8 opções fixas | cb_status | Vendas | "Cancelada" e "No Show" sempre retornam zero — excluídos na carga |
| Estágio | 6 stages fixos | stage | CRM | Nomes com grafia própria do GHL ("Em Negociaçao") copiados à mão |
| O que você vê | Tabela que alimenta | Fonte original | Atualiza | Observação |
|---|---|---|---|---|
| Total Leads · Ganhos · Perdidos · Em Aberto · Taxa Conversão | sd_opportunities | MySQL HD · opportunities (espelho do GHL) | 15 min · Mac | Recorta pelo pipeline "Central de Vendas - El Misti" e pelos 5 vendedores |
| T. Resposta Médio | sd_response_times | MySQL HD · sessões e mensagens | 15 min · Mac | Cálculo hora a hora respeitando o turno individual — o método bom |
| T. Atendimento Médio | sd_response_times | MySQL HD | 15 min · Mac | Primeira mensagem até última resposta da sessão; descarta o que passa de 24h úteis, o que corta atendimentos longos |
| Leads por Vendedor · por Mês | sd_opportunities | MySQL HD | 15 min · Mac | — |
| Funil de Stages | sd_opportunities.stage_name | MySQL HD | 15 min · Mac | A ordem do funil é uma lista fixa no código; stage que não estiver nela some do gráfico sem aviso |
| Tabela · Detalhamento | sd_opportunities + sd_response_times | MySQL HD | 15 min · Mac | Aqui os tempos são individuais de verdade |
Esta aba lê sd_reservations, que não vem do Cloudbeds direto: é uma cópia filtrada de cloudbeds_bookings, feita dentro do próprio banco pelo script que roda no seu Mac.
| O que você vê | Tabela que alimenta | Fonte original | Atualiza | Observação |
|---|---|---|---|---|
| Revenue Total | sd_reservations.revenue | Cloudbeds → cloudbeds_bookings → cópia filtrada | 15 min · Mac | Soma sem separar moeda. Hoje não morde porque só há Brasil — se BA entrar, vira BRL + USD no mesmo total |
| Reservas Criadas · Noites | sd_reservations | Cloudbeds | 15 min · Mac | Exclui os quartos fictícios Voluntarios e VIRTUAL CARNAVAL |
| ADR Médio | sd_reservations | Cloudbeds | 15 min · Mac | Receita ÷ noites, calculado na hora |
| Toggle Criação / Partida | — | — | — | Troca a coluna de data entre created_at e checkout_date; muda bastante o desenho mensal |
| Revenue por Vendedor + tabela | sd_reservations.seller_name | Cloudbeds · booking_source | 15 min · Mac | "Vendedor" é o canal Central 1/2/9/10/13 traduzido por uma tabela fixa — não é o usuário real do Cloudbeds |
(rota alternativa sync_cloudbeds) | sd_reservations | mesma cópia, acionada por URL | manual | Segundo caminho de escrita na mesma tabela, exposto na API pública |
| O que você vê | Tabela que alimenta | Fonte original | Atualiza | Observação |
|---|---|---|---|---|
| Won no CRM (GHL) | sd_opportunities | MySQL HD | 15 min · Mac | 118 no período |
| Reservas (Cloudbeds) | sd_reservations | Cloudbeds | 15 min · Mac | 440 no mesmo período |
| Divergência | cálculo entre as duas | — | — | 272,9% no total; Gustavo 1.016%. A aba cumpre o papel de denunciar — o problema está no registro, não no cálculo |
| Período efetivo | MIN(created_at) do GHL | MySQL HD | 15 min · Mac | Ignora sua data inicial e começa em 21/05/2026 — quando o GHL de vendas passou a existir. Só aparece numa nota discreta |
| O que você vê | Tabela que alimenta | Fonte original | Atualiza | Observação |
|---|---|---|---|---|
| Tom · Abordagem · Reação · Geral | sd_raiox | Groq · Llama 3.3 70B lendo conversas do MySQL HD | diário 06h, mas só re-executa se a última análise tiver +15 dias | Nota gerada por IA sobre no máximo 30 conversas por vendedor — é amostra, não censo |
| Radar · Barras · Tabela · Pontos fortes e a melhorar | sd_raiox | Groq | quinzenal na prática | A tela mostra sempre a análise mais recente de cada vendedor, que pode ser de períodos diferentes entre si |
| — (aba "Atividade", removida) | sd_seller_activity | ninguém escreve | vazia | Tabela órfã; a rota continua respondendo com campos nulos |
Vinte e uma tabelas no mesmo banco, três prefixos, três gerações de decisão. O prefixo conta a história: csd_ e sd_ nasceram para os dashboards; cs_ veio do projeto de Power BI; cloudbeds_ é o webhook.
| Tabela | Quem escreve | Onde roda | Frequência | Quem lê | Estado |
|---|---|---|---|---|---|
cs_opportunities | powerbi-sync | Cloudflare | 5 min | CS: Atendimento, Contatos | sem hotel_id |
csd_opportunities | sync_cs_dashboard | Mac | 15 min | CS: só o menu Membro | quase aposentada |
csd_messages | sync_dh_messages | Mac | 15 min | CS: Interações, cards IA/Humano | ativa |
csd_contacts | sync_cs_dashboard + script CSAT | Mac | 15 min + manual | CS: FAQ, CSAT | 2 produtores |
csd_response_times | sync_cs_dashboard | Mac | 15 min | CS: tempo de resposta | ativa |
csd_sessions | sync_cs_dashboard | Mac | 15 min | CS: gráfico, só com filtro Interação | uso condicional |
csd_bookings | sync_cs_dashboard | Mac | 15 min | API kpis, fora da tela | duplica Cloudbeds |
csd_reviews | sync_cs_dashboard | Mac | 15 min | CS: Reviews | ativa |
csd_checkins | sync_cs_dashboard | Mac | 15 min | CS: Fidelidade | ativa |
csd_checkin_reviews | sync_cs_dashboard | Mac | 15 min | CS: Fidelidade | ativa |
csd_fidelimax | script rodado à mão | Mac | nenhuma | CS: Fidelimax | sem agendamento |
csd_indicacoes | webhook Fidelimax | Cloudflare | tempo real | CS: Fidelimax | ativa |
csd_session_types | — | — | parada jun/26 | rota fora da tela | órfã |
cloudbeds_bookings | webhook + backfill | Cloudflare | tempo real | CS: Cancelamentos · origem de sd_reservations | fonte confiável |
sd_opportunities | sync_sales_dashboard | Mac | 15 min | Sales: CRM, CRM vs Vendas | ativa |
sd_reservations | sync_sales_dashboard + rota na API | Mac | 15 min + manual | Sales: Vendas, CRM vs Vendas | 2 produtores · só Rio |
sd_response_times | sync_sales_dashboard | Mac | 15 min | Sales: CRM | ativa |
sd_raiox | sync_sales_raiox (Groq) | Mac | quinzenal | Sales: Raio-X | ativa |
sd_seller_activity | — | — | vazia | rota fora da tela | órfã |
sd_sync_log | scripts de sync | Mac | 15 min | ninguém | log, não exibido |
cs_stage_history | powerbi-sync | Cloudflare | 5 min | Power BI | fora destes 2 painéis |
Os consertos técnicos o time resolve. Estes cinco pontos são escolha de negócio — envolvem trocar o significado de um número que alguém já usa.