Arquitetura — Workflows MO e MP¶
Atualizado: 2026-07-08 · por: sessão automática · fix Distribuir MO (lookup por código+norm) + humanizador Code2 3 categorias · rev: 3
Visão geral¶
BPEX (orçamento aprovado vira PC)
│ GET webhook (httpMethod default — NÃO setar POST)
▼
n8n.bepex.com.br/webhook/{path}
│
├─ Workflow MP (66DDyQXmXQqyV3Ah) ← Categoria = COMPRA MP
└─ Workflow MO (WwDzIseoYVxbzJZK) ← Categoria = COMPRA MO
Cada workflow termina chamando o BPEX de volta (status de sucesso) pra atualizar o status do PC.
Workflow MP (66DDyQXmXQqyV3Ah)¶
Cadeia de nós (em ordem)¶
- Webhook (GET, path UUID, sem httpMethod explícito)
- Code — define
appkey/appsecretda empresa Omie correta com base nopedido_compra.empresa_id(Onix/Atares/LGR/Dale/BPX) ecomprador(id de comprador no Omie por empresa) - Edit Fields — extrai
body.itens - Acha código do cliente — POST Omie
ConsultarClientepor CNPJ/CPF - Split Out — quebra
body.itensem N items (1 por linha do orçamento BPEX; pode haver duplicatas de descrição MP) - Dedupe descrições MP (Code) — agrega por
omie.mp.descricaoúnico; preservapairedItemoriginal - Acha código do produto — POST Omie
ListarProdutosResumidocomfiltrar_apenas_descricao(batching 1/1.5s pra evitar consumo redundante) - IF Produto encontrado —
total_de_registros > 0? - true → Set Achado (extrai
codigo_produtodo Omie;descricao= a do BEPEX, para o lookup do Distribuir bater) - false → ConsultarProduto por codigo (consulta pelo
omie.mp.codigoBPX)- IF Existe pelo codigo —
codigo_produto > 0(produto já existe no Omie pelo código)? - true (existe) → Alum equivalente? (IF boolean) — é alumínio e as descrições só diferem na posição do token "ALUMINIO"/espaços?
- true → Set Reaproveitado (reusa
codigo_produtodo Omie;descricao= a do BEPEX) — trata como o mesmo produto e segue - false → Stop and Error codigo conflito (conflito real: mesmo código, descrição diferente — NÃO sobrescreve)
- true → Set Reaproveitado (reusa
- false (não existe) → IncluirProduto (cadastra novo no Omie) → Set Criado
- IF Existe pelo codigo —
- Merge Achado/Criado (mode append,
numberInputs: 3— Set Achado / Set Criado / Set Reaproveitado) — junta os ramos, ainda agregado por descrição (6 items deduplicados se PC tem 13 com 7 duplicatas) - Distribuir codigo_produto (Code) — reexpande pros 13 items originais, anexando
_codigo_produto_resolvidovia lookup por descrição. Faz spread completo deit.jsonoriginal. - Edit Fields1 — monta cada item Omie:
nCodProd,nQtde,nValUnit,cObs. Importante: lê de$json.X(do próprio item, vindo do spread no Distribuir), não de$('Split Out').item.X(paired-item quebra após dedupe+expand). - Aggregate — junta os 13 items num array
produtos - adiciona pedido de compra — POST Omie
IncluirPedCompracom cabeçalho, frete eprodutos_incluir - status de sucesso — POST callback ao BPEX atualizando status do PC
Tratamento de erro¶
- Error Trigger (
pflBwIjA8OBpKFHj) é osettings.errorWorkflowdo workflow - O Error Trigger chama Code1 (humaniza mensagem de erro) → HTTP Request (callback BPEX com status "Deu errado: ...")
Workflow MO (WwDzIseoYVxbzJZK)¶
Estrutura similar à de MP, mas com 2 diferenças intencionais:
- Usa projeto — antes de criar o pedido, garante que o
projeto(=pedido_venda.id) existe no Omie. Se não existe, cria via IncluirProjeto comnome = pedido_venda.id. Se não tiver permissão (caso da Dale), falha. - Não tem
omie.mp.codigo— MO usaomie.mo.codigoque é por orçamento (não por matéria-prima base).
Cadeia de nós (em ordem) — atualizada 2026-05-12¶
- Webhook (GET, path UUID, sem httpMethod explícito)
- Code — define
appkey/appsecretda empresa Omie correta +comprador - Edit Fields — extrai
body.itens - Split Out — quebra em N itens (1 por linha do pedido)
- Dedupe descrições MO (Code) — agrega por
omie.mo.descricaoúnico, preservapairedItem. Adicionado 2026-05-12 para evitar consumo redundante com itens de mesma descrição. - Acha código do produto — POST Omie
ListarProdutosResumidocomfiltrar_apenas_descricao(batchSize 2) - Distribuir codigo_produto MO (Code) — re-expande para todos os itens originais de
Split Out, anexando_codigo_produto_resolvido. Adicionado 2026-05-12. Fix 2026-07-08 (Opção B): o lookup é feito por código BPX (list.find(x => x.codigo === bCod)) — não por posição [0]. Ocodigo_produtodo Omie só é reutilizado se (1) o código BPX bate E (2) as descrições são iguais após normalizar espaços e caixa. Qualquer outra divergência →_codigo_produto_resolvido = null→ Code2 reporta mensagem específica ao humano. Faz spread completo deit.json. - Edit Fields1 — monta cada item Omie:
nCodProd($json._codigo_produto_resolvido),nQtde($json.quantidade),nValUnit($json.valor),cObs. Lê de$json.X(spread do Distribuir), não de$('Split Out').item.X. - Aggregate — junta todos os itens num array
produtos - Acha código do fornecedor — POST Omie
ConsultarCliente - Merge — junta ramo de fornecedor com ramo de projeto
- adiciona pedido de compra — POST Omie
IncluirPedCompra - status de sucesso — POST callback ao BPEX
Ramo paralelo de projeto (dispara junto com Edit Fields em step 2):
- Acha código do projeto → IF Projeto encontrado → Set nCodProj (achou) / Upsert Projeto → Set nCodProj (criou) → Merge
Pendência conhecida¶
- Dale Omie: app_key
${OMIE_APP_KEY_DALE}não tem permissão "Cadastro de Projetos / Inclusão" liberada no painel Omie da Dale. Documentado emBACKLOG.mdda pasta-raiz e na memóriaproject_dale_omie_pendencia.
Fluxos auxiliares (callback de erro)¶
Workflow MP/MO falha (qualquer nó)
│
▼
Error Trigger workflow pflBwIjA8OBpKFHj
│
├─ Get an execution (puxa runData via REST)
├─ Code1 (humaniza mensagem de erro)
└─ HTTP Request (POST BPEX → "Deu errado: <mensagem>")
Humanizações conhecidas no Code1:
- tag [cProduto] ou produtos sem código → "Produto(s) não cadastrado(s) no Omie: já existe no Omie com descrição diferente → passa-through (msg do Stop and Error)
- Pedido de compra j(á cadastrado) → "Pedido já cadastrado no omie anteriormente"
- Cannot read properties of undefined (reading 'toLowerCase') → "empresa emissora não definida"
- tag [nValUnit] → "Existe campo valor unitário não preenchido"
- existem registros para a → "Cliente provavelmente não cadastrado no Omie"
- Consumo redundante → "Omie bloqueou por consumo redundante (aguarde alguns minutos e reenvie)"
Pegadinhas críticas (consolidado)¶
- Webhook httpMethod: BPEX manda GET. Default do n8n é GET. NÃO setar
httpMethod: POSTem PATCHes — quebra prod silencioso com 404. Memória:feedback_n8n_webhook_httpmethod. - errorWorkflow ao clonar: clone via REST herda
settings.errorWorkflowdo workflow original. Se você desativar o original, callbacks de erro silenciam. Atualizar pro ID do clone após PATCH. Memória:feedback_n8n_clone_errorworkflow. - pairedItem manual em Code expand:
pairedItem: { item: i }aponta pro INPUT do Code, não pro nó upstream que você imagina. Em Set/Edit Fields downstream, usar$json.X(com spread no Code) e nunca$('NóLonge').item.json.X. Memória:feedback_n8n_paireditem_manual. codigo_produto_integracaoobrigatório na ATARES: oIncluirProdutoprecisa enviar esse campo, senão 500 (SOAP-ENV:Client-103). Solução: passar mesmo valor decodigoBPX. Memória:reference_omie_atares_codigo_produto_integracao.- Retry de execução no n8n usa snapshot antigo: NUNCA validar fix via retry — disparar webhook de novo.
- n8n SQLite tem retenção curta (~20 execs por workflow) — bugs antigos não auditáveis depois.
- Code1/Code2 humanizador: nunca usar
||chain misturandomessageedescription:error.messagedo n8n é sempre"The service was not able to process your request"para erros HTTP — truthy, então o||nunca chega emerror.description(onde o Omie coloca o faultstring). Padrão correto:const errorRaw = ..message..; const errorDesc = ..description..; const errorMessage = errorRaw + ' ' + errorDesc;. Descoberto 2026-05-12. - Dedupe é obrigatório antes de qualquer nó que chama Omie por descrição quando há itens duplicados: sem dedupe, itens com mesma
omie.mo.descricao/omie.mp.descricaogeram chamadas idênticas e o Omie retornaSOAP-ENV:Client-6consumo redundante. O par Dedupe+Distribuir é o padrão para resolver isso (ver MP e MO). Descoberto 2026-05-12 (exec 427584, MO, 9 itens com 5 duplicatas de BPX68231137-MO). Validado em prod 2026-05-12 com PC real 1816 (exec 430068: 9 itens → Dedupe=5 → Acha produto 5× → Distribuir=9 → status sucesso ✅). Exec sintético 430061 (3→2→3) também ok. - Code node usa
empresa.nome, nãoempresa_id: seleção do tenant Omie lêbody.pedido_compra.empresa.nome.toLowerCase()com includes-match. Seempresanão existir no body, explode com "Cannot read properties of undefined (reading 'nome')" → humanizado como "empresa emissora não definida". Appkeys corretos estão emreferencia-tecnica.md. - Set Achado DEVE exportar descricao com chave do BEPEX, não do Omie (MP): o
Distribuir codigo_produtofaz lookup poromie.mp.descricao(BEPEX). SeSet Achadoexportarproduto_servico_resumido[0].descricao(descrição do Omie), o lookup falha quando as descrições divergem →_codigo_produto_resolvido = null→ erro "tag [cProduto]". Padrão correto:Set AchadoeSet Criadoambos usam$('Dedupe descrições MP').itemMatching($itemIndex).json.omie.mp.descricao. Descoberto 2026-05-14, causava ~70% de falhas. Validado em prod 2026-05-15 (execs 440809, 440840, 440901 — todos sucesso ✅). - Equivalência de alumínio no ramo de conflito (MP) — quando o produto é achado pelo código mas o
ListarProdutosResumidopor descrição falhou (0 registros), o nóAlum equivalente?decide se é conflito real ou só divergência de formato do alumínio. Regra: só para alumínio (uma das descrições contém "ALUMINIO") e só quando, removido o token "ALUMINIO" e normalizados espaços, as descrições batem — aí reusa ocodigo_produtodo Omie viaSet Reaproveitadoem vez de parar. Motivação: o Omie tem produtos de alumínio cadastrados como"ALUMINIO <dims>"(via SYNC) enquanto o BEPEX manda"<dims> ALUMINIO". É medida temporária até a base de alumínio no Omie ser normalizada; qualquer outra divergência (dimensão, separador decimal, não-alumínio) continua noStop and Error. Cronologia e validação (exec 633727, Omie PC 3911) nobugs-fix-log.md(2026-07-07).