Drie procent ziet er volkomen normaal uit

De prijslijst van een leverancier lag drie procent te hoog. Precies het soort fout dat niemand opmerkt.

Installatie en retail
Drie weken, doorlopend

Een Nederlandse zonweringinstallateur

Leveranciers sturen hun prijzen als pdf's van tweehonderd pagina's en als spreadsheets. Voor elke offerte moest een getal met de hand worden opgezocht. We bouwden de pipeline die ze leest, en belangrijker nog: de controles die weigeren een prijs te publiceren waarover twijfel bestaat.

29.962
prijzen uit één catalogus gelezen
0 naar 3.301
hersteld uit een onleesbaar bestand
~73.000
prijscellen bij 18 leveranciers

Het vraagstuk

Achttien leveranciers, elk met een eigen formaat. Sommigen sturen een pdf die voor een printer is gemaakt en niet voor een computer. Sommigen sturen een spreadsheet met de echte getallen op een verborgen tabblad. Niemand is het eens over de vraag of een prijs inclusief btw is, of een maat in centimeters of millimeters.

De reactie van de medewerkers was rationeel: elke keer met de hand opzoeken. Dat is traag, en het garandeert stilletjes dat sommige offertes met fouten de deur uitgaan.

Het voor de hand liggende project is "de prijzen automatisch uitlezen". Het voor de hand liggende project is ook het gevaarlijke. Een extractie die voor negenennegentig procent klopt, levert een prijslijst op die er volledig af uitziet en fout is op manieren die niemand opmerkt tot er een klant wordt gefactureerd.

Wat we deden

01

Het bestand lezen, niet het formaat

Een pipeline die prijstabellen in pdf's en spreadsheets herkent, de assen eromheen leest en bij elk uitgelezen getal de ruwe tekst bewaart.

02

De aannames expliciet maken

Elk bestand moet zijn btw-grondslag en zijn eenheid opgeven voordat er iets uit wordt gelezen. Dat zijn de twee aannames die ongemerkt een hele catalogus bederven, dus werden het verplichte velden in plaats van standaardwaarden.

03

Alles klaarzetten voor review

Geen enkele import publiceert rechtstreeks. Elke run wordt klaargezet, een mens bevestigt welke prijstabel bij welk product hoort, en alleen beoordeeld werk wordt een live prijs.

04

De lezers repareren die niets opleveren

Het bestand van één leverancier leverde nul leesbare getallen op: de pdf had geen tekentabel en had zijn cijfers intern als woorden benoemd. Herstel betekende werken op glyphniveau in plaats van het lege resultaat te accepteren.

05

Controleren tegen iets externs

Herstelde getallen werden gecontroleerd tegen kleurcodes die los van het bronbestand te verifiëren zijn, zodat de controle niet afhangt van dezelfde kapotte bron.

Hoe het werkt

Tabelherkenning

Prijstabellen worden op structuur gevonden in plaats van op positie, zodat een leverancier die zijn catalogus anders opmaakt de lezer niet breekt.

Opgegeven eenheden en btw

Eenheid en btw-grondslag zijn verplichte opgaven per bestand. Een bestand zonder die opgaven kan helemaal niet worden geïmporteerd.

Klaargezette runs

Een import levert een klaargezette run op, geen prijswijziging. Publicatie is een aparte, beoordeelde stap.

Opnieuw leesbaar door ontwerp

Een bestand kan opnieuw worden gelezen nadat een lezer is verbeterd, en het nieuwe resultaat vervangt het oude zonder reviewwerk dat al is gedaan te vernietigen.

De btw-vangst

De lijst van één leverancier was geprijsd inclusief btw, terwijl het systeem uitging van exclusief. Elke gepubliceerde prijs zou eenentwintig procent te hoog zijn geweest. De verplichte opgave ving het op vóór publicatie.

De vangst van het verborgen blad

Elf van de vierendertig gekoppelde prijsblokken in één werkmap verwezen naar een verborgen duplicaat met oudere getallen, ongeveer drie procent ernaast. Drie procent ziet er niet uit als een bug. Het ziet eruit als een prijs.

Bewust een mens in de lus

De koppeling van prijstabel aan product is een beslissing, geen gok. Het systeem stelt voor; een mens bevestigt.

Herkomst bewaard

Elke gepubliceerde prijs is terug te voeren op het bestand, de pagina en de cel waar die vandaan komt.

Technologie

TypeScript Node.js PostgreSQL Prisma PDF-parsing XLSX-parsing Next.js

Het resultaat

Eén catalogus van 238 pagina's leverde 278 prijsblokken en 29.962 afzonderlijke prijzen op. Over achttien leveranciersbestanden zijn ongeveer 2.000 pagina's en zo'n 73.000 prijscellen nu leesbaar in plaats van handwerk.

Het bestand dat niets had opgeleverd, ging van nul naar 3.301 prijzen zodra de lezer op glyphniveau werkte, gecontroleerd tegen extern verifieerbare kleurcodes.

De twee vangsten zijn belangrijker dan het volume. Een fout van eenentwintig procent is gênant maar zichtbaar: iemand zou er vragen over hebben gesteld. Een fout van drie procent is niet zichtbaar. Die kost gewoon stilletjes geld op elke offerte, totdat iemand een jaar aan marge naloopt. De controle ving de fout die er niet uitzag als een fout.

Namen van leveranciers, inkoopprijzen en kortingsstructuren zijn commercieel vertrouwelijk en worden hier niet gepubliceerd. De cijfers hierboven beschrijven het volume en het gedrag van de pipeline, niet de voorwaarden van een leverancier.

Verwant werk

Een Nederlandse zonweringinstallateur

We lazen zeven maanden aan offertes en ontdekten dat we het verkeerde bouwden

De roadmap was gebouwd op wat makkelijk te lezen was. Niet op wat het bedrijf echt verkoopt.

TypeScript Node.js PDF-parsing
Een Nederlandse zonweringinstallateur

De eerste automatische lezing zag er perfect uit. Elk veld dat ertoe deed, was leeg

Een extractie die een compleet ogend resultaat teruggeeft zonder iets belangrijks erin, is erger dan een extractie die faalt.

TypeScript PDF-rendering Visionmodellen