0009 — Cliente do transform vem da aba Faturamento (invoice), não do Integrador (company)¶
Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-08-11 · Decidido em: 2026-08-11
Contexto¶
Os pedidos entravam no X-Adm com o cliente errado. O TransformService montava a identidade do
cliente (propriedades/fones/contratos) 100% de data.company — a aba Integrador da PIED, que
é a revenda/instaladora (CNPJ, ex. ARGON SOLAR LTDA). O X-Adm precisa do cliente final faturado
— a aba Faturamento = data.invoice (tipicamente pessoa física/CPF, ex. IVO ANZINI). Bug reportado
pela Maxsul (Felype, 2026-08-10), confirmado no pedido #260068273 e no dump json.json (243 pedidos
REST únicos: 63 CNPJ / 180 CPF em invoice).
A raiz é dupla:
- o transform lê a aba errada (
companyem vez deinvoice); invoiceefreightsó existem no REST — o webhook de pedido é um subconjunto (0/150 webhooks trazeminvoice/freight). OupsertPedidogravavaorder.toString()inteiro nopied_pedido.payload, então umorder.updatedde webhook chegando depois de um poll REST apagavainvoice/freight. Sem corrigir (2), corrigir (1) não se sustenta.
Decisão¶
Identidade do cliente = data.invoice sempre. company deixa de alimentar propriedades/fones/
contratos; segue alimentando só o catálogo pied_cliente no normalizado (keado pelo doc do
company).
- Documento (
CgcCpf/CgcCpfCliente) =digitos(invoice.cnpj || invoice.cpf). Opied_pedido.documento_clientepassa a ser o doc doinvoice; opied_clientesegue keado pelo doc docompany(dois documentos distintos noupsertPedido). - Nome/Fantasia/Contato =
invoice.razaoSocial/invoice.nomeFantasia/invoice.telephone/invoice.email(oinvoicenão temmainContact— o nome do contato é a razão social). - InscEst =
invoice.ieinline (dropa o lookup nopied_clientepara o cliente). - Endereço = entrega quando divergir, senão faturamento, com discriminador robusto CIF + completude (não diff cru):
fonte = freight.address SE (freight.type == "CIF" E freight.address completo)
senão invoice.address
completo := CEP != null E patio != null E number != null
No FOB o freight.address traz só city/state (incompleto) → cai no faturamento. Dump: FOB 0/12
completo → faturamento; CIF 230/231 completo → entrega. Endereço único (CodProp " 0") — não se
manda faturamento+entrega em dois CodProp (evita travar a ordem de fabricação do X-Adm).
- UF fiscal do NatOp = invoice.address.state (nota). Pode divergir de propriedades.Estado
(entrega) num CIF interestadual — divergência intencional.
- Merge no upsert: NormalizadorService.upsertPedido lê o pied_pedido.payload existente e mescla
o pedido novo sobre ele no nível de topo (setAll só das chaves que a fonte nova traz não-nulas;
ausentes são preservadas). Corrige o cliente e o bug latente do frete. Vale para os 3 callers
(normalizarCronologico, normalizarPedidosElegiveis, renormalizar).
- Guarda de envio: pedido sem invoice (só-webhook) não é enviado — PushService.enviarPedido
devolve PULADO (estado transitório até o REST preencher), sem marcar ERRO.
Alternativas preteridas¶
- Manter
companycomo cliente — é a revenda, não o comprador faturado (o bug). - Discriminar endereço por
type=='FOB'na unha — a completude dofreight.addressé mais robusta (cobre o CIF anômalo com endereço incompleto). - Coluna
invoicededicada no schema — não corrige o frete; o merge shallow já resolve sem migração. - Deep-merge do payload — as chaves em jogo (
invoice/freight/payment) são de topo; shallow basta.
Consequências¶
- Sem tabela nova, sem migração. Tudo deriva do raw
pied_pedido.payload(REST). - Mudança de identidade canônica (o doc do cliente passa a ser do
invoice): entra com reset da base do zero, sem backfill/re-envio dos pedidos existentes (ver reset). - Tradeoff do merge (read-modify-write): o
upsertPedidodeixa de ser função pura do input — passa a depender do estado no banco; uma chave de topo escrita não é apagada por um snapshot posterior que a dropa (intencional — nunca perderinvoice/freight). A ordem cronológica global + merge converge. invoice.iepresente em só 59/243 no dump — os demais gravamInscEstem branco (coerente cominvoice.temInscricaoEstadual == "nao").
Em aberto¶
- UF fiscal do
NatOpno CIF interestadual: defaultinvoice.address.state; confirmar com a equipe X-Adm se não deveria serfreight.address.state(reversão = 1 linha emnatOpCompl). - Deprecar
pied_cliente/ pollcompanies? Perdem parte do propósito (InscEst agora inline); decisão adiada.