Deze fouten betekenen hetzelfde: je import wijst naar een record dat nog niet in de database bestaat. Voeg een id-kolom toe aan elk bestand, refereer aan ouders met het /id-achtervoegsel, importeer ouders vóór kinderen, en de fouten verdwijnen.
Je exporteert een schone CSV, koppelt de kolommen, klikt op Importeren, en Odoo gooit het terug: "No matching record found for external id" op één kolom, of "No such external ID currently defined" op een hele batch rijen. Je wijzigt een waarde, voert opnieuw uit, en nu heb je dubbele contacten of dubbele producten omdat de tweede import de eerste niet herkende.
Dit is de meest voorkomende muur waar mensen tegenaan lopen als ze data naar Odoo verhuizen. Het is geen bug en je CSV is niet kapot. Het is Odoo dat je vertelt dat een koppeling wijst naar een record dat het nog niet kan vinden. Zodra je begrijpt wat een externe ID is en in welke volgorde Odoo je bestanden leest, worden de fouten voorspelbaar en is de oplossing mechanisch.
En omdat de oplossing mechanisch is, is het ook werk dat je niet meer met de hand hoeft te doen. Tegenwoordig bouw ik mijn importbestanden met een AI-assistent (Claude Cowork, in mijn geval) en laat ik die de kolommen en externe ID's matchen tegen Odoo voordat er ook maar iets de database raakt. Dat werkt alleen als je de onderstaande regels goed genoeg begrijpt om te controleren wat hij heeft gebouwd, dus lees verder.
Waarom het gebeurt
Elk record in Odoo heeft twee identificatoren. Er is de interne database-id (een getal zoals 4271) en er is de externe ID, ook wel externe identificator of XML-ID genoemd, een tekstlabel zoals __import__.customer_acme of base.nl. De externe ID is de stabiele, mensvriendelijke handgreep die exports, herimporten en verhuizingen tussen databases overleeft. Het interne getal overleeft een verhuizing niet, de externe ID wel.
De fouten komen uit drie oorzaken voort, en bijna elke mislukte import is er één van.
1. Importvolgorde. Een many2one-veld is een koppeling naar een ander record. Een verkooporder koppelt aan een klant, een product koppelt aan een categorie, een contact koppelt aan een land. Als je de order importeert voordat de klant bestaat, zoekt Odoo de externe ID op in de koppelkolom, vindt niets, en meldt "No matching record found for external id". Het gerefereerde record moet bestaan voordat het record dat ernaar verwijst wordt geïmporteerd.
2. Verkeerde referentie voor een many2one-veld. Om een koppeling te vullen via externe ID moet de kolomkop eindigen op /id. Dus je importeert customer_id/id, niet customer_id. De gewone kolom verwacht een weergavenaam en probeert een naamzoekopdracht, de /id-kolom verwacht een externe ID en doet een exacte match. Verwissel je deze, dan vindt Odoo óf geen match óf maakt het stilletjes de verkeerde koppeling aan.
3. Een externe ID hergebruiken die nooit is weggeschreven. "No such external ID currently defined" betekent dat een rij verwijst naar een externe ID die helemaal niet in de database staat. Meestal is het gerefereerde bestand halverwege mislukt, of zit er een typefout in de externe ID in de koppelkolom, of is hij gedefinieerd in een ander bestand dat je nog niet hebt geïmporteerd.
De oplossing, in genummerde stappen
Geef elk record zijn eigen kolom met externe ID
Voeg in elk bestand een kolom toe met de naam id (letterlijk id, de meest linkse kolom). Zet daar voor elke rij een uniek tekstlabel in, bijvoorbeeld prod_chair_oak voor een product of partner_acme_nl voor een contact. Dit is de externe ID die Odoo opslaat. Kies een duidelijk, consistent naamschema en hergebruik nooit een label voor twee verschillende records. Deze ene kolom maakt dat de hele import veilig herhaalbaar is.
Refereer aan ouders via hun externe ID, met /id
Wijs in een kindbestand naar de ouder via de externe ID van de ouder. Noem de koppelkolom met het achtervoegsel /id en vul hem met het label dat je in het ouderbestand hebt gebruikt. Een productregel die bij categorie cat_furniture hoort, krijgt een kolom categ_id/id met de waarde cat_furniture. Een contact in Nederland krijgt country_id/id met base.nl (een externe land-ID die al met Odoo wordt meegeleverd). Het achtervoegsel /id vertelt Odoo "dit is een externe ID, match hem exact", niet "zoek een record met deze naam".
Importeer in afhankelijkheidsvolgorde, ouders eerst
Sorteer je bestanden zo dat alles waarnaar wordt verwezen wordt geïmporteerd vóór het bestand dat ernaar verwijst. Een typische retail- of groothandelsorder is:
- Landen en valuta (meestal al in Odoo, sla over als dat zo is)
- Productcategorieën
- Producten
- Contacten en bedrijven
- Verkooporders en orderregels
Elke stap mag veilig /id-referenties gebruiken naar alles erboven, omdat die records nu hun externe ID's dragen. Importeer van boven naar beneden en de "No matching record"-fouten verdwijnen.
Importeer hetzelfde bestand opnieuw om bij te werken, niet te dupliceren
Omdat elke rij zijn eigen externe ID in de id-kolom draagt, maakt een tweede import van hetzelfde bestand geen nieuwe records aan. Odoo matcht op de externe ID en werkt het bestaande record ter plekke bij. Dit is een upsert: invoegen als het nieuw is, bijwerken als de externe ID al bestaat. Corrigeer een prijs, herstel een typefout, voeg een kolom toe, importeer exact hetzelfde bestand opnieuw, en je data is gecorrigeerd zonder duplicaten. Dit werkt alleen als de id-kolom aanwezig is en de labels identiek zijn aan de eerste keer.
Lees het rijnummer in de fout, herstel, voer opnieuw uit
Wanneer een import mislukt, noemt Odoo de rij en de kolom. Open die rij, controleer of de gerefereerde externe ID daadwerkelijk bestaat (zoek hem op onder Instellingen, met ontwikkelaarsmodus aan, in Externe identificatoren), herstel het label of importeer eerst het ontbrekende ouderbestand, en voer daarna opnieuw uit. Omdat de import idempotent is, is opnieuw uitvoeren gratis.
Het stuk waar mensen over struikelen
Een paar dingen overkomen vrijwel iedereen
De id-kolom is niet de name-kolom. Mensen zetten de productnaam in de id-kolom. Namen bevatten spaties, accenten en duplicaten, dus ze zijn waardeloos als externe ID's. Gebruik een korte slug zonder spaties, zoals prod_chair_oak, en houd de menselijke naam in de name-kolom waar hij thuishoort.
/id versus .id zijn verschillende dingen. customer_id/id matcht op de externe ID (het tekstlabel). customer_id.id matcht op de ruwe database-id (het getal). Het getal overleeft een verhuizing tussen databases niet, dus gebruik bij voorkeur /id met externe ID's voor alles wat je opnieuw wilt importeren of verplaatsen.
Ingebouwde records hebben al externe ID's. Landen, valuta, het hoofdbedrijf, standaardbelastingen: die worden met Odoo meegeleverd en hebben stabiele externe ID's zoals base.nl of base.main_company. Je importeert ze niet, je refereert eraan. Zet de ontwikkelaarsmodus aan en zoek ze op onder Instellingen > Technisch > Externe identificatoren, zodat je het echte label gebruikt in plaats van te gokken.
Een half afgemaakte import laat wezen achter. Als een ouderbestand fout gaat bij rij 300 van 1000, bestaan de eerste 299 records en de rest niet. Een kindbestand dat naar de ontbrekende 701 verwijst mislukt dan met "No such external ID". Bevestig altijd dat de ouderimport netjes is afgerond voordat je aan het kind begint.
Eén mechanisme per veld. Odoo biedt drie manieren om een relationeel veld te vullen: op naam, op externe ID (/id), of op database-id (.id). Gebruik er één per kolom. Twee kolommen die op hetzelfde veld mikken, werken elkaar tegen.
Snelle checklist
- Elk bestand heeft een meest linkse
id-kolom met een uniek label zonder spaties per rij. - Many2one-koppelingen gebruiken het achtervoegsel
/iden wijzen naar de externe ID van een ouder. - Bestanden worden geïmporteerd: eerst de ouders, als laatste de kinderen.
- Ingebouwde records (landen, valuta, bedrijf) worden gerefereerd, niet geïmporteerd.
- Hetzelfde bestand opnieuw importeren werkt het record bij in plaats van het te dupliceren.
- Na een fout heb je de genoemde rij gecontroleerd en bevestigd dat de gerefereerde externe ID bestaat.
FAQ
Wat betekent "No matching record found for external id" in Odoo?
Het betekent dat een kolom in je import wijst naar een externe ID die nog niet in de database bestaat. Het gerefereerde record (de klant, de categorie, het land) is niet geïmporteerd, dus Odoo kan er niet aan koppelen. Importeer eerst de gerefereerde records en voer daarna het bestand dat ernaar verwijst opnieuw uit.
Wat is het verschil tussen de externe ID en de database-id in Odoo?
De database-id is een intern getal dat verandert wanneer je data tussen databases verplaatst. De externe ID is een stabiel tekstlabel (zoals base.nl of __import__.prod_chair_oak) dat hetzelfde blijft bij exports en herimporten. Gebruik externe ID's als matchsleutel voor alles wat je importeert of migreert.
Hoe importeer ik een many2one-veld op externe ID?
Noem de kolom met een /id-achtervoegsel, bijvoorbeeld category_id/id, en zet de externe ID van de ouder als waarde. De /id vertelt Odoo om exact op de externe ID te matchen in plaats van op naam te zoeken.
Hoe werk ik bestaande records in Odoo bij zonder duplicaten te maken?
Neem een id-kolom op met de externe ID van elk record en importeer hetzelfde bestand opnieuw. Odoo matcht op de externe ID en werkt het bestaande record bij in plaats van een nieuw aan te maken. Hiermee wordt de import een idempotente upsert: veilig om keer op keer uit te voeren.
In welke volgorde moet ik gerelateerde bestanden in Odoo importeren?
Ouders vóór kinderen. Importeer landen en valuta (of refereer aan de ingebouwde), dan categorieën, dan producten, dan contacten, dan orders. Alles waarnaar een /id-kolom verwijst, moet bestaan voordat het bestand dat ernaar verwijst draait.