Maak van deze leveranciersfactuur de nieuwe artikelen aan in de
boekhouding, met leveranciersartikel en verkoopprijs, en laat me eerst
zien wat je gaat doen.
Een koppeling die leest uit een boekhoudpakket bouw je in een middag. Lijstjes ophalen, in je eigen database zetten, klaar. Een koppeling die schrijft is iets anders. Dan ontdek je dat een veld dat in de documentatie staat gewoon genegeerd wordt, dat een ander veld een intern nummer wil in plaats van de code die je overal elders gebruikt, en dat je bij het vijftigste artikel tegen een limiet loopt die je bij het testen met drie artikelen nooit zag.
Wij bouwden voor een klant een tool die een leveranciersfactuur inleest en de nieuwe artikelen in Exact Online aanmaakt. De klant blijft anoniem en er staan hier geen prijzen, inkoopbedragen of marges. Dit gaat over de API. Het verhaal over de kant die alleen leest, staat in je boekhouding bevragen vanuit Claude.
1. De verkoopprijs die bij aanmaken verdwijnt
Je maakt een artikel aan en je zet er netjes StandardSalesPrice in. De API antwoordt met een 201 en het artikel bestaat. Je kijkt in de boekhouding en de verkoopprijs is nul.
Wat er gebeurt: bij het aanmaken wordt dat veld genegeerd en maakt Exact zelf een prijsregel aan met waarde nul. De prijs zet je daarna met een aparte PUT op logistics/SalesItemPrices. Twee aanroepen dus, niet één, en nergens een foutmelding die je dat vertelt.
Dit is het soort fout waar je uren aan kunt besteden, omdat alles klopt behalve de uitkomst. De les die we eruit meenamen: na elke schrijfactie terugvragen wat er nu echt staat. Niet de 201 vertrouwen, maar de gegevens.
2. Een leveranciersartikel wil een intern nummer
Een leveranciersartikel vastleggen lijkt eenvoudig: leverancier, artikelcode van de leverancier, prijs, eenheid. Alleen wil het veld voor de eenheid niet de code die je in de interface ziet staan, maar het interne nummer van die eenheid. Dat nummer zoek je op in logistics/Units, en dan koppel je het als ItemUnit.
Dat is een patroon dat je vaker ziet in deze API: de codes die mensen gebruiken en de verwijzingen die de API wil, zijn niet hetzelfde. Reken erop dat je een paar vertaaltabellen in je eigen database moet bijhouden, en haal die één keer per nacht op in plaats van per aanroep.
3. Niet elk veld dat je ziet mag je zetten
De gemiddelde kostprijs van een artikel kun je uitlezen maar niet wegschrijven. Dat is logisch zodra je erover nadenkt, want dat getal rolt uit de voorraadadministratie. Het is alleen niet gemarkeerd op een manier die je ziet voordat je het probeert.
Eén aanvulling die we onderweg leerden: dat gemiddelde negeert gratis geleverde artikelen. Reken je er iets op door, dan reken je met een getal dat net iets te hoog is. Dat is geen API-probleem maar een boekhoudkundig probleem, en het is precies het soort ding dat een koppeling zichtbaar maakt.
4. Een GET die weigert zonder $select
SalesItemPrices geeft geen antwoord op een gewone GET. Je moet er een $select bij zetten met de velden die je wil. Niet omdat de lijst zo lang is, maar omdat dat endpoint het zo wil.
Op zich een kleinigheid, maar wel een die je bouwt in de veronderstelling dat je overal hetzelfde patroon kunt gebruiken. Wij hebben er een laag voor gemaakt die voor elk endpoint de velden meestuurt, ook waar het niet hoeft. Dat is minder elegant en bespaart elke keer een zoektocht.
5. De limiet die een artikel half afmaakt
Exact staat 60 aanroepen per minuut toe en 5000 per dag. Je leest je stand af uit de X-RateLimit-koppen in elk antwoord, dus je hoeft niet te gokken.
Dat klinkt als veel, tot je bedenkt dat één artikel aanmaken in de praktijk drie tot vier aanroepen kost: het artikel zelf, de prijs, het leveranciersartikel, en een controle. Twaalf artikelen uit één factuur is dan al bijna een hele minuut. Bij ons liep een rij artikelen tegen de limiet aan en bleef er één half af: aangemaakt, maar zonder prijs.
Dat is de belangrijkste les van het hele project. Niet "pas op voor de limiet", maar: maak van één artikel de kleinste eenheid werk die of helemaal lukt of helemaal niet. Houd per artikel bij hoe ver je bent, zodat je na een onderbreking verder kunt waar je gebleven was in plaats van opnieuw te beginnen. Een wachtrij die je kunt hervatten is bij een schrijvende koppeling geen luxe.
Voor de leesrichting geldt hetzelfde rekenwerk, alleen dan met grotere getallen. Een eerste volledige synchronisatie van de prijslijsten kostte ons 1104 aanroepen voor 78 lijsten met samen 63.707 regels. Daarna haal je alleen nog op wat gewijzigd is, en dan valt het terug naar een handvol aanroepen per keer.
Eén ding dat niets met de API te maken had
Terwijl wij artikelen aanmaakten, maakte iemand in de boekhouding er met de hand ook een paar aan. Die kregen codes uit dezelfde reeks die onze tool gebruikte. Een paar nummers waren daarmee ineens bezet.
Daar is geen API-oplossing voor. De enige werkwijze die het voorkomt: vraag de volgende vrije code op het moment dat je hem nodig hebt, niet aan het begin van de rij, en controleer na het aanmaken of je gekregen hebt wat je dacht.
Veelgemaakte fouten
"De documentatie zegt het, dus het werkt zo." Behandel documentatie van een API die je niet zelf beheert als een aanname die je test. Eén keer een artikel aanmaken en daarna uitlezen wat er staat, kost tien minuten en bespaart een dag.
"Ik test met drie regels, dat is genoeg." Limieten, botsende codes en halve rijen zie je alleen bij aantallen. Test met een lijst die lang genoeg is om tegen iets aan te lopen.
"Ik laat de koppeling ook op mijn testomgeving schrijven." Dan schrijf je vroeg of laat testartikelen in de echte boekhouding. Wij laten de koppeling alleen in productie bestaan en draaien ontwikkelwerk op een kopie zonder verbinding, met een test die bewaakt dat er nooit geschreven wordt.
"Een 201 betekent dat het goed staat." Een 201 betekent dat je verzoek is aangenomen. Wat er staat, weet je pas als je het teruggevraagd hebt.
"Ik vang de limiet op met een pauze van een seconde." Dat werkt tot het niet werkt. Lees de koppen uit het antwoord en wacht op wat er echt staat, en zorg dat de rij te hervatten is als je er alsnog tegenaan loopt.