Ponto de partida
Oito decisões
São dois conceitos e um deles já existe. Ship-to é o CNPJ que responde pela operação — é o Establishment, com IE, icmsTaxpayerStatus, Suframa e código IBGE. Deliver-to é o destino: doca, almoxarifado, andar, obra. Só o segundo nasce aqui. establishmentId é obrigatório e imutável — um ponto de entrega não muda de destinatário fiscal, isso seria outro ponto de entrega.
Endereço próprio é opcional; ausente, herda o do estabelecimento. É o que faz um conceito só cobrir os quatro casos reais: doca e setor não têm endereço (herdam), CD alugado e canteiro de obra têm. Sem essa opcionalidade seriam duas entidades — uma de rótulo interno e outra de endereço — e toda consulta teria que olhar nas duas.
UF diferente da do estabelecimento exige motivo nomeado. Entregar em outro estado mantendo o destinatário original não cria tratamento interestadual — cria autuação. O modelo não impede o caso legítimo e não deixa o ilegítimo passar calado: interstateReason obrigatório, com os motivos que a legislação reconhece, e 422 sem ele.
CNPJ próprio no ponto de entrega só quando é de terceiro. Armazém geral, industrializador, entrega por conta e ordem. CNPJ nosso é Establishment, sempre — cadastrar filial como ponto de entrega é onde IE, Suframa e situação cadastral somem, e é exatamente o erro que a spec de Empresa já nomeia.
Endereço eventual existe, mas nunca vira linha de catálogo. Vive só no snapshot do documento, exige permissão própria e obedece à mesma regra interestadual. E o catálogo se defende sozinho: a tela de administração lista os endereços eventuais repetidos com a contagem de uso, para virarem cadastro por um clique.
Cabeçalho define o padrão, linha sobrescreve — e o destinatário é um só. Todas as linhas de um documento entregam em locais do mesmo establishmentId do documento. Destinatário diferente é outro documento, não outra linha. É a regra da Oracle, e é a que evita nota impossível de emitir.
Obra é nó de centro de custo; aqui mora só o endereço dela. O CostCenter já ganhou vigência própria para obra e projeto. Modelar obra de novo aqui daria duas verdades sobre a mesma coisa. O vínculo é um defaultCostCenterId opcional que sugere o rateio — e nunca o contrário: centro de custo não ganha endereço.
A linha congela endereço, UF e motivo; a chave estrangeira é só rastro. Regra 1 do mapa de domínio, sem exceção. Renomear a obra, corrigir o CEP ou encerrar o ponto de entrega não pode reescrever o que a nota de seis meses atrás dizia — e a UF congelada é o que torna a conferência fiscal reproduzível.
Fundamento
Por que local de entrega é dimensão fiscal, e não campo de logística
A NF-e separa quem responde de onde chega — e o segundo grupo não transfere jurisdição
O leiaute tem o grupo dest, que identifica o destinatário da operação, e o grupo G · entrega, que identifica o local de entrega quando ele difere. Desde a NT 2018.005 esse grupo carrega CNPJ ou CPF, razão social do recebedor, endereço completo, município, UF, CEP, país, telefone, e-mail e inscrição estadual — e ganhou rejeições próprias para código de país e IE inválidos. É um grupo rico justamente porque a Receita quer o dado, não porque ele seja decorativo.
O que ele não faz é mudar de quem é a operação. Quem decide alíquota é o destinatário jurídico, o CNPJ que consta como dest.
E o critério de operação interna ou interestadual é a circulação física
As Respostas à Consulta da SEFAZ/SP são consistentes: o que define se a operação é interna ou interestadual é o fluxo físico efetivo da mercadoria, não o endereço cadastral do destinatário. A nota deve ser emitida para o adquirente com o endereço onde a entrega vai ocorrer, e o Grupo G preenchido quando o local difere do cadastro.
As duas coisas juntas produzem a única regra que este documento precisa impor
Entregar em outra UF mantendo o destinatário original não produz tratamento interestadual correto — produz divergência entre a alíquota aplicada e o destino real, que é o padrão que o fisco procura. O caminho certo é um dos três: o CNPJ que recebe passa a ser o destinatário; a operação usa uma triangulação que a legislação nomeia; ou a entrega acontece na mesma UF.
A construção civil tem uma dessas triangulações escrita: o RICMS/SP (Anexo XI, art. 4º, §3º) admite a compra com entrega direta no canteiro, sem passar pelo estabelecimento principal. É legislação estadual e varia por UF — o Nexio registra o motivo declarado e a UF efetiva; quem responde pela adequação é a assessoria fiscal do cliente.
O que muda de preço quando a UF muda
Alíquota interestadual de ICMS, DIFAL de destino, FCP e os convênios de substituição tributária — todos dependem da UF de destino, e todos entram no preço que o fornecedor cota. Um sistema de compras que trata destino como texto livre não consegue nem comparar duas propostas de UFs diferentes, nem conferir a nota contra o pedido. É por isso que stateCode é campo derivado do código IBGE e congelado na linha, exatamente como já é no Establishment.
O que esta spec deliberadamente não é. Não é motor de cálculo fiscal. O Nexio não apura ICMS, não resolve DIFAL e não decide CFOP. O que ele faz é garantir que a dimensão exista, seja obrigatória onde importa, esteja congelada no documento e chegue íntegra a quem calcula — que é o ERP fiscal. Registrar com precisão o fato é o escopo; tributá-lo não é.
Modelo
Mapa de entidades
Establishment, tem endereço próprio opcional, vigência, contato, janela de recebimento e instruções de acesso.
stateCode, destinatário fiscal, CNPJ do recebedor e o motivo interestadual. Não é tabela própria — são colunas da linha.
icmsTaxpayerStatus, Suframa, endereço fiscal e allowsReceiving. Nada é remodelado aqui.
defaultCostCenterId, que sugere o rateio quando a entrega é naquele ponto.
shipToLocationId vira shipToStateCode. Preço e prazo variam por UF, não por doca — e a UF já viaja congelada na linha, então o cadastro de fornecimento não precisa conhecer o ponto. Preço por sítio específico é do Contract.
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, cityIbgeCode, suframaCode.
Uma tabela só: admin.delivery_locations. Índices: único parcial em (tenantId, companyId, code) filtrando isDeleted = false; um em (tenantId, establishmentId, isActive) para o lookup; e um em (tenantId, stateCode), que é o que a cascata de preço do ItemSupplier consulta.
Domínio
Campos · DeliveryLocation
Legenda: obrig sempre · cond conforme a operação · opc opcional · gerado calculado pelo sistema ou pelo banco.
Identificação
| Campo | Tipo | Regra | |
|---|---|---|---|
| id | Guid | gerado | Atribuído pelo agregado, não pelo banco. |
| tenantId | Guid | gerado | Do token, nunca aceito do cliente. |
| establishmentId | Guid | obrig | Quem recebe fiscalmente. Imutável — mudar de destinatário é criar outro ponto de entrega, porque todo documento já emitido congelou este valor. O estabelecimento precisa ter allowsReceiving = true. |
| companyId | Guid | gerado | Derivado do estabelecimento e denormalizado. Existe para o escopo por empresa caber no filtro de linha sem join — mesmo padrão do Establishment. |
| code | string(20) | obrig | Único por empresa, imutável, normalizado em maiúsculas. CD-REC, OBRA-01, DOCA-3. Por empresa e não por estabelecimento porque é assim que a pessoa fala dele. |
| name | string(160) | obrig | Como o lugar é chamado. É o que aparece no seletor, sob o nome do estabelecimento. |
| type | enum | obrig | OwnSite · Warehouse · ConstructionSite · ThirdParty. Não é decorativo: ThirdParty é o único tipo que libera recipientCnpj, e ConstructionSite é o único que pré-seleciona o motivo interestadual. |
| externalCode | string(40) | opc | O código do local no ERP. Único por (tenant, empresa) quando preenchido. |
Endereço · ausente, herda o do estabelecimento
| Campo | Tipo | Regra | |
|---|---|---|---|
| hasOwnAddress | bool | gerado | Verdadeiro quando zipCode está preenchido. Existe como campo derivado para a consulta não testar seis colunas — e para a tela dizer "herda o endereço da filial" em vez de mostrar campos vazios. |
| zipCode | varchar(8) | cond | Sem máscara. Preenchido, obriga o bloco inteiro; vazio, o bloco inteiro tem que estar vazio. Nunca endereço pela metade — 422. |
| street · number · complement · district | string | cond | Logradouro e bairro obrigatórios com o bloco ativo; número aceita S/N, que é o caso normal de canteiro de obra. |
| cityIbgeCode | varchar(7) | cond | Código do município, não o nome. É o que a NF-e carrega no Grupo G e o que resolve homônimo entre estados. O nome é derivado dele, nunca digitado livre. |
| stateCode | char(2) | gerado | Derivado do código IBGE quando há endereço próprio; copiado do estabelecimento quando não há. É a dimensão que muda o ICMS — por isso nunca é digitada em separado e nunca fica nula. |
| countryCode | char(2) | opc | ISO 3166-1, padrão BR. Reservado; entrega fora do Brasil está fora de escopo. |
Fiscal
| Campo | Tipo | Regra | |
|---|---|---|---|
| interstateReason | enum? | cond | Obrigatório quando stateCode difere do stateCode do estabelecimento, e recusado quando não difere. ConstructionSite · GeneralWarehouse · ThirdPartyProcessing · DirectToCustomer · Other. Ver § A regra interestadual. |
| interstateReasonNote | string(400)? | cond | Obrigatória quando o motivo é Other. É o campo que a auditoria lê primeiro, e é o único texto livre desta entidade — de propósito. |
| recipientCnpj | varchar(14)? | cond | Só com type = ThirdParty. Recusado quando o CNPJ é de um Establishment do próprio tenant — a mensagem nomeia o estabelecimento e manda usar ele. VARCHAR, nunca numérico: CNPJ alfanumérico está em produção desde 31/07/2026. |
| recipientName | string(160)? | cond | Razão social do recebedor. Obrigatória com recipientCnpj. Vai para o xNome do Grupo G da NF-e. |
| recipientStateRegistration | varchar(20)? | opc | IE do recebedor terceiro. A NT 2018.005 criou rejeição própria para IE inválida no grupo de entrega — o campo existe para que ela nunca seja disparada. |
Operação · o que faz valer a pena ser entidade
| Campo | Tipo | Regra | |
|---|---|---|---|
| contactName · contactPhone · contactEmail | string | opc | Quem recebe no local. É o attention line da Coupa, e é o nome que o motorista procura no portão. |
| receivingHours | string(120)? | opc | Janela de recebimento como texto exibível — Seg a Sex, 08:00–11:30 e 13:00–16:30. Texto e não agenda: agendamento de doca é outro produto, e uma agenda que ninguém mantém é pior que uma frase certa. |
| deliveryInstructions | text? | opc | Acesso, portaria, restrição de veículo, exigência de EPI. Sai impressa no PDF do pedido — é a razão pela qual o fornecedor acerta a entrega na primeira tentativa. |
| requiresScheduling | bool | opc | Padrão false. Verdadeiro, o pedido sai com aviso de agendamento obrigatório e o recebimento sem agendamento gera divergência, não bloqueio. |
| defaultCostCenterId | Guid? | opc | Sugere o rateio quando a entrega é aqui. Sugestão, nunca imposição — o rateio continua sendo da linha. Ver § Obra e centro de custo. |
Vigência e estado
| Campo | Tipo | Regra | |
|---|---|---|---|
| validFrom | date | obrig | Padrão hoje. Mesmo padrão de CostCenterAssignment. |
| validTo | date? | opc | Obra acaba. Nulo é o caso comum. Vencido, o ponto some do seletor sem que nenhum documento antigo mude de significado. |
| isActive | bool | gerado | Só por /activate e /deactivate; PUT nunca muda. Desativar com documento em aberto que entrega ali é 409 com a lista do que impede — mesma decisão da unidade de medida. |
Invariante fiscal
A regra interestadual
Uma comparação, disparada em três lugares: ao salvar o ponto de entrega, ao usá-lo num documento e ao aceitar um endereço eventual. deliveryStateCode ≠ establishment.stateCode exige motivo declarado.
| Motivo | O caso | O que o Nexio faz |
|---|---|---|
ConstructionSite | Canteiro de obra em outro estado. O RICMS/SP (Anexo XI, art. 4º, §3º) admite a compra com entrega direta no canteiro, sem passar pelo estabelecimento principal — e regra equivalente existe na maioria das UFs, com redação própria. | Pré-selecionado quando type = ConstructionSite. Exige defaultCostCenterId preenchido, porque obra sem centro de custo não tem como ser conferida depois. |
GeneralWarehouse | Armazém geral ou operador logístico terceirizado, com documentação própria de depósito. | Exige type = ThirdParty e recipientCnpj. O armazém é pessoa jurídica distinta e precisa aparecer no Grupo G. |
ThirdPartyProcessing | Remessa por conta e ordem para industrialização — a mercadoria vai direto ao industrializador. | Idem. O CFOP da operação é do ERP fiscal; o Nexio registra o fato e o CNPJ. |
DirectToCustomer | Entrega direta ao cliente final (drop ship). Existe em revenda e em contrato de fornecimento com montagem. | Marca a linha para o relatório de operação triangular. É o único motivo que aceita destinatário no endereço eventual, com CPF ou CNPJ no snapshot (RN-DL-13); no cadastro, como os dois de cima, exige type = ThirdParty e CNPJ. |
Other | O que a lista não cobre. | interstateReasonNote obrigatória. É o caso que a auditoria vai ler um por um, e é por isso que ele custa uma frase escrita. |
Por que a checagem é de UF e não de município
Município diferente na mesma UF não muda ICMS e não precisa de justificativa — muda só o Grupo G da nota, que é registro, não escolha. ISS é outra história e está fora desta versão: em serviço, o município de prestação decide a retenção, e a dimensão relevante não é para onde a mercadoria vai. O gancho está declarado no fora de escopo; quando o módulo de serviços existir, a checagem ganha um irmão municipal.
O Nexio avisa, não julga
O sistema não diz que a operação é legal — ele diz que ela é interestadual e obriga alguém a declarar sob qual hipótese. A adequação é da assessoria fiscal do cliente, e a legislação é estadual. O que o produto garante é que a informação existe, é estruturada e chega ao ERP fiscal junto com o documento, em vez de ser descoberta na conferência da nota.
A saída óbvia continua disponível — e é a certa
Se o grupo tem CNPJ no estado de destino, o caminho correto é esse CNPJ ser o destinatário: cadastra-se o Establishment, e o ponto de entrega pendura nele. Aí stateCode coincide, nenhum motivo é exigido e a operação é uma venda interestadual comum entre fornecedor e filial. A mensagem de erro diz isso — não é só uma recusa, é uma sugestão nomeada.
A válvula de escape
Endereço eventual
Toda operação real tem a entrega que não cabe no cadastro: o evento, a feira, a casa do executivo que recebe o notebook, a obra de dois meses que não vale um registro. O SAP tem one-time delivery address exatamente para isso. Negar o caso não o elimina — empurra a informação para o campo de observação, onde ela não é dimensão de nada.
Existe, e vive só no documento
O endereço eventual é preenchido no documento e gravado no snapshot da linha com isOneTimeDelivery = true e deliveryLocationId nulo. Nenhuma linha entra em delivery_locations. Não tem código, não aparece em lookup, não pode ser reutilizado por busca e não polui o catálogo — que é a única coisa que fazia valer a pena proibi-lo.
Não é exceção às regras, é exceção ao cadastro
Continua exigindo establishmentId — alguém tem que ser o destinatário, e endereço eventual não cria CNPJ. Continua derivando stateCode do código IBGE. E continua obedecendo à regra interestadual: endereço eventual em outra UF exige interstateReason no documento, com a mesma lista fechada. O que ele dispensa é o cadastro, não o rigor.
Permissão própria, porque é uma decisão e não uma conveniência
use:delivery-location-one-time é separada de read:delivery-location. Um cliente que quer disciplina de catálogo simplesmente não concede — e a funcionalidade desaparece da tela, sem flag de configuração e sem código morto. É a mesma mecânica de permissão que o resto do produto já usa para separar quem lê de quem decide.
O catálogo se defende sozinho
O padrão de falha do endereço eventual é conhecido: ele vira o caminho normal, e em um ano o cadastro tem três pontos de entrega e o histórico tem quatrocentos endereços digitados. A defesa é medir. GET /delivery-locations/one-time-usage agrupa os snapshots eventuais dos últimos N dias por CEP e número, com a contagem de uso e os documentos — e a tela oferece "cadastrar como ponto de entrega" num clique, já preenchido. Endereço eventual usado cinco vezes não é eventual; é um cadastro que ninguém fez.
O que o eventual quase nunca faz. Não aceita destinatário de terceiro — entrega a terceiro é decisão de cadastro, com CNPJ conferido, e não texto digitado às pressas na tela do pedido. A exceção, aberta em 08/09/2026, é uma só: interstateReason = DirectToCustomer (RN-DL-13). Não aceita defaultCostCenterId, porque o rateio da linha já está lá. E não é editável depois de o documento sair do rascunho: a partir daí é snapshot, e snapshot não se corrige — corrige-se o documento, que gera versão nova.
Consumo
Cabeçalho, linha e o que a linha congela
O cabeçalho define o padrão; a linha sobrescreve
Requisição, pedido e recebimento carregam deliveryLocationId no cabeçalho, e cada linha pode apontar para outro ponto. É consistente com o rateio, que já é por linha desde o Centro de Custo — uma requisição de escritório com três itens para a matriz e um para a obra é o caso comum, não a exceção.
Trocar no cabeçalho pergunta o que fazer com as linhas
A documentação do SAP tem um aviso que vale copiar inteiro: editar só o cabeçalho ou só o item cria descasamento silencioso — "edite tudo ou não edite nada". Aqui a propagação é explícita: mudar o ponto do cabeçalho pergunta "aplicar às N linhas que ainda usam o local anterior?", e as linhas que já foram alteradas à mão nunca são sobrescritas em silêncio.
Um documento, um destinatário
Todas as linhas entregam em pontos do mesmo establishmentId do documento. É a regra da Oracle — o deliver-to só é válido quando o ship-to dele bate com o do documento — e a razão é que uma nota tem um destinatário só. Precisa de dois CNPJs? São dois pedidos. DELIVERY_LOCATION_ESTABLISHMENT_MISMATCH_UNPROCESSABLE, 422, nomeando a linha.
O que a linha congela
Pela Regra 1 do mapa de domínio, sem exceção: a chave estrangeira é rastro, o snapshot é a verdade. Renomear a obra, corrigir o CEP, encerrar o ponto ou desativá-lo não pode reescrever o que o pedido de seis meses atrás dizia.
| Campo na linha | Tipo | Regra |
|---|---|---|
| deliveryLocationId | Guid? | Rastro. Nulo quando a entrega é eventual. Nunca usado para ler o endereço na exibição do documento. |
| deliveryEstablishmentId | Guid | O destinatário fiscal, congelado. Igual ao do cabeçalho, sempre. |
| deliveryAddress | colunas | CEP, logradouro, número, complemento, bairro e cityIbgeCode. Colunas, não jsonb: a UF entra em índice e em relatório, e jsonb aqui é conveniência de escrita paga na leitura. |
| deliveryStateCode | char(2) | A dimensão fiscal congelada. É por ela que a conferência a três compara pedido, recebimento e nota, e é ela que o ERP fiscal consome. |
| deliveryRecipientTaxId · deliveryRecipientName | varchar(14)? · string(160)? | Grupo G da NF-e, quando há terceiro. Congelados junto. Renomeado em 08/09/2026: chamava-se deliveryRecipientCnpj, e o nome era estreito demais — o drop ship a consumidor final põe um CPF no Grupo G. varchar(14) cobre os dois; quem valida o dígito olha o comprimento. O campo do cadastro continua recipientCnpj e continua só CNPJ (RN-DL-06): é no snapshot que a pessoa física cabe, porque ali ela é um fato daquele documento, não uma linha permanente de cadastro. |
| deliveryInterstateReason · Note | enum? · string(400)? | Congelados. Mudar a política do cadastro depois não reescreve a justificativa que valeu naquele documento. |
| isOneTimeDelivery | bool | Verdadeiro só no endereço eventual. É o que alimenta /one-time-usage. |
Fronteira interna
Obra e centro de custo — o caso que obriga o desenho
Obra já existe, e existe do outro lado
O CostCenter ganhou vigência própria com a justificativa escrita: "obra e projeto". Obra é nó da árvore de quem paga, com responsável, alçada, orçamento e data de encerramento. Modelar obra aqui de novo daria duas verdades sobre a mesma coisa — e a segunda sempre fica desatualizada, porque é a que ninguém abre.
São dimensões diferentes de propósito, e a assimetria é o desenho
Uma obra pode receber material em dois pontos (o canteiro e o depósito de apoio) e um ponto de entrega pode servir a várias obras (o CD que abastece três frentes). Não é um para um, e por isso não é um campo. O vínculo é DeliveryLocation.defaultCostCenterId, opcional e unidirecional: o ponto de entrega sugere o centro de custo, e o centro de custo nunca ganha endereço.
Sugerir é preencher o rateio, não travá-lo
Ao escolher um ponto de entrega com defaultCostCenterId, a linha nasce com aquele centro de custo em 100% — e o requisitante pode trocar ou ratear. É a mesma mecânica da conta contábil sugerida por Item.defaultGlAccountId e depois por CategoryNode: o sistema propõe, a pessoa responde, o documento congela.
As duas vigências são independentes, e a divergência é avisada
A obra encerra no centro de custo e o ponto de entrega fica ativo, ou o contrário. Nenhum dos dois cascateia para o outro — cascata entre agregados diferentes é como se cria exclusão que ninguém pediu. O que existe é aviso: desativar um nó de centro de custo que é defaultCostCenterId de pontos ativos lista quais são, e segue.
Canteiro em outra UF junta as duas regras. type = ConstructionSite com stateCode diferente exige interstateReason = ConstructionSite e defaultCostCenterId preenchido. A segunda exigência não é fiscal, é de conferência: entrega em canteiro é o caso em que material some entre a nota e a obra, e sem o nó de custo amarrado desde o cadastro ninguém reconstrói para onde foi.
Transversal
Escopo e visibilidade
O escopo vem de graça, e é essa a vantagem de establishmentId obrigatório
companyId é derivado do estabelecimento e denormalizado na linha. O conjunto de empresas resolvido a cada requisição por UserCompanyScope é o filtro — não há segunda regra de visibilidade, não há tabela nova e não há nada para lembrar de validar. Ponto de entrega de empresa fora do escopo do usuário responde 404, nunca 403, pela mesma decisão já tomada em centro de custo e em empresa.
O seletor mostra o que dá para usar, e some quando não há escolha
GET /delivery-locations/lookup?establishmentId=… devolve os pontos vigentes e ativos daquele estabelecimento, agrupados. Um item na resposta e nenhuma permissão de eventual: o campo não aparece na tela, e o único ponto é aplicado em silêncio — mesma decisão do seletor de estabelecimento. Zero itens e nenhuma permissão de eventual é erro de cadastro, e a tela diz isso com o link para o cadastro em vez de mostrar um combo vazio.
CD compartilhado entre empresas do grupo são N registros, e está certo
O mesmo galpão que recebe para três empresas do grupo vira três DeliveryLocation, um por CNPJ destinatário. Parece duplicação e não é: fiscalmente são três operações diferentes, com três notas, três destinatários e possivelmente três tratamentos de ICMS. Um registro com N donos precisaria escolher um destinatário na hora de emitir — que é a decisão que este campo existe para não deixar implícita.
Integração
Relação com os demais domínios
| Domínio | Mudança | O quê |
|---|---|---|
| Empresa e Estabelecimento | nenhuma | É a dependência, e ela já está pronta. allowsReceiving e allowsPurchasing já nasceram como eixos separados, e stateCode já é derivado do IBGE. Nenhum campo novo. |
| Centro de Custo | nenhuma | A vigência de obra já existe no Centro de Custo. O vínculo é de mão única e mora aqui, em defaultCostCenterId. O nó não ganha endereço. |
| Cadastro de Item | acrescenta | O gancho shipToLocationId vira shipToStateCode e entra na chave do ItemSupplier. O que muda preço é frete e tributação, que são por UF — dois pontos na mesma cidade não têm preço diferente, e chavear pelo ponto faria o catálogo crescer sem teto. A chave de busca é o deliveryStateCode já congelado na linha, então resolver preço não consulta o cadastro do ponto. Cascata: UF exata > linha sem destino. Preço por sítio específico — canteiro remoto, ilha — é negociado, e por isso é do Contract, que já é o topo da precedência. |
| Fluxo de Aprovação | acrescenta | Ganha o critério UF de entrega em lista, irmão dos critérios de empresa e estabelecimento. Motivo real: entrega interestadual muda o custo e várias empresas querem que ela passe por quem responde pelo fiscal. Nenhuma entidade nova. |
| Orçamento | nenhuma | Verba é por empresa, centro de custo, conta e período. Local de entrega não é dimensão orçamentária e não deve virar uma — a linha já leva o centro de custo que o ponto sugeriu. |
| Moeda e Câmbio | nenhuma | Sem interseção. A moeda é do documento e do fornecedor. |
| Fornecedor | nenhuma | SupplierAddress continua sendo o endereço dele, com as flags de propósito. Este documento modela o endereço nosso. A simetria termina aí de propósito: o fornecedor tem um endereço com vários papéis; nós temos vários pontos com um papel só. |
| Requisição · Pedido · Recebimento · Nota | a especificar | Nascem já com as sete colunas de snapshot, deliveryLocationId no cabeçalho e por linha, e a validação de destinatário único. É o motivo de esta spec vir antes delas. |
Interface
Telas
Pontos de entrega agrupados por empresa e estabelecimento, com UF, tipo, vigência e estado. Filtro por UF é o primeiro da barra, não o último — é a coluna que a pessoa do fiscal procura. Ponto interestadual leva marca visual com o motivo no tooltip.
Estabelecimento primeiro, tudo depois — a UF dele é o que decide se o bloco de motivo aparece. O endereço começa recolhido com "herda o endereço da filial" escrito e um botão para abrir; o CEP preenche logradouro, bairro e município, e o município traz a UF, sempre bloqueada.
A tela que impede o catálogo de apodrecer. Lista os snapshots eventuais dos últimos 90 dias agrupados por CEP e número, ordenados por contagem de uso, com os documentos ao lado e o botão "cadastrar como ponto de entrega" já preenchido.
Um campo no cabeçalho, agrupado por estabelecimento, com o padrão do usuário pré-selecionado. Um item só e sem permissão de eventual: o campo não aparece. Escolha interestadual abre o motivo ali mesmo, com a hipótese pré-selecionada quando o tipo do ponto a determina.
Coluna de entrega visível só quando a linha diverge do cabeçalho — caso contrário é ruído em cima do caso comum. Trocar o cabeçalho pergunta antes de propagar, e nomeia quantas linhas serão afetadas e quantas ficaram como estão.
O PDF traz o bloco de entrega inteiro: nome, endereço, contato, janela de recebimento e as instruções de acesso. É a única tela desta spec que o fornecedor vê, e é onde ela paga o próprio custo.
API
Endpoints
| Rota | Permissão | Devolve | Erros |
|---|---|---|---|
| GET /delivery-locations | read:delivery-location | PagedResult<DeliveryLocationDto>, filtrável por empresa, estabelecimento, tipo, UF e vigência | 400 |
| GET /delivery-locations/{id} | read:delivery-location | DeliveryLocationDto com o endereço efetivo — o próprio ou o herdado, resolvido no servidor | 404 |
| GET /delivery-locations/lookup | read:delivery-location | O seletor. Só vigentes e ativos do estabelecimento pedido, agrupados, com stateCode para a tela decidir se pede motivo | 400 |
| POST /delivery-locations | manage:delivery-location | DeliveryLocationDto | 400 · 404 · 409 · 422 |
| PUT /delivery-locations/{id} | manage:delivery-location | DeliveryLocationDto. establishmentId e code são recusados quando divergem | 404 · 409 · 422 |
| PATCH /delivery-locations/{id}/activate | manage:delivery-location | Estado só por rota própria, nunca por PUT | 404 · 422 |
| PATCH /delivery-locations/{id}/deactivate | manage:delivery-location | — | 404 · 409 |
| GET /delivery-locations/{id}/blocking-documents | read:delivery-location | O que impede desativar, por tipo e com contagem. A tela pergunta antes de agir, em vez de descobrir no 409 | 404 |
| DELETE /delivery-locations/{id} | manage:delivery-location | Existe só para cadastro errado no mesmo dia. Com documento, é desativação | 404 · 409 |
| GET /delivery-locations/one-time-usage | manage:delivery-location | Endereços eventuais agrupados por CEP e número, com contagem e documentos. Janela padrão de 90 dias | 400 |
| POST /delivery-locations/from-one-time | manage:delivery-location | Cria o cadastro a partir de um snapshot eventual. Não reescreve os documentos passados — eles continuam com o que congelaram | 400 · 409 · 422 |
| POST /delivery-locations:import | manage:delivery-location | Lote, tudo ou nada, com ?dryRun=true obrigatório antes — a mesma mecânica do centro de custo. Cliente com quarenta filiais e canteiros não digita um por um | 400 · 422 |
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ódigo | HTTP | Quando |
|---|---|---|
| DELIVERY_LOCATION_NOT_FOUND | 404 | Não existe neste tenant — ou não está no escopo do usuário. Os dois casos respondem igual, de propósito |
| DELIVERY_LOCATION_CODE_ALREADY_EXISTS | 409 | Código repetido dentro da mesma empresa. A mensagem carrega o código |
| DELIVERY_LOCATION_EXTERNAL_CODE_ALREADY_EXISTS | 409 | Código do ERP repetido na mesma empresa |
| DELIVERY_LOCATION_IN_USE_CONFLICT | 409 | Desativação ou exclusão com documento em aberto entregando ali. A mensagem traz a contagem por tipo de documento |
| DELIVERY_LOCATION_ESTABLISHMENT_IMMUTABLE_CONFLICT | 409 | Tentativa de trocar o destinatário fiscal. A mensagem manda criar outro ponto |
| DELIVERY_LOCATION_ESTABLISHMENT_NOT_RECEIVING_UNPROCESSABLE | 422 | Estabelecimento com allowsReceiving = false |
| DELIVERY_LOCATION_ADDRESS_INCOMPLETE_UNPROCESSABLE | 422 | Bloco de endereço pela metade. Ou tudo, ou nada |
| DELIVERY_LOCATION_INTERSTATE_REASON_REQUIRED_UNPROCESSABLE | 422 | UF do endereço difere da do estabelecimento e nenhum motivo foi declarado. A mensagem sugere o caminho alternativo: cadastrar um estabelecimento na UF de destino |
| DELIVERY_LOCATION_INTERSTATE_REASON_NOT_APPLICABLE_UNPROCESSABLE | 422 | Motivo informado com UF igual. Justificativa que não justifica nada é ruído em relatório de auditoria |
| DELIVERY_LOCATION_INTERSTATE_NOTE_REQUIRED_UNPROCESSABLE | 422 | Motivo Other sem interstateReasonNote |
| DELIVERY_LOCATION_RECIPIENT_NOT_ALLOWED_UNPROCESSABLE | 422 | recipientCnpj com type diferente de ThirdParty |
| DELIVERY_LOCATION_ONE_TIME_RECIPIENT_NOT_ALLOWED_UNPROCESSABLE | 422 | Novo. Destinatário no endereço eventual com interstateReason diferente de DirectToCustomer. A mensagem diz qual é o único motivo que o aceita (RN-DL-13) |
| DELIVERY_LOCATION_RECIPIENT_IS_OWN_ESTABLISHMENT_UNPROCESSABLE | 422 | O CNPJ do recebedor é de um estabelecimento do próprio tenant. A mensagem nomeia o estabelecimento e manda usá-lo |
| DELIVERY_LOCATION_CONSTRUCTION_COST_CENTER_REQUIRED_UNPROCESSABLE | 422 | Canteiro interestadual sem defaultCostCenterId |
| DELIVERY_LOCATION_COST_CENTER_UNPROCESSABLE | 422 | defaultCostCenterId de outra empresa, fora de vigência, ou nó que não aceita lançamento |
| DELIVERY_LOCATION_ESTABLISHMENT_MISMATCH_UNPROCESSABLE | 422 | Linha de documento entregando em ponto de outro estabelecimento. Nomeia a linha |
| DELIVERY_LOCATION_NOT_EFFECTIVE_UNPROCESSABLE | 422 | Ponto fora da vigência na data do documento, ou inativo |
| ONE_TIME_DELIVERY_NOT_PERMITTED_UNPROCESSABLE | 422 | Endereço eventual sem a permissão use:delivery-location-one-time |
| ONE_TIME_DELIVERY_RECIPIENT_NOT_ALLOWED_UNPROCESSABLE | 422 | CNPJ de recebedor num endereço eventual. Entrega a terceiro é decisão de cadastro |
| DELIVERY_LOCATION_CODE_REQUIRED · _TOO_LONG · _NOT_ALPHANUMERIC | 400 | Validações de código, herdadas do padrão do centro de custo |
| DELIVERY_LOCATION_NAME_REQUIRED · _TOO_LONG | 400 | Nome em branco ou acima de 160 |
| DELIVERY_LOCATION_ZIP_CODE_INVALID · _CITY_IBGE_CODE_INVALID | 400 | CEP fora de oito dígitos; código IBGE que não existe na tabela de municípios |
Autorização
Permissões
| Permissão | Alcance | Observação |
|---|---|---|
| read:delivery-location | Listar, ler e usar no documento | Recortada pelo escopo do usuário. Todo papel que emite documento precisa dela |
| manage:delivery-location | Criar, editar, ativar, desativar, importar e promover eventual | Administração de cadastro. Separada de read pelo mesmo motivo de sempre |
| use:delivery-location-one-time | Digitar endereço eventual no documento | Separada de propósito. Não conceder é a forma de exigir disciplina de catálogo — sem parâmetro de configuração e sem código morto |
Invariantes
Regras de negócio
establishmentId é obrigatório e o estabelecimento precisa ter allowsReceiving = true. Não existe ponto de entrega solto no tenant: alguém sempre responde pela nota.
establishmentId e code não mudam depois de criados. Todo documento emitido congelou o primeiro; o segundo é a identidade pela qual as pessoas falam do lugar. Mudança é registro novo.
CEP preenchido obriga logradouro, bairro e município. CEP vazio obriga o bloco inteiro vazio. Endereço pela metade não herda nem sobrescreve — só produz nota rejeitada.
stateCode nunca é digitado e nunca é nuloDerivado do cityIbgeCode quando há endereço próprio, copiado do estabelecimento quando não há. É a dimensão que muda o imposto — não pode divergir do município e não pode faltar.
A invariante central desta spec. stateCode diferente do estabelecimento exige interstateReason da lista fechada; igual, recusa o motivo. Verificada no cadastro, no uso em documento e no endereço eventual — os três caminhos, sempre.
recipientCnpj exige type = ThirdParty, e é recusado quando o CNPJ pertence a um Establishment do próprio tenant. Filial cadastrada como endereço perde IE, Suframa e situação cadastral — e o sistema não deixa acontecer por engano.
type = ConstructionSite com UF divergente exige defaultCostCenterId. Não é regra fiscal, é de conferência: é o caso em que material some entre a nota e a obra.
Todas as linhas entregam em pontos do mesmo establishmentId do documento. Dois CNPJs destinatários são dois documentos, porque são duas notas.
Endereço, UF, destinatário, CNPJ do recebedor e motivo vão para colunas da linha na criação. A exibição do documento nunca lê o cadastro. Regra 1 do mapa de domínio, sem exceção.
Trocar o ponto do cabeçalho pergunta antes, informa quantas linhas serão afetadas e nunca sobrescreve linha alterada à mão.
Vive só no snapshot, com isOneTimeDelivery = true e deliveryLocationId nulo. Não tem código, não entra em lookup e não é reutilizável por busca. Virar cadastro é ato explícito, por /from-one-time.
Nova em 08/09/2026. O snapshot eventual aceita deliveryRecipientTaxId e deliveryRecipientName se e somente se interstateReason = DirectToCustomer; em qualquer outro motivo é 422. Por que era preciso abrir: entrega direta ao cliente final é, por natureza, eventual — o comprador não volta a entregar naquele endereço —, e o único caminho eventual do produto recusava destinatário. O resultado era um valor de enum que não funcionava: DirectToCustomer só servia para pessoa jurídica já cadastrada como ponto de entrega, que é justamente o caso que ele não descreve. Por que a exceção para aqui: o consumidor final costuma ser pessoa física, e CPF em cadastro permanente é dado pessoal que o produto guardaria sem precisar. No snapshot ele é o que sempre foi — um fato congelado daquele documento, que a nota já vai carregar no Grupo G de qualquer forma. O cadastro não muda: ponto de entrega de terceiro continua exigindo type = ThirdParty e CNPJ (RN-DL-06). E o /one-time-usage continua contando: drop ship repetido para o mesmo destinatário é revenda com carteira, e aí vira cadastro.
Exige estabelecimento, deriva stateCode do IBGE e cumpre a RN-DL-05. O que ele dispensa é o cadastro, não o rigor. E não aceita destinatário de terceiro, salvo o caso único da RN-DL-13.
/from-one-time cria o registro daqui para a frente. Os documentos que usaram o endereço continuam com deliveryLocationId nulo e o snapshot que congelaram — porque naquele momento não havia cadastro, e o histórico registra o que aconteceu.
Encerrar o nó de centro de custo não encerra o ponto de entrega, e vice-versa. O que existe é aviso com a lista do que ficou pendurado. Cascata entre agregados diferentes é como se cria exclusão que ninguém pediu.
409 com a contagem por tipo de documento. Documento fechado e histórico não impedem: continuam legíveis com o que congelaram, com marca visual de ponto inativo. Mesma decisão da unidade de medida.
defaultCostCenterId sugere, nunca impõeA linha nasce com aquele centro de custo em 100% e o requisitante pode trocar ou ratear. O sistema propõe, a pessoa responde, o documento congela — a mesma mecânica da conta contábil sugerida pelo item.
Nenhum cliente reimplementa a herança. A API sempre devolve o endereço final e diz de onde ele veio, do mesmo jeito que a política de orçamento devolve de qual nó cada valor foi resolvido.
Aferição
Referência de mercado
| Nexio | SAP | Coupa | Oracle Fusion | Nota |
|---|---|---|---|---|
| Separação ship-to × deliver-to | Werk / Lagerort × endereço de entrega no item | — | Ship-to Location × Deliver-to Location | A Oracle é a mais explícita, e a frase deles resume: "end users care about Deliver-To; suppliers care about Ship-To" |
establishmentId obrigatório no ponto | endereço herda do plant | — | o deliver-to site carrega o ship-to associado | A Oracle recusa a combinação quando o ship-to do local não bate com o do documento. É a nossa RN-DL-08 |
| Propósito do endereço | partner roles | campo purposes | flags billing / shipping / deliver-to site | Três produtos, três formas da mesma ideia. Aqui não precisamos: o registro só existe para receber |
| Endereço eventual | one-time delivery address | — | — | Só o SAP tem. Endereço divergente no pedido gera até uma one-time location na integração com o TM |
| Código e contato no local | ADRC | location-code, attention, active | Location | O attention line da Coupa é o nosso contactName, e existe pelo mesmo motivo prático |
| Grupo fiscal de entrega | localização brasileira | — | — | Nenhum produto internacional modela o Grupo G da NF-e — é onde a localização brasileira sempre entra depois, como adaptação |
| Motivo interestadual declarado | — | — | — | Não existe em nenhum deles. É consequência de uma regra que só o Brasil tem, e é a única invenção desta spec |
Três notas. A primeira: a separação em dois níveis aparece nos três produtos internacionais, com nomes diferentes — é a forma certa, e a nossa vantagem é que o nível fiscal já existia como Establishment antes de esta spec começar. A segunda: o endereço eventual é um caso em que o SAP está sozinho e certo — o caso é real, e negá-lo empurra a informação para o campo de observação. A terceira, e a que importa: a exigência de motivo para entrega interestadual não tem equivalente em nenhum dos quatro, porque nenhum deles nasceu num país onde a UF de destino muda a alíquota. É o mesmo tipo de vantagem estrutural que a spec de Empresa identificou nos dois níveis de CNPJ.
Limites
Fora de escopo
| O que | Por quê |
|---|---|
| Cálculo fiscal — ICMS, DIFAL, ST, FCP, CFOP | O Nexio registra a dimensão e a entrega íntegra a quem calcula. Apurar imposto é do ERP fiscal, e entrar nisso multiplica a superfície de manutenção por 27 legislações estaduais |
| Agendamento de doca com capacidade | receivingHours é texto exibível e requiresScheduling é um aviso. Agenda com janelas, capacidade e reserva é produto próprio — e agenda que ninguém mantém é pior que uma frase certa |
| Roteirização, frete e transportadora | Transportadora é dado do pedido, não do ponto de entrega. Roteirização é TMS |
| Depósito e endereçamento de estoque | O storage location do SAP. Ponto de entrega responde onde chega; depósito responde onde fica. Estoque segue fora de escopo, com ItemType.controlsStock sempre false |
| Endereço fora do Brasil | countryCode existe e é sempre BR. Importação tem local de desembaraço, regime aduaneiro e moeda — é outro documento inteiro |
| ISS e local de prestação de serviço | Gancho declarado. Em serviço o município decide a retenção, e a dimensão não é para onde a mercadoria vai. Quando o módulo de serviços existir, a checagem de UF ganha um irmão municipal |
| Endereço de cobrança | Não existe bill-to separado do lado comprador: a nota vem para o estabelecimento, e o endereço dele já está lá |
| Geolocalização | Coordenadas servem a roteirização e a prova de entrega — as duas fora. Voltam com o módulo que as justificar |
| Ponto de entrega compartilhado entre empresas | Por decisão, não por limitação: o mesmo galpão vira N registros, um por CNPJ destinatário, porque fiscalmente são N operações. Ver § Escopo e visibilidade |