Marts do DW — o que falta pros relatórios Belite
Pedido pro time de dados — o pipeline (Pentaho) fica fora deste repo, aqui só especifica o que falta. Só fica o que ainda não foi feito; itens já entregues/resolvidos foram removidos deste doc (histórico completo no Git, git log -- docs/belite/pendencias_marts.md).
Validado direto contra o DW de produção (conexão real, \d nas tabelas + queries de contagem) — não é só levantamento de código. Última revisão: 2026-08-14.
1. Monitor F&I — Retorno médio / Taxa média
Hoje queryRetornoTaxaByGroup (fi-queries.ts) roda avg(retorno_pct) e avg(taxa_mes) direto em fandi_fat_contrato (fato), pras colunas extras das tabelas Fabricantes/Lojas/Bancos.
Confirmado no DW (2026-07-25): dw_concessionarias_mart.fandi_kpi_contrato_dia não tem soma_retorno_pct nem soma_taxa_mes (nem equivalente) — colunas atuais do mart são só valor_vendido/financiado/retorno/plus/bonus/meta/prod_banco/prod_loja + qtd_contratos. retorno_pct e taxa_mes existem em dw_concessionarias_gold.fandi_fat_contrato (fato), confirmando a fonte.
Pedido: 2 colunas novas em dw_concessionarias_mart.fandi_kpi_contrato_dia, mesmo grão de hoje (grupo_sk × fabricante_sk × loja_sk × banco_sk × departamento × dia):
soma_retorno_pct -- sum(retorno_pct)
soma_taxa_mes -- sum(taxa_mes)
Não pedir a média já pronta — média não soma entre dias (a média de 2 dias não é a média das médias). Com a soma bruta, o app calcula soma / qtd_contratos (coluna que o mart já tem) pra qualquer intervalo, e bate exatamente com rodar avg() na fato pro mesmo período.
2. Títulos — Usuários
Hoje queryTitulosPorUsuario (titulos-queries.ts) agrupa por usuario_sk/perfil_usuario direto em fandi_fat_recebimento (fato).
Confirmado no DW (2026-07-25): nenhum mart em dw_concessionarias_mart tem dimensão de usuário — fandi_dim_usuario só existe em dw_concessionarias_gold.
Pedido: mart novo fandi_kpi_recebimento_usuario_dia, mesmo grão de fandi_kpi_recebimento_dia + usuario_sk, com qtd_titulos e valor_total. Proposital não pedir pra adicionar usuario_sk no mart existente (fandi_kpi_recebimento_dia) — ele já foi validado linha a linha contra a fato e é usado por outras queries que não precisam de usuário; aumentar a cardinalidade dele à toa reabre essa validação sem necessidade.
Grão exato do novo mart (chave composta, uma linha por combinação):
grupo_sk, fabricante_sk, loja_sk, banco_sk, departamento,
recebimento_tipo_sk, data_sk (ou dia), usuario_sk
Medidas pré-agregadas:
qtd_titulos -- count(*) sobre fandi_fat_recebimento
valor_total -- sum(valor_recebimento) sobre fandi_fat_recebimento
Não denormalizar a dimensão de usuário no mart: levar só usuario_sk;
nome_usuario/perfil_usuario continuam sendo buscados pelo app via LEFT JOIN fandi_dim_usuario ON us.usuario_sk = k.usuario_sk no momento da query (mesmo
desenho de hoje, titulos-queries.ts:221). Motivos: manter fandi_dim_usuario
como único source-of-truth de perfil_usuario (se o ERP reescrever perfis, atualiza
só a dim e não o(s) mart(s) denormalizados); reduzir o tamanho do mart; não mudar
o desenho da query no app — só trocar a cláusula FROM e remover a agregação.
Query do app que migra após entrega do mart: queryTitulosPorUsuario
(apps/web/app/[locale]/belite/home/[account]/relatorios/titulos/_lib/server/titulos-queries.ts:370).
Hoje ela roda count(*) + sum(valor_recebimento) em fandi_fat_recebimento JOIN
fandi_dim_usuario, agrupado por usuario_sk implícito (us.nome_usuario, us.perfil_usuario). Após o mart, vira SELECT k.usuario_sk, us.nome_usuario, us.perfil_usuario, k.qtd_titulos AS n, k.valor_total AS valor FROM fandi_kpi_recebimento_usuario_dia k LEFT JOIN fandi_dim_usuario us ON ... WHERE <mesmos filtros de mart, sem o de "VL.FINANC." que é só pra Contagem do card> GROUP BY k.usuario_sk, us.nome_usuario, us.perfil_usuario.
Nota — não é mais pedido: Contagem de títulos, qtd. do gráfico por tipo e aba Devedores (
queryTitulosContagem,queryTitulosTipoChart,queryTitulosPorDevedor) bateriam na fato "porque faltava coluna de contagem no mart" — falso. Confirmado no DW:fandi_kpi_recebimento_dia.qtd_recebimentosjá existe e está 100% preenchida (694.534/694.534 linhas, soma real de 1.062.261). É bug de código (queries não usam a coluna que já existe), não pendência de dado — não entra nesse doc.
3. One Page — DMS (Vendas, RPUV, Penetração e Margem por carro desbloqueados)
Vendas, RPUV, Penetração e Margem por carro dependem de dados do DMS.
Atualizado (2026-07-26, TEC-475): a fonte DMS foi ingerida e o time de dados já entregou marts purpose-built pro One Page — dw_concessionarias_mart.kpi_onepage_dia e dw_concessionarias_mart.dms_kpi_venda_dia, ambas já tenant-linked (grupo_sk de primeira classe). O bloqueio de 2026-07-25 abaixo está totalmente desatualizado — a investigação daquela data rodou contra o projeto Supabase de produção real (tmyxcpfhmupflbwikfcw, via conector MCP supabase), mas só olhou os schemas dw_dms/dw_dms_mart e não achou essas marts em dw_concessionarias_mart, que já existiam lá.
Nota sobre ambientes (importante pra quem for revalidar): o dev local (.env.local/SUPA_DB_* deste worktree) aponta pro projeto hml (ocduzdhgldtogpihdpmb, conector MCP supabase-hml) — diferente do projeto de produção real (tmyxcpfhmupflbwikfcw, conector MCP supabase). dms_kpi_venda_dia (a mart usada pelo código) existe e tem os mesmos dados/schema nos dois — confirmado GRUPO CAMINHO presente nos dois, mesmo range de datas (abril–julho/2026), números diferentes entre os ambientes (esperado, dados diferentes) mas a mesma lógica de query funciona nos dois. kpi_onepage_dia (usada só pra investigar/validar, não é consultada pelo código) existe só no hml, não em produção — sem impacto na implementação.
- Vendas, Penetração, Margem por carro: desbloqueados via
dw_concessionarias_mart.dms_kpi_venda_dia(grupo_sk, fabricante_sk, loja_sk, departamento, data, qtd_vendas, valor_vendido, valor_compra, margem_valor, margem_pct). Implementado emone-page/_lib/server/one-page-dms-queries.ts.- Não usar
dw_dms_mart.kpi_financiamento_dia.qtd_nfspra Vendas — validado no DW que mede outra coisa (provavelmente só NFs com canal de financiamento classificado). Pro grupo piloto/mês de referência (hml): 520 (qtd_nfs) vs 742 (dms_kpi_venda_dia.qtd_vendas=kpi_onepage_dia.vendas, as duas batem entre si). Ficou registrado como tentativa descartada no comentário do arquivo. - Margem por carro: soma
valor_vendido/valor_comprabrutos e calcula a razão no fim — não usar a colunamargem_pct/margem_valorda mart (pré-calculada por linha, não soma entre linhas). Excluir linhas comvalor_compra = 0/nulo (custo não registrado no DMS): confirmado (nos dois ambientes) que uma fração relevante do valor vendido vinha de linhas sem custo, inflando a margem agregada — no hml, de ~8,5% real pra ~75% se não excluir.
- Não usar
- RPUV — cuidado, a coluna
rpuvdekpi_onepage_diacalcula a coisa errada: validado (no hml, onde essa mart existe) quekpi_onepage_dia.rpuv = (valor_vendido − valor_compra) ÷ vendas(a mesma base demargem_por_carro_pct, em R$ em vez de %) — não é a fórmula oficial do RPUV (que também não é mais sóRentabilidade C/Produto ÷ Vendascomo a doc definia em 6.1 — foi redefinida a pedido do usuário pra(Rentabilidade C/Produto + Margem do carro em R$) ÷ Vendas, verairflow-dags/ONEPAGE.mditem 5). Confirmado com múltiplas linhas batendo exato na fórmula errada da coluna, inclusive linhas onderentabilidade_c_produto = 0masrpuvtem valor (só possível serpuvnão depender de rentabilidade nenhuma). Implementação calcula RPUV manualmente ((rentabilidade_c_produto + margem_por_carro_valor) ÷ vendas, verbuild-indicators.ts/one-page-dms-queries.ts) em vez de usar a coluna. Pedido pro time de dados: renomear/corrigir esse campo pra não colidir com o nome do indicador oficial — hoje é uma armadilha pra quem for consumir a mart direto sem saber dessa pegadinha. Como a mart nem existe em produção ainda, vale confirmar se ela é pra ser promovida com esse bug corrigido, ou se foi descontinuada em favor de outra coisa. - Só 1 grupo tem integração DMS ativa hoje (
GRUPO CAMINHO) — não é uma ingestão piloto/temporária, é o único grupo com o ERP integrado até agora, e mesmo esse grupo tem lacunas de ingestão em alguns meses. A tela degrada pra "—" nos demais grupos e nos períodos sem linha (queryOnePageDmsTotalsretornadisponivel: false). - Departamento não é filtrável no DMS ainda: a taxonomia da mart (
Novos, Usados, Venda Direta, Repasse, Imobilizados) não bate 1:1 com a do FANDI (NOVOS, SEMINOVOS, VENDAS DIRETA) — sem de-para validado com o negócio, o filtro de Departamento em tela não se aplica a Vendas/RPUV/Penetração/Margem por carro (só a Contratos/Total Financiado, que continuam vindo do FANDI). - Share por banco (fórmula completa, incluindo financiamento externo): fora do escopo do TEC-475 — exigiria um de-para de banco entre a dim do FANDI e a do DMS, ainda não validado.
4. Monitor F&I — Vendedores: carga histórica do usuario_sk
dw_concessionarias_mart.fandi_kpi_contrato_dia.usuario_sk está preenchido somente de junho/2026 em diante. Períodos anteriores têm a coluna integralmente nula, embora as linhas existam (mai/2026: 46.269 linhas, todas sem vendedor; confirmado 2026-08-13).
Pedido: reprocessar os meses anteriores. Caso haja limite na origem, indicar a partir de qual mês a carga é viável.
Impacto atual: o filtro de período do relatório permite qualquer mês, então a aba exibe um aviso explicativo ("disponível a partir de junho/2026") em vez de tabela vazia — sem histórico não é possível comparar desempenho de vendedor entre períodos nem ver evolução, que é o principal uso do relatório. A constante VENDEDOR_DATA_START em fi-queries.ts marca esse corte e deve ser removida quando a carga for feita.
Resumo — pedidos pro time de dados
| # | Pedido | Tipo | Status |
|---|---|---|---|
| 1 | Adicionar soma_retorno_pct/soma_taxa_mes em fandi_kpi_contrato_dia | Ajuste em mart existente | Confirmado ausente no DW (2026-07-25) — ver Seção 1 |
| 2 | Criar fandi_kpi_recebimento_usuario_dia (usuario_sk, qtd_titulos, valor_total) | Mart novo | Confirmado que nenhum mart tem usuario_sk (2026-07-25) — ver Seção 2 |
| 3 | Corrigir/renomear a coluna rpuv de kpi_onepage_dia (calcula margem de compra/venda, não Rentabilidade C/Produto ÷ Vendas) | Ajuste em mart existente | Confirmado no DW (2026-07-26) — ver Seção 3 |
| 4 | Reprocessar carga histórica do usuario_sk em fandi_kpi_contrato_dia (hoje só jun/2026+) | Recarga de mart existente | Confirmado no DW (2026-08-13) — ver Seção 4 |
Não pedidos — bug de código, não de dado
| Assunto | Por que não é pedido pro time de dados |
|---|---|
Títulos — contagem, qtd. do gráfico por tipo e aba Devedores (queryTitulosContagem, queryTitulosTipoChart, queryTitulosPorDevedor) | Confirmado no DW (2026-07-25): fandi_kpi_recebimento_dia.qtd_recebimentos já existe, 100% preenchida (694.534/694.534 linhas, soma real de 1.062.261). As queries não usam a coluna que já existe. |
Monitor F&I — Operações — tipo_pessoa, taxa_mes, nome_tabela (fi-queries.ts:821-828, interface OperacaoRow em columns.ts:130-135) | Confirmado no DW (2026-07-25): tabela dw_concessionarias_gold.fandi_fat_contrato (a mesma que queryOperacoesRows já lê, alias e) já tem os campos populados — tipo_pessoa (text, 465.270/469.707 linhas, valores F/J), taxa_mes (double precision, 265.741 linhas), nome_tabela_financ (text, 323.276 linhas, 17.742 distintos) e nome_tabela_financ_tipo (text, 323.276 linhas, 5 distintos — PROMOCIONAL/PERSONALIZADA/...). Deve ser feito usando esses campos direto da fato, sem JOIN extra nem pedido pro time de dados. |