UNO Data Lake

Documentação das Tabelas

Este repositório contém os modelos de dados utilizados no Uno Data Lake, um sistema para gestão e armazenamento de dados de franquias e seus negócios relacionados. Esta documentação explica a estrutura de cada tabela e seus campos.

Bem-vindo(a) à documentação do UNO Data Lake. Este ambiente concentra os dados operacionais de cada franqueadora e suas unidades (franquias/empresas) em um banco PostgreSQL, permitindo a construção de dashboards, relatórios e análises de BI sobre uma base unificada.

Esta documentação descreve o que cada tabela representa, quais colunas existem, como elas se relacionam e como consultá-las corretamente. Foi escrita para um público misto: analistas de BI (com SQL básico) e engenheiros de dados.


Índice


Visão geral

O Data Lake recebe réplicas dos dados transacionais das aplicações operacionais (Vendas, CRM, Atendimento, Financeiro, Marketing) e os organiza em tabelas analíticas amplas, denormalizadas (com nomes já materializados em colunas *_name) para facilitar a leitura por ferramentas de BI.

Características principais:

  • Banco: PostgreSQL (Amazon RDS), com SSL obrigatório.
  • Um schema por franqueadora: os dados de cada rede ficam em um schema próprio, chamado frq<ID> (ex.: frq4163). O seu login enxerga apenas o schema da sua franqueadora; não há como consultar outra rede.
  • Modelo: estrela "achatada" — cada tabela já contém o nome (denormalizado) das principais entidades relacionadas, evitando a maior parte dos JOINs em consultas típicas.
  • Multi-tenant dentro do schema: todas as tabelas operacionais possuem a coluna schema, que identifica a unidade (franquia/empresa) dona do registro. Toda consulta deve filtrar por schema.
  • Carga direta: as tabelas são projetadas diretamente da base de origem, sem camada intermediária. As tabelas de vendas são atualizadas a cada 30 minutos; as demais, diariamente (ver Atualização e frescor).
  • Histórico: as tabelas refletem o estado atual da operação. Para análises temporais, use as colunas de data (created_at, updated_at, due_date, liquidation_date, etc.).

Como acessar o Data Lake

O acesso é feito com um login individual (por pessoa ou por ferramenta de BI), criado pelo operador do Data Lake no painel de operação, na aba Acesso BI da franqueadora.

ItemDetalhe
Loginbi_frq<ID>_<login> (ex.: bi_frq4163_powerbi). A senha é exibida uma única vez, na criação. Se for perdida, o operador rotaciona a senha pelo painel.
PermissõesSomente leitura (SELECT) nas tabelas do schema da franqueadora. Nenhum acesso a outros schemas nem às tabelas internas do pipeline.
Schema padrãoO search_path do login já aponta para frq<ID>: as tabelas aparecem sem prefixo (orders, e não frq4163.orders).
SSLObrigatório (sslmode=require). Sem SSL o servidor recusa a conexão.
Liberação de IPO IP de saída da sua rede/ferramenta precisa estar liberado pelo operador (tela Acesso ao banco do painel).
LimitesAté 10 conexões simultâneas por login (configure pool na ferramenta de BI) e 15 minutos por consulta (statement_timeout; uma sessão pode subir o próprio limite com SET statement_timeout).

Exemplo de string de conexão (host, porta e nome do banco são informados pelo operador ao criar o login):

postgres://bi_frq<ID>_<login>:<senha>@<host>:5432/<database>?sslmode=require

Se uma ferramenta exigir o schema explicitamente, informe frq<ID>.


Convenções importantes

Multi-tenant: a coluna schema e o franchisor_id

Quase todas as tabelas possuem duas colunas de contexto:

ColunaDescrição
schemaIdentificador (texto) da unidade (franquia/empresa) dona do registro. Funciona como o "tenant" dentro do schema da franqueadora e é a primeira coluna de todo índice.
franchisor_idIdentificador da franqueadora (rede/marca). Como cada franqueadora tem seu próprio schema Postgres, esse valor é o mesmo em todas as linhas que você enxerga; ele existe por compatibilidade e não precisa entrar em filtros. As tabelas de royalties e taxas (franchise_*) não o possuem.

Por que isso importa? Os IDs de negócio (como order_id, customer_id, appointment_id) são gerados por unidade — ou seja, não são únicos dentro do schema da franqueadora. Duas unidades diferentes podem ter um order_id = 1234 apontando para vendas completamente distintas.

⚠️

Sempre filtre por schema.

Regra de ouro dos JOINs

Toda relação entre tabelas deve ser feita pela combinação schema + <id_de_negócio> — nunca apenas pelo ID.

Exemplos corretos:

-- ✅ CORRETO: JOIN por schema + order_id
SELECT *
FROM orders o
JOIN payments p
  ON p.schema   = o.schema
 AND p.order_id = o.order_id
WHERE o.schema = 'minha_unidade';

-- ❌ INCORRETO: JOIN apenas por order_id
SELECT *
FROM orders o
JOIN payments p ON p.order_id = o.order_id;  -- vai cruzar dados de unidades diferentes!

Para análises da rede inteira (todas as unidades da franqueadora), basta não filtrar schema no WHERE — mas mantenha o schema em todos os JOINs:

SELECT o.schema, SUM(p.value) AS recebido
FROM orders o
JOIN payments p
  ON p.schema   = o.schema
 AND p.order_id = o.order_id
GROUP BY o.schema;

Chaves: não existe id técnico

A chave primária de cada tabela é composta: (schema, <entidade>_id) — por exemplo (schema, order_id) em orders. Não há coluna id técnica.

Use sempre as colunas com sufixo _id (order_id, customer_id, appointment_id, …) — são essas que vêm dos sistemas operacionais e que devem ser usadas em filtros e JOINs (sempre combinadas com schema).

Única exceção: franchise_category_types tem uma coluna id (bigint) gerada pelo Data Lake, porque a origem não tem identificador próprio para essa junção.

Índices e desempenho no Postgres

Toda tabela tem a chave primária (schema, <id>) e, quando a consulta típica pede, índices adicionais que também começam por schema:

TabelaÍndices além da chave primária
orders(schema, created_at), (schema, customer_id), (schema, status)
order_products, order_services, order_plans, order_courses(schema, order_id)
payments(schema, order_id), (schema, created_at), (schema, liquidation_date)
budgets(schema, created_at)
customers(schema, created_at)
appointments(schema, start_date), (schema, customer_id)
order_lessons(schema, customer_id)
deals(schema, created_at)
accounts_payable(schema, due_date)
measures(schema, customer_id)
facebook_campaign_data(schema, date)

Implicação prática: uma consulta que não filtra por schema não consegue usar nenhum desses índices e varre a tabela inteira. Com o filtro, um recorte por período (created_at, due_date, start_date, date) ou por cliente usa o índice e responde rápido.

Datas, fuso horário e tipos

  • Todas as colunas de data/hora usam timestamptz (UTC). Converta para o fuso local da empresa (a coluna companies.timezone indica o fuso, ex.: America/Sao_Paulo) quando for exibir para o usuário final: created_at AT TIME ZONE 'America/Sao_Paulo'.
  • facebook_campaign_data.date é do tipo date (dia da métrica, sem hora).
  • Textos são text (sem limite de tamanho declarado); inteiros são integer; flags são boolean.
  • A moeda padrão de cada empresa está em companies.currency.

Dinheiro e números

  • Valores monetários são numeric(14,2)exatos, sem erro de arredondamento de ponto flutuante. SUM/AVG sobre eles devolvem numeric.
  • Percentuais e taxas ficam em numeric(14,4): facebook_campaign_data.frequency, franchise_royalties.percentage, franchise_advertising_fees.percentage.
  • Número de parcela é numeric(6,1) em payments.installment e accounts_payable.installment.
  • Guarda de faixa: se um valor na origem for impossível (não cabe em 14 dígitos, infinito ou inválido), a coluna chega como NULL — nunca como um número inventado. Nas colunas de total de orders, budgets e dos itens de venda, que são NOT NULL DEFAULT 0, esse caso vira 0.
  • A mesma guarda vale para colunas inteiras vindas de campos numéricos da origem (ex.: room_time_slots.start_time, goals.indication): fora da faixa, NULL.

Textos truncados

Alguns nomes e observações são cortados na carga para manter compatibilidade com dashboards existentes:

Coluna(s)Tamanho máximo
orders.customer_name, deals.name149
budgets.customer_name, budgets.employee_name, budgets.observation, budgets.observation_discarded220
services.name, appointments.service_name199
accounts_payable.description, accounts_payable.supplier_name, facebook_campaign_data.ad_name200
accounts_payable.chart_account_name, accounts_payable.cost_center_name254

Nomes mascarados

O banco é compartilhado entre franqueadoras, então nomes de empresas e franquias nunca chegam ao Data Lake:

  • companies.name e companies.legal_name trazem o alias cadastrado no painel para a unidade (padrão frq<ID>-u<company_id>). Uma unidade sem alias cadastrado não aparece em companies.
  • franchises.name traz o alias da franquia ou, na ausência dele, franquia-<franchise_id>.
  • As demais tabelas (customers, employees, deals, …) não são mascaradas.

Se precisar exibir o nome comercial da unidade num dashboard, mantenha um de-para schema → nome na própria ferramenta de BI.

Atualização e frescor dos dados

As tabelas são carregadas em cadências (grupos), configuradas por franqueadora no painel de operação. Valores padrão:

CadênciaTabelasIntervalo padrão
Vendasorders, order_products, order_services, order_plans, order_courses, payments, budgets30 min
Diáriocompanies, customers, employees, catálogo, agenda, CRM, metas, financeiro, Facebook Ads24 h
Royalties e taxasfranchises, franchise_categories, franchise_category_types, franchise_royalties, franchise_royalty_periods, franchise_advertising_fees, franchise_advertising_fee_periods — dados da unidade franqueadora24 h
Verificação de exclusõestodas as tabelas — remove linhas que deixaram de existir na origem24 h

A carga é incremental: só as linhas cujas tabelas de origem mudaram são recalculadas. A coluna updated_at de cada linha é o maior updated_at entre as tabelas de origem que a compõem — por exemplo, orders.updated_at avança quando um pagamento da venda é liquidado, mesmo sem alteração no pedido.

Para saber até quando os dados de uma unidade estão atualizados, consulte a view metadata: ela informa, por unidade e tabela, o dado mais novo já carregado. Atenção: isso não é atraso do pipeline — uma unidade sem venda nova terá last_updated antigo em orders e estará em dia.

Exclusões e regras de inclusão

  • Uma linha apagada na origem (ou que deixou de atender à regra de inclusão da tabela) é removida do Data Lake pela verificação de exclusões (diária) ou por uma carga completa. Entre uma coisa e outra, ela pode aparecer por até 24 h.
  • Regras de inclusão aplicadas na carga:
TabelaSó entram linhas que…
orders, budgetstêm total_price > 0
appointmentsnão foram excluídas, têm cliente e não estão com status_id = 7 (Bloqueio)
payments, accounts_payablenão foram excluídas na origem
companiessão unidades operacionais (a empresa-franqueadora não entra) e têm alias cadastrado no painel
franchise_*pertencem à unidade franqueadora (o schema dessas tabelas é o da própria franqueadora)

Mapa de relacionamentos

                    franchises (1) ──< companies (N)
                         │
                         └──< franchise_royalties ──< franchise_royalty_periods
                         └──< franchise_advertising_fees ──< franchise_advertising_fee_periods
                         └──< franchise_category_types >── franchise_categories

  customers ──┐
              ├──< deals
              ├──< budgets ──> orders
              ├──< orders ──< order_services ──> services
              │                ──< order_products ──> products
              │                ──< order_plans    ──> plans
              │                ──< order_courses  ──> courses ──< classes
              │                ──< order_lessons
              │                ──< payments
              ├──< appointments ──> rooms ──< room_time_slots
              ├──< measures
              └──< slimming_goals

  employees ──< employee_goals >── goals
  accounts_payable (financeiro – contas a pagar)
  facebook_accounts ──< facebook_campaign_data

Todos os JOINs implícitos acima usam schema + <id>.


Catálogo de tabelas

TabelaDomínioCadênciaDescrição rápida
franchisesOrganizaçãoRoyalties e taxasFranquias (unidades) cadastradas.
companiesOrganizaçãoDiárioDados cadastrais e de endereço das empresas.
employeesOrganizaçãoDiárioFuncionários (vendedores, atendentes, etc.).
customersOrganizaçãoDiárioClientes finais (PF/PJ).
dealsCRMDiárioNegociações/oportunidades do funil comercial.
budgetsCRMVendasOrçamentos gerados para clientes.
ordersVendasVendasVendas (pedidos) consolidadas.
order_servicesVendasVendasItens de serviço dentro de uma venda.
order_productsVendasVendasItens de produto dentro de uma venda.
order_plansVendasVendasPlanos contratados em uma venda.
order_coursesVendasVendasCursos contratados em uma venda.
order_lessonsVendasDiárioAulas individuais relacionadas a order_courses.
paymentsFinanceiroVendasRecebíveis (parcelas) gerados por vendas.
accounts_payableFinanceiroDiárioContas a pagar da empresa.
servicesCatálogoDiárioServiços oferecidos.
productsCatálogoDiárioProdutos oferecidos.
plansCatálogoDiárioPlanos comercializados.
coursesCatálogoDiárioCursos oferecidos.
classesCatálogoDiárioTurmas de cursos.
appointmentsAtendimentoDiárioAgendamentos de serviços.
roomsAtendimentoDiárioSalas de atendimento.
room_time_slotsAtendimentoDiárioJanelas de funcionamento das salas.
measuresAtendimentoDiárioMedidas corporais coletadas (ramo estética/saúde).
slimming_goalsAtendimentoDiárioMetas de emagrecimento por cliente.
goalsMetasDiárioMetas globais da empresa por período.
employee_goalsMetasDiárioMetas individualizadas por funcionário.
franchise_royaltiesFranqueadoraRoyalties e taxasRegras de cobrança de royalties por franquia.
franchise_royalty_periodsFranqueadoraRoyalties e taxasValores apurados de royalties por período.
franchise_advertising_feesFranqueadoraRoyalties e taxasRegras de taxa de publicidade.
franchise_advertising_fee_periodsFranqueadoraRoyalties e taxasValores apurados de publicidade por período.
franchise_categoriesFranqueadoraRoyalties e taxasCategorias de classificação de franquias.
franchise_category_typesFranqueadoraRoyalties e taxasVínculo entre franquia e categoria.
facebook_accountsMarketingDiárioContas de anúncio do Facebook integradas.
facebook_campaign_dataMarketingDiárioMétricas diárias por campanha/anúncio.
metadataSistemaView: dado mais novo carregado por tabela/unidade.

Tabelas — Estrutura organizacional

franchises

Franquias (unidades operacionais) cadastradas. Carregada a partir da unidade franqueadora; o schema aqui é o da própria franqueadora. Não possui franchisor_id.

ColunaTipoNullableDescrição
schematextnãoUnidade franqueadora.
franchise_idintegernãoID da franquia (chave de negócio).
nametextsimMascarado: alias da franquia ou franquia-<franchise_id>.
statustextsimVer Status de franquia.
created_attimestamptzsimCriação.
updated_attimestamptzsimÚltima atualização.

Chave primária: (schema, franchise_id).


companies

Empresas (CNPJs) operadas pelas unidades. Existe uma company por schema; a empresa-franqueadora não entra. Os nomes são mascarados.

ColunaTipoNullableDescrição
schematextnãoUnidade.
franchisor_idintegersimFranqueadora.
company_idintegernãoID da empresa.
nametextsimMascarado: alias da unidade (ex.: frq4163-u123).
legal_nametextsimMascarado: mesmo alias.
statustextsimVer Status de empresa.
street, neighborhood, number, zip_codetextsimEndereço (primeiro endereço cadastrado).
city_name, city_identifiertextsimCidade e código IBGE.
state_name, state_initialstextsimEstado.
country_nametextsimPaís.
timezonetextsimFuso horário (ex.: America/Sao_Paulo).
localetextsimLocale (ex.: pt-BR).
currencytextsimMoeda (ex.: BRL).
cnpjtextsimCNPJ (documento mais recente).
created_at / updated_attimestamptzsimAuditoria.

Chave primária: (schema, company_id).


employees

Funcionários das empresas (vendedores, atendentes, profissionais, etc.).

ColunaTipoNullableDescrição
schema / franchisor_idtext / integernão / simUnidade / rede.
employee_idintegernãoID do funcionário.
nametextsimNome.
activebooleansimEstá ativo?
employee_type_idintegersimID do tipo (cargo/função).
employee_type_nametextsimNome do tipo.
employee_type_franchise_identifierintegersimID do tipo no escopo da franquia.
employee_type_franchisorbooleansimSe o tipo é definido pela franqueadora.
expected_merit_leveltextsimNível de mérito esperado.
created_at / updated_attimestamptzsimAuditoria.

Chave primária: (schema, employee_id).


customers

Clientes finais (PF e PJ).

ColunaTipoNullableDescrição
schema / franchisor_idtext / integernão / simUnidade / rede.
customer_idintegernãoID do cliente.
typetextsimTipo: pessoa física, jurídica, etc.
nametextsimNome (PF) ou nome fantasia (PJ).
legal_nametextsimRazão social (PJ).
cpf / cnpjtextsimDocumento (mais recente de cada tipo).
cellphone, email, phonetextsimContatos.
gendertextsimGênero.
occupation_id, occupation_nameinteger / textsimOcupação/profissão.
birthdaytimestamptzsimData de nascimento.
street, neighborhood, number, zip_codetextsimEndereço.
city_id, city_name, state_id, state_name, country_id, country_nameinteger / textsimLocalização.
franchise_idintegersimFranquia de origem.
activebooleannãoAtivo? (default true).
created_at / updated_attimestamptzsimAuditoria.

Chave primária: (schema, customer_id). Índice: (schema, created_at).


Tabelas — CRM e funil comercial

deals

Negociações/oportunidades do funil comercial. Um deal pode (ou não) ser convertido em uma order. Quando o deal tem mais de um agendamento, a linha reflete o mais recente (customer_appointment_*).

ColunaTipoNullableDescrição
schema / franchisor_idtext / integernão / simUnidade / rede.
deal_idintegernãoID do deal.
nametextsimNome do contato/lead.
converted_attimestamptzsimQuando foi convertido em venda.
discarded_attimestamptzsimQuando foi descartado.
ratingintegersimPontuação/qualificação.
order_idintegersimVenda gerada (JOIN com orders por schema + order_id).
deal_origin_id, deal_origin_nameinteger / textsimOrigem do lead.
deal_stage_id, deal_stage_nameinteger / textsimEstágio atual no funil.
deal_campaign_id, deal_campaign_name, deal_campaign_sluginteger / text / textsimCampanha de CRM associada.
ad_campaign_name, ad_set_name, ad_nametextsimHierarquia do anúncio que originou o lead.
facebook_source_id, facebook_wacl_idtextsimRastreio de origem do Facebook Ads.
employee_id, employee_nameinteger / textsimResponsável.
customer_idintegersimCliente (da venda vinculada).
seller_id, seller_nameinteger / textsimVendedor (da venda vinculada).
aux_id, aux_nameinteger / textsimAuxiliar (da venda vinculada).
total_pricenumeric(14,2)simValor da venda vinculada.
customer_appointment_datetimestamptzsimData do agendamento mais recente.
customer_appointment_status_idintegersimStatus desse agendamento.
created_at / updated_attimestamptzsimAuditoria.

Chave primária: (schema, deal_id). Índice: (schema, created_at).

Para taxa de conversão: deals com converted_at IS NOT NULL ou com order_id preenchido.


budgets

Orçamentos elaborados para clientes. Só entram orçamentos com total_price > 0.

ColunaTipoNullableDescrição
schema / franchisor_idtext / integernão / simUnidade / rede.
budget_idintegernãoID do orçamento.
statustextsimVer Status de orçamento.
customer_id, customer_nameinteger / textsimCliente.
employee_id, employee_nameinteger / textsimFuncionário responsável.
service_total_price, product_total_price, plan_total_price, course_total_pricenumeric(14,2)nãoSubtotais por categoria (default 0).
total_pricenumeric(14,2)nãoValor total.
total_discountnumeric(14,2)nãoDesconto aplicado.
installmentintegersimNº de parcelas previsto.
expiration_attimestamptzsimValidade do orçamento.
discarded_attimestamptzsimDescartado em.
converted_attimestamptzsimConvertido em venda em.
observationtextsimObservação livre.
observation_discardedtextsimMotivo do descarte (texto).
transformationbooleansimIndica orçamento de transformação (renegociação).
order_service_idintegersimItem de venda originado (quando aplicável).
order_idintegersimVenda originada deste orçamento.
budget_discard_reason_idintegersimID do motivo de descarte.
created_at / updated_attimestamptznãoAuditoria.

Chave primária: (schema, budget_id). Índice: (schema, created_at).


Tabelas — Vendas (orders e itens)

orders

Cabeçalho da venda. Os itens estão nas tabelas order_services, order_products, order_plans, order_courses. Só entram vendas com total_price > 0.

ColunaTipoNullableDescrição
schema / franchisor_idtext / integernão / simUnidade / rede.
order_idintegernãoID da venda.
statustextnãoVer Status de venda.
customer_id, customer_nameinteger / textnão / simCliente.
seller_id, seller_nameinteger / textnão / simVendedor.
aux_id, aux_nameinteger / textsimVendedor auxiliar.
service_total_price, product_total_price, plan_total_price, course_total_pricenumeric(14,2)nãoSubtotais por categoria (default 0).
total_pricenumeric(14,2)nãoValor total da venda.
total_discountnumeric(14,2)nãoDesconto total.
total_paidnumeric(14,2)nãoTotal já pago (conforme a origem).
total_liquidatednumeric(14,2)nãoSoma das parcelas liquidadas (payment_status_id = 4) da venda.
is_accountingbooleannãoSe entra em contabilidade/relatórios financeiros (default true).
order_cancellation_reason_id, order_cancellation_nameinteger / textsimMotivo do cancelamento.
order_abandonment_nametextsimMotivo de abandono.
created_attimestamptznãoCriação.
updated_attimestamptznãoÚltima alteração da venda ou de um pagamento dela.
canceled_attimestamptzsimData de cancelamento.

Chave primária: (schema, order_id). Índices: (schema, created_at), (schema, customer_id), (schema, status).


order_services

Itens de serviço de uma venda.

ColunaTipoNullableDescrição
schema / franchisor_idtext / integernão / simUnidade / rede.
order_service_idintegernãoID do item.
service_idintegersimServiço do catálogo (JOIN schema + service_id).
order_idintegernãoVenda (JOIN schema + order_id).
order_plan_idintegersimSe o serviço veio incluído num plano.
quantityintegernãoQuantidade (default 1).
pricenumeric(14,2)nãoPreço unitário.
discountnumeric(14,2)nãoDesconto.
additional_valuenumeric(14,2)nãoAcréscimo.
created_at / updated_attimestamptznãoAuditoria.

Chave primária: (schema, order_service_id). Índice: (schema, order_id).


order_products

Itens de produto de uma venda.

ColunaTipoNullableDescrição
schema / franchisor_idtext / integernão / simUnidade / rede.
order_product_idintegernãoID do item.
product_idintegersimProduto (JOIN schema + product_id).
order_idintegernãoVenda.
order_plan_idintegersimSe incluso em plano.
quantityintegernãoQuantidade (default 1).
price, discount, additional_valuenumeric(14,2)nãoValores (default 0).
created_at / updated_attimestamptznãoAuditoria.

Chave primária: (schema, order_product_id). Índice: (schema, order_id).


order_plans

Planos contratados em uma venda.

ColunaTipoNullableDescrição
schema / franchisor_idtext / integernão / simUnidade / rede.
order_plan_idintegernãoID do item.
plan_idintegersimPlano (JOIN schema + plan_id).
order_idintegernãoVenda.
price, discount, additional_valuenumeric(14,2)nãoValores (default 0).
created_at / updated_attimestamptznãoAuditoria.

Chave primária: (schema, order_plan_id). Índice: (schema, order_id).


order_courses

Cursos contratados em uma venda.

ColunaTipoNullableDescrição
schema / franchisor_idtext / integernão / simUnidade / rede.
order_course_idintegernãoID do item.
course_idintegersimCurso (JOIN schema + course_id).
order_idintegernãoVenda.
statustextsimStatus do curso contratado.
price, discount, additional_valuenumeric(14,2)nãoValores (default 0).
created_at / updated_attimestamptznãoAuditoria.

Chave primária: (schema, order_course_id). Índice: (schema, order_id).


order_lessons

Aulas individuais associadas a order_courses (controle de presença).

ColunaTipoNullableDescrição
schema / franchisor_idtext / integernão / simUnidade / rede.
order_lesson_idintegernãoID da aula contratada.
attendancebooleansimPresença.
start_date, end_datetimestamptzsimJanela da aula.
order_course_idintegersimVínculo com o curso comprado.
class_id, class_nameinteger / textsimTurma.
lesson_id, lesson_nameinteger / textsimAula.
course_id, course_nameinteger / textsimCurso.
customer_id, customer_nameinteger / textsimAluno.
created_at / updated_attimestamptzsimAuditoria.

Chave primária: (schema, order_lesson_id). Índice: (schema, customer_id).


Tabelas — Financeiro

payments

Recebíveis (parcelas a receber) gerados pelas vendas. Parcelas excluídas na origem não entram.

ColunaTipoNullableDescrição
schema / franchisor_idtext / integernão / simUnidade / rede.
payment_idintegernãoID do recebível.
valuenumeric(14,2)nãoValor da parcela.
original_due_datetimestamptzsimVencimento original.
due_datetimestamptzsimVencimento atual.
liquidation_datetimestamptzsimData de liquidação (compensação).
billed_datetimestamptzsimData de faturamento.
interest_valuenumeric(14,2)simJuros.
fee_valuenumeric(14,2)simTarifa.
discount_valuenumeric(14,2)simDesconto concedido.
installmentnumeric(6,1)simNúmero da parcela.
storebooleannãoRecebido no balcão (loja)? (default false).
customer_id, customer_nameinteger / textsimCliente.
order_idintegersimVenda originadora (JOIN schema + order_id).
employee_id, employee_nameinteger / textsimFuncionário responsável (vendedor/financeiro).
payment_status_id, payment_status_nameinteger / textsimStatus. Ver Status de pagamento.
payment_type_id, payment_type_nameinteger / textsimForma/tipo de pagamento.
payment_brand_id, payment_brand_nameinteger / textsimBandeira (cartão, boleto, etc.).
chart_account_id, chart_account_nameinteger / textsimPlano de contas.
created_at / updated_attimestamptznãoAuditoria.
credit_card_brandtextsimBandeira do cartão (integrações).
last_credit_card_numbertextsimÚltimos 4 dígitos.

Chave primária: (schema, payment_id). Índices: (schema, order_id), (schema, created_at), (schema, liquidation_date).


accounts_payable

Contas a pagar da empresa (despesas, fornecedores, etc.). Contas excluídas na origem não entram.

ColunaTipoNullableDescrição
schema / franchisor_idtext / integernão / simUnidade / rede.
account_payable_idintegernãoID da conta.
descriptiontextsimDescrição.
installmentnumeric(6,1)simParcela.
valuenumeric(14,2)simValor.
feesnumeric(14,2)simJuros.
finenumeric(14,2)simMulta.
due_datetimestamptzsimVencimento.
payment_datetimestamptzsimData do pagamento.
competence_datetimestamptzsimCompetência contábil.
typetextsimTipo.
supplier_id, supplier_nameinteger / textsimFornecedor.
payment_brand_id, payment_brand_nameinteger / textsimForma de pagamento.
chart_account_id, chart_account_nameinteger / textsimPlano de contas.
cost_center_id, cost_center_nameinteger / textsimCentro de custo.
created_at / updated_attimestamptzsimAuditoria.

Chave primária: (schema, account_payable_id). Índice: (schema, due_date).


Tabelas — Catálogo (produtos, serviços, planos, cursos)

Todas as tabelas de catálogo seguem o mesmo padrão: schema, franchisor_id, <item>_id (integer, não nulo), franchise_identifier (integer — id no escopo da franquia), name (text), price (numeric(14,2)), active (boolean), created_at, updated_at (timestamptz). Chave primária (schema, <item>_id). Todas as colunas além de schema e do id são nullable.

services

Serviços oferecidos pela unidade.

Coluna extraTipoDescrição
is_budgetbooleanSe o serviço só pode ser vendido via orçamento.

Chave: service_id.

products

Produtos comercializados. Sem colunas adicionais além do padrão. Chave: product_id.

plans

Planos comercializados. Sem colunas adicionais além do padrão. Chave: plan_id.

courses

Cursos oferecidos. Sem colunas adicionais além do padrão. Chave: course_id.

classes

Turmas de cursos.

ColunaTipoNullableDescrição
schema / franchisor_idtext / integernão / simUnidade / rede.
class_idintegernãoID da turma.
nametextsimNome da turma.
start_date, end_datetimestamptzsimPeríodo da turma.
is_cyclicbooleansimTurma cíclica?
class_limitnumeric(14,2)simLimite de alunos.
minimum_frequencynumeric(14,2)simFrequência mínima exigida.
class_status_id, class_status_nameinteger / textsimStatus da turma.
course_id, course_nameinteger / textsimCurso.
employee_id, employee_nameinteger / textsimProfessor responsável.
created_at / updated_attimestamptzsimAuditoria.

Chave primária: (schema, class_id).


Tabelas — Operação clínica/atendimento

appointments

Agendamentos de serviços para clientes. Não entram agendamentos excluídos, sem cliente ou com status_id = 7 (Bloqueio de agenda).

ColunaTipoNullableDescrição
schema / franchisor_idtext / integernão / simUnidade / rede.
appointment_idintegernãoID do agendamento.
order_idintegersimVenda relacionada.
customer_id, customer_nameinteger / textsimCliente.
employee_id, employee_nameinteger / textsimProfissional.
service_id, service_franchise_identifier, service_nameinteger / integer / textsimServiço.
service_budgetbooleansimÉ serviço de orçamento?
status_idintegersimStatus. Ver Status de agendamento.
start_date, end_datetimestamptzsimJanela do atendimento.
service_sessionintegersimNº da sessão.
ratingintegersimAvaliação do cliente.
rescheduledintegersimQuantidade de reagendamentos.
created_at / updated_attimestamptzsimAuditoria.
room_id, room_nameinteger / textsimSala (JOIN schema + room_id).

Chave primária: (schema, appointment_id). Índices: (schema, start_date), (schema, customer_id).


rooms

Salas de atendimento.

ColunaTipoNullableDescrição
schema / franchisor_idtext / integernão / simUnidade / rede.
room_idintegernãoID da sala.
nametextsimNome.
customer_limitsintegersimCapacidade simultânea.
statustextsimStatus (ativa/inativa, etc.).
employee_idintegersimFuncionário responsável.
created_at / updated_attimestamptzsimAuditoria.

Chave primária: (schema, room_id).

room_time_slots

Janelas de funcionamento das salas (por dia da semana).

ColunaTipoNullableDescrição
schema / franchisor_idtext / integernão / simUnidade / rede.
room_time_slot_idintegernãoID.
day_of_weektextsimDia da semana (ex.: MONDAY).
start_timeintegersimInício (em minutos a partir de 00:00).
end_timeintegersimFim (em minutos a partir de 00:00).
room_idintegersimSala.
created_at / updated_attimestamptzsimAuditoria.

Chave primária: (schema, room_time_slot_id).

measures

Medidas corporais coletadas (típico de redes de estética/saúde). Medidas fora da faixa numérica chegam como NULL.

ColunaTipoNullableDescrição
schema / franchisor_idtext / integernão / simUnidade / rede.
measure_idintegernãoID da medição.
customer_idintegersimCliente.
datetimestamptzsimData da medição.
weight, heightnumeric(14,2)simPeso e altura.
hip, waist, high_waist, low_waistnumeric(14,2)simQuadril e cinturas.
left_arm, right_armnumeric(14,2)simBraços.
left_thigh_high, left_thigh_low, right_thigh_high, right_thigh_lownumeric(14,2)simCoxas.
calfnumeric(14,2)simPanturrilha.
baseline_measurenumeric(14,2)simLinha de base.
abdominal_circumferencenumeric(14,2)simCircunferência abdominal.
fat_percentagenumeric(14,2)sim% de gordura.
blood_pressure_max, blood_pressure_minnumeric(14,2)simPressão arterial.
first_measurebooleansimÉ a primeira medição?
order_service_idintegersimItem de venda associado.
observationtextsimObservação.
created_at / updated_attimestamptzsimAuditoria.

Chave primária: (schema, measure_id). Índice: (schema, customer_id).

slimming_goals

Metas de emagrecimento por cliente.

ColunaTipoNullableDescrição
schema / franchisor_idtext / integernão / simUnidade / rede.
slimming_goal_idintegernãoID.
customer_idintegersimCliente.
start_date, end_datetimestamptzsimPeríodo da meta.
finished_attimestamptzsimQuando concluída.
weight, height, hip, waist, high_waist, low_waist, left_arm, right_arm, left_thigh_high, left_thigh_low, right_thigh_high, right_thigh_low, calfnumeric(14,2)simMedidas-alvo.
observationtextsimObservação.
created_at / updated_attimestamptzsimAuditoria.

Chave primária: (schema, slimming_goal_id).


Tabelas — Metas e desempenho

goals

Metas globais da empresa por período.

ColunaTipoNullableDescrição
schema / franchisor_idtext / integernão / simUnidade / rede.
goal_idintegernãoID.
initial_date, end_datetimestamptzsimPeríodo da meta.
indicationintegersimMeta de indicações.
product, service, plan, coursenumeric(14,2)simMetas por categoria.
ticketnumeric(14,2)simMeta de ticket médio.
efficiencynumeric(14,2)simMeta de eficiência.
transformationnumeric(14,2)simMeta de transformação.
totalnumeric(14,2)simMeta total (R$).
count_days, count_past_daysintegersimDias úteis no período / já transcorridos.
created_at / updated_attimestamptzsimAuditoria.

Chave primária: (schema, goal_id).

employee_goals

Metas individuais por funcionário.

ColunaTipoNullableDescrição
schema / franchisor_idtext / integernão / simUnidade / rede.
employee_goal_idintegernãoID.
employee_idintegersimFuncionário.
goal_idintegersimMeta global associada.
initial_date, end_datetimestamptzsimPeríodo.
indicationintegersimMeta de indicações.
product, service, plan, course, transformation, totalnumeric(14,2)simMetas por categoria e total.
count_days, count_past_daysintegersimDias do período / dias passados.
created_at / updated_attimestamptzsimAuditoria.

Chave primária: (schema, employee_goal_id).


Tabelas — Royalties e taxas da franqueadora

As tabelas deste grupo vêm da unidade franqueadora: o schema é o da própria franqueadora e não há coluna franchisor_id.

franchise_royalties

Regras de cobrança de royalties por franquia.

ColunaTipoNullableDescrição
schematextnãoUnidade franqueadora.
franchise_royalty_idintegernãoID da regra.
start_at, end_attimestamptzsimVigência.
due_dayintegersimDia de vencimento.
typetextsimTipo de cobrança.
min_valuenumeric(14,2)simValor mínimo.
percentagenumeric(14,4)simPercentual.
calculation_typetextsimForma de cálculo (sobre faturamento, etc.).
payment_brand_id, payment_brand_nameinteger / textsimForma de pagamento.
franchise_idintegersimFranquia.
created_at / updated_attimestamptzsimAuditoria.

Chave primária: (schema, franchise_royalty_id).

franchise_royalty_periods

Valores apurados de royalties por período.

ColunaTipoNullableDescrição
schematextnãoUnidade franqueadora.
franchise_royalty_period_idintegernãoID.
start_at, end_attimestamptzsimPeríodo de apuração.
valuenumeric(14,2)simValor apurado.
franchise_royalty_idintegersimRegra associada.
created_at / updated_attimestamptzsimAuditoria.

Chave primária: (schema, franchise_royalty_period_id).

franchise_advertising_fees

Regras de taxa de publicidade. Mesma estrutura de franchise_royalties, trocando franchise_royalty_id por franchise_advertising_fee_id. Chave primária: (schema, franchise_advertising_fee_id).

franchise_advertising_fee_periods

Valores apurados de taxa de publicidade. Mesma estrutura de franchise_royalty_periods, com franchise_advertising_fee_period_id (chave) e franchise_advertising_fee_id (regra associada).

franchise_categories

Categorias para classificar franquias.

ColunaTipoNullableDescrição
schematextnãoUnidade franqueadora.
franchise_category_idintegernãoID.
nametextsimNome da categoria.
activebooleansimAtiva?
created_at / updated_attimestamptzsimAuditoria.

Chave primária: (schema, franchise_category_id).

franchise_category_types

Tabela de junção entre franquia e categoria.

ColunaTipoNullableDescrição
schematextnãoUnidade franqueadora.
idbigintnãoIdentificador gerado pelo Data Lake: (franchise_category_id << 32) + franchise_id. A origem não tem id para esta junção.
franchise_category_idintegersimCategoria.
franchise_idintegersimFranquia.
created_at / updated_attimestamptzsimAuditoria.

Chave primária: (schema, id).


Tabelas — Marketing (Facebook Ads)

facebook_accounts

Contas de anúncio do Facebook integradas.

ColunaTipoNullableDescrição
schema / franchisor_idtext / integernão / simUnidade / rede.
facebook_account_idintegernãoID.
nametextsimNome da conta.
account_statusintegersimStatus (códigos do Facebook).
currencytextsimMoeda.
amount_spenttextsimTotal gasto (acumulado, como texto).
spend_captextsimLimite de gasto (como texto).
business_idtextsimBusiness Manager.
created_timetimestamptzsimQuando a conta foi criada no Facebook.
bm_idtextsimID do BM.
last_campaign_synctimestamptzsimÚltima sincronização.
activebooleansimEstá ativa no nosso sistema?
business_nametextsimNome do Business Manager.
created_at / updated_attimestamptzsimAuditoria.

Chave primária: (schema, facebook_account_id).

facebook_campaign_data

Métricas diárias por anúncio.

ColunaTipoNullableDescrição
schema / franchisor_idtext / integernão / simUnidade / rede.
facebook_campaign_data_idintegernãoID.
account_idtextsimConta de anúncio.
identifiertextsimIdentificador do registro.
datedatesimDia da métrica.
source_idtextsimID de origem (anúncio).
total_spentnumeric(14,2)simGasto.
ad_campaign_name, ad_set_name, ad_nametextsimHierarquia do anúncio.
impressions, link_clicks, reachintegersimMétricas de entrega.
frequencynumeric(14,4)simFrequência.
started_messagesintegersimConversas iniciadas.
video_view_25_count, video_view_50_count, video_view_75_count, video_view_95_countintegersimVisualizações de vídeo por % assistido.
created_at / updated_attimestamptzsimAuditoria.

Chave primária: (schema, facebook_campaign_data_id). Índice: (schema, date).

Para atribuir leads/vendas a campanhas, cruze com deals.facebook_source_id ou com deals.ad_campaign_name / ad_set_name / ad_name.


Tabelas — Sistema

metadata

View (somente leitura) que informa, para cada par (schema, table), o dado mais novo já carregado no Data Lake.

ColunaTipoDescrição
schematextUnidade.
tabletextNome da tabela final (ex.: orders).
last_updatedtimestamptzMaior updated_at da origem já carregado para essa tabela/unidade.

Use para verificar até quando os dados de uma unidade estão atualizados:

SELECT "table", last_updated
FROM metadata
WHERE schema = 'minha_unidade'
ORDER BY "table";

last_updated é a idade do dado, não do pipeline: uma unidade sem movimentação recente terá valores antigos e estará em dia. A verificação de exclusões não altera esta view.


Enums e dicionários de valores

franchises.status

ValorSignificado
IN_IMPLEMENTATIONEm implementação.
ACTIVEAtiva.
TERMINATION_REQUESTSolicitação de encerramento.
TERMINATIONEm encerramento.
CANCELEDCancelada.
LEGALEm situação jurídica.

companies.status

ValorSignificado
ACTIVEAtiva.
INACTIVEInativa.
IMPLANTATIONEm implantação.
BLOCKEDBloqueada.

orders.status

ValorSignificado
BILLEDFaturada.
CLOSEDFechada.
CANCELEDCancelada.
ABANDONMENTAbandono.
PENDING_BALANCESaldo pendente.

budgets.status

ValorSignificado
PENDINGAguardando.
ACCOMPLISHEDRealizada.
CONVERTEDConvertida em venda.
DISCARDEDDescartada.

payments.payment_status_id

IDNome
0Cancelado
1Pendente
2Conciliação
3Vencido
4Liquidado

appointments.status_id

IDNomeObservação
1Agendado
2Confirmado
3Em espera
4Em andamento
5Realizado
6Falta
7BloqueioNão é carregado em appointments.

Exemplos de queries para BI

Em todos os exemplos, troque 'minha_unidade' pelo schema desejado.

1. Faturamento mensal (vendas faturadas)

SELECT date_trunc('month', created_at) AS mes,
       SUM(total_price)               AS faturamento,
       COUNT(*)                       AS qtd_vendas
FROM orders
WHERE schema = 'minha_unidade'
  AND status = 'BILLED'
  AND created_at >= '2025-01-01'
GROUP BY 1
ORDER BY 1;

2. Recebíveis pendentes (a vencer e vencidos)

SELECT payment_status_name,
       COUNT(*)        AS qtd,
       SUM(value)      AS total
FROM payments
WHERE schema = 'minha_unidade'
  AND payment_status_id IN (1, 3) -- Pendente, Vencido
GROUP BY 1;

3. Ranking de vendedores no mês

SELECT seller_id,
       seller_name,
       COUNT(*)            AS qtd_vendas,
       SUM(total_price)    AS receita,
       SUM(total_paid)     AS recebido
FROM orders
WHERE schema = 'minha_unidade'
  AND status = 'BILLED'
  AND created_at >= date_trunc('month', current_date)
GROUP BY 1, 2
ORDER BY receita DESC;

4. Conversão do funil de CRM

SELECT COUNT(*)                                                 AS leads,
       COUNT(converted_at)                                      AS convertidos,
       COUNT(discarded_at)                                      AS descartados,
       ROUND(100.0 * COUNT(converted_at) / NULLIF(COUNT(*),0), 2) AS taxa_conversao_pct
FROM deals
WHERE schema = 'minha_unidade'
  AND created_at >= current_date - INTERVAL '90 days';

5. Vendas com seus pagamentos (uso correto do JOIN)

SELECT o.order_id,
       o.customer_name,
       o.total_price,
       o.total_paid,
       p.payment_id,
       p.value,
       p.due_date,
       p.payment_status_name
FROM orders o
JOIN payments p
  ON p.schema   = o.schema    -- ✅ obrigatório
 AND p.order_id = o.order_id
WHERE o.schema = 'minha_unidade'
  AND o.created_at >= current_date - INTERVAL '30 days';

6. Ocupação de agenda por profissional

SELECT employee_id,
       employee_name,
       COUNT(*) FILTER (WHERE status_id = 5) AS realizados,
       COUNT(*) FILTER (WHERE status_id = 6) AS faltas,
       COUNT(*)                              AS total_agendamentos
FROM appointments
WHERE schema = 'minha_unidade'
  AND start_date >= date_trunc('month', current_date)
GROUP BY 1, 2
ORDER BY total_agendamentos DESC;

7. Custo por lead (Facebook Ads × Deals)

WITH gasto AS (
  SELECT date, SUM(total_spent) AS spent
  FROM facebook_campaign_data
  WHERE schema = 'minha_unidade'
    AND date >= current_date - INTERVAL '30 days'
  GROUP BY 1
),
leads AS (
  SELECT created_at::date AS date, COUNT(*) AS qtd
  FROM deals
  WHERE schema = 'minha_unidade'
    AND facebook_source_id IS NOT NULL
    AND created_at >= current_date - INTERVAL '30 days'
  GROUP BY 1
)
SELECT g.date,
       g.spent,
       l.qtd                                AS leads,
       ROUND(g.spent / NULLIF(l.qtd, 0), 2) AS cpl
FROM gasto g
LEFT JOIN leads l ON l.date = g.date
ORDER BY g.date;

8. Consolidado da rede (todas as unidades da franqueadora)

Como o schema Postgres já é o da franqueadora, basta agrupar por schema sem filtrá-lo. Para exibir o nome da unidade, cruze com companies (o name é o alias cadastrado no painel).

SELECT o.schema,
       c.name           AS unidade,
       SUM(o.total_price) AS faturamento
FROM orders o
LEFT JOIN companies c
  ON c.schema = o.schema
WHERE o.status = 'BILLED'
  AND o.created_at >= date_trunc('month', current_date)
GROUP BY o.schema, c.name
ORDER BY faturamento DESC;

9. Frescor dos dados por unidade

SELECT schema,
       MAX(last_updated) FILTER (WHERE "table" = 'orders')   AS ultima_venda_vista,
       MAX(last_updated) FILTER (WHERE "table" = 'payments') AS ultimo_pagamento_visto
FROM metadata
GROUP BY schema
ORDER BY schema;

Boas práticas de desempenho no Postgres

  1. Sempre filtre por schema. Todo índice começa por essa coluna; sem o filtro, a consulta varre a tabela inteira.
  2. Faça JOINs por schema + <id>. Além de correto, é o que permite usar a chave primária composta dos dois lados.
  3. Use os campos *_name denormalizados sempre que possível, em vez de fazer JOIN com tabelas-mestras (customers, services, etc.). Eles existem justamente para evitar JOINs em queries comuns de BI.
  4. Filtre por colunas indexadas junto com schema: created_at (vendas, pagamentos, clientes, orçamentos, deals), due_date (contas a pagar), start_date (agendamentos), date (Facebook Ads), liquidation_date (pagamentos), customer_id, order_id, status (vendas).
  5. Janelas curtas de data (ex.: últimos 90 dias) reduzem muito o volume lido.
  6. Selecione só as colunas necessárias — menos dados trafegando para a ferramenta de BI.
  7. Prefira agregações GROUP BY em colunas de baixa cardinalidade (status, employee_id, schema) e dimensões temporais (date_trunc(...)).
  8. Use WITH (CTEs) para deixar a query legível; o planejador do Postgres as otimiza normalmente.
  9. Confira com EXPLAIN se a consulta usa índice (Index Scan / Bitmap Index Scan) em vez de Seq Scan quando ela estiver lenta.
  10. Lembre dos limites do login: 15 minutos por consulta e 10 conexões simultâneas. Configure pool e cache na ferramenta de BI em vez de abrir uma conexão por painel.
  11. Para saber até onde o Data Lake está atualizado, consulte a view metadata.

Em caso de dúvidas sobre o significado de um campo ou enum específico, consulte o time de dados — esta documentação é viva e será atualizada conforme novas tabelas e colunas forem adicionadas ao Data Lake.


Did this page help you?