Funcionalidades do app · Dados

Empresas e unidades: separar os dados de cada cliente

Um app feito no MadBuilder pode atender vários clientes ao mesmo tempo sem que um veja os dados do outro. Esta página explica os dois jeitos de separar, por empresa e por unidade, como escolher entre eles no painel Tenancy & Banco e o que muda nas telas do app depois de publicar.

Dono do app MadBuilder › Tenancy & Banco Plano Advanced, Pro e Pro IA
01

Empresa, unidade e usuário

Três palavras aparecem em toda esta página. Vale fixar o sentido de cada uma antes de ligar qualquer opção:

TermoO que éExemplo
Empresa tenantO cliente que contrata o seu sistema. É o limite mais forte: os dados de uma empresa nunca aparecem para outra.Rede Bem Viver, Clínica Vida Plena.
Unidade filialUma filial dentro de uma empresa. Toda unidade pertence a uma empresa só.Bem Viver — Centro e Bem Viver — Norte, as duas da Rede Bem Viver.
UsuárioUma pessoa com login. Ela é vinculada a uma ou mais unidades e trabalha em uma de cada vez, a unidade ativa.Letícia, que atende nas duas unidades da Rede Bem Viver.
Empresa Rede Bem Viver Unidade Bem Viver — Centro Unidade Bem Viver — Norte Empresa Clínica Vida Plena Unidade Vida Plena — Sede Letícia vínculo nas duas unidades; trabalha em uma de cada vez
A empresa contém as unidades, e o usuário se vincula a unidades. A empresa de uma pessoa é a empresa das unidades dela.

Separar por empresa e separar por unidade são opções independentes. Um app pode usar só uma delas, as duas juntas ou nenhuma. Empresas e unidades são cadastradas dentro do app publicado, em Administração › Empresas e Administração › Unidades; o passo a passo dessas telas está em Login, usuários e unidades, seção 06.

Cadastro não é isolamento. Um app recém-criado já tem as telas de Empresas e Unidades, com uma empresa e uma unidade chamadas Default. Mas, enquanto nada for ligado no painel Tenancy & Banco, todos os usuários enxergam todos os registros das suas tabelas. É o painel que manda o app separar os dados.
02

Qual configuração escolher

Responda pela situação dos seus clientes. A primeira coluna é o que você tem; as outras dizem o que ligar.

SituaçãoTenancy (cliente)Multi-unidade
Sistema interno de uma organização só, sem filiais.Cliente únicoDesligado
Uma organização com filiais que não devem ver os registros umas das outras.Cliente únicoLigado
Sistema vendido para muitos clientes (SaaS), cada cliente com uma equipe só.Cliente único (cada cliente é uma unidade) ou PoolLigado (com Cliente único)
Sistema vendido para redes com filiais: a rede não pode ver outra rede, e as filiais separam os próprios registros.PoolLigado
Cliente que exige os dados num banco separado (contrato, volume, exigência legal).Dedicado (Bridge)Conforme as filiais
Começando um SaaS? O caminho mais simples é Cliente único + Multi-unidade: cada cliente vira uma unidade, e o Licenciamento dá a cada unidade um plano. Passe para Pool quando um cliente tiver filiais que precisam ficar agrupadas numa empresa.
03

O painel Tenancy & Banco

Toda a configuração fica num painel do projeto, no MadBuilder. Há três caminhos para abri-lo:

  • No menu lateral do Studio, grupo Geração & ferramentas, item Tenancy & Banco.
  • Em Propriedades do projeto, seção Tenancy & Banco, botão Abrir configuração.
  • Na tela de Modelos de dados, pelo selo Tenancy: … no alto. Ele mostra o modo atual e as colunas que o painel acrescenta (tenant_id, unit_id).
MadBuilder › Tenancy & Banco
Painel Tenancy & Banco com os blocos 1 Banco, 2 Tenancy (cliente), 2b Unidade (filial) e 3 IA · acesso, e à direita a coluna O que isto gera com o .env
O painel aberto num projeto novo: banco SQLite, Cliente único e nenhuma separação ligada. À direita, O que isto gera mostra as variáveis do app, os comandos e os avisos, e se atualiza a cada mudança.

O painel grava sozinho a cada alteração: no alto aparece Salvando… e depois Salvo, sem botão de salvar. São quatro blocos:

BlocoO que defineNesta página
1 · BancoOnde as tabelas ficam: SQLite, MySQL, MariaDB ou PostgreSQL, os dados de acesso ao servidor, conexão segura (SSL/TLS) e um nome de banco por modelo de dados.Não é assunto desta página.
2 · Tenancy (cliente)Como as empresas são separadas: Cliente único, Pool ou Dedicado (Bridge).Seção 04
2b · Unidade (filial)Separação por unidade, o modo de leitura (só a ativa ou todas), o banco por empresa e o licenciamento.Seções 05 e 06
3 · IA · acessoSó um atalho para o painel MCP Server, onde se define o que o agente de IA pode ler e alterar.—
Plano. O painel faz parte dos planos Advanced, Pro e Pro IA. Nos outros ele aparece com cadeado e a mensagem "Disponível no plano Advanced", com o botão Ver planos.
Os identificadores iam e log ficam travados. O bloco 2 mostra "Control-plane (travado)" com os selos iam e log: usuários, permissões e auditoria são sempre compartilhados pelo app inteiro. É isso que permite a mesma pessoa ter vínculo em várias empresas.
04

Separar por empresa (bloco 2 · Tenancy)

O bloco Tenancy (cliente) tem três cartões. Eles mudam onde os dados de cada empresa ficam guardados:

Cliente único um banco todos veem tudo Pool um banco cada registro leva a empresa dona tenant_id Dedicado (Bridge) banco da Bem Viver banco da Vida Plena compartilhado: usuários e auditoria um banco por empresa
Os três cartões do bloco 2. Em azul e laranja, os dados de duas empresas diferentes.
CartãoComo guardaQuando usar
Cliente único padrãoUm banco, sem separação por empresa.App interno, ou SaaS em que cada cliente é uma unidade (seção 05).
PoolUm banco para todas as empresas. Cada registro guarda a empresa dona, e o app só mostra os da empresa de quem está logado.Muitos clientes, custo baixo, um banco só para administrar.
Dedicado (Bridge)Um banco separado para cada empresa. Usuários, permissões e auditoria continuam num banco compartilhado.Cliente que exige isolamento físico, ou muito volume por cliente.

Pool: um banco, registros marcados com a empresa

O Pool pede dois passos no bloco 2. Só escolher o cartão não separa nada:

  1. Clique no cartão Pool.
  2. Ligue Isolar por linha (tenant_id): "Liga o filtro automático por tenant nos models data-plane".
  3. Em Conexões isoladas por tenant, marque os modelos de dados cujas tabelas devem ser separadas por empresa. Os selos iam e log aparecem travados.
MadBuilder › Tenancy & Banco › Tenancy (cliente)
Bloco Tenancy (cliente) com o cartão Pool selecionado, Isolar por linha ligado e o modelo clinica marcado em Conexões isoladas por tenant; à direita o .env com MAD_TENANT_ROW_SCOPE_ENABLED=true
Pool configurado: cartão Pool, Isolar por linha ligado e o modelo clinica marcado. Em O que isto gera, a linha MAD_TENANT_ROW_SCOPE_ENABLED=true é o filtro por empresa ligado no app.

O que acontece nos modelos marcados:

  • Toda tabela ganha a coluna tenant_id, inclusive as que já existiam. As tabelas criadas depois já nascem com ela.
  • Nas telas do app, cada listagem, campo de seleção, filtro e dashboard mostra só os registros da empresa de quem está logado. Vale também para quem é administrador.
  • Todo registro novo é gravado com a empresa de quem está logado. A coluna não aparece nos formulários e nas listagens geradas, e o formulário não consegue escolher outra empresa: um valor de tenant_id enviado por ele é ignorado. Para cadastrar em outra empresa, a pessoa troca de empresa no topo do app (pergunta frequente).
  • Coluna marcada como única passa a ser única dentro da empresa: duas empresas podem usar o mesmo código.
Sem modelo marcado, nada é separado. Com Isolar por linha ligado e nenhum modelo em Conexões isoladas por tenant, nenhuma tabela recebe a coluna. A coluna Avisos do painel avisa: "Pool sem nenhum modelo marcado em "Conexões isoladas por tenant": nenhuma tabela recebe tenant_id e os dados NÃO ficam separados por empresa — marque ao menos um modelo."

Dedicado (Bridge): um banco por empresa

No Dedicado, cada empresa tem um banco próprio. O app escolhe o banco no login, pela empresa da pessoa, e passa a ler e gravar só nele. Usuários, permissões e auditoria continuam num banco compartilhado.

  1. Clique no cartão Dedicado (Bridge). No bloco 2b, Banco próprio por empresa aparece ligado e travado: o cartão já liga essa opção.
  2. Em Conexões de tenant (allowlist), adicione uma linha por banco de empresa. O Nome lógico segue o formato tenant_<nome>, só com letras minúsculas, números e _ (ex.: tenant_bemviver). Na coluna Banco, informe o nome do banco dessa empresa.
  3. Crie os bancos. Em Comandos, o painel lista um comando por linha para rodar no app instalado. Pelo próprio app, a lista Administração › Empresas ganha a ação Provisionar banco para as empresas que ainda não têm banco.
MadBuilder › Tenancy & Banco › Tenancy (cliente)
Bloco Tenancy (cliente) com o cartão Dedicado (Bridge) selecionado e a allowlist com tenant_bemviver e tenant_vidaplena; à direita o .env com MAD_MULTI_DATABASE=1 e os comandos de provisionamento
Dedicado com duas empresas na allowlist. Em Comandos, um php artisan mad:tenant:provision … por empresa, com o nome do banco informado na linha.
Mesmo servidor do banco principal. Da linha da allowlist vale só o nome do banco: o banco da empresa é criado no mesmo servidor e com a mesma engine do bloco 1 · Banco. Se a linha informar outro host ou outra engine, o painel avisa que esses campos não são usados.

No app, o cadastro da empresa ganha o campo Conexão, preenchido sozinho ao provisionar ("Deixe em branco para empresas em banco único"), e a linha Modo de tenancy acima da lista de empresas passa a dizer Bridge (banco por empresa).

05

Separar por unidade (bloco 2b · Unidade)

A unidade é uma camada dentro da empresa. Com ela ligada, cada registro guarda a unidade dona, e cada pessoa trabalha numa unidade por vez. O bloco Unidade (filial) tem quatro opções:

MadBuilder › Tenancy & Banco › Unidade (filial)
Bloco Unidade (filial) com Multi-unidade e Ver todas as unidades do usuário ligados, Banco próprio por empresa e Licenciamento desligados
O bloco 2b com Multi-unidade e Ver todas as unidades do usuário ligados. As outras opções só ficam disponíveis com Multi-unidade ligado (o licenciamento também aceita Pool ou Dedicado).
OpçãoO que faz
Multi-unidadeLiga a separação por unidade: coluna unit_id nas tabelas, escolha da unidade no login e troca de unidade no topo do app.
Ver todas as unidades do usuárioListagens, campos de seleção e filtros mostram os registros de todas as unidades da pessoa, sem trocar de unidade. Detalhes na seção 06.
Banco próprio por empresaCada empresa, com as filiais dela, usa um banco separado. É o mesmo efeito do cartão Dedicado, que já liga esta opção.
LicenciamentoTransforma cada unidade ou empresa num cliente com plano, módulos, validade e limite de usuários. Ver Licenciamento.

O que muda com Multi-unidade ligado

  • Nas tabelas. Todas as tabelas dos modelos de dados do projeto ganham a coluna unit_id, inclusive as que já existiam. Ela não aparece nos formulários e nas listagens geradas: o app preenche sozinho.
  • No login. Quem tem vínculo com mais de uma unidade escolhe em qual entrar, na janela Escolha a unidade. Quem tem uma só entra direto.
  • No topo do app. Um seletor mostra a unidade ativa e troca para outra sem sair. Com mais de uma empresa, o seletor é de empresa e o nome da unidade fica ao lado, clicável. No menu do perfil há também Trocar Unidade. As telas estão em Login, usuários e unidades, seção 04.
  • Nas telas. Listagem, campo de seleção (combo, busca, rádio, lista de marcação), combo dependente, filtro de coluna, filtro avançado e dashboard mostram só os registros da unidade ativa. Com Ver todas as unidades do usuário, mostram os de todas as unidades da pessoa.
  • No cadastro. O registro novo vai para a unidade ativa. A edição mantém a unidade que o registro já tinha, mesmo que a pessoa esteja em outra unidade.
  • Nos códigos únicos. Uma coluna única passa a ser única dentro da unidade: duas filiais podem ter o pedido PED-0001.
Ligando num app que já tem registros. Os registros que existiam antes ficam com a unidade vazia e deixam de aparecer nas telas, porque nenhuma unidade é dona deles. Antes de ligar num app em uso, planeje preencher a coluna unit_id desses registros com o código da unidade dona (por exemplo, com um UPDATE no banco do app). O mesmo vale para tenant_id ao ligar o Pool.
Empresa e unidade juntas Com Pool e Multi-unidade ligados, a empresa separa os clientes e a unidade separa as filiais de cada cliente. A pessoa só vê unidades da empresa em que entrou.
06

Ver só a unidade ativa ou todas as do usuário

Por padrão, a pessoa vê só os registros da unidade ativa. Para consultar outra filial, ela troca de unidade no topo. Em muitos negócios isso atrapalha: a gerente regional quer ver os atendimentos das duas filiais na mesma lista. É para isso que existe Ver todas as unidades do usuário, no bloco 2b.

Só a unidade ativa (padrão) Todas as unidades do usuário Letícia, ativa em Bem Viver — Centro Letícia, ativa em Bem Viver — Centro ATD-101Bem Viver — Centro ATD-102Bem Viver — Norte ATD-103Bem Viver — Norte ATD-104Bem Viver — Sul ATD-101Bem Viver — Centro ATD-102Bem Viver — Norte ATD-103Bem Viver — Norte ATD-104Bem Viver — Sul Letícia tem vínculo com Centro e Norte. A unidade Sul não é dela: nunca aparece.
A mesma listagem nos dois modos. Em destaque, o que aparece para Letícia.

Regras do modo "todas as unidades"

SituaçãoSó a unidade ativaTodas as unidades do usuário
Listagem, campo de seleção, combo dependente, filtros, dashboardsRegistros da unidade ativa.Registros de todas as unidades da pessoa.
Cadastrar um registro novoVai para a unidade ativa, a que está no seletor do topo.
Editar um registro de outra unidadeNão acontece: o registro não aparece.O registro continua na unidade dele.
Escolher um registro de outra filial num campo de seleção (ex.: o cliente atendido no Norte)Não aparece na lista.Aparece e é aceito ao salvar.
Código único (ex.: número do atendimento)Pode repetir em outra unidade.É recusado se já existe em qualquer unidade da pessoa. Em unidade de que ela não participa, pode repetir.

Algumas partes do app continuam na unidade ativa nos dois modos: os filtros salvos da listagem, os painéis de conciliação, o PDV, o acesso do agente de IA e as telas em que o próprio desenvolvedor pediu o filtro da unidade ativa (por exemplo, o calendário com filtro de unidade ligado).

O seletor do topo continua valendo. No modo "todas as unidades", a unidade ativa define onde os cadastros novos entram, o menu, o perfil e a licença daquela unidade. Trocar de unidade não muda o que a lista mostra, só para onde vai o próximo cadastro.
07

O que o app garante

As regras abaixo valem sem nenhuma configuração extra, em qualquer tela gerada:

  • Ninguém grava na unidade dos outros. Se um formulário tentar gravar um registro numa unidade de que a pessoa não participa, o app recusa com a mensagem "Você não tem acesso à unidade escolhida", no campo da unidade ou num aviso no canto da tela.
  • O formulário não escolhe a empresa. No Pool, o registro é sempre gravado na empresa de quem está logado.
  • Validações enxergam o mesmo que a tela. "Este código já está sendo utilizado" só considera os registros que a pessoa enxerga, e um campo de seleção só aceita um registro que ela poderia escolher. Nenhuma mensagem revela que um código existe em outro cliente.
  • Administrador também é separado. Quem tem perfil de administrador vê os dados da empresa e da unidade em que entrou, como todo mundo. Para ver outra empresa ou unidade, troca no topo.
  • Usuários e permissões são do app inteiro. Cadastro de pessoa, perfis e auditoria nunca são separados por empresa: é o que permite a mesma pessoa ter vínculo em vários clientes.
08

Aplicar as mudanças no app

O painel grava a configuração no projeto; o app só muda depois de publicado.

  1. Republique o projeto. É a publicação que cria as colunas novas no banco, ajusta os índices únicos e atualiza as regras das telas.
  2. No MadCloud, se a Hospedagem mostrar "O app está com variáveis da plataforma desatualizadas", clique em Sincronizar agora.
  3. Em servidor próprio ou pelo download do projeto, as variáveis vêm no .env gerado, num bloco "Multi-tenancy & Banco". Quando há banco externo, comandos ou avisos, o pacote traz também um SETUP-INSTALL.md com o passo a passo. Ver Instalação em servidor próprio.
Teste Online. O ambiente de teste recebe as opções de comportamento (Pool, Multi-unidade, todas as unidades, licenciamento), mas roda com o banco dele. Os dados de acesso ao seu servidor e os bancos por empresa do Dedicado não são usados no teste.
Desligar não apaga colunas. Voltar para Cliente único ou desligar Multi-unidade não remove tenant_id e unit_id das tabelas nem os dados delas. Elas passam a ser colunas comuns: voltam a aparecer nos formulários e nas listagens gerados, e o app deixa de preenchê-las sozinho.
09

Em código próprio

Telas geradas já respeitam empresa e unidade. Esta seção é para quem escreve código PHP próprio no projeto (códigos, serviços, relatórios com SQL).

  • Os models das tabelas com unit_id ou tenant_id já filtram sozinhos, no modo configurado. Não acrescente um filtro pela unidade ativa num model desses: no modo "todas as unidades", ele esconderia registros que a pessoa deveria ver.
  • Em consulta direta ao banco (sem model), use a lista de unidades que a leitura enxerga.
// Unidade ativa (onde os cadastros novos entram)
$unidade = \Mad\Database\UnitContext::id();

// Unidades que a leitura enxerga: a ativa, ou todas as da pessoa
$unidades = \Mad\Database\UnitContext::readIds();   // null = sem filtro

$total = DB::table('atendimento')
    ->when($unidades !== null, fn ($q) => $q->whereIn('unit_id', $unidades))
    ->count();

// Empresa de quem está logado
$empresa = \Mad\Database\TenantContext::id();

No PDF exportado, os campos {UNIT_NAME} e {TENANT_NAME} trazem o nome da unidade e da empresa: ver Cabeçalho e rodapé do PDF.

10

Perguntas frequentes

Coloquei um campo de empresa no formulário, escolhi outra empresa e o registro foi gravado na minha.

É proposital. No Pool, o registro é sempre gravado na empresa de quem está logado, mesmo para o administrador: se o formulário pudesse escolher, qualquer pessoa gravaria dados no cliente de outra. E, mesmo que gravasse, o registro sumiria da sua lista, porque a leitura também é da sua empresa. Para cadastrar em outra empresa, troque de empresa no seletor do topo (a pessoa precisa ter vínculo com uma unidade dela) e cadastre de lá.

Liguei o Pool e cada empresa continua vendo tudo.

Confira os três pontos: o cartão Pool selecionado, Isolar por linha (tenant_id) ligado e pelo menos um modelo marcado em Conexões isoladas por tenant. A coluna Avisos do painel aponta quando falta o último. Depois, republique o projeto (seção 08).

Liguei Multi-unidade e os registros antigos sumiram.

Eles continuam no banco, mas estão com a unidade vazia e nenhuma unidade é dona deles. Preencha a coluna unit_id desses registros com o código da unidade dona. Veja o aviso da seção 05.

Sou administrador e não vejo os dados das outras filiais.

A separação vale para todos. Troque de unidade no topo. Se a sua equipe precisa consultar várias filiais ao mesmo tempo, ligue Ver todas as unidades do usuário e vincule essas pessoas às unidades que devem ver (seção 06).

No modo "todas as unidades", o app diz que um código já está em uso, mas ele é de outra filial.

Nesse modo, a verificação de código único considera todas as unidades que a pessoa enxerga, porque ela vê os registros de todas na mesma lista. Use outro código, ou cadastre pela filial que não tem esse código com o modo "só a unidade ativa".

Posso mudar de Pool para Dedicado depois?

A configuração muda no painel, mas os dados já gravados não mudam de banco sozinhos: os registros do Pool ficam no banco principal. Trocar de modo com o app em uso exige migrar os dados de cada empresa para o banco dela. Escolha o modo antes de colocar clientes no app sempre que possível.

O agente de IA respeita empresa e unidade?

Nas telas, sim: o que ele consulta pelas telas do app segue as mesmas regras. O acesso direto aos dados pelo servidor MCP tem configuração própria, no painel MCP Server (bloco 3 · IA · acesso leva até lá).

Onde cadastro as empresas e as unidades?

No app publicado, em Administração › Empresas e Administração › Unidades. Os vínculos de cada pessoa ficam na aba Unidades do cadastro do usuário. Passo a passo em Login, usuários e unidades.