Vai al contenuto principale
Automazione · n8n e Odoo

n8n e Odoo: cosa automatizzare con un flusso e cosa richiede un modulo

n8n va bene per ciò che accade intorno a Odoo; ciò che cambia lo stato del business al suo interno richiede un modulo. Il confine, con il JSON-RPC in prima linea e risposte reali di un laboratorio con Odoo 19 e n8n 2.30.6.

Dashboard di Odoo: dove finiscono i record creati da un flusso di n8n e dove si verifica se sono andati a buon fine

n8n serve per ciò che accade intorno a Odoo: raccogliere un modulo esterno, avvisare una chat, preparare un report giornaliero. Ciò che cambia lo stato di business all'interno (prezzi, prenotazioni di giacenza, convalide di prelievo, riconciliazione dei pagamenti) richiede un modulo. Il confine si vede eseguendo entrambe le cose contro un Odoo vero: questo articolo nasce da un laboratorio interno con Odoo 19.0-20260630 e n8n 2.30.6.

Idea chiave: un flusso che chiama l'API di Odoo non esegue la sua interfaccia. Può ricevere un 200 con una procedura guidata all'interno e credere di aver convalidato un prelievo che resta aperto.

Come parla n8n con Odoo: quattro strade, non una

  • XML-RPC: /xmlrpc/2/common per autenticarsi e /xmlrpc/2/object per execute_kw. Quello della documentazione.
  • JSON-RPC: un POST /jsonrpc con service, method e args. Lo stesso servizio in JSON.
  • Sessione web: /web/session/authenticate e /web/dataset/call_kw con il cookie. Ciò che fa il tuo browser.
  • L'API JSON-2, solo in Odoo 19: POST /json/2/<modelo>/<método> con Authorization: bearer <clave API> e X-Odoo-Database. Senza sessione: gli argomenti vanno come campi con nome, context incluso.

I primi tre continuano a funzionare in 19, ma il server avvisa nel log: «The /xmlrpc, /xmlrpc/2 and /jsonrpc endpoints are deprecated in Odoo 19 and scheduled for removal in Odoo 22». Un flusso nuovo contro un 19 dovrebbe usare /json/2; se serve anche 17 e 18, la sessione.

# Sesion + call_kw: funciona igual en 17, 18 y 19.
POST /web/session/authenticate
 {"params":{"db":"lab","login":"n8n_bot2","password":"..."}}
-> 200 {"result":{"uid":6,"server_version":"19.0-20260630"}}   # + cookie

POST /web/dataset/call_kw          # con esa cookie
 {"params":{"model":"crm.lead","method":"create",
            "args":[{"name":"Lead web","type":"lead"}],
            "kwargs":{"context":{"lang":"es_ES"}}}}
-> 200 {"result": 10}

# Clave API en la cabecera, sin sesion: solo Odoo 19.
POST /json/2/crm.lead/create
Authorization: bearer <clave>     X-Odoo-Database: lab
 {"vals_list":[{"name":"Lead via /json/2 (API key)"}]}
-> 200 [9]

# Sin cabecera -> 401 "User not authenticated, use an API Key with a
#                 Bearer Authorization header."
# Metodo privado -> 403 "Private methods (such as
#                 'crm.lead._compute_display_name') cannot be called remotely." 

Il nodo Odoo di n8n 2.30.6 sceglie per te in base alla credenziale: quella con chiave API usa /json/2; quella classica usa /jsonrpc con execute, posizionale, che non accetta kwargs e non può passare il contesto. La credenziale con chiave API avverte inoltre che richiede «Odoo 19+ and a Custom pricing plan».

Il nodo HTTP Request, così com'è rimasto

Nota dove non c'è la chiave: il corpo contiene solo dati e l'intestazione Authorization la imposta una credenziale di n8n, che viene cifrata e ruotata a parte.

// Lineas del nodo real, copiadas de `n8n export:workflow`.
"type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2,
"url": "http://odoo:8069/json/2/crm.lead/create",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"headerParameters": { "parameters": [
    { "name": "X-Odoo-Database", "value": "lab" } ] },
"jsonBody": "={{ JSON.stringify({ vals_list: [{ name: 'Web: ' + $json.body.empresa, contact_name: $json.body.nombre, email_from: $json.body.email, description: $json.body.mensaje, type: 'lead' }] }) }}",
"credentials": { "httpHeaderAuth": { "name": "Odoo lab - API key (header)" } }
// La clave no esta aqui: la pone esa credencial.

Il flusso non entra nemmeno come amministratore, ma come utente tecnico con i gruppi strettamente necessari e il limite che Odoo 19 impone alla sua chiave:

# Usuario tecnico nuevo (solo grupo de usuario interno):
POST /json/2/crm.lead/create
-> 403 "No puede crear informes 'Lead' (crm.lead) ...
            - Sales/Administrator
            - Sales/User: Own Documents Only"
# Tras anadirle sales_team.group_sale_salesman:  -> 200

# Su clave API tampoco dura lo que uno quiera:
_generate(..., +200 dias) -> "No puede exceder 90.0 dias."
_generate(..., sin fecha) -> "La clave API debe tener una
                              fecha de vencimiento"

Quel limite di 90 giorni è la data in cui il tuo flusso smetterà di funzionare senza che nessuno tocchi nulla. Annotala il giorno in cui crei la chiave.

Cinque automazioni che stanno bene in un flusso

Hanno tutte in comune una cosa: il fatto è già avvenuto e il flusso si limita a spostarlo altrove.

CasoTriggerChiamata a Odoo
Lead da un modulo esternoWebhook di n8ncrm.lead / create
Avviso in chat alla convalida di un prelievoAutomazione di Odoo con azione webhookNessuna: Odoo spinge
Contatti verso uno strumento di marketingPianificato, ogni notteres.partner / search_read per write_date
Allerta di giacenza bassaPianificato, ogni mattinaproduct.product / search_read di qty_available
Report giornaliero alla direzionePianificatoread_group su sale.order

Il secondo caso ha una postilla. L'azione webhook invia _model, _id e il nome dell'azione, ed è «spara e dimentica»: un POST di un secondo, senza nuovo tentativo. In 19 parte dopo il commit; in 17 e 18 avviene dentro la transazione, quindi un n8n lento rallenta l'utente.

# Accion de servidor state='webhook' al crear un lead.
INFO    Webhook call to http://n8n:5678/webhook/odoo-lead
INFO    Webhook call to http://n8n:5678/webhook/odoo-lead - succeeded

# Apuntando a un flujo borrado (el lead se crea igual):
WARNING Webhook call failed: 404 Client Error: Not Found for url:
        http://n8n:5678/webhook/flujo-borrado
WARNING Webhook call timed out after 1s - it may or may not have failed.

Cinque che no, e il motivo esatto

  1. Calcolare prezzi o sconti. Odoo li calcola dal listino; se il flusso li calcola all'esterno, quando cambia il listino ci sono due verità.
  2. Prenotare giacenza per più canali. Odoo risolve la competizione dentro una transazione; un flusso che legge, decide e scrive separatamente vende più del disponibile.
  3. Riconciliare pagamenti. Abbinare e registrare vanno insieme: se uno si fa fuori e l'altro dentro, un'interruzione di rete lascia una riconciliazione a metà.
  4. Ciò che qualcuno dovrà verificare. Lo storico di n8n non è il chatter: «chi ha cambiato questo e perché?» si risponde nel documento.
  5. Ciò che dipende da un campo che cambia tra le versioni. L'abbiamo verificato scrivendo in Odoo 19 ciò che funzionava in 18.

Il caso che lo riassume meglio: convalidare un prelievo da un flusso. Ne abbiamo preparato uno da 10 unità con 4 riservate e abbiamo chiamato button_validate, lo stesso del pulsante.

# WH/OUT/00003: 10 pedidas, 4 reservadas.
POST /web/dataset/call_kw
 {"model":"stock.picking","method":"button_validate","args":[[2]]}
-> 200 {"result": {"name": "¿Crear entrega parcial?",
                   "type": "ir.actions.act_window",
                   "res_model": "stock.backorder.confirmation", ...}}

read -> [{"name":"WH/OUT/00003","state":"assigned"}]   # sigue abierto

HTTP 200, nessun errore, e un prelievo in assigned. Per n8n il passo è andato in verde; per il magazzino, l'ordine non è partito. Un modulo riconosce la procedura guidata e lo lascia scritto nel documento: lo stesso confine di Odoo 19 rispetto ai connettori middleware.

E i campi che cambiano nome tra le versioni, ciò che rompe un flusso stabile dopo una migrazione:

# Odoo 19, escribiendo lo que funcionaba en 17 y 18:
res.users write {"groups_id": [[4, 1]]}
-> "Invalid field 'groups_id' in 'res.users'"     # en 19 se llama group_ids

stock.move create {"name": "Latiguillo", "product_id": 1, ...}
-> "Invalid field 'name' in 'stock.move'"         # en 17 y 18 era obligatorio

Manteniamo lo stesso codice in Odoo 19, 18 e 17 in 105 moduli pubblicati: queste differenze le paga il modulo, non il cliente. È anche il limite di Odoo Studio quando il processo smette di essere un campo e diventa una regola.

I quattro errori che vediamo sempre

  1. Il ciclo. Un'automazione avvisa n8n quando cambia un campo, il flusso scrive in Odoo e ciò che ha scritto la fa scattare di nuovo. Si interrompe con una condizione nel trigger.
  2. La credenziale in chiaro. Né nel corpo del nodo né in un nodo di codice: in una credenziale di n8n, con un utente tecnico dai gruppi minimi.
  3. Senza idempotenza. Un webhook ritentato dall'altra parte crea di nuovo il record: tre POST identici al nostro flusso hanno dato tre lead.
  4. Senza un vero nuovo tentativo. Un nodo non riprova a meno che tu non attivi Retry On Fail, e il motore limita i tentativi a cinque e l'attesa a cinque secondi. Basta per una chat; non per un corriere: il nostro modulo di Correos Express riprova dopo 5, 15, 30, 60, 120 e 240 minuti.

La soluzione non è di n8n, è di progettazione: salva in Odoo il riferimento esterno e controllalo prima di creare. In un modulo è un vincolo di unicità nel database; in un flusso, una lettura preventiva con finestra di competizione.

Quando un flusso diventa un modulo

Il flusso è un modo economico per scoprire se il processo merita di esistere. Il segnale che è diventato insufficiente è uno di questi sei:

  • Scrive un campo che Odoo calcola da solo.
  • Due scritture devono avvenire insieme o non avvenire.
  • Qualcuno verificherà dentro Odoo cosa è successo e quando.
  • Il processo deve sopravvivere alla prossima migrazione.
  • Bisogna poter ripetere la chiamata senza duplicare nulla.
  • L'utente deve poter riprovare dal documento stesso.

A quel punto il flusso non si butta: si porta nel modulo la parte che tocca lo stato di business e n8n resta con il perimetro. Succede lo stesso con l'automazione dei compiti amministrativi e con l'integrazione con Signaturit.

Riepilogo: se il flusso si ferma e l'unica conseguenza è che qualcuno non riceve un avviso, n8n va bene. Se si ferma e il magazzino o il registro delle fatture restano in uno stato che nessuno sa leggere, era un modulo.

Domande frequenti

Posso passare la lingua o l'azienda nella chiamata?

Con le tre strade moderne sì: kwargs.context in call_kw e in execute_kw, e un campo context in /json/2 (un paese esce «Spain» con en_US e «España» con es_ES). Non puoi con la credenziale classica, che chiama execute, posizionale.

Dove salvo la chiave API e quanto dura?

In una credenziale di n8n, mai nel corpo del nodo. In Odoo 19 la chiave di un utente non amministratore richiede una scadenza, e il limite lo fissa il gruppo: 90 giorni per un utente interno.

Il mio flusso si rompe migrando da Odoo 18 a 19?

Dipende dai campi che tocca, e non avvisa: res.users.groups_id è passato a group_ids e stock.move.name, obbligatorio in 17 e 18, non esiste in 19. Prova i tuoi flussi su una copia.

Link utili dentro FlexigoTech

Servizi di ingegneria OdooImplementazione e sviluppo su OdooSviluppo su misuraQuando serve un moduloUn connettore Odoo per il tuo softwarePer partner tecnologiciSoluzioni per esigenzaOrdinato per problema, non per moduloOdoo 19 rispetto ai connettori middlewareLo stesso confine, un livello più suCos'è Odoo Studio e come funzionaL'altro «senza codice» e i suoi limitiAutomatizzare i compiti amministrativiAutomazione dentro l'ERPFirma elettronica in Odoo con SignaturitUn altro caso in cui il flusso non basta

Cosa facciamo su questo

Automazione con OperariaCosa fa, schermate, versioni e prezzo.

Hai flussi di n8n contro Odoo e non sai più quali reggono?

Li esaminiamo, ti diciamo quali restano come sono e quali vanno portati in un modulo, e realizziamo il modulo se serve. Scrivi a comercial@flexigobe.com o chiama il +34 616 809 504.

Parla con un ingegnere