Terug naar projecten

AH Loontracker

Webapp die je supermarktsalaris live uitrekent en voorspelt uit je rooster.

PersoonlijkJuni 2026
Next.js 14React 18TypeScriptTailwind CSSFirebaseiCal

Over het project

Als je in de supermarkt werkt, weet je pas wat een dienst opleverde als het loonstrookje binnen is. En dat duurt: het loon over een periode van vier weken wordt pas een periode later gestort, dus je kijkt naar een bedrag voor werk van soms twee maanden geleden. Op dat strookje staat één totaal, zonder welke weken en welke diensten erin zitten. Terugdenken waar het vandaan komt, laat staan controleren of het klopt, lukt eigenlijk niet.

Daar komt bij dat het uurloon in deze CAO geen enkel getal is. Er is jeugdloon per leeftijd, functiejaren vanaf je 21e, een personeelstoeslag per uur die na je 20e vervalt, +50% op zondag, +100% op feestdagen, +50% voor uren na 22:00, en daarbovenop komen vakantiegeld, vakantiedagen en ATV all-in mee. Dat narekenen doet niemand, dus niemand weet precies wat een werkdag oplevert.

Daarom bouwde ik deze webapp, eerst voor mezelf, en uiteindelijk uitgewerkt voor iedereen in de Albert Heijn-winkel waar ik werk. Je koppelt eenmalig je rooster via een iCal-link uit de personeelstool; daarna hoef je nooit meer iets handmatig in te vullen. Per dienst zie je meteen het bruto bedrag, wat je er per uur aan overhoudt, en een uitklapbare opbouw waarin elke euro te herleiden is naar een toeslag.

Loopt er een dienst, dan staat bovenaan een teller die je verdiende bruto op de cent laat oplopen, met een balk hoe ver je in je dienst bent. Dat was voor mij het leukste onderdeel om te bouwen. Als teamleider is dit een luchtige manier om collega's, en mezelf, net wat extra motivatie te geven: je ziet je geld letterlijk binnenkomen terwijl je aan het werk bent.

Per periode van vier weken toont de app wat je tot nu toe hebt opgebouwd, wat het eindtotaal wordt en op welke datum het op je rekening staat. Komende periodes worden voorspeld uit je ingeplande diensten, dus je weet weken vooruit waar je aan toe bent. En omdat de iCal maar zo'n vier weken aan geschiedenis bevat, worden gewerkte diensten los bewaard: zonder account in je eigen browser, met een optioneel account in de cloud zodat je geschiedenis op al je apparaten blijft staan. Komt het strookje dan binnen, dan leg je het ernaast en zie je precies waar dat bedrag vandaan komt.

Onder de motorkap haalt de app je rooster server-side op en leest het uit met een iCal-parser die ik zelf heb geschreven. Het loon wordt vervolgens in je browser berekend, want van elke dienst bewaar ik alleen de ruwe gegevens: begin- en eindtijd, pauze en afdeling, nooit een uitgerekend bedrag. Daardoor klopt je hele historie ook nog als er later iets aan de loontabel of aan je eigen instellingen verandert.

Wat ik heb geleerd

Het lastigste zat niet in de code maar in het uitzoekwerk. Ik heb loonstroken en de CAO Levensmiddelenbedrijf naast elkaar gelegd en per regel teruggerekend waar elk bedrag vandaan komt, tot de reken-engine echte loonstroken op de cent reproduceerde. Dat is geen eenmalige controle: er draait een validatie-script dat het na elke wijziging opnieuw bewijst.

Daarbij kwamen regels boven water die nergens leesbaar opgeschreven staan. Je leeftijd voor het uurloon wordt bepaald op het einde van de vierwekenperiode en niet op de dag van de dienst, dus word je halverwege een periode jarig, dan krijg je het hogere loon over die hele periode. En de personeelstoeslag vervalt na de periode waarin je 20 wordt. Zulke dingen vind je alleen door strookjes te vergelijken en te blijven doorrekenen tot het laatste verschil verklaard is.

Dit was mijn eerste project dat data uit een systeem van iemand anders haalt, en dat leverde twee lessen op. De eerste kwam meteen: vanuit de browser kreeg ik de rooster-feed niet binnen omdat CORS het blokkeert. Zo begreep ik pas echt waarom je zo'n koppeling via je eigen server laat lopen. De tweede was dat ik het uitlezen zelf ben gaan schrijven in plaats van er een library bij te halen. Dat kostte meer tijd, maar ik snap nu precies wat er in zo'n feed staat en kan het aanpassen als de personeelstool morgen iets verandert.

De les die ik het meest meeneem gaat over één ontwerpkeuze aan het begin. Ik had per dienst het uitgerekende bedrag kunnen opslaan, maar besloot alleen de ruwe gegevens te bewaren en het loon telkens opnieuw te berekenen. Dat leek eerst extra werk. Toen ik later ontdekte dat de personeelstoeslag na je twintigste vervalt en dat het uurloon halverwege 2026 omhoogging, was één aanpassing in de loontabel genoeg en klopte de volledige historie van iedereen weer. Had ik bedragen opgeslagen, dan had ik ieders oude data moeten repareren. Sindsdien denk ik bij het opslaan van gegevens eerst na over wat er echt vaststaat en wat ik altijd opnieuw kan afleiden.

Ten slotte leerde ik veel van het feit dat collega's het echt gebruiken. Het duidelijkste voorbeeld: een paar van hen zeiden dat de app niet klopte, omdat hun periodetotaal lager uitviel dan wat er op hun loonstrook stond. De berekening was gewoon goed, maar hun rooster gaat maar een paar weken terug, dus de oudere diensten uit die periode ontbraken simpelweg. Mijn eerste reflex was om in de reken-engine te gaan zoeken, terwijl het probleem in de uitleg zat. Ik heb daarom vlak na de onboarding een korte pop-up toegevoegd die uitlegt waarom oudere diensten ontbreken en hoe je ze zelf aanvult. Daarmee waren de klachten meteen weg. Dat een gebruiker zegt dat iets niet klopt, betekent dus niet automatisch dat er een bug is, en soms lost een goed getimede zin meer op dan code. Op dezelfde manier kwamen er later functies bij, zoals een instelling voor een afwijkend uurloon.

Beelden