Referência · Modelo

Modelo de classes

Relações entre as classes especificadas, com diagramas dos módulos e contratos mínimos de revisão, parcelas, integração e decisão humana. Os campos completos pertencem às páginas canônicas. Diagrama documentado não significa entidade implementada.

deriva de Empresa e Estabelecimento, Centro de Custo, Cadastro de Item, Fornecedor, UoM, Fluxo de Aprovação, Orçamento, Moeda e Câmbio, Local de Entrega, Anexo, Notificação, Motivo Diagramas por módulo · satélites · contratos transacionais v1.27 · 09/09/2026

Ponto de partida

Como ler

Notação

Caixa cheia é agregado especificado; borda azul é raiz de agregado ou árvore; borda tracejada é classe da plataforma gryd, que o Nexio consome e não possui. ? no fim do nome é anulável. Campo em azul nasceu ou mudou entre 01 e 03/09/2026.

O que foi omitido

id e tenantId não aparecem: toda classe tem os dois, id atribuído pelo agregado e tenantId injetado do token e nunca aceito do cliente. createdAt, updatedAt e isDeleted também são universais. Classes com muitos campos trazem a contagem do que ficou de fora.

Onde está a verdade

Este documento é derivado. Regra, invariante e contrato de erro vivem na spec de cada módulo; aqui só a forma. Divergência entre os dois é defeito deste arquivo — a spec ganha sempre.

Contrato vigente · revisão consolidada

Identidade e concessões empresariais

GrydAuth: User / TenantUserTenant: vínculo organizacionalUserRole → Role → PermissionCatálogo e autenticação da plataformaUserCompanyScopetenantId / userId / companyId?allCompanies explícito / vigênciaAusência de vínculo = conjunto vazioUserCompanyRoletenantId / userId / companyId?roleId / allCompanies / vigênciaOperação local ∩ teto da plataforma
Plataforma fornece o teto; Nexio restringe empresa e operação.

Concessão global exige escolha explícita. Visibilidade não concede operação; papel local não supera o teto da plataforma. Empresa e autorização são verificadas em consulta, aprovação, delegação, download e worker. Alçada financeira continua em CostCenterAssignment e não nasce do papel.

Banda 2 · o dinheiro

Organização e dinheiro

Nove classes. Duas árvores — centro de custo e plano de contas — com a mesma mecânica: parentId, caminho materializado, depth, allowsPosting só em folha, duas portas de status, path como guarda de move e version int como token de concorrência. E, desde 02/09/2026, o DeliveryLocation, que pendura no estabelecimento e cruza com a árvore de custo por sugestão.

Company aggregate root code string(20) cnpjRoot varchar(8) legalName string(200) tradeName? string(160) taxRegime TaxRegime functionalCurrency char(3) headquartersId Guid → Est. isActive bool Establishment 1..N por empresa companyId Guid → Company code string(20) cnpj varchar(14) type EstablishmentType icmsTaxpayerStatus enum stateRegistration? varchar(20) suframaCode? varchar(9) cityIbgeCode varchar(7) stateCode char(2) registrationStatus? enum allowsPurchasing bool allowsReceiving bool GLAccount árvore code string(30) name string(160) parentId? Guid → GLAccount path / depth varchar / int accountType AccountType allowsPosting bool validFrom / validTo? date isActive bool GLAccountCompany vínculo N×N glAccountId Guid → GLAccount companyId Guid → Company isEnabled bool externalCode? string(40) CostCenter árvore code string(20) name string(160) parentId? Guid → CostCenter path / depth varchar / int companyId? Guid → Company validFrom / validTo? date allowsPosting bool isActive / isDeleted bool CostCenterAssignment N por nó costCenterId Guid → CostCenter userId Guid → User role AssignmentRole approvalLimit? numeric(21,4) approvalLimitCurrency? char(3) hasUnlimitedAuthority bool validFrom / validTo? date endedReason? string(200) CostAllocation value object allocationId Guid companyId Guid → Company order int costCenterId Guid → CostCenter glAccountId? Guid → GLAccount percent numeric(9,6) amount numeric(21,4) NumberSequence por tipo de doc documentType enum scope SequenceScope companyId Guid · sentinela establishmentId Guid · sentinela prefix? / padding string / int resetPolicy enum currentPeriodKey varchar(7) nextValue bigint 1 · 1..N 1 · N 0..1 · N 1 · N 1 · N parentId parentId DeliveryLocation aggregate root establishmentId Guid → Establishment companyId Guid → Company code string(20) name string(160) type DeliveryLocationType cityIbgeCode? varchar(7) stateCode char(2) interstateReason? InterstateReason recipientCnpj? varchar(14) defaultCostCenterId? Guid → CostCenter validFrom · validTo? date isActive bool + 17 campos 1 · N 0..1 · N
Duas árvores com a mesma mecânica de caminho materializado, o rateio que cruza as duas, e o ponto de entrega — que pendura no Establishment porque é ele quem recebe fiscalmente, e só sugere o centro de custo.

Os dois eixos se cruzam em um lugar só. CostAllocation é o único ponto do modelo onde centro de custo e conta contábil se encontram, e ele é value object da linha do documento — não vive em nenhum dos dois. Derivar a conta do centro de custo, ou o contrário, é o erro que transforma relatório contábil em relatório gerencial errado.

Contrato vigente · revisão consolidada

Razão e parcelas

Budget / BudgetLineEmpresa, moeda, período e versãoLimites próprios e ancestraisGuardas por limite e janelaBudgetPolicyrequiresBudget / overBudgetPolicyVigência / empresa / hierarquiaRequireApproval é obrigatórioBudgetPostingGrouppostingGroupId / payloadHashoperationId / resultadoUma transação, N entradasBudgetEntryentryOrdinal / origem / parcelaliquidates / reversal / reclassifiesCommitment / Obligation / ActualBudgetLineBalanceProjeção reconstruível por limiteUma entrada por consumo econômicoTravas antes da decisãoBudgetChangeRequestAlteração autorizada da verbaAdjustment com motivoBackfill/revisão por pares
Uma operação contém várias entradas; reclassificação conserva o consumo.

Dinheiro numeric(21,4) respeita as casas da moeda. Negative/positive de reclassificação conservam estágio e valor; cada parcela mantém sua origem. Actual ocorrido pode ter pendência de classificação. Nota ajusta Actual correspondente e não cria verba. Ver Orçamento.

Banda 4 · a conversão

Moeda e câmbio

Cinco classes transversais, e a única do modelo com linha sem tenant: ExchangeRate.tenantId nulo é a cotação oficial compartilhada por todos os clientes. A taxa própria do tenant vence a oficial para o mesmo par, tipo e data.

Currency catálogo do produto code char(3) numericCode char(3) name string(60) symbol? string(6) minorUnits smallint bcbSymbol? char(3) isActive bool TenantCurrency ativação por cliente tenantId Guid currencyCode char(3) → Currency isEnabled bool enabledAt / By timestamptz / Guid CurrencySettings 1 por tenant tenantId Guid defaultRateType RateType maxRateAgeDays smallint warnRateAgeDays smallint fetchTimesLocal time[] notifyOnFailureAfter smallint alertRecipients Guid[] allowContractedOverride bool CurrencyPair o que se captura fromCurrency / to char(3) → Currency quoteUnit int providerCode string(30) defaultRateType RateType alsoFetchRateTypes RateType[] autoFetch / isActive bool lastSuccessAt? timestamptz lastFailureAt? timestamptz consecutiveFailures int ExchangeRate append-only · a cotação tenantId? Guid fromCurrency / to char(3) → Currency rateType RateType rateDate date rate numeric(18,8) quoteUnit int source / sourceReference string(30) / (120) capturedAt / By timestamptz / Guid? supersededById? Guid → ExchangeRate supersedeReasonCodeId? Guid 1 · N 1 · N 1 · N supersededById
Currency é catálogo do produto e não tem tenantId — o cliente ativa, não cria. CurrencyPair é o que faz a captura automática existir: sem a lista de pares, a rotina diária não sabe o que buscar, e ela se mantém sozinha porque ativar uma moeda garante o par. ExchangeRate não se edita: corrigir é gravar linha nova e apontar supersededById na antiga — daí a auto-relação. E o movimento que já converteu guarda taxa, tipo, data e quoteUnit como valores, não como chave estrangeira, para que substituir a cotação corrija o futuro sem reescrever o passado.

Banda 6 · quem decide

Motor de aprovação

Nove classes de configuração — a oitava é ApprovalGroupMember, entidade-filha do agregado ApprovalGroup, que dá vigência ao membro do comitê, e a nona é o ApprovalSettings do tenant, que nasceu em 08/09/2026 quando o que não varia por processo saiu do ApprovalProcessSettings — e quatro de execução. A separação é a tese do módulo: a instância congela as políticas que a formaram, com versão — editar a regra não reescreve o que já está em curso.

ApprovalProcess por tenant Qual documento é aprovável valor avaliado · critérios ações · reserva · uso de motivo ApprovalPolicy N por processo kind Base | Supplemental priority · isDefault · version validFrom / validTo fallbackApproverId? ApprovalLevel 1..N por política order · approverType · target mode · quorum · scopeMode activationMinValue maxApprovalHours · label escalationTarget? ApprovalGroup N por tenant Pool nomeado de usuários referenciado por nível ApprovalGroupMember N por grupo groupId · userId validFrom · validTo 1 · N ApprovalProcessSettings × processo dedupe · tolerância de reavaliação reserva · uso de motivo SLA padrão · escalonamento ApprovalCriteria value object faixa de valor · nó de CC nó de categoria · tipo de item empresa · estabelecimento fornecedor · flags · orçamento ApprovalDelegation N por usuário Ausência temporária um processo por linha · teto validFrom · validTo obrigatório ApprovalSettings por tenant materialidade do rateio expediente · dias não úteis teto de delegação ApprovalRequest aggregate root subjectType + subjectId empresa · requisitante · valor snapshot das políticas estado · budgetRequirementUnmet roundNumber · version ApprovalStep 1..N por instância order · política e nível de origem escopo · modo · SLA · estado authorityExceeded bool resolvedOutOfScope bool resolvedEmptyOnActivation bool roundNumber int ApprovalAssignment 1..N por passo A tarefa de uma pessoa origem · estado · decisão comentário · momento ApprovalEvent append-only approvalRequestId Guid → Request sequence long · ordenação roundNumber int stepId? / assignmentId? Guid action ApprovalEventAction actorUserId? / actorType Guid / enum scopeAmount? / scopeCurrency? decimal exchangeRate? / quoteUnit? decimal / int reasonCodeId? / reasonCode? Guid / str occurredAt / metadata timestamptz / doc + 11 campos 1 · N 1 · 1..N N · 0..1 1 · 1 1 · 1 1 · 1..N 1 · 1..N 1 · 0..N
Configuração acima, execução abaixo. As caixas trazem o papel de cada entidade — os campos completos estão na spec do fluxo. Desde 04/09/2026 toda aprovação humana do Nexio é uma instância deste motor: Supplier (cadastro), SupplierQualification, BankAccountChangeProposal, Item, Budget e BudgetChangeRequest guardam um approvalRequestId e aplicam o efeito quando a instância termina — dez processos-semente, quinze critérios, e os tipos de nível PermissionHolder e Panel que as políticas padrão e a homologação por área exigem.

Empresa entra aqui como critério, não como coluna. ApprovalCriteria ganhou empresa e estabelecimento na lista de recortes, e ApprovalRequest carrega a empresa do documento. Dar um companyId à própria política criaria um segundo mecanismo concorrente para a mesma pergunta — e o critério já resolve, inclusive o caso de uma política Base do grupo convivendo com uma Supplemental de uma empresa só.

Banda 7 · a prova

Anexo e arquivo

Quatro classes, e duas delas não são nossas. É a única banda do modelo que desenha a fronteira com um módulo da plataforma, porque é a única em que o Nexio depende de uma entidade de outro bounded context em cada operação — e a dependência é sem chave estrangeira, dos dois lados.

StoredFile GrydFiles profile string(40) originalFileName / contentType sizeBytes / sha256 char(64) status FileStatus pendingOwnerScope? / Key? string canonicalFileId? Guid · dedupe retentionMode / retainUntil? legalHold bool referenceCount int + 10 campos Attachment aggregate root storedFileId Guid → StoredFile attachmentTypeId Guid → AttachmentType ownerType AttachmentOwnerType ownerId Guid · sem FK lastKnownOwnerState AttachmentOwnerState companyId? Guid → Company originalFileName / contentType / size visibility AttachmentVisibility description? string(300) copiedFromAttachmentId? → Attachment rootAttachmentId? Guid uploadedByKind UploadedByKind uploadedByUserId? / portalUserId? supplierId? Guid → Supplier uploadedAt DateTime revokedAt? / revokedBy? reasonCodeId? Guid → ReasonCode revocationSource? RevocationSource fileReferenceReleased bool AttachmentType configuração tenantId? Guid · nulo = global code string(40) · maiúsculas name string(120) allowedOwnerTypes enum[] defaultVisibility AttachmentVisibility allowsSupplierVisibility bool propagates bool retentionMode RetentionMode retentionYears? int legalHold bool maxCountPerOwner? int isActive / sortOrder FileReference GrydFiles · N storedFileId Guid → StoredFile ownerScope string(60) ownerKey string(80) releasedAt? / releasedBy? 1 · N N · 1 1 · N 1 · 1 copiedFromAttachmentId
O Attachment aponta para o StoredFile e para um dono polimórfico, e nenhum dos dois é chave estrangeira: o primeiro é de outro DbContext, o segundo é de outro contexto do próprio Nexio. Quem sustenta a integridade é o registro de descritores por ownerType, o evento de descarte de rascunho e a varredura diária. Cada anexo cria a sua própria FileReference — inclusive a cópia propagada, que compartilha o storedFileId mas não a referência. As setas para Company, Supplier e ReasonCode saem da banda e estão nas cardinalidades.

A regra de corte, em uma frase. Arquivo com metadado próprio — número, órgão emissor, validade, status derivado da data — é entidade do domínio: ItemDocument e SupplierDocument, que desde 02/09/2026 guardam storedFileId em vez de URL. Os dois são satélites: não têm caixa aqui, e os campos vivem na spec do agregado dono — ver a seção de satélites. Arquivo que é só evidência de um documento é Attachment. Sem essa linha, ou o anexo ganha campos de validade que quase nunca são preenchidos, ou a certidão perde a vigência que é a razão de ela existir.

Banda 8 · o aviso

Notificação

Quatro classes, e a banda mais magra do modelo — de propósito. Canal, template, fila e entrega são do GrydNotifications; o Nexio guarda a exceção que o usuário configurou, a memória curta que impede o mesmo aviso duas vezes, e a fila que submete o documento quando o antivírus libera. O catálogo de slugs não aparece aqui porque não é tabela: é registro em código.

NotificationTemplate GrydNotif slug string(120) tenantId? Guid · nulo = global subject / htmlBody / textBody locale string(10) version int requiredVariables string[] NotificationPreference agregado userId Guid → User slug string(120) → catálogo channel NotificationChannel enabled bool · só a exceção NotificationThrottle estado userId Guid / slug string(120) entityId string(120) lastSentAt DateTime DeferredSubmission agregado ownerType AttachmentOwnerType ownerId Guid · sem FK requestedBy Guid → User mode DeferredSubmissionMode state DeferredSubmissionState abandonReason? AbandonReason attemptCount / lastAttemptAt? int / ts lastRefusalReason? string(80) expiresAt DateTime · 72 h 1 · N
NotificationPreference só existe quando o usuário difere do padrão do catálogo — ausência de linha é a configuração, e é isso que faz um slug publicado depois funcionar para quem configurou antes. NotificationThrottle e DeferredSubmission não se ligam a nada nesta banda, e não é omissão: a primeira é memória de entrega, sem dono; a segunda aponta para um dono polimórfico, com o mesmo AttachmentOwnerType e o mesmo registro de descritores da banda 7 — que ganhou por causa dela a nona pergunta, SubmitPolicyOf. As setas que importam saem da banda e estão nas cardinalidades.

Onde está o resto do módulo — as classes que não existem. O catálogo de avisos é uma NotificationDefinition por slug, em código, validada na inicialização — mesma escolha do AttachmentOwnerRegistry da banda 7 e pela mesma razão: o que é do produto e muda com release não vira linha que o tenant edita. O tenant personaliza o texto pelo override de NotificationTemplate, que a plataforma já oferece, e não o comportamento. Quantos slugs existem é conta da Notificação, não desta página — aqui só se contam classes.

Banda 9 · o porquê

Motivo

Uma classe, e é a mais conectada do modelo — ela é lida por cinco bandas: aprovação, catálogo, fornecimento, moeda e anexo. Nenhuma dessas setas é desenhada aqui, e não por economia: um ReasonCode não é dono nem filho de nada, ele é uma dimensão que os atos congelam. As relações estão nas cardinalidades.

ReasonCode configuração tenantId? Guid · nulo = global code string(40) · maiúsculas name string(120) description? string(300) appliesTo ReasonCodeUsage[] requiresComment bool isActive / sortOrder
O que o desenho não mostra, e é o essencial: o motivo não fica aqui. Cada ato grava a quádrupla reasonCodeId · reasonCode · reasonLabel · reasonComment?, com os dois do meio congelados no instante da decisão — regra 1 do mapa aplicada a um cadastro de apoio. Não existe contador de uso, não existe coleção de ocorrências e não existe campo de cor, severidade ou peso: cada um desses seria uma porta para o motivo passar a decidir comportamento, que é o que a spec proíbe na primeira regra.

Integridade

Cardinalidades

Toda relação do modelo, com a regra que a governa. É a tabela que responde "posso apagar isso?".

Plataforma e escopo

RelaçãoCardinalidadeRegra
User → UserTenant1 · NUma linha por organização. Remover o vínculo apaga em cascata papéis e permissões diretas naquela organização
UserTenant → UserRole1 · NO papel é sempre dentro de um tenant. Não existe papel global
UserCompanyScope → CompanyN · 0..1Nulo exige allCompanies explícito. Sem linha vigente não há escopo. FK composta por tenant; operação ainda exige UserCompanyRole.
UserCompanyScope → UserN · 1userId do gryd sem FK — é outro serviço. Validado contra o diretório na escrita; revogação exige revalidação de vínculo e concessões nas próximas operações; um token ainda válido não mantém autorização removida
Estabelecimento padrão do usuário—Não é tabela do Nexio. Vive na UserPreference do GrydAuth, chave nexio.default-establishment-id, lida e escrita por API — sem caixa neste diagrama e sem entrar na contagem da plataforma, porque o produto a consome como contrato, não a modela. "Preferências do usuário" é conceito da plataforma, e é por isso que o lugar é a tabela dela: uma homônima no Nexio criaria duas fontes para a mesma pergunta. Revisto em 08/09/2026: até esta versão a linha dizia localStorage e a decisão se apoiava em UserPreference não existir — existe, é TenantScopedEntity, e é justamente o escopo por (usuário, tenant) que o localStorage não tem. Sem esquema e sem cascata: quem lê valida
UserCompanyRole → Company / RoleN · 0..1 / N · 1Concessão local com vigência. Role é definição da plataforma; operação efetiva é interseção com teto e visibilidade.
ItemSupplier → ItemSupplierOffer → ItemSupplierPrice1 · N · NOferta/revisão separa embalagem, código, mínimo e prazo. Preço referencia a oferta, não apenas a relação.
ItemSupplierPrice → ItemSupplierPriceComponent1 · NCusto total explícito, com inclusão, base, moeda, origem e recuperabilidade.
BudgetPostingGroup → BudgetEntry1 · NGrupo identifica a operação; entryOrdinal cada entrada. Dedupe não descarta liquidações legítimas da mesma fatia.

Organização e dinheiro

RelaçãoCardinalidadeRegra
Company → Establishment1 · 1..NExatamente um Headquarters, que nasce na mesma transação que a empresa (POST /companies com a matriz inline) — por isso headquartersId deixou de ser anulável em 03/09/2026. Empresa ativa nunca fica sem estabelecimento ativo; companyId é imutável
Establishment → cnpj—Os 8 primeiros caracteres têm que ser o cnpjRoot da empresa. Divergência é 422
Establishment → DeliveryLocation1 · NestablishmentId é obrigatório e imutável — é quem recebe fiscalmente. O estabelecimento precisa ter allowsReceiving. O mesmo galpão servindo três empresas do grupo são três linhas, uma por CNPJ destinatário
DeliveryLocation → CostCenterN · 0..1defaultCostCenterId sugere o rateio da linha, nunca o impõe. Mão única: o nó de centro de custo não ganha endereço. Obra é nó da árvore; aqui mora só o endereço dela
DeliveryLocation → stateCode—Derivado do cityIbgeCode quando há endereço próprio, copiado do estabelecimento quando não há. Divergir da UF do estabelecimento exige interstateReason; nunca é nulo e nunca é digitado
GLAccount → GLAccount0..1 · NÁrvore. Ciclo recusado; accountType herdado do pai e divergir dele é 422
GLAccount × CompanyN · NVia GLAccountCompany. Ausência de linha = habilitada — a linha existe para restringir ou carregar o código externo
CostCenter → CostCenter0..1 · NÁrvore. Nó de empresa não aceita filho de outra empresa; nó corporativo aceita qualquer filho
CostCenter → CompanyN · 0..1Nulo = corporativo. Aponta para a empresa, nunca para o estabelecimento
CostCenter → CostCenterAssignment1 · NMesma pessoa e papel não podem ter períodos sobrepostos no mesmo nó — garantido por EXCLUDE no banco, não em memória
CostAllocation → CostCenter / GLAccountN · 1 / N · 0..1 em rascunho, N · 1 na submissãoSó nó e conta com allowsPosting. A conta é anulável só enquanto o documento é rascunho — na submissão toda fatia a tem (RN-CC-29, 422 ALLOCATION_GL_ACCOUNT_REQUIRED_UNPROCESSABLE), o que garante a tupla que BudgetEntry exige. Soma dos percentuais fecha em 100 com regra de resíduo declarada
NumberSequence → Company / EstablishmentN · 0..1Exigidos conforme o scope; preenchidos fora dele é 422

Orçamento

RelaçãoCardinalidadeRegra
Company → Budget1 · NUm exercício por empresa e período. Dois Approved com períodos sobrepostos são recusados por EXCLUDE — a mesma mecânica de ApprovalPolicy e CostCenterAssignment
Budget → BudgetLine1 · NChave lógica (budgetId, costCenterId, glAccountId, periodKey) com quatro índices únicos parciais, porque duas colunas anuláveis não colidem sozinhas no Postgres
BudgetLine → CostCenter / GLAccountN · 0..1Nulo = qualquer. Preenchido, cobre o nó e a subárvore por prefixo de path. Aceita nó sem allowsPosting — orçar em nó intermediário é o caso normal
BudgetLine → BudgetEntry0..1 · NResolvido na gravação e congelado. Nulo quando não há controle orçamentário — o movimento é gravado assim mesmo, pela tupla de dimensões
BudgetEntry → BudgetEntry0..1 · NliquidatesEntryId encadeia os estágios; reversalOfEntryId marca estorno integral. Liquidar é avançar, estornar é desfazer — não são a mesma coisa
BudgetLine → BudgetLineBalance1 · NUma linha por estágio. Cache, não verdade: atualizada na mesma transação e reconstruível a partir do razão
BudgetPolicy → CostCenterN · 0..1Nulo = padrão do tenant. Cada campo é herdado subindo o path independentemente dos outros; sobreposição de vigência no mesmo nó recusada por EXCLUDE. Desde 03/09/2026 é aqui que mora a decisão de bloqueio por verba — o motor de aprovação lê a overBudgetPolicy resolvida e obedece, e o budgetBlockBehavior por tenant que ele tinha foi removido
Budget → BudgetChangeRequest1 · NOrigem e destino têm que ser do mesmo exercício. Aprovado, grava Adjustment; o valor efetivo da linha é amount + Σ Adjustment
BudgetEntry → ExchangeRateN · 0..1A taxa é copiada para o movimento, não referenciada — o razão não muda quando a cotação muda. A relação existe só para rastro

Moeda e câmbio

RelaçãoCardinalidadeRegra
Currency → TenantCurrency1 · NUma linha por tenant que ativou. BRL nasce ativa e não é desativável na v1; desativar com documento em aberto é 409
Currency → CurrencyPair1 · NDuas vezes, por fromCurrency e toCurrency. Único por par. O par inverso é outro par — não se deriva por inversão, porque compra e venda não são recíprocas. Global: autoFetch é a união das ativações dos tenants, e a edição exige permissão de plataforma (04/09/2026)
CurrencyPair → ExchangeRate1 · NÚnico parcial em (tenantId, from, to, rateType, rateDate) WHERE supersededById IS NULL. tenantId nulo = cotação oficial compartilhada, e a do tenant vence a oficial
ExchangeRate → ExchangeRate0..1 · NsupersededById. Cotação não se edita: corrigir é gravar linha nova e marcar a antiga, com motivo obrigatório. A resolução só enxerga a vigente
Tenant → CurrencySettings1 · 1Tipo padrão, janela de idade da cotação, horários da rotina e destinatários do alerta. É a entidade que rascunhos da spec de Orçamento chamavam de BudgetSettings

Anexo e arquivo

RelaçãoCardinalidadeRegra
StoredFile → Attachment1 · NSem chave estrangeira — outro módulo, outro DbContext. N porque a propagação e o dedupe fazem vários anexos compartilharem o mesmo blob. storedFileId é imutável no anexo
StoredFile → FileReference1 · NDo GrydFiles. Único por (storedFileId, ownerScope, ownerKey) entre as não liberadas; liberar é append de releasedAt, nunca exclusão
Attachment → FileReference1 · 0..1ownerScope = "nexio.attachment", ownerKey = attachment.id. A referência não nasce na confirmação do upload: ela nasce na liberação do arquivo (09/09/2026), a partir do par pendente do StoredFile — até lá o anexo existe, não é referenciável e não é baixável, e a invariante do GrydFiles ("nada é referenciável antes de ser escaneado") sobrevive intacta. Infected e Purged descartam o anexo, com aviso ao dono. A cópia propagada cria a sua — com uma referência só, descartar a origem liberaria o arquivo enquanto o destino ainda o exibe
AttachmentType → Attachment1 · NtenantId nulo no tipo é tipo global do produto; linha do tenant com o mesmo code o sobrepõe — padrão do NotificationTemplate, e o mesmo do ReasonCode desde 04/09/2026. Tipo desativado não invalida anexo existente; trocar de tipo só entre tipos de mesma retenção
Attachment → Attachment0..1 · NcopiedFromAttachmentId dá o pai, rootAttachmentId a origem da cadeia sem recursão. Revogar a origem não revoga as cópias
Attachment → donoN · 1ownerType + ownerId, sem FK e sem navegação de volta: nenhum agregado tem coleção de anexos. ownerType sem descritor registrado derruba a inicialização
Company → Attachment1 · NcompanyId derivado do dono e denormalizado, para o filtro por UserCompanyScope caber na linha sem join entre contextos. Fora do escopo é 404
Supplier → Attachment0..1 · NObrigatório em anexo de origem portal, resolvido do token da RFQ e congelado. É o que dá o isolamento entre concorrentes por comparação de coluna, e o que permite promover o arquivo a SupplierDocument

Catálogo, fornecimento e aprovação

RelaçãoCardinalidadeRegra
CategoryNode → CategoryNode0..1 · NÁrvore de profundidade livre, com teto de política por tenant (configuração do Nexio, a especificar — TenantSettings não existe na plataforma). Mesma mecânica das outras duas árvores desde 08/09/2026
CategoryNode → GLAccountN · 0..1Sugestão. A busca sobe a árvore: o nó mais próximo com conta preenchida vence
Item → GLAccountN · 0..1Primeiro degrau da cascata; a categoria é o segundo. Sempre editável na linha
Item → UnitOfMeasureN · 1baseUomId imutável depois do primeiro documento. Conversões vivem no item, não na unidade
Supplier → SupplierEstablishment1 · 1..NEspelha Company → Establishment, para todo entityType: quem não tem CNPJ tem um estabelecimento sem cnpj, e a identidade (cpf, ou taxIdCountry + foreignTaxId) fica na raiz. Filial pode estar baixada com a matriz ativa
ItemSupplier → Item / SupplierN · 1A chave da relação tem três partes — itemId, supplierId, supplierEstablishmentId?; o nulo vale para todos os estabelecimentos do fornecedor. A linha referencia a oferta e sua revisão, além da relação e do preço selecionado; congela condições e fatores efetivamente usados
ItemSupplierOffer → ItemSupplierPrice1 · 0..NVersões de preço por oferta, empresa, UF e condição comercial, com vigência sem cruzamento. Incoterm condicional. Zero preços é possível para oferta ainda em negociação.
Tenant → ApprovalSettings1 · 1Nova em 08/09/2026. Limiar de materialidade do rateio, teto de duração da delegação, expediente e lista de dias não úteis — o que vale para todos os processos. A de processo chama-se ApprovalProcessSettings desde a mesma data, para que cada nome declare a própria chave. Mesmo padrão de Tenant → CurrencySettings: entidade por assunto, nunca JSON genérico
ApprovalPolicy → ApprovalLevel1 · 1..NPolítica sem nível é recusada na publicação. Editar publica versão nova; a anterior segue respondendo pelas instâncias vivas
ApprovalRequest → ApprovalStep → ApprovalAssignment1 · 1..N · 1..NA instância congela as políticas com versão. ApprovalEvent é append-only e é a fonte da verdade da trilha
Assunto → ApprovalRequest1 · 0..1 por cicloSupplier, SupplierQualification, BankAccountChangeProposal, Item, Budget e BudgetChangeRequest apontam para a instância viva por approvalRequestId, sem FK (contexto do motor). O assunto submete e aplica o efeito; nenhum deles tem rota própria de aprovar — princípio 7 do Fluxo, 04/09/2026

Contrato vigente · revisão consolidada

Invariantes entre contextos

  • Referência empresarial sempre no tenant correto. CostAllocation.companyId é a empresa do documento; pedidos centralizados separam destinatários fiscais.
  • Teto de permissão, escopo e concessão empresarial precisam autorizar a ação; ausência de vínculo nega acesso. Responsabilidade e alçada são verificadas separadamente.
  • Decisão vincula revisão, conteúdo, valor autorizado, política/versão e evidência imutável. Mudança material exige revisão.
  • Pedido mantém effectiveRevisionId e obrigação durante aprovação da candidata; alteração efetiva aplica delta.
  • Razão é append-only, com grupo, entrada e parcela identificados. Backfill, reorçamento e virada são balanceados. Actual ocorrido não desaparece por falta de verba.
  • Transações obrigatórias usam chamadas explícitas. Evento em memória espera o commit produtor; reação essencial tem outbox/inbox duráveis.
  • FK estratégica pode ser registrada na persistência compartilhada. Relatório controlado pode fazer join, com filtros de todas as fontes. Separar bancos exige nova análise de atomicidade.
  • Arquivo referencia versão selada; hash, scan e download correspondem aos mesmos bytes. Desfecho GrydFiles/Nexio exige conexão/transação compartilhadas.
  • Portal vê a própria proposta/anexos com vínculo válido, sem acessar concorrentes. Identidade convidada não prova pessoa física autora.

Vocabulário

Enumerações

EnumValoresOnde
AttachmentOwnerTypeRequisition | RequisitionItem | QuotationRequest | QuotationResponse | PurchaseOrder | PurchaseOrderItem | Receipt | Invoice | Contract | QuotationRequestItem | ApprovalRequest | Supplier | Item | Budget | BankAccountChangeProposalAttachment. Fechado: entrar na lista custa escrever o descritor com as onze respostas do registro. Nove valores até 08/09/2026; quinze desde 09/09 — a migração de enum fechado foi feita agora, com o catálogo ainda sem código além do centro de custo, e não depois. Os seis novos estão declarados na tabela de descritores da página de Anexo, e a RN-ATT-03 confere no startup que todo valor do enum tem descritor registrado
AttachmentVisibilityInternal | SupplierAttachment e AttachmentType. Desde a v1, porque decidir depois obrigaria a reclassificar acervo e o modo de falha é vazamento
UploadedByKindUser | PortalUser | SystemAttachment. Autoria de negócio; o uploadedBy do StoredFile continua sendo identidade do tenant no GrydAuth
RevocationSourceUser | OwnerDiscarded | OrphanSweep | FileInfectedAttachment. Separa o que uma pessoa decidiu do que o sistema concluiu — é o que a tela usa para escrever a frase certa
RetentionModeUntilReleased | RetainUntil | PermanentAttachmentType declara, StoredFile executa. O Nexio decide quanto tempo guardar; o GrydFiles cobra a declaração
AttachmentOwnerStateDraft | InProgress | ClosedAttachment.lastKnownOwnerState. É o mesmo vocabulário que o StateOf do descritor devolve, menos o Missing — que é resposta do descritor, nunca estado gravado. Quem grava é o módulo dono, na transição, no mesmo método que muda o estado do documento
FileOutcomeReleased | Infected | RejectedO que o contrato de veredito (IFileOutcomeHandler) entrega a quem espera pelo arquivo. Não é o FileStatus: é o subconjunto que interessa ao consumidor
FileStatusPending | Scanning | Available | Infected | Failed | PurgedStoredFile, da plataforma. Não é espelhado no anexo: muda, e por isso é consultado em lote
ApprovalRequestStatusInApproval | Approved | Rejected | Returned | CancelledApprovalRequest. Returned volta ao requisitante e a instância continua viva; os outros três são terminais. Draft e Expired saíram em 08/09/2026: nada os produzia — a instância nasce em POST /approvals e nada vence por decurso
ApprovalStepStatusPending | Active | Approved | Rejected | Skipped | ObsoleteApprovalStep. Skipped só por activationMinValue ou dedupe — nunca por falta de aprovador
ApprovalAssignmentStatusPending | Approved | Rejected | Returned | Obsolete | ReassignedApprovalAssignment. Obsolete quando outra tarefa do nível já fechou o passo em modo Any. A linha permanece
ApprovalModeAny | All | Quorum(n)ApprovalLevel. É aqui que vive o paralelo — entre níveis é sempre sequencial. Rejeição encerra a instância em todos os modos; Panel é tipo de nível, sempre All. Vote(n) — votação com dissidência tolerada — é fase 2, restrito a três processos, e não entra no enum até lá
ApprovalScopeModeDocument | Line | SliceApprovalLevel e ApprovalStep. Line entrou no enum em 09/09/2026: o valor já era usado em três lugares e não existia aqui, o que deixava o padrão "interestadual passa pelo fiscal" inconfigurável. Um passo por linha que casa o critério, scopeAmount = valor da linha, materialidade não se aplica e a dedupe é por (pessoa, linha)
AssignmentOriginDirect | ByRole | ByGroup | ByDelegation | ByEscalation | ByReassignmentApprovalAssignment, com o id da delegação, do grupo ou do papel em originRef
EscalationBehaviorNone | Notify | EscalateApprovalProcessSettings, por processo — dono único desde 08/09/2026: política e nível herdam o prazo, não o que fazer com ele; o para quem é escalationTarget, no nível, e allowEscalation = false proíbe escalar no processo. Não existe AutoApprove — a ausência é a decisão. NotifyManager saiu no mesmo dia: dependia da árvore de subordinação do GrydAuth, que é fase 2
ApproverTypeSpecificUser | Role | PermissionHolder | Panel | CostCenterOwner | CostCenterOwnerUpToAmount | CostCenterOwnerAtDistance | ApprovalGroup | CategoryOwner | RequesterManager | ExternalEmail | AutoApprovalLevel. PermissionHolder e Panel entraram em 04/09/2026 — o primeiro é o alvo das políticas padrão semeadas, o segundo o paralelo por área da homologação; os quatro últimos são fase 2
ApprovalProcessCodeREQUISITION | QUOTATION | PURCHASE_ORDER | CONTRACT | SUPPLIER_REGISTRATION | SUPPLIER | BANK_ACCOUNT_CHANGE | ITEM | BUDGET | BUDGET_CHANGEOs dez processos-semente desde 04/09/2026. SUPPLIER_REGISTRATION, ITEM e BUDGET entraram com o motor universal; ORCAMENTO e ALTERACAO_ORCAMENTARIA, que o Orçamento citava, nunca existiram
TenantTypeCompany | GroupPlataforma. Group é eixo de acesso e nunca guarda dado
TaxRegimeSimplesNacional | MEI | LucroPresumido | LucroReal | TaxExempt | NotApplicableCompany e Supplier — o mesmo enum dos dois lados
EstablishmentTypeHeadquarters | BranchEstablishment e SupplierEstablishment
IcmsTaxpayerStatusTaxpayer | Exempt | NonTaxpayerEstablishment. É o indIEDest da NF-e
DeliveryLocationTypeOwnSite | Warehouse | ConstructionSite | ThirdPartyDeliveryLocation. Só ThirdParty libera recipientCnpj; só ConstructionSite pré-seleciona o motivo interestadual
InterstateReasonConstructionSite | GeneralWarehouse | ThirdPartyProcessing | DirectToCustomer | IntraGroupTransfer | OtherDeliveryLocation e o snapshot da linha. Fechado de propósito: motivo em texto livre vira "conforme combinado" no terceiro mês. IntraGroupTransfer é de 09/09/2026 — entregar na filial de PE com a matriz de SP como destinatária fiscal é o arranjo que a compra centralizada produz o tempo todo, e até então só cabia em Other + texto livre
RegistrationStatusNull | Active | Suspended | Unfit | ClosedDownSituação cadastral — Establishment e SupplierEstablishment, o mesmo enum dos dois lados, com os códigos 01 · 02 · 03 · 04 · 08 da Receita. Alinhado em 04/09/2026; nulo para fornecedor sem CNPJ
AccountTypeAsset | Liability | Equity | Revenue | ExpenseGLAccount. Herdado do pai
SequenceScopeTenant | Company | EstablishmentNumberSequence. Padrão Company
ResetPolicyNever | Yearly | MonthlyNumberSequence. Padrão Yearly
AssignmentRoleOwner | Deputy | WatcherCostCenterAssignment
PredominantUseProductionInput | Resale | ConsumableUse | FixedAsset | PersonalBenefitItem e ItemType. Critério de aprovação
UomClassCount | Weight | Volume | Length | Area | Time | ServiceUnitOfMeasure. Conversão só dentro da classe
BankAccountKindDomestic | InternationalSupplierBankAccount.accountKind. Discriminador do bloco bancário: iban, swiftBic, bankCountryCode e os campos de banco intermediário são obrigatórios em International
RoutingCodeTypeABA | TransitNumber | SortCode | OtherSupplierBankAccount, no bloco internacional
ProtectedChangeKindBankAccount | NoticeContactBankAccountChangeProposal.changeKind. Mudar o e-mail ou o telefone do contato financeiro é proposta pela mesma entidade e pela mesma instância de BANK_ACCOUNT_CHANGE — porque trocar o canal do aviso é o primeiro passo da fraude que a proposta bancária existe para deter
SupplierEntityTypeCompany | MEI | Individual | RuralProducer | ForeignSupplier. Governa a obrigatoriedade de todo o resto
CompanySizeNotInformed | ME | EPP | OtherSupplier. Indicativo, nunca verdade contratual
PolicyKindBase | SupplementalApprovalPolicy. Base concorre, Supplemental empilha
BudgetStatusDraft | InReview | Approved | Closed | ArchivedBudget. Só Approved e Closed participam da resolução
BudgetOriginImported | PlannedBudget. Muda por onde as linhas entraram, não o ciclo de vida
PeriodGranularityMonthly | Quarterly | YearlyBudget. Imutável depois de aprovado
BudgetEntryReasonPost | Liquidate | Reverse | DocumentCancelled | Return | ReceiptReversal | QuantityChange | DateChange | ResidueRelease | YearEndRollover | BudgetBackfill | BudgetReclassification12 valores, mapeamento para operation em Motivo. Idempotência é por postingGroupId e entryOrdinal, não pelo motivo.
BudgetStageCommitment | Obligation | Actual | AdjustmentBudgetEntry. Seis valores até 08/09/2026; quatro desde 09/09. Os três primeiros consomem; Adjustment altera a verba. CarryOut e CarryIn saíram porque não consumiam, não tinham campo em amounts e não entravam no somatório: a virada de exercício grava nos próprios estágios, com entryReason = YearEndRollover
BudgetSignalNotControlled | Ok | Warning | Exceeded | UncoveredResposta de evaluate:budget-states. É o que a tela pinta e o que o motor lê como critério
ResolutionReasonNotControlled | Covered | UncoveredBudgetEntry. Congelado na gravação
CarryForwardModePeriodOnly | CumulativeBudgetPolicy. É modo de leitura do saldo, não movimento de transporte. Padrão PeriodOnly
OverBudgetPolicyWarn | Block | RequireApprovalBudgetPolicy. Padrão Warn; não existe permissão que fure Block
YearEndCommitmentPolicyCarryCommitmentOnly | CarryCommitmentAndBudget | CancelOpenBudgetPolicy. Padrão CarryCommitmentOnly — o compromisso segue o documento, a verba não vai junto
ChangeRequestTypeSupplement | Transfer | ReductionBudgetChangeRequest. O sinal vem do tipo, não do número digitado
BudgetSourceTypeRequisition | PurchaseOrder | Receipt | Invoice | Contract | BudgetChangeRequest | Rollover | Import | ManualBudgetEntry. É o rastro que responde "quem comeu minha verba"
RateTypePtaxVenda | PtaxCompra | Commercial | Contracted | ManualExchangeRate. Padrão PtaxVenda de fechamento D−1
NotificationChannelEmail | InApp | Push | SmsNotificationPreference e o catálogo. Canal fora de allowedChannels do slug não aparece na tela e é recusado na API. Sms é dependência declarada do GrydNotifications, que hoje entrega três canais — um único slug o usa, o do aviso bancário, onde a redundância de canal é o controle; enquanto não existir, o aviso sai por e-mail e a ausência do segundo canal é registrada
NotificationCategoryApproval | Budget | Catalog | Supplier | Attachment | SystemCatálogo. Só agrupa a tela — a preferência é por slug, e não existe interruptor de categoria
AudienceTypeUser | TenantRole | CostCenterOwners | PendingApprovers | SupplierFinanceContactCatálogo. Cinco resolvedores, cada um implementado pelo módulo dono do conceito; tipo sem resolvedor derruba a inicialização
DeferredSubmissionModeNotifyOnly | SubmitDeferredSubmission.mode. As duas escolhas da tela criam a fila — "só avisar" deixou de ser "não cria nada", que era o desenho em que ninguém sabia dizer o que tinha ficado para trás
DeferredSubmissionStateWaiting | Submitting | Completed | Abandoned | CancelledDeferredSubmission. Submitting existe para duas instâncias da aplicação não submeterem o mesmo documento
ReasonCodeUsageApprovalReject | ApprovalReturn | BankAccountChangeReject | BudgetReject | BudgetChangeReject | ItemReject | ItemDuplicateOverride | SupplierReject | SupplierBlock | SupplierUnblock | SupplierManualPromotion | AttachmentRevoke | BudgetChangeJustify | PriceVarianceAccept | ExchangeRateSupersede | DocumentCancel | SupplierAwardOverrideReasonCode.appliesTo. Doze usos até 08/09/2026; dezessete desde 09/09, com a semente do produto em 45 códigos. Três rejeições que faltavam (BankAccountChangeReject, BudgetReject, BudgetChangeReject), a justificativa da mudança de verba (BudgetChangeJustify, que aposentou o justification em texto livre do BudgetChangeRequest) e o aceite de diferença de preço do three-way match (PriceVarianceAccept, declarado)
AbandonReasonFileInfected | FileScanFailed | PermissionLost | OwnerChanged | OwnerDiscarded | Expired | OwnerRefusedDeferredSubmission. Motivo de sistema, por isso continua enum quando o ReasonCode existir. OwnerRefused entrou em 09/09/2026, com a transição Submitting → Waiting: o dono recusar a submissão devolve a fila à espera e não é falha

Contrato vigente · revisão consolidada

Convenções vigentes

ConvençãoContrato
Fonte canônicaCampos e regras pertencem à sua especificação; este modelo mostra relações. Sem acúmulo de decisões históricas contraditórias.
Estado realEspecificado não significa implementado; código em branch não significa pacote publicado. Ver Arquitetura e relato datado do spike.
PersistênciaPostgreSQL compartilhado e NexioDbContext simplificam a v1. FK estratégica requer registro de exceção; testes atuais ainda proíbem todas e deverão ser adaptados antes do uso.
TransaçõesEfeitos obrigatórios explícitos; memória após commit produtor; outbox durável também para reações locais essenciais.
Dinheironumeric(21,4), Currency.minorUnits 0–4; unitário numeric(19,10); quantidade numeric(19,6) com original e fatores preservados; taxa numeric(18,8); percentual numeric(9,6).
ResíduoHALF_UP nas casas da moeda, maior fatia e empate pelo menor order. Total funcional soma fatias convertidas.
ConcorrênciaexpectedVersion na raiz e operationId estável. Orçamento acrescenta travas de todos os limites/janelas; inbox e efeito são atômicos.
RevisõesrevisionId identifica conteúdo; roundNumber identifica rodada. Não trocar versão efetiva enquanto candidata aguarda.
Permissãoverbo:recurso, com regras de catálogo por módulo; autorização efetiva inclui empresa. Contratos de portal não criam usuário de tenant.
Ações e motivosAção estável e reasonCode snapshot; entryReason é enum do sistema. Campo Action da plataforma permanece dependência a conferir no pacote.
EvoluçãoMigração coordenada, compatibilidade de payload e restore testado. Particionamento, isolamento físico e infraestrutura de IA aguardam gatilhos observados.

Inventário

Satélites — as classes que não têm caixa

Catorze classes especificadas que não aparecem nos diagramas, de propósito. A regra de corte é esta: ganha caixa o que se lê ao lado de outra coisa — raiz, configuração, árvore, o que participa de uma invariante entre agregados. Satélite é o que só se lê dentro do seu próprio agregado, e desenhá-lo aqui dobraria o tamanho de nove diagramas para repetir o que a spec do dono já diz melhor. O que esta tabela garante é o que faltava: que ninguém invente um campo que já existe, porque a linha diz onde procurá-lo.

ClasseAgregado donoOnde os campos vivem
ItemPhotoItemCadastro de Item · § Coleções filhas. Máx. 5, storedFileId no perfil nexio.item-photo
ItemDocumentItemCadastro de Item · § Coleções filhas. FISPQ, certificado, laudo; tipo, versão, emissão, validade e storedFileId
ItemComplianceItemCadastro de Item · § Compliance. Vencimento bloqueia a compra quando marcado
ItemUomConversionItemUnidade de Medida · § Conversão, e Cadastro de Item · conversions. Append-only com vigência (RN-UOM-09)
ItemLifecyclePhaseItem · configuraçãoCadastro de Item. É o destino de Item.lifecyclePhaseId, que até 08/09/2026 apontava para lugar nenhum nesta página
AttributeDefinitionCatálogo · raiz própriaCadastro de Item · § Atributos. É raiz e mesmo assim não tem caixa: o esquema por categoria é assunto de uma spec só, e desenhá-lo custaria a banda inteira
SupplierAddressSupplierFornecedor · § Endereços. Um endereço com vários papéis — não confundir com DeliveryLocation, que são vários pontos com um papel só
SupplierContactSupplierFornecedor · § Contatos. SupplierFinanceContact é o recorte financeiro dele, usado pela Notificação
SupplierDocumentSupplierFornecedor · § Documentos. Certidão com validade e storedFileId; libera a referência em vez de apagar. Ganhou supplierEstablishmentId? em 09/09/2026 — obrigatório quando scope = Establishment, proibido quando scope = Supplier: certidão é por CNPJ, e o fornecedor tem vários
SupplierBankAccountSupplierFornecedor · § Dados bancários. Números mascarados por padrão, sob read:supplier-banks. Duas colunas de 09/09/2026: accountKind (Domestic | International), que discrimina o bloco de IBAN/SWIFT, e usableFrom, que materializa a carência de 24–72 h — conta com usableFrom no futuro não sai no pacote do ERP. Antes disso a carência existia só como frase
SupplierFinanceContactSupplierFornecedor e Notificação · é audiência de aviso, não identidade
SupplierQualificationSupplierFornecedor · § Homologação. approvals[] vem da instância do processo SUPPLIER do motor; companyId anulável é gancho declarado. Desde 09/09/2026 é ela que autoriza o gasto, por consulta às qualificações vigentes (supplierId × categoryNode, casando por prefixo de path e respeitando validTo) — e não mais o estado SpendAuthorized sem escopo, que tornava autorizado para ácido sulfúrico quem foi aprovado para limpeza
BankAccountChangeProposalSupplierFornecedor · § Alteração bancária. Abre instância de BANK_ACCOUNT_CHANGE; o Fornecedor aplica ao registro vivo pelo contrato de desfecho (RN-AP-38). changeKind (09/09/2026) faz a mesma entidade servir à troca do canal de aviso — e-mail e telefone do contato financeiro viraram campos protegidos, porque trocar o canal é o primeiro passo da fraude que a proposta existe para deter. noticeSentAt sustenta a janela mínima entre o aviso e a primeira decisão
PortalUserSupplier · condicionalFornecedor · § Portal. Três campos reservados. A decisão do portal deixou de ser uma folha em branco em 08/09/2026 — há proposta escrita, em validação; ver o gate do mapa de domínio

Nem tudo o que tem nome é classe. Commitment, Obligation, Actual e Adjustment aparecem em toda a spec de Orçamento e não são entidades: são valores do estágio de um BudgetEntry. Procurar uma tabela Commitment é o erro que esta linha existe para evitar.

Limites

O que ainda não está aqui

Seis agregados e um transversal a especificar — Receipt e Invoice dividem uma linha da tabela e são duas classes; são as mesmas seis caixas a especificar do mapa de domínio. A ordem e o custo de adiar estão no mapa de domínio. Notification e ReasonCode saíram em 03/09/2026, nas bandas 8 e 9 — as duas entraram no fim, que é a inserção mais barata desta página.

ClasseOnde encaixaO que já está reservado
RequisitionEspinha transacional · a primeiraestablishmentId obrigatório, companyId derivado e congelado, linhas com CostAllocation, número por NumberSequence
PurchaseOrderEspinha transacionalMais requestingEstablishmentId, para compra centralizada
Receipt · InvoiceEspinha transacional · three-way matchA nota chega endereçada a um CNPJ de 14, e o leiaute já tem IBS e CBS — exigidos a partir da data que a norma fixar, ausentes sem erro até lá (regra do Item)
QuotationRequestEspinha transacionalDepende da decisão sobre portal do fornecedor, que tem proposta na mesa e ainda não foi validada. QuotationRequestItem já entrou no AttachmentOwnerType, com as onze respostas declaradas — de propósito: o custo de deixá-lo fora era uma migração de enum fechado depois
ContractContrato transacional mínimoOferta pode referenciar contractLineId e revisão; mínimo obrigatório gera obrigação na ativação, com liberações sem dupla contagem. Especificação completa ainda pendente.
TransversaisExternalRef · OutboxSobrou um. NumberSequence saiu com a spec de empresa, Currency · ExchangeRate com a de Moeda e Câmbio, Attachment · AttachmentType com a de Anexo e as três de notificação com a de Notificação, que levou junto o Motivo — e o AuditLog saiu do backlog porque o GrydAudit já o entrega inteiro

Contrato vigente · revisão consolidada

Contratos mínimos entre documentos

DocumentRevision / LineSnapshotdocumentId / revisionId / lineIdcandidate / effective / contentHashIdentidade estável por linha e fatiaTraceAllocation / Schedulesource → target / quantity / amountscheduleId / promessa / recebimentoParciais e N:M sem duplicidadeExternalReference / Operationorigem / empresa externa / tipo / idoperationId / hash / resultadoSent ≠ Received ≠ AppliedRecommendation / EvidenceSnapshotFontes, atualidade e limitaçõesAlternativas / custo / restriçõesVersão de regra ou modeloHumanDecision / OutcomeEscolha, motivo e revisãoCompra e resultado observadosIA consultiva, sem autoaprovação
Rastreabilidade mínima; especificações completas dos seis módulos ainda pendentes.

Ver Contratos transacionais para responsabilidade, condições de efetivação, aceite de serviço, mínimo contratual e cenários de validação. Os blocos são contratos mínimos; não afirmam entidades já implementadas.