Pentru a emite facturi din n8n în Oblio fără duplicate, ai nevoie de o cheie stabilă a operațiunii, o rezervare atomică în baza de date și o procedură pentru răspunsurile incerte. Un pas „caută factura, apoi creeaz-o” nu rezolvă două execuții simultane. Iar un timeout nu înseamnă că factura nu a fost emisă.
Rețeta de mai jos separă pregătirea datelor, emiterea și reconcilierea. Este un ghid de configurare manuală, nu un workflow JSON gata de importat: autentificarea, sursa comenzilor, regulile fiscale și câmpurile firmei trebuie configurate explicit. Primele verificări se fac cu date fictive și un endpoint local simulat, fără emiterea unor documente financiare reale.
Stabilește ce anume declanșează o factură
Alege un singur eveniment de business: de exemplu, o comandă aprobată pentru facturare în sistemul tău. Nu factura orice notificare de plată primită. Același furnizor poate trimite de mai multe ori o notificare, iar o comandă poate avea plăți parțiale sau rambursări.
Sursa trebuie să păstreze evenimentul până când prelucrarea este confirmată. Pentru un magazin, cel mai simplu este adesea un registru de comenzi pregătite pentru facturare, citit periodic de n8n. Pentru un webhook, verifică autentificarea sau semnătura furnizorului și salvează întâi mesajul. Nu confirma primirea dacă nu ai reușit să îl înregistrezi durabil.
Cheia internă poate fi firma-demo:comanda-1042:factura-v1. Ea identifică o intenție financiară, nu o execuție n8n. Nu folosi ora curentă, un UUID nou la fiecare încercare sau ID-ul execuției drept cheie de deduplicare. Include societatea și tipul operațiunii, ca să nu amesteci comenzile mai multor firme.
Validează în amonte clientul, moneda, produsele, cantitățile, prețurile și tratamentul TVA. Ia cotele aplicabile din configurația aprobată și nomenclatoare; nu copia o cotă numerică dintr-un exemplu vechi. Pentru calcule financiare, folosește zecimale controlate și regulile de rotunjire ale sistemului contabil, nu aritmetică improvizată cu numere în virgulă mobilă.
Salvează payloadul final aprobat, serializat în mod stabil, și hash-ul lui SHA-256. Același eveniment cu alt hash este un conflict de date și trebuie oprit. Schimbarea adresei sau a prețului în sursă nu trebuie să transforme o reîncercare într-o a doua factură.
Rezervă emiterea printr-o operațiune atomică
Păstrează registrul într-o bază de date dedicată integrării, nu în tabelele interne n8n. O constrângere unică este cea care decide cine poate continua când două execuții ajung în același timp, folosind comportamentul INSERT ... ON CONFLICT din PostgreSQL. Tabelul minimal de mai jos păstrează atât intenția, cât și identificatorii documentului confirmat.
CREATE TABLE invoice_jobs (
event_key text PRIMARY KEY,
payload_sha256 text NOT NULL,
payload jsonb NOT NULL,
state text NOT NULL CHECK (
state IN ('sending', 'confirmed', 'unknown', 'rejected')
),
series_name text,
invoice_number text,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
În nodul Postgres, operațiunea Execute Query, folosește parametri, fără concatenarea datelor în SQL. Documentația nodului descrie substituenții $1, $2 și opțiunea Query Parameters. Parametrii următori sunt cheia, hash-ul și payloadul JSON serializat.
WITH claimed AS (
INSERT INTO invoice_jobs (
event_key, payload_sha256, payload, state
) VALUES ($1, $2, $3::jsonb, 'sending')
ON CONFLICT (event_key) DO NOTHING
RETURNING event_key
)
SELECT EXISTS (SELECT 1 FROM claimed) AS acquired;
Un singur apel primește acquired = true și ajunge la emitere. Celălalt citește rândul existent într-o interogare separată: dacă hash-ul diferă, trimite conflictul la revizuire; dacă starea este confirmed, returnează referința deja salvată; dacă este sending sau unknown, nu emite nimic.
Starea sending se salvează înaintea cererii externe. Dacă procesul moare exact după rezervare, rândul poate rămâne blocat deși nu a plecat nicio cerere. Acesta este un compromis deliberat în favoarea evitării dublurilor. Un verificator mută rândurile prea vechi în unknown, după un prag mai mare decât durata maximă a execuției; nu le reactivează automat pentru emitere.
Nu ține o tranzacție SQL deschisă cât aștepți răspunsul Oblio. O blocare lungă nu rezolvă lipsa unei tranzacții comune între cele două sisteme. Registrul descrie explicit incertitudinea și păstrează dovada necesară pentru a o rezolva.
Configurează fluxul n8n, nod cu nod
La 1 noiembrie 2026, documentația oficială Oblio include parametrul de corp idempotencyKey pentru crearea documentelor. Trimite aceeași cheie stabilă la fiecare încercare aprobată. Documentația publică nu explică suficient durata memorării cheii sau toate cazurile concurente; păstrează și registrul local.
1. Citește și pregătește comenzile
Adaugă Schedule Trigger, de exemplu la cinci minute, și setează fusul workflowului la Europe/Bucharest. Publică workflowul după verificare: nodul de programare folosește fusul configurat. Citește un lot mic din sursa durabilă, normalizează datele și execută rezervarea de mai sus pentru fiecare intenție.
Adaugă un nod IF care verifică booleanul acquired. Ramura adevărată continuă; cea falsă citește starea existentă și aplică regulile registrului. Nu folosi textul „false” ca boolean și nu activa un comportament care continuă emiterea după o eroare SQL.
2. Obține tokenul fără secrete în export
Oblio documentează POST https://www.oblio.eu/api/authorize/token, cu client_id și client_secret în formular; răspunsul oferă tokenul Bearer. Primul este e-mailul contului, al doilea se preia din setările contului, conform documentației API.
În HTTP Request, alege Generic Credential Type, Custom Auth. Salvează în credențial JSON-ul de mai jos, înlocuind valorile fictive în interfața credențialelor. Activează Send Body și selectează Form URLencoded pentru cererea de token. Custom Auth permite injectarea câmpurilor în corp.
{
"body": {
"client_id": "cont@example.invalid",
"client_secret": "INLOCUIESTE_IN_CREDENTIAL"
}
}
Verifică structura răspunsului înainte să continui. În următorul nod HTTP, trimite antetul Authorization cu valoarea construită din Bearer și access_token al nodului de token. Nu salva tokenul în tabelul facturilor. Restrânge accesul la editor, istoricul execuțiilor și loguri: mascarea credențialului nu garantează că tokenul din răspuns este ascuns peste tot.
3. Trimite o singură cerere de emitere
Configurează POST https://www.oblio.eu/api/docs/invoice, corp JSON din payloadul aprobat, completat cu idempotencyKey. Firma, seria, clientul și produsele se mapează după documentația Oblio. Adaugă în orderNumber referința comenzii pentru reconciliere. Păstrează sendEmail dezactivat în prima etapă.
În HTTP Request, activează Include Response Headers and Status, alege răspuns JSON și lasă Retry On Fail dezactivat pentru emitere. Dacă folosești Never Error pentru a inspecta răspunsurile HTTP nereușite, adaugă imediat clasificarea explicită a statutului și corpului; opțiunea nu înseamnă succes financiar.
Respectă limita documentată de Oblio pentru generarea documentelor: 30 de cereri în 100 de secunde. Pune o limitare comună tuturor fluxurilor care folosesc contul, cu marjă, nu câte un contor independent în fiecare execuție.
4. Salvează rezultatul înaintea notificărilor
Confirmă reușita numai dacă răspunsul are statutul așteptat și identificatorii documentului: seriesName și number. Scrie-i în registru, împreună cu starea confirmed, înainte să actualizezi comanda sau să trimiți o notificare.
Tratează trimiterea e-mailului separat. Dacă notificarea eșuează, reiei notificarea, nu emiterea facturii. Pentru fiecare efect ulterior păstrează propria stare. Nici factura creată în Oblio, nici e-mailul trimis nu dovedesc singure acceptarea ei în RO e-Factura; urmărirea transmisiei fiscale este un proces separat.
Timeoutul intră la reconciliere, nu la reîncercare oarbă
Un timeout, o conexiune întreruptă, un răspuns imposibil de interpretat sau eșecul salvării rezultatului după emitere lasă rezultatul necunoscut. Marchează unknown. Dacă nu mai poți scrie în bază, rândul rămâne sending și este preluat ulterior de verificator. Niciuna dintre stări nu permite automat un nou POST.
Configurează un error workflow cu Error Trigger care anunță responsabilul și include cheia internă, execuția și categoria erorii. Exclude tokenuri și date personale din alertă. Nu folosi butonul de reluare a execuției ca substitut pentru procedura de reconciliere.
| Situație | Acțiune |
|---|---|
| Factura este confirmată în registru | Reutilizează referința; nu emite din nou. |
| Timeout sau eroare după trimitere | Blochează emiterea și caută documentul în Oblio. |
| Document găsit și verificat | Completează registrul cu seria și numărul. |
| Niciun rezultat sau mai multe potriviri | Păstrează blocarea și cere verificare umană. |
| Date respinse explicit înaintea emiterii | Corectează și aprobă reluarea; păstrează istoricul. |
La reconciliere, compară societatea, referința comenzii, clientul, moneda, totalul și liniile. Lista documentelor sau interfața Oblio ajută la investigație; o primă căutare fără rezultate nu dovedește că operațiunea nu a fost executată. Nu inventa un endpoint de căutare după idempotencyKey și nu presupune că poți deduce numărul facturii.
O reluare se aprobă numai după clarificarea rezultatului, cu aceeași intenție și aceeași cheie, conform comportamentului confirmat cu furnizorul. Corecțiile fiscale și stornările au fluxuri distincte; nu șterge automat documente pentru a „repara” un retry.
Verifică scenariile dificile înainte de activare
Înlocuiește nodul de emitere cu un endpoint local de simulare, fără token Oblio. Folosește clientul Client Exemplu, comanda fictivă demo-1042 și produse de test. Endpointul simulează răspunsul, nu produce facturi. Verifică cel puțin aceste cazuri:
- Două execuții simultane cu aceeași cheie: numai una trebuie să ajungă la endpoint.
- Aceeași cheie cu payload modificat: fluxul trebuie să ceară revizuire.
- Endpointul înregistrează cererea, apoi întârzie până la timeout: starea devine necunoscută, fără al doilea POST.
- Răspuns reușit, dar baza de date indisponibilă: nicio reemitere automată după repornire.
- Notificarea eșuează după confirmare: se reia numai notificarea.
Abia după aceste verificări configurează accesul real și validează integrarea prin citiri permise. Orice probă care emite documente trebuie convenită cu responsabilul financiar și furnizorul; nu presupune existența unui sandbox doar pentru că ai folosit date fictive.
Păstrează backupuri ale registrului și configurației și verifică restaurarea lor. Înainte să restaurezi un registru mai vechi, pune pe pauză toți producătorii și toate fluxurile care emit facturi. Documentele emise după momentul backupului pot exista în Oblio fără ca cheile lor să mai apară în registrul restaurat. Reconciliază întregul interval de la backup până la oprirea emiterii, reconstruiește referințele și cheile lipsă din sursa comenzilor și documentele confirmate, apoi reia procesarea numai după verificare. Nu retrimite automat comenzile care par „neprocesate” în copia veche și nu te baza pe durata nedocumentată a cheilor Oblio. Păstrează blocate pentru revizuire cazurile pe care nu le poți reconcilia. Poți folosi ghidul de configurare n8n, comparația dintre administrare și găzduire proprie și recomandările de securitate S3 pentru infrastructura din jurul fluxului.
Întrebări frecvente
Este suficient idempotencyKey?
Este o protecție documentată și merită folosită, dar nu înlocuiește registrul, verificarea payloadului și reconcilierea. Nu atribui cheii garanții de durată sau comportament pe care furnizorul nu le-a precizat.
Pot folosi un tabel Excel pentru deduplicare?
Un tabel este util pentru urmărire umană, însă o citire urmată de o scriere nu oferă rezervarea atomică necesară execuțiilor simultane. Păstrează decizia de emitere într-un sistem cu unicitate impusă.
Pot activa Retry On Fail pentru tot workflowul?
Nu pentru scrieri financiare fără analizarea rezultatului. Separă citirile repetabile de emitere și aplică politica potrivită fiecărui pas. Un restart trebuie să consulte registrul înaintea oricărei scrieri.
Găzduirea administrată rezolvă duplicatele?
Administrarea infrastructurii nu înlocuiește regulile fluxului. Poți consulta n8n administrat DanubeData și stocarea S3 pentru automatizări și copiile lor, păstrând validarea financiară și reconcilierea în responsabilitatea integrării.
Documentație de referință
- Oblio: API, autentificare, documente și nomenclatoare
- n8n: HTTP Request și credențiale HTTP
- n8n: Postgres și parametri SQL
- PostgreSQL: INSERT, unicitate și ON CONFLICT
- n8n: tratarea erorilor și Schedule Trigger