Een bon uit Sydney, opgeslagen in Thaise baht
Een bonnenscanner heeft een voor de hand liggende taak — het totaal lezen — en een tweede waar niemand bij stilstaat: uitzoeken in welke valuta dat totaal staat. Gaat de eerste mis, dan ziet de gebruiker dat meteen. Gaat de tweede mis, dan lijkt er niets kapot. Het bedrag op het scherm is precies het bedrag op het papier. Het wordt alleen als het verkeerde soort geld opgeslagen, en dat vervuilt stilletjes je budget.
In twee dagen vond ik er vijf. Ze hadden allemaal dezelfde oorzaak, en dat zag ik pas toen ze naast elkaar lagen.
1. Een terminalnummer
Een bon van een Japanse buurtsupermarkt kwam terug als Canadese dollars. Op die bon staat 小計 ¥380, 税 ¥38, 合計 ¥418 — drie keer een yen-teken. Er staat ook dit:
端末番号:1CAD
Dat is een terminalnummer. De scanner zocht naar ISO-valutacodes, vond CAD binnen 1CAD, en gaf Canadese dollars terug voordat hij ooit naar de ¥-tekens keek. De controle sloot een voorafgaande letter uit, maar geen voorafgaand cijfer.
2. De btw-kolom van ALDI
Twee bonnen uit Australië — één van een restaurant in Double Bay, Sydney, één van ALDI in Carindale — werden opgeslagen als Thaise baht. Ruwweg drieëntwintig keer verkeerd.
Op de Sydney-bon staat de regel ฿ $10.00. De ฿ is geen valuta. Het is wat tekstherkenning ervan maakt als ze een letter verkeerd leest. Het echte bedrag staat er in dollars naast.
De ALDI-bon is mooier. Daar staat:
1.39 ฿
Op een Australische ALDI-bon betekent een B in die kolom dat het artikel vrijgesteld is van GST. De scanner las die B als ฿ en concludeerde dat de bon Thais was.
Op beide bonnen staat TAX INVOICE. Op de één een ABN, op de ander een postcode uit New South Wales. De landenscore stond 10 tegen 1 voor Australië — en verloor van één verkeerd gevormd teken.
3. Een woord op een menukaart
Daarna werd het onnozeler. Een gedrukte ISO-code geldt als sterk bewijs, wat verstandig is bij Total EUR 12.50. Maar zeven van die codes zijn ook gewone Engelse woorden:
TRY OUR NEW MENU→ Turkse liraPLEASE TRY AGAIN→ Turkse lira, op een geweigerde pinbonMOP AND BUCKET SET→ Macause patacaAED PADS REPLACEMENT→ dirham, op een defibrillatorRM 214→ Maleisische ringgit, op een kamernummerFr. 12 Sept→ Zwitserse frank, op het Duits voor vrijdag
Hier is geen leesfout voor nodig. De bon drukt gewoon normaal Engels af. En AED, INR en CHF zijn bovendien medische afkortingen — in een app die apotheekbonnen leest.
4. Afwezigheid aangezien voor bewijs
Bonnen uit Hongkong drukken een kale $ af zonder landaanduiding, dus werden ze meestal opgeslagen in de valuta van de telefoon. De voor de hand liggende oplossing zag er netjes uit: traditioneel Chinees schrift, plus een $, en geen Taiwanese aanduiding, moet Hongkong zijn. Hij klopte bij 9 van de 10 bonnen uit Hongkong en bij geen van de 454 bonnen van elders.
Toen kwam er een echte Taiwanese bon binnen — 台灣大哥大, een telefoonrekening:
實繳金額 $ 665
Traditioneel Chinees, een kale dollar, geen NT$, geen 元, geen 統一發票. Precies de vorm die de regel Hongkong noemde. Twee echte bonnen waren genoeg om hem te schrappen.
5. Wat wél werkte
De oplossing voor Taiwan was de omgekeerde beweging. Elke Taiwanese zakelijke bon drukt 統編 af, het fiscale nummer van het bedrijf. Het staat op 43 van de 43 Taiwanese bonnen in onze testset, en op nul van de andere 430. Dat is bewijs dat er ís, en het ging dezelfde dag live.
Het patroon
Alle vijf zijn dezelfde bug: één misleidend stukje tekst dat het wint van opgebouwd bewijs. Een terminalnummer won van drie yen-tekens. Eén verkeerd gelezen teken won van een ABN en een postcode. Het woord "try" won van alles.
De regels die echt papier overleven, zijn die welke steunen op bewijs dat er is — nooit op bewijs dat ontbreekt. 統編 bleef. "Geen Taiwanese aanduidingen, dus Hongkong" niet.
En de Nederlandse bonnen?
Nederlandse bonnen hebben hun eigen valstrik, en die vonden we in dezelfde week. Onze "Nederlandse" testset bleek grotendeels niet Europees: van de 74 bonnen drukken er 67 TOTAAL … $42,36 af. Het zijn bonnen uit Bonaire — Nederlandstalig, maar in Amerikaanse dollars.
Dat is precies dezelfde denkfout in een ander jasje: taal verwarren met valuta. Een bon in het Nederlands is niet automatisch een bon in euro's, net zomin als Traditioneel Chinees automatisch Hongkong betekent. We hebben daardoor nog nauwelijks échte eurobonnen om tegen te testen — dat is op dit moment ons grootste gat.
Waarom de tests ze niet vonden
Niets hiervan kwam uit de testsuite. In de 431 bonnen die we bij elke release controleren komt het woord TRY toevallig niet in hoofdletters voor, en de twee Australische bonnen stonden in een openbare dataset onder de Verenigde Staten — niemand had ze ooit als Australisch bekeken.
Ze kwamen boven water door de bugs naast elkaar te leggen en te vragen welke vorm ze hadden. Daarna bewees de testset dat de oplossingen veilig waren: elke wijziging is over alle 431 bonnen gemeten, en de woordfix veranderde er precies nul, terwijl hij zes faalvormen wegnam.
Dat is de eerlijke rolverdeling. Een testsuite vertelt je of een wijziging veilig is. Hij vertelt je niet wat er mis is. Daarvoor moet je nog steeds naar de bonnen kijken.
ReceiptIQ wordt voor elke release gecontroleerd tegen 431 bonnen uit tien landen — 391 foto’s van echte bonnen, plus 40 synthetische Taiwanese e-facturen. De bovenstaande oplossingen gingen live in dezelfde week waarin ze gevonden werden.