Especificação · Admin

Empresa e Estabelecimento

A entidade que faltava em quatro specs seguidas. Quem paga já tem resposta no centro de custo; quem assina e quem recebe a nota não tinha nenhuma — o tenant guardava um CNPJ solto, e grupo econômico só cabia no produto duplicando catálogo, árvore, alçada e usuário. Esta spec fecha a dupla Company + GLAccount e desbloqueia a espinha transacional inteira.

desbloqueia Requisition · PurchaseOrder · Receipt · Invoice revisou Centro de Custo, Fluxo de Aprovação e Cadastro de Item 4 agregados · 2 agregados filhos · 2 tabelas de vínculo v1.11 · 09/09/2026

Ponto de partida

Oito decisões

Decisão 1 · Empresa

Empresa é entidade dentro do tenant, não tenant filho. Company nasce obrigatória e sempre existe, mesmo quando é uma só; o CNPJ solto do tenant morre — e TenantSettings, que esta spec tratava como entidade da plataforma, não existe no Gryd.IO. A plataforma já sabia agrupar organizações — o que ela não sabia era que uma organização tem mais de um CNPJ.

Decisão 2 · Tenancy

Os dois modos de isolamento são o mesmo modelo. Tenant com uma Company é o modo isolado; tenant com N é o consolidado. O domínio é escrito uma vez, para o consolidado — no isolado o segundo filtro simplesmente não recorta nada. Sem if, sem duas trilhas, sem dois jeitos de emitir um pedido.

Decisão 3 · Grupo

O tenant Group é eixo de acesso, nunca de dados. Nenhuma linha de nenhuma tabela pertence a um tenant do tipo grupo. No minuto em que ele guardar dado, todo filtro do sistema vira "tenant ou pai do tenant" — e um filtro que aceita o pai é um filtro que vaza.

Decisão 4 · Identidade

Dois níveis de CNPJ, espelhando o fornecedor. Company é a pessoa jurídica, chaveada pela raiz de 8; Establishment é o CNPJ de 14, matriz ou filial. É o mesmo corte de Supplier e SupplierEstablishment: um CNPJ tem um modelo mental só no produto, de dentro e de fora.

Decisão 5 · Escopo

O acesso empresarial é do Nexio: UserCompanyScope concede visibilidade e UserCompanyRole limita as operações por empresa. Permissões da plataforma são o teto. Nenhuma linha vigente significa acesso negado; allCompanies exige concessão explícita. Alçada financeira é separada.

Decisão 6 · Documento

O documento aponta para o estabelecimento; a empresa é derivada. Quem emite pedido e recebe nota é um CNPJ de 14, mas alçada, orçamento e conta contábil raciocinam em empresa. Um campo na interface, dois congelados no documento.

Decisão 7 · Contábil

Plano de contas no tenant, habilitação e código externo por empresa. Grupo econômico brasileiro usa plano unificado; o que varia é o código no ERP de cada CNPJ. Um plano por empresa multiplicaria manutenção por N e faria o consolidado depender de casar códigos por convenção.

Decisão 8 · Numeração

NumberSequence configurável, com empresa como padrão. Grupo brasileiro quase sempre quer pedido numerado por empresa; parte quer por filial; quase ninguém quer número global. O escopo é dado, não código.

Fronteira

O que a plataforma já resolve

A camada gryd nunca entrou no mapa de domínio, e foi por isso que empresa quase foi modelada duas vezes. Validado contra Design/Tenants Handoff, Usuarios Handoff e Roles Handoff.

Camada de plataforma · não se mexe
Já existeO que éO que o Nexio faz com isso
TenantA fronteira de isolamento. Type = Company | Group, ParentTenantId, hierarquia de um nível.Um tenant = um cliente contratante. O Nexio não usa ParentTenantId no modo consolidado.
UserGlobal — não pertence a tenant nenhum.Intacto. Nenhum campo novo.
UserTenantA matriz de acesso: uma linha por organização, com vigência, isDefault e isActive.intacto A preferência de estabelecimento padrão vive do lado do Nexio.
UserRoleO papel dentro daquele tenant. Não existe papel global.intacto O recorte por empresa é UserCompanyScope, do Nexio.
Role · PermissionCatálogo por tenant.Intactos. Os papéis são do tenant, não da empresa — o que varia é onde o papel vale.
Configuração de tenantA plataforma não tem TenantSettings. O CNPJ único por tenant que o Nexio lia era do próprio Nexio. Mas tem Tenant.Settings, e a recusa precisa dizer isso (09/09/2026): um string? mapeado como documento semiestruturado, com UpdateSettings e leitura por JsonNode na mão — a única leitura tipada hoje é a política de MFA. O JSON genérico que a decisão recusa já está lá, a uma linha de distância, e alguém vai encontrá-lo. Recusá-lo é escolha: parâmetro do Nexio ali seria configuração sem esquema, sem validação de domínio e sem migration, num campo de outro módulo — e o primeiro a ler valida, ou não valida ninguém.removido — o CNPJ vive em Company.cnpjRoot e Establishment.cnpj. Os parâmetros por tenant do Nexio (teto de profundidade das árvores, materialidade do rateio, expediente e lista de dias não úteis) ganham entidade própria — pendência registrada no mapa de domínio. Desde 08/09/2026 o calendário não pede entidade: virou campo do tenant no fluxo de aprovação, porque o SLA é o único consumidor de dia útil no produto.

Duas palavras, dois sentidos — e um deles muda. A tela de Tenants rotula o tenant filho como "Empresa". A partir desta spec, "empresa" significa Company e nada mais: o tenant passa a ser chamado de organização em tela, spec e conversa. É troca de rótulo, não de modelo — a tela já se chama "Tenants · Organizações" e só precisa parar de se contradizer. O valor de enum TenantType.Company permanece: é membro de enum, sempre qualificado, e não colide com a classe Company do domínio.

Tenancy

Os dois modos

Modo isolado · N organizações, uma empresa cada

Cada CNPJ vira um tenant, opcionalmente sob um tenant Group. O isolamento é estrutural: o dado da empresa A não é filtrado do dado da empresa B, ele está em outra fronteira. É o modo para quem tem restrição de verdade — empresas concorrentes dentro do mesmo grupo, sociedade com terceiros, exigência de auditoria independente.

O que custa: catálogo, fornecedores homologados, estrutura de centro de custo, papéis e alçadas são recadastrados por empresa, e não sincronizam. Aprovação consolidada não existe. Relatório do grupo exige leitura cross-tenant.

Modo consolidado · uma organização, N empresas

O tenant é o grupo econômico. Cadastro mestre é único e compartilhado; documento, orçamento, conta contábil e alçada são por empresa. O isolamento entre empresas passa a ser escopo — companyId vindo do token, nunca do cliente.

O que ganha: matriz aprova pedido de filial com uma política, não com integração. Fornecedor é homologado uma vez. Consolidado do grupo é consulta comum.

O critério de qual vender

Não é tamanho do grupo nem número de CNPJs. É uma pergunta só: as empresas do grupo podem ver o cadastro mestre umas das outras? Catálogo, fornecedores, estrutura de centro de custo, tabela de preço. Sim — que é o caso da maioria dos grupos brasileiros, porque a estrutura é espelhada mesmo — consolidado. Não — tenants separados, e o cliente aceita duplicar cadastro.

A pergunta é sobre mestre, não sobre documento. Esconder as requisições da empresa B do usuário da empresa A o modo consolidado faz bem; o que ele não faz é esconder que a empresa B existe.

Na dúvida, consolidado

Um tenant consolidado consegue se comportar como isolado: escopa todo usuário a uma empresa, nunca cria registro corporativo, e na prática ninguém vê nada da outra. O inverso não existe sem cross-tenant. Migração entre os dois é dolorosa nos dois sentidos — juntar catálogos com códigos colidindo, ou partir documentos — então erra-se para o lado reversível por configuração.

O que não pode acontecer. O modo isolado não pode pular a Company e continuar lendo um CNPJ solto do tenant. Duas formas de descobrir de que CNPJ um documento é significa dois modelos, dois caminhos de código e dois conjuntos de bug. Company é obrigatória nos dois modos; no isolado ela simplesmente tem uma linha.

Modelo

Mapa de entidades

Convenção de nomes: o documento é escrito em português e todo identificador é em inglês — entidade, atributo, valor de enum, rota, permissão e código de erro. A exceção são os dados de negócio brasileiros, que ficam como são no mundo real: cnpj, stateRegistration, suframaCode, primaryCnae e os nomes de regime tributário (SimplesNacional, LucroReal) — esses são conteúdo, não código.

Tenant plataforma · gryd A fronteira de isolamento. Group agrega organizações e nunca guarda dado.
Company aggregate root · raiz de 8 A pessoa jurídica. Quem assina, quem tem regime tributário, quem tem orçamento e alçada.
Establishment N por empresa · CNPJ de 14 Matriz ou filial. Quem recebe a nota, com IE, endereço fiscal e situação cadastral próprios.
GLAccount aggregate root · árvore Conta contábil. Plano único do tenant, com caminho materializado — a terceira árvore do produto, e a mesma mecânica das outras duas.
GLAccountCompany vínculo · N×N Habilitação e código externo da conta em cada empresa. É o que a integração com o ERP consome.
NumberSequence N por tipo de documento Numeração com escopo declarado: tenant, empresa ou estabelecimento. Incremento atômico no banco.
UserCompanyScope Nexio · N por usuário Quais empresas cada pessoa alcança. Referencia o userId do gryd sem FK. Nulo — e a ausência da linha — = todas.
CostCenter eixo irmão legalEntityId vira companyId. A regra do nó corporativo já estava escrita e não muda.
DeliveryLocation futuro · depende desta Armazém, obra, ponto de entrega. Aponta para o estabelecimento que recebe fiscalmente. Modelado antes desta spec, nasceria errado.

Domínio · Company

Campos · Company

Legenda: obrig sempre · cond conforme a operação · opc opcional · gerado calculado pelo sistema ou pelo banco.

Identificação

CampoTipoRegra
idGuidgeradoAtribuído pelo agregado, não pelo banco.
tenantIdGuidgeradoInjetado a partir do token. Nunca aceito do cliente.
codestring(20)obrigTrim e maiúsculas, apenas [A-Za-z0-9_-], único por tenant, imutável. É o que a importação, a política de aprovação e a integração referenciam.
cnpjRootvarchar(8)obrigA raiz do CNPJ. Nunca numérico — aceita A–Z e dígitos. Único por tenant, imutável. Duas empresas do mesmo tenant não compartilham raiz: se compartilhassem, seriam a mesma pessoa jurídica.
legalNamestring(200)obrigRazão social. Trim; vazio é 400.
tradeNamestring(160)opcNome fantasia. É o que aparece no seletor de documento — quando ausente, cai para legalName.
externalCodestring(40)opcO código da mesma empresa no ERP do cliente. Único por tenant quando preenchido.

Natureza e regime

CampoTipoRegra
taxRegimeenumobrigSimplesNacional | MEI | LucroPresumido | LucroReal | TaxExempt | NotApplicable — o mesmo enum de Supplier.taxRegime, e não um paralelo: regime tributário é o mesmo conceito visto dos dois lados. Do lado comprador, NotApplicable é recusado. Vive na empresa, não na filial — o Simples é opção da pessoa jurídica inteira. É o campo que decide se a compra gera crédito.
legalNaturevarchar(4)opcCódigo de natureza jurídica da tabela do IBGE/Receita (2062, 2054…). Metadado; nenhuma regra depende dele hoje.
shareCapitaldecimal(18,2)opcCapital social. Informativo, e usado por alguns clientes como referência de porte na alçada.
functionalCurrencychar(3)opcISO 4217, padrão BRL, e qualquer código ISO 4217 vale já na v1 — a spec de Moeda e Câmbio restringia a BRL e a restrição saiu em 09/09/2026, com o erro que a sustentava. A moeda em que a empresa fecha; compra em moeda estrangeira converte para ela. Duas consequências escritas na mesma data: o limite de alçada tem moeda própria (approvalLimitCurrency no Centro de Custo, obrigatória com o limite preenchido, nó corporativo incluído), e submeter documento cuja moeda não tenha par de conversão vigente contra a moeda do limite é recusa declarada — nunca comparação silenciosa entre moedas no ponto em que a alçada decide. O que continua fora é a conversão cruzada: par que não passe pelo real precisa de fonte própria cadastrada. Deixou de ser informativo: a spec de Orçamento copia este valor para Budget.currency na criação do exercício e o congela, e todo movimento do razão orçamentário guarda a taxa usada na conversão.
foundedOndateopcData de abertura.
closedOndateopcData de baixa. Revisto em 09/09/2026: é fato de calendário e bloqueia documento novo com data posterior, mas não mexe em isActive. Quem desativa é PATCH /deactivate, e só ele. Até 08/09 esta linha dizia “força isActive = false” — era a única exceção à convenção “estado só por rota própria”, e existia só porque era a única saída de um ciclo entre duas guardas que a rota de desativação passou a resolver. Preenchido em empresa ainda ativa, o PUT grava e a resposta avisa que falta desativar.

Estado e derivados

CampoTipoRegra
isActiveboolgeradoAlterado só por /activate e /deactivate, nunca por PUT. Desde 09/09/2026 a desativação cascateia — os estabelecimentos ativos caem na mesma transação, e desativar com estabelecimento ativo deixou de ser 409. E isActive = false restringe escrita: o que a empresa emitiu continua legível para quem a alcança. Ver Escopo e segurança.
headquartersIdGuidgeradoO estabelecimento matriz. Derivado, não digitado — é o único com type = Headquarters. Nunca nulo: a matriz nasce na mesma transação que a empresa (v1.4), então não existe empresa sem matriz nem por uma chamada.
establishmentCountintgeradoExibido na lista. Ativos e inativos separados no detalhe.

O que não está aqui, de propósito. Certidões, quadro societário, situação cadastral consultada na Receita, CNAE secundário e regime de retenção são do fornecedor, não da empresa compradora: o Nexio faz diligência de quem vende, não de si mesmo. Se um dia o produto virar portal e a empresa compradora precisar se apresentar a alguém, esses campos entram — e entram no mesmo formato que já existe em Supplier.

Domínio · Establishment

Campos · Establishment

A empresa é quem assina; o estabelecimento é quem recebe a nota. Tudo o que muda de filial para filial vive aqui.

Identificação

CampoTipoRegra
idGuidgerado—
tenantIdGuidgeradoDo token. Denormalizado para caber no filtro de linha sem join.
companyIdGuidobrigDono. Imutável — um estabelecimento não muda de pessoa jurídica; isso seria baixa e abertura.
codestring(20)obrigÚnico por empresa, imutável. Cliente vindo de Protheus digita 0001, 0002; cliente novo digita MATRIZ, CD-REC. As duas convenções cabem.
cnpjvarchar(14)obrigCNPJ completo, sem máscara. Único por tenant. Os 8 primeiros caracteres têm que ser o cnpjRoot da empresa — divergência é 422, não 400: o dado é bem formado e semanticamente impossível.
typeenumobrigatórioHeadquarters ou Branch, coerente com indicador cadastral e evidência oficial; não inferir apenas da ordem 0001. Exatamente uma matriz cadastrada por Company. Principal operacional de fornecedor é outro conceito.
namestring(160)obrigComo a filial é chamada internamente. É o que aparece no seletor, sob o nome da empresa.
externalCodestring(40)opcO código da filial no ERP. Único por (tenant, empresa) quando preenchido.

Fiscal

CampoTipoRegra
icmsTaxpayerStatusenumobrigTaxpayer · Exempt · NonTaxpayer. É o indIEDest da NF-e (1 · 2 · 9) visto do lado do destinatário. Decide se stateRegistration é exigida e como o fornecedor tributa a operação.
stateRegistrationvarchar(20)condInscrição estadual. Obrigatória quando icmsTaxpayerStatus = Taxpayer; recusada nos outros dois. Formato varia por UF — validação de formato é por estado e fica na camada fiscal, não aqui.
municipalRegistrationvarchar(20)opcInscrição municipal. Necessária para tomar serviço com retenção de ISS.
suframaCodevarchar(9)opcInscrição Suframa. Presente, muda a tributação de entrada — e é justamente o campo que some quando alguém modela filial como endereço.
primaryCnaevarchar(7)opcCNAE principal do estabelecimento. Pode divergir entre filiais da mesma empresa.
registrationStatusenumopcRegistrationStatus = Null · Active · Suspended · Unfit · ClosedDown — os códigos 01 · 02 · 03 · 04 · 08 da Receita, o mesmo enum do SupplierEstablishment, alinhado em 04/09/2026 (o comprador e o vendedor são consultados na mesma fonte). Situação cadastral do estabelecimento: uma filial pode estar baixada com a matriz ativa, e o sistema precisa saber disso antes de emitir um pedido em nome dela.

Endereço fiscal

CampoTipoRegra
zipCodevarchar(8)obrigSem máscara.
street · number · complement · districtstringcondLogradouro e bairro obrigatórios; número aceita S/N.
cityIbgeCodevarchar(7)obrigCódigo do município, não o nome. É o que a NF-e carrega e o que resolve homônimo entre estados. O nome é derivado dele, nunca digitado livre.
stateCodechar(2)geradoDerivado do código IBGE. É a dimensão que muda o ICMS interestadual — por isso não pode ser digitada em separado e divergir do município.
countryCodechar(2)opcISO 3166-1, padrão BR. Reservado; estabelecimento fora do Brasil está fora de escopo.

Operação

CampoTipoRegra
allowsPurchasingboolopcPadrão true. Falso, o estabelecimento não aparece no seletor de documento — mas continua podendo ser destino de entrega. Escritório administrativo que não compra é caso real.
allowsReceivingboolopcPadrão true. Falso, não pode ser destinatário de nota.
isActiveboolgeradoSó por /activate e /deactivate. Desativar o último estabelecimento ativo de uma empresa ativa é 409: empresa sem estabelecimento não consegue emitir nada e vira um registro morto que ninguém percebe.

Contrato vigente · revisão consolidada

Acesso e permissões por empresa

A permissão efetiva é a interseção de três condições: teto de permissões vigentes da plataforma, empresa visível por UserCompanyScope e operação concedida por UserCompanyRole. Ausência de concessão significa acesso negado. Um papel atribuído no tenant não concede automaticamente todas as suas operações em todas as empresas.

EntidadeCampos mínimosRegra
UserCompanyScopeid, tenantId, userId, companyId?, allCompanies, validFrom, validTo, isActiveVisibilidade. companyId nulo exige allCompanies=true escolhido explicitamente. Sem vínculo vigente, conjunto vazio. FK (tenantId, companyId) para Company; userId validado no diretório da plataforma.
UserCompanyRoleid, tenantId, userId, companyId?, allCompanies, roleId, validFrom, validTo, isActiveConcessão local da operação. roleId referencia definição de papel da plataforma; não duplica o catálogo de Permission. companyId nulo exige allCompanies=true explícito. Um papel local jamais excede as permissões vigentes da plataforma.
Autorização efetivatenantId, userId, companyId, operation, authzVersionConjunto resolvido no servidor por requisição. Consulta, download, aprovação, delegação, relatórios e workers aplicam o mesmo contrato. Revogação invalida uso futuro, preservando autoria histórica.

Unicidade de vínculos vigentes por usuário/empresa e por usuário/empresa/papel, respectivamente, com índices próprios para concessão global e prevenção de sobreposição de vigência. Não há coluna roleId reservada em UserCompanyScope: a concessão é entidade própria e necessária desde a v1.

SituaçãoResultado
Nenhum vínculo vigenteNenhuma empresa ou operação permitida.
Visibilidade global explícita, sem papel localPode alcançar a empresa apenas nas operações locais que também forem concedidas; visibilidade isolada não autoriza ler documentos.
Ana: Aprovar na Fábrica; Solicitar na DistribuidoraPode aprovar na Fábrica se também tiver alçada; na Distribuidora só solicita. O token conter approve não amplia a concessão local.
Empresa inativaHistórico continua legível com autorização. Novos documentos são impedidos; fatos ocorridos e encerramento de obrigações vigentes seguem operação específica autorizada.
Vínculo vencido ou revogadoSai imediatamente da próxima avaliação no servidor. Não vira acesso global.

O cliente só restringe o conjunto resolvido: filtros de empresa nunca o substituem. IDs fora de escopo retornam 404; falta de permissão sobre operação de objeto já acessível retorna 403. Recorte de valores financeiros aplica ainda read:budget-amounts. Preferência nexio.default-establishment-id na UserPreference da plataforma é validada contra concessões atuais antes de preencher o formulário.

Serviços públicos de consulta e escrita recebem contexto empresarial explícito. Identificadores externos, cache e chaves de idempotência incluem tenant e empresa quando aplicável. FK composta impede referências de outra organização; relatórios autorizados filtram todas as fontes, inclusive anexos e projeções.

RLS é defesa adicional a especificar/testar onde adotada; os pacotes atuais não demonstram isolamento empresarial completo. Com conexão compartilhada, SET LOCAL deve ser instalado e limpo por transação, inclusive em worker. Credencial de serviço declara empresas e operações; execução em background não implica acesso irrestrito.

Na criação da primeira empresa, provisionar explicitamente visibilidade e papéis locais do administrador responsável, limitados pelo teto da plataforma e auditados. Criar a segunda empresa não altera concessões existentes. Administração global exige concessão global explícita e não confere alçada financeira. Desativação de empresa e estabelecimentos é atômica; reativar empresa não reativa filiais automaticamente.

Domínio · GLAccount

Plano de contas

O eixo que apareceu como pendência explícita na spec de Item ("mapeamento categoria → conta contábil … provavelmente deveria estar na próxima") e como fatia do rateio na de Centro de Custo. Fecha aqui.

A terceira árvore — e a mesma mecânica

Centro de custo e categoria já são árvores com caminho materializado, código imutável e ancestrais derivados sem recursão. Plano de contas é a terceira, e reusa a mecânica inteira: parentId, path com separador nas duas pontas, depth, duas portas de status com cascata assimétrica, path como guarda de move e version int + expectedVersion como token de concorrência de toda escrita — o mesmo token de Item, Fornecedor e, desde 03/09/2026, do Centro de Custo. Inventar uma quarta forma de fazer árvore neste produto seria erro por escolha, não por descuido.

GLAccount
CampoTipoRegra
codestring(30)obrigÚnico por tenant, imutável. Contas contábeis brasileiras já são hierárquicas no próprio código (3.1.01.001) — o produto não deriva o pai do código: quem manda é parentId, e o código é rótulo.
namestring(160)obrigÚnico entre irmãos.
parentIdGuid?opcNulo = raiz. Ciclo é recusado. Movimentação só por PATCH /{id}/parent.
path · depthvarchar · intgeradoIdênticos aos do centro de custo, inclusive o escape do separador.
accountTypeenumobrigAsset · Liability · Equity · Revenue · Expense. Herdado do pai por padrão; divergir do pai é 422.
versionintgeradoNovo em v1.4. Token de concorrência de toda escrita, como no Centro de Custo: expectedVersion no PUT, no PATCH /parent e na habilitação por empresa; divergência é 409 GL_ACCOUNT_CONCURRENT_CHANGE_CONFLICT. path fica como guarda de move.
allowsPostingboolopcVerdadeiro em folha, falso em nó com filhos — a mesma regra do centro de custo. Nó intermediário existe para consolidar, não para receber lançamento.
validFrom · validTodate?opcPlano de contas muda de exercício para exercício. Conta fora de vigência não é oferecida em documento novo e continua legível em documento antigo.
isActiveboolgeradoDuas portas, cascata assimétrica.
GLAccountCompany · o vínculo que a integração consome
CampoTipoRegra
glAccountId · companyIdGuidobrigChave composta única.
isEnabledboolobrigConta habilitada naquela empresa. Ausência de linha = habilitada — o padrão é o plano inteiro valer para todas, e a linha existe para restringir ou para carregar o código externo. O ponto de aplicação está escrito desde 09/09/2026 e é um só: a escolha da conta na fatia de rateio, nos três degraus da cascata, no lookup e de novo na submissão. Até 08/09 nenhuma das superfícies onde a conta é escolhida conhecia a empresa, e metade da razão de existir desta tabela — restringir — não era executável.
externalCodestring(40)opcO código da mesma conta no ERP daquela empresa. É a razão de existir desta tabela: plano unificado no Nexio, códigos diferentes lá fora.

De onde vem a conta da linha

Sugerida em cascata, escolhida por gente

A conta da fatia de rateio é sugerida por, nesta ordem: 1 Item.defaultGlAccountId, 2 CategoryNode.defaultGlAccountId do nó do item, subindo a árvore de categoria até achar, 3 vazio — e vazio significa o usuário escolhe: a fatia aceita conta nula só em rascunho, e a submissão do documento a exige (422 ALLOCATION_GL_ACCOUNT_REQUIRED_UNPROCESSABLE, RN-CC-29 do Centro de Custo). Sempre editável por quem tem permissão, e sempre com a origem visível na interface — "sugerida pela categoria Materiais de escritório" é a diferença entre um campo que o usuário confere e um campo que ele ignora.

E a cascata é filtrada pela empresa, desde 09/09/2026. Nos três degraus, conta com GLAccountCompany.isEnabled = false para aquela empresa não é sugerida; escolhida à mão, é recusada com 422 GL_ACCOUNT_NOT_ENABLED_FOR_COMPANY_UNPROCESSABLE — na fatia e outra vez na submissão, pela mesma razão de a conta obrigatória ser conferida duas vezes. O GET /gl-accounts/lookup passou a exigir companyId na query: lookup que não sabe a empresa devolve conta que o documento vai recusar, e devolver opção inválida é pior do que não devolver nada. A empresa lida é a da fatia, não a do documento — mesma regra do centro de custo, ver Compra centralizada.

Nenhuma das duas árvores manda na outra

Centro de custo responde quem paga; conta contábil responde que natureza é o gasto. A linha carrega as duas, e derivar uma da outra é o erro que transforma relatório contábil em relatório gerencial errado. Os dois eixos se cruzam na fatia de rateio e em nenhum outro lugar.

O Nexio classifica, não escritura

A conta existe para que o documento saia classificado e a integração com o ERP não precise adivinhar. Partida dobrada, encerramento, razão e balancete continuam do outro lado. Essa fronteira precisa estar escrita porque é a primeira coisa que um controller pede na demo.

Transversal

Numeração

NumberSequence
CampoTipoRegra
documentTypeenumobrigRequisition · QuotationRequest · PurchaseOrder · Receipt · BudgetChangeRequest. Cresce com a espinha transacional. O BudgetChangeRequest entrou em 08/09/2026: o Orçamento já numerava o pedido de verba por aqui, com escopo Company, e o enum não o listava — sequência que uma spec usa e a outra não declara é numeração que ninguém consegue configurar na tela. Contract continua fora, e de propósito: nenhuma página o numera.
scopeenumobrigTenant · Company · Establishment. Padrão Company. Entra na chave de unicidade junto com os dois campos abaixo, que são materializados.
companyId · establishmentIdGuid?condExigidos conforme o scope; preenchidos fora dele é 422. Materializados, nunca nulos (09/09/2026): fora do escopo eles recebem valor sentinela — o Guid zero —, não nulo. O requisito é de unicidade: não pode existir mais de uma sequência para a mesma combinação (tenant, documentType, scope, companyId, establishmentId), e duas sequências de mesmo escopo têm de colidir de fato. Com nulo elas não colidem em nenhum banco que trate nulo como valor desconhecido — esta mesma página já ensina isso ao explicar por que o UserCompanyScope precisa de dois índices parciais. Materializar resolve com um índice único só e sem depender de recurso do banco; onde houver equivalente (no Postgres, UNIQUE NULLS NOT DISTINCT, da versão 15 em diante), ele serve igual. Duas sequências para o mesmo documento é número repetido — e número emitido não se renumera.
prefixstring(10)opcTexto fixo antes do número. Cliente que quer PC- ou a sigla da filial resolve aqui.
paddingintopcZeros à esquerda. Padrão 6.
resetPolicyenumopcNever · Yearly · Monthly. Padrão Yearly, que é o hábito do mercado. Não vale sozinha: quem zera precisa saber em que período está — ver NumberSequenceCounter.periodKey, abaixo.
NumberSequenceCounter.periodKeyvarchar(7)geradoContador separado por sequência e período: chave única (tenantId, sequenceId, periodKey). Incrementar atomicamente; emitir retroativo não zera contador de período já usado. Never usa sentinela explícita.
nextValuebigintgeradoIncrementado por UPDATE … RETURNING atômico no banco, nunca por leitura seguida de escrita na aplicação. Dois POST simultâneos com o mesmo número é o defeito clássico deste componente.

O número composto carrega o período

Novo em 09/09/2026. Com resetPolicy diferente de Never, o período entra no número, entre o prefixo e o contador: PC-2026-000001, PC-2026-03-000001. Não é enfeite — é o que faz o número ser único. Zerar o contador em janeiro sem carregar o ano faz o PC-000001 de 2027 ser idêntico ao de 2026; o requisito de unicidade da sequência garante que existe uma sequência, e quem garante que ela não repete um número já emitido é o par período + contador. Com Never o número não ganha o segmento de período, e não precisa. Número emitido é imutável: trocar resetPolicy depois vale do próximo número em diante e não reescreve nada.

O número pode ter buracos, e isso não é defeito. Transação abortada consome o número e não o devolve. Prometer sequência sem furo obrigaria a serializar a emissão do documento inteiro, e o cliente que precisa de sequência contínua está pensando em NF-e — que o Nexio não emite: quem emite é o fornecedor. Está escrito aqui para não virar chamado.

Contrato vigente · revisão consolidada

Compra centralizada com destinatário fiscal definido

A central pode consolidar demandas e negociar uma cotação comum. A adjudicação gera pedidos separados por destinatário fiscal (establishmentId), cada um com empresa, moeda, numeração, aprovação e orçamento próprios. O usuário comprador precisa de concessão para a operação em cada empresa envolvida.

CampoContrato
establishmentIdDestinatário fiscal do pedido; define a Company e sua política.
requestingEstablishmentIdOrigem da demanda, quando diferente. Referenciar outra empresa exige concessões nas duas; não transfere o custo.
companyIdDerivada do destinatário fiscal e congelada. Toda CostAllocation pertence a esta mesma empresa.
sourcingGroupId / sourceLineRefsLiga cotação e adjudicação comuns aos pedidos separados, preservando quantidades e parcelas por demandante.

Se A compra e recebe para posterior transferência a B, o pedido e seu consumo pertencem a A; transferência física, fiscal e intercompany é processo do ERP, com referências vinculadas. Rateio dentro do Nexio não substitui esse processo nem lança automaticamente na verba de B. Matriz e filial da mesma pessoa jurídica podem compartilhar Company, mas mantêm destinatários fiscais distintos.

A política de aprovação segue a empresa do pedido; a lotação do comprador não muda a autorização necessária. O Nexio emite o pedido oficial e alterações; o ERP registra seu recebimento e aplicação. Ver Contratos transacionais.

Contexto

Gancho fiscal

Duas mudanças de 2026 que tornam o estabelecimento obrigatório, e não uma conveniência de modelagem.

CNPJ alfanumérico — em produção desde 31/07/2026

Coluna varchar, nunca numérica, dos dois lados do produto. Os 12 primeiros caracteres aceitam A–Z; os 2 dígitos verificadores continuam numéricos e o cálculo usa ASCII − 48. Já era regra em Supplier; entra idêntica em Company e Establishment.

IBS e CBS obrigatórios na NF-e desde 03/08/2026

Os campos de IBS e CBS entraram no leiaute do documento fiscal eletrônico, com a alíquota de teste de 1% (0,1% IBS e 0,9% CBS) durante 2026. O Nexio não emite NF-e — mas recebe, e o módulo de nota e o three-way match nascem tendo que saber lê-los. Corrigido em 08/09/2026: até esta versão a frase dizia que nota sem esses campos não é autorizada, e codificada assim ela faria o Nexio recusar nota válida — durante 2026 as notas chegam com IBS/CBS incompletos ou ausentes e ainda assim autorizadas, porque o Ato Técnico Conjunto RFB/CGIBS nº 1/2026 adiou as validações, não a obrigação. A regra do produto é a que o Cadastro de Item já escrevia, e é ela que vale nas três páginas que tocam no assunto: os campos existem no leiaute, o preenchimento só é exigido a partir da data que a norma fixar, e o parser não trata ausência como erro.

Para esta spec a consequência é uma só, e é a razão de o estabelecimento existir: os campos que decidem tributação e crédito estão espalhados nos dois níveis — regime na empresa, contribuinte de ICMS, Suframa e município IBGE no estabelecimento. Modelar filial como endereço perde metade deles.

O que continua fora

Parametrização fiscal completa — CFOP, CST, substituição tributária, benefícios estaduais, regra de crédito por operação — é spec própria, junto com a nota fiscal. Aqui nascem apenas os portadores de identidade fiscal, e eles nascem certos.

Integração

Relação com os demais domínios

O que muda em cada spec já fechada
DomínioMudançaDetalhe
Centro de CustofeitolegalEntityId → companyId no nó e em legalEntityOf(nodeId) → companyOf(nodeId). A regra — nó corporativo, nó de empresa, filho não muda de empresa, linha da empresa A só aponta para corporativo ou A — já estava escrita e não muda uma vírgula. Rename de coluna sem dado para migrar, aplicado em 01/09/2026. A pendência que o Centro de Custo deixou registrada — apontar para a empresa ou para o estabelecimento — foi resolvida no mesmo dia: aponta para a empresa; apontar para o estabelecimento multiplicaria a árvore por CNPJ, que é o que o nó corporativo existe para evitar.
Fluxo de AprovaçãofeitoO critério "Empresa em lista" lê companyId do documento, derivado do estabelecimento. A política não ganha coluna de empresa: o critério já faz o recorte, e uma Base do grupo convive com uma Supplemental de uma empresa só. Novo critério irmão por establishmentId, e um nível que resolve para aprovador sem escopo na empresa do documento passa a cair no fallback.
Cadastro de ItemfeitoItem.defaultGlAccountId e CategoryNode.defaultGlAccountId, ambos opcionais, aplicados em 01/09/2026. Encerra as duas pendências que a spec carregava: o eixo contábil ausente e o multi-empresa. Nenhuma mudança de dimensionalidade: item continua sem empresa.
FornecedornenhumaFornecedor é do tenant. Fica declarado o gancho SupplierQualification.companyId anulável — homologar um fornecedor só para uma empresa do grupo é pedido plausível, e o campo tem o lugar reservado sem ser especificado agora.
Plataforma GrydIdentidade e teto de permissõesNexio consome diretório e definições Role → Permission para resolver concessão empresarial, além das permissões vigentes do usuário. Essa integração é necessária na v1, não campo reservado. Contrato exige versão, revogação e validação de disponibilidade no pacote; Company permanece entidade do Nexio.
Unidade de MedidanenhumaEscopo global ou de tenant, como está.
Poda do webappacrescentaEmpresa e estabelecimento entram na lista do que o admin precisa ter na primeira versão — sem eles, o cadastro de centro de custo por empresa não tem o que oferecer no seletor.

Camada API

Endpoints

Prefixo api/v{version}/nexio. Todas exigem autenticação e declaram exatamente uma permissão. Toda listagem já vem recortada pelo escopo do usuário — não há parâmetro para pedir fora dele.

Empresa
RotaPermissãoRetornoErros
GET /companiesread:companiesPagedResult<CompanyDto>400
GET /companies/lookupread:companiesarray puro400
GET /companies/{id}read:companiesCompanyDetailDto404
POST /companiescreate:companiesCompanyDetailDto. O corpo traz a matriz inline — headquarters com o CNPJ completo e os campos do estabelecimento — e empresa e Establishment Headquarters nascem na mesma transação. Sem headquarters é 400. É também a tela do onboarding de um tenant novo400 · 409 · 422
PUT /companies/{id}update:companiesCompanyDetailDto400 · 404 · 409
PATCH /companies/{id}/activateupdate:companiesCompanyDetailDto404 · 409
PATCH /companies/{id}/deactivateupdate:companiesCompanyDetailDto. Cascateia desde 09/09/2026: desativa os estabelecimentos ativos da empresa na mesma transação, um evento por estabelecimento, e devolve a lista do que caiu junto404
Estabelecimento
RotaPermissãoRetornoErros
GET /companies/{id}/establishmentsread:companiesarray puro404
GET /establishments/lookupread:companiesagrupado por empresa400
GET /establishments/{id}read:companiesEstablishmentDetailDto404
POST /companies/{id}/establishmentscreate:companiesEstablishmentDetailDto400 · 404 · 409 · 422
PUT /establishments/{id}update:companiesEstablishmentDetailDto. Desde 09/09/2026, alterar município, UF ou allowsReceiving revalida os pontos de entrega que herdam o endereço deste estabelecimento, na mesma transação: a edição que deixaria algum deles interestadual sem motivo é recusada com a lista nomeada. Ver Local de Entrega400 · 404 · 409 · 422
PATCH /establishments/{id}/activateupdate:companiesEstablishmentDetailDto404 · 409
PATCH /establishments/{id}/deactivateupdate:companiesEstablishmentDetailDto404 · 409

GET /establishments/lookup é o endpoint que a interface inteira consome. Devolve só o que aquele usuário pode usar, já agrupado por empresa, já filtrado por isActive e allowsPurchasing, com defaultEstablishmentId marcado. Um item na resposta significa que o formulário não mostra o campo — a decisão de exibir o seletor é derivada do tamanho desta lista, nunca de uma configuração que alguém precisa lembrar de ligar.

Plano de contas e numeração
RotaPermissãoRetornoErros
GET /gl-accountsread:gl-accountsPagedResult<GLAccountDto>400
GET /gl-accounts/lookupread:gl-accountscompanyId obrigatório na query desde 09/09/2026. Devolve só as contas com allowsPosting, vigentes e habilitadas naquela empresa — linha ausente em GLAccountCompany ou isEnabled = true400 · 404
GET /gl-accounts/{id}/childrenread:gl-accountsPagedResult<GLAccountDto>400 · 404
POST /gl-accountscreate:gl-accountsGLAccountDetailDto400 · 409 · 422
PUT /gl-accounts/{id}update:gl-accountsGLAccountDetailDto400 · 404 · 409
PATCH /gl-accounts/{id}/parentupdate:gl-accountsGLAccountDetailDto404 · 409 · 422
PUT /gl-accounts/{id}/companies/{companyId}update:gl-accountshabilitação e código externo404 · 422
GET /number-sequencesmanage:number-sequencesarray puro400
POST /number-sequencesmanage:number-sequencesNumberSequenceDto. Novo em 09/09/2026. Sem ele, trocar o scope de Company para Establishment — que esta página declara permitido — exigia N linhas que nenhuma rota criava. Recusa a combinação já existente com 409400 · 409 · 422
PUT /number-sequences/{id}manage:number-sequencesNumberSequenceDto. documentType, scope, companyId e establishmentId são a identidade da sequência e não mudam aqui — troca de escopo é sequência nova404 · 409 · 422
Escopo por empresa e preferência
RotaPermissãoRetornoErros
GET /users/{userId}/company-scopesread:company-scopesarray puro400 · 404
PUT /users/{userId}/company-scopesmanage:company-scopessubstitui o conjunto400 · 404 · 409 · 422
GET /me/company-scopeautenticadoo conjunto resolvido do usuário atual—

PUT substitui o conjunto inteiro, não faz merge. É a mesma semântica que a tela de Papéis do gryd já usa — "salvar substitui permissões" — e é o que torna a tela de escopo uma lista de checkboxes honesta em vez de um diff que ninguém consegue conferir. GET /me/company-scope devolve o conjunto já resolvido, com a razão (Explicito ou SemRestricao), porque a interface precisa saber a diferença entre "você tem uma empresa" e "você tem uma empresa por enquanto".

Contrato

Contrato de erros

O status HTTP é derivado do sufixo do código, nunca da mensagem: o mapeador do Core reconhece nove sufixos — _NOT_FOUND → 404 · _ALREADY_EXISTS, _ALREADY_ASSIGNED e _CONFLICT → 409 · _FORBIDDEN, _ACCESS_DENIED e _BLOCKED → 403 · _UNAUTHORIZED → 401 · _UNPROCESSABLE → 422 · sem sufixo → 400. _UNAVAILABLE → 503 seria o décimo: é extensão pedida ao Core pelo épico do GrydFiles, ainda não vigente — conferido no código da plataforma em 08/09/2026.

CódigoHTTPQuando
COMPANY_NOT_FOUND404Não existe neste tenant — ou não está no escopo do usuário. Os dois casos respondem igual, de propósito
COMPANY_CODE_ALREADY_EXISTS409Código já usado no tenant. A mensagem carrega o código
COMPANY_CNPJ_ROOT_ALREADY_EXISTS409Já existe empresa com essa raiz. Duas empresas com a mesma raiz seriam a mesma pessoa jurídica
COMPANY_INACTIVE_UNPROCESSABLE422Novo em 09/09/2026. Criação ou escrita de documento em estabelecimento de empresa inativa. Leitura nunca devolve este código. Entra no lugar de COMPANY_HAS_ACTIVE_ESTABLISHMENTS_CONFLICT, que exigia zerar os estabelecimentos antes de desativar — passo que a cascata da rota de desativação tornou desnecessário e que, contra a guarda do último estabelecimento ativo, era impossível de cumprir
COMPANY_HAS_DOCUMENTS_CONFLICT409Exclusão com documento emitido. Empresa com histórico se desativa, não se apaga
ESTABLISHMENT_NOT_FOUND404Idem — fora de escopo responde como inexistente
ESTABLISHMENT_CNPJ_ALREADY_EXISTS409CNPJ de 14 repetido no tenant
ESTABLISHMENT_CODE_ALREADY_EXISTS409Código repetido dentro da mesma empresa
ESTABLISHMENT_CNPJ_ROOT_MISMATCH_UNPROCESSABLE422O CNPJ não pertence à raiz da empresa. O dado é bem formado e semanticamente impossível — por isso 422 e não 400
ESTABLISHMENT_HEADQUARTERS_ALREADY_EXISTS409Segunda matriz na mesma empresa
ESTABLISHMENT_HEADQUARTERS_REQUIRED_UNPROCESSABLE422Tentativa de deixar a empresa sem matriz
ESTABLISHMENT_LAST_ACTIVE_CONFLICT409Desativação do último estabelecimento ativo de uma empresa ativa, pela rota do estabelecimento. Não se aplica à cascata de /companies/{id}/deactivate, que desativa as duas coisas na mesma transação
ESTABLISHMENT_STATE_REGISTRATION_REQUIRED_UNPROCESSABLE422icmsTaxpayerStatus = Taxpayer sem inscrição estadual
GL_ACCOUNT_NOT_FOUND404—
GL_ACCOUNT_CODE_ALREADY_EXISTS409—
GL_ACCOUNT_POSTING_NOT_ALLOWED_UNPROCESSABLE422Fatia de rateio apontando para conta que não aceita lançamento
GL_ACCOUNT_NOT_ENABLED_FOR_COMPANY_UNPROCESSABLE422Novo em 09/09/2026. Fatia de rateio apontando para conta desabilitada em GLAccountCompany na empresa da fatia. Verificado na fatia e na submissão; a mensagem carrega o código da conta e o da empresa
GL_ACCOUNT_TYPE_MISMATCH_UNPROCESSABLE422accountType divergente do pai
GL_ACCOUNT_CONCURRENT_CHANGE_CONFLICT409expectedVersion divergente — ou, num move, o path mudou desde a leitura
USER_COMPANY_SCOPE_NOT_FOUND404Linha inexistente, ou de um tenant que não é o do token
USER_COMPANY_SCOPE_DUPLICATE_CONFLICT409Duas linhas para a mesma pessoa e empresa, ou dois "todas as empresas"
USER_COMPANY_ROLE_INVALID_UNPROCESSABLE422Papel/concessão inexistente, fora do tenant ou superior ao teto delegável. UserCompanyRole é necessário desde a v1.
USER_NOT_IN_TENANT_UNPROCESSABLE422O userId não tem vínculo ativo com esta organização no GrydAuth. Verificado só na escrita
NUMBER_SEQUENCE_SCOPE_UNPROCESSABLE422companyId ou establishmentId incoerente com o scope declarado
NUMBER_SEQUENCE_ALREADY_EXISTS409Novo em 09/09/2026. Já existe sequência para a mesma combinação (documentType, scope, companyId, establishmentId). É a guarda de aplicação sobre o mesmo requisito de unicidade que o índice garante — as duas existem, e a de banco é a que decide

Autorização

Permissões

PermissãoAlcanceObservação
read:companiesLer empresas e estabelecimentosRecortada pelo escopo do usuário. Quase todo papel operacional precisa dela para o seletor funcionar
create:companiesCriar empresa e estabelecimentoAdministrativa. Criar empresa muda a forma da interface para todo mundo do tenant
update:companiesEditar, ativar e desativarAdministrativa
read:gl-accountsLer o plano de contasNecessária para preencher a fatia de rateio
create:gl-accountsCriar contaControladoria
update:gl-accountsEditar, mover, habilitar por empresaControladoria
read:company-scopesLer o escopo por empresa dos usuáriosAdministrativa. Não é necessária para operar — GET /me/company-scope não exige permissão
manage:company-scopesDefinir quais empresas cada pessoa alcançaAdministrativa e sensível: é o que separa as empresas do grupo umas das outras
manage:number-sequencesCriar e configurar numeraçãoAdministrativa. Mudar o escopo de numeração com documento emitido é permitido — cria-se a sequência nova, e nada é renumerado

Escopo não é permissão — é o alcance dela. A permissão, que vem do gryd, diz o que a pessoa pode fazer; o UserCompanyScope, que é do Nexio, diz onde. Confundir os dois leva a criar read:companies:alfa no catálogo do tenant, que é a forma mais rápida de tornar um catálogo de papéis impossível de manter — e a razão de o recorte por empresa não ter virado permissão.

Invariantes

Regras de negócio

RegraOnde é garantida
Tenant sem empresa ativa está em onboardingEstado explícito, não seed: a plataforma não conhece Company e não chama o Nexio ao criar tenant. Nesse estado nenhum documento é emitido e a única tela disponível é o cadastro da primeira empresa, com o CNPJ pedido ali — mesmo precedente da união vazia de escopos: o usuário não opera, e a interface diz por quê
Toda empresa tem exatamente uma matrizNo máximo uma: índice único parcial em (companyId) filtrado por type = Headquarters. Pelo menos uma: POST /companies exige a matriz inline e grava as duas na mesma transação; ESTABLISHMENT_HEADQUARTERS_REQUIRED_UNPROCESSABLE protege a saída
Todo estabelecimento carrega a raiz da sua empresaValidação no agregado, 422. Coluna gerada left(cnpj, 8) com check contra cnpjRoot
Empresa ativa não fica sem estabelecimento ativoNa criação, pela matriz inline; depois, verificação na desativação do último estabelecimento ativo, 409. A desativação da empresa não passa por essa guarda (09/09/2026): ela cascateia, e empresa inativa sem estabelecimento ativo é o estado correto
Empresa inativa restringe escrita, nunca leituraDuas metades: a escrita verifica isActive e recusa com 422 COMPANY_INACTIVE_UNPROCESSABLE; a resolução do UserCompanyScope não lê isActive. Nenhuma leitura de histórico depende do estado da empresa
A fatia de rateio carrega a empresaColuna companyId na fatia, gravada na criação e congelada com a linha. É ela que a invariante do centro de custo e a resolução do Budget leem; na v1 recebe sempre a empresa do documento
cnpjRoot, code e companyId são imutáveisIgnorados no PUT. Não é erro pedir para mudar — é campo que não existe na atualização
companyId nunca vem do clienteInjetado do claim em toda escrita, como tenantId
Documento aponta para estabelecimento e congela a empresaRegra da linha do documento. Alteração de cadastro não reescreve documento emitido
A política de aprovação segue o estabelecimento do documentoCritério Empresa em lista do Fluxo de Aprovação, avaliado sobre o companyId do documento. Quem opera a compra é usuário; não existe eixo de política pelo lado do comprador
Linha da empresa A só usa centro de custo corporativo ou de AJá especificado no Centro de Custo, validado na linha com o companyId efetivo do nó
Conta sem allowsPosting não recebe rateioValidação na fatia, 422
Fatia sem conta não submeteValidação na submissão do documento, 422 ALLOCATION_GL_ACCOUNT_REQUIRED_UNPROCESSABLE; nulo só em rascunho (RN-CC-29)
UserCompanyScope só estreita, nunca concedeDesenho. A tabela não dá organização, papel nem permissão — o token do GrydAuth continua decidindo isso. Linha órfã é inerte
Ausência de concessão vigente = acesso negadoResolvedor por empresa/operação, com allCompanies explícito para concessão global.
Nenhuma tabela do gryd é alteradaFronteira de camada. A plataforma não conhece Company, e não deve conhecer
Mudar município ou UF do estabelecimento revalida quem herdaPonto de entrega sem endereço próprio copia o stateCode daqui. O PUT revalida a regra interestadual e o allowsReceiving nesses pontos na mesma transação, recusa a edição que os deixaria interestaduais sem motivo e emite um evento por ponto recalculado. Escrita no Local de Entrega, aplicada aqui
CNPJ é varchar nos dois lados do produtoEsquema. Validação de DV com ASCII − 48

Implantação

Migração

Sem cliente em produção e com o único módulo desenvolvido — centro de custo — autorizado a ser recriado. Esta é a janela em que a mudança custa quase nada.

1 · Onboarding no lugar do seed

Até a v1.3 esta seção mandava semear uma Company por tenant a partir de TenantSettings.cnpj. Não há como: a plataforma não conhece Company, nada no Gryd.IO chama o Nexio ao criar um tenant, e TenantSettings não existe nela. O tenant novo nasce sem empresa e isso é um estado explícito de onboarding — não emite documento, e a única tela disponível é o cadastro da primeira empresa, que pede o CNPJ ali e cria Company e Establishment Headquarters numa chamada só (POST /companies com a matriz inline). Não há tenant em produção: para os que existirem antes deste release, o mesmo onboarding serve, sem migrar dado.

2 · O CNPJ solto do tenant sai no mesmo release

O que o Nexio tinha de CNPJ único por tenant — do lado do Nexio, nunca da plataforma — sai junto com a entrada de Company. Manter os dois por um ciclo cria a segunda fonte de verdade que a Decisão 2 existe para impedir. Sai junto ou não sai.

3 · Renomes sem dado

cost_centers.legal_entity_id → company_id. Nenhuma tabela do gryd é tocada. Nasce uma tabela no esquema do Nexio, user_company_scopes, vazia — e vazia significa "todo mundo enxerga todas as empresas", que é exatamente o comportamento de hoje. ApprovalPolicy também não ganha coluna: o critério resolve. Todos anuláveis e todos vazios: nenhum backfill, nenhuma janela.

4 · Tenants existentes não mudam de comportamento

Com uma empresa por tenant, o seletor não aparece, a numeração continua única e o escopo não recorta nada. A migração é invisível para quem tem um CNPJ só — que é o teste de que o modelo está certo.

Validação

Referência de mercado

NexioOracle FusionSAPCoupaProtheus
TenantEnterpriseClientInstanceAmbiente
CompanyLegal EntityCompany CodeLegal EntityEmpresa
EstablishmentNão como site — o site não tem identificação fiscal própria. O que existe é a Legal Reporting Unit, subdivisão da entidade legal que carrega registros fiscais; usá-la por filial resolve o CNPJ e cobra o preço de modelar cada filial na estrutura legalBusiness Place · Local de Negócio, construto criado para o Brasilnão existeFilial
GLAccountNatural AccountGL AccountAccountPlano de contas
NumberSequenceDocument SequenceNumber Range—Numeração por filial

A linha que importa é a do estabelecimento. Oracle e Coupa não têm o conceito: para eles o site é uma unidade operacional sem CNPJ, e o identificador fiscal mora na entidade legal. Quem implanta esses produtos no Brasil resolve com campo livre, código de site colado no CNPJ ou uma entidade legal por filial — as três saídas ruins. A SAP precisou inventar o Business Place especificamente para a legislação brasileira. O Nexio nasce com o construto certo porque nasce aqui — e é exatamente a mesma vantagem que a spec de Fornecedor identificou ao derivar grupo econômico da raiz de 8.

Limites

Fora de escopo

O quePor quê
Escrituração contábilO Nexio classifica o gasto; partida dobrada, razão e balancete continuam no ERP
Transferência intercompanyO documento registra quem comprou e quem pediu; o acerto entre as empresas é lançamento no ERP
Consolidação cross-tenantSe um dia existir, é serviço de leitura com permissão própria — nunca um filtro de tenant relaxado
Herança de cadastro do tenant Group para os filhosContraria a Decisão 3. Quem quer cadastro compartilhado usa o modo consolidado
Hierarquia entre empresas do mesmo tenantHolding e controlada dentro do tenant: lista plana até que consolidação societária exista, o que não está no roteiro. parentCompanyId não é reservado
Estabelecimento fora do BrasilcountryCode reservado, sem regra. Fornecedor estrangeiro já é suportado; comprar como empresa estrangeira, não
Parametrização fiscal por operaçãoCFOP, CST, ST, benefícios e regra de crédito nascem com a nota fiscal
DeliveryLocation e BudgetSpecs próprias. As duas dependem desta e nenhuma das duas depende da outra

Contrato vigente · revisão consolidada

Administração das concessões

Contratos mínimos propostos: listar e manter UserCompanyScope e UserCompanyRole por usuário; simular permissão efetiva por empresa/operação; revogar vínculos com motivo, versão esperada e trilha. Exigir manage:company-access no teto da plataforma e no escopo administrado. Não permitir que o administrador conceda operações acima de seu teto delegável, amplie seu próprio escopo ou use gestão de acesso para se atribuir alçada financeira.

Auditoria guarda antes/depois, autor, empresas atingidas, validade e revisão. Delegações de aprovação verificam as concessões e limites na criação, ativação e decisão. Restauração do banco precisa invalidar sessões e caches para não ressuscitar permissões revogadas. Estes contratos ainda exigem implementação no Nexio; não são capacidade comprovada do pacote GrydAuth consumido.