AZ Turnhout — internal tooling
Internal applications and migration tooling for the AZ Turnhout pharmacy compounding workflow.
- Role
- Developer
- Year
- 2025
- Stack
- Next.js · TypeScript · SQL Server
- Status
- Private
Where this lives
AZ Turnhout is a regional hospital in the Kempen. The pharmacy department runs the compounding workflow that prepares patient-specific oncology mixtures — drugs like Fluorouracil, Paclitaxel and Oxaliplatin diluted in NaCl 0.9% or Glucose 5%, each with their own preparation method, holding time and traceability requirements.
Behind that workflow sat a long-lived FileMaker database. It worked, but every new request meant working around it. The brief was to lift the system into a modern stack without disrupting daily operations — a SQL Server back-end, a focused Next.js front-end for the people who actually use it, and a migration path that didn't require manual cleanup.
The audience is internal: pharmacy technicians (bereiders), supervising pharmacists (apothekers), and a small group of admins. Nobody on the floor has time for a clever UI. The product has to read like an instrument, not a dashboard.
The legacy FileMaker export held years of preparation records: every batch with its teller, lot number, preparation date, product, method and solvent. Schema-wise it was flat and tolerant — the fields were strings, the relationships were implicit, and the validation lived in muscle memory.
Beyond the data itself, the harder problem was that the system had no clean seam to extend from. New workflows landed as patches on the existing layout. Filtering and sorting the bereidings-logboek across years, lot numbers and products was slow; reporting required exporting and post-processing in Excel.
Three constraints shaped the rewrite: nothing could be lost in migration, the new system had to be auditable end-to-end, and the daily workflow had to feel familiar — same vocabulary, same shortcuts, same speed of entry.
Four apps, one internship
Four distinct internal tools shipped under the AZ Turnhout brief. Each one solved its own workflow problem — switch tabs to step through them.
Voorraadbeheer
Pharmacy compounding logbook — preparation records, stamdata and inventory in a single auditable system.
The daily-driver app for the AZ Turnhout pharmacy compounding team. Every batch of patient-specific oncology mixtures is recorded here — teller, lot number, preparation date, product, method, solvent — and the same data feeds the audit trail and the inventory view.
Replaces a long-lived FileMaker setup that had grown to the edge of its lifespan. Same vocabulary as the paper rhythm the team already uses, so the switch felt like a tooling upgrade, not a process change.
Next.js front-end on the bereidings-logboek with filter / sort across apotheker, bereider, datum, jaar, lotnummer, maand and product. SQL Server back-end exposed through a repository layer of stored procedures (Activate / Delete / Filter / Get / Insert / Sorteer / Update per entity).
Python migration pipeline that lifted years of historical records from the FileMaker export through a normalised xlsx staging file into idempotent SQL — every INSERT guarded by IF NOT EXISTS so the pipeline can re-run against the same target without producing duplicates.
Compounding records, stamdata and inventory now live in a single auditable system. Filters and sorts that previously required an Excel export run inside the UI.
Migration runs are reproducible; staging-to-production switches are no-ops when nothing has changed.
+ 13 supporting captures archived internally
How it holds up
The non-glamourous parts of internal hospital software — auditability, idempotency, data migration discipline.
Repository-style stored procedures
Every entity has the same vocabulary of operations: Activate, Delete, Filter, Get, Insert, Sorteer, Update. The front-end never writes raw SQL — it calls a named procedure with a fixed parameter shape, which keeps the data layer predictable and the audit story simple.
Idempotent migrations
Every generated INSERT is wrapped in IF NOT EXISTS guards against the right business key. A migration run can be repeated against the same database without producing duplicates, which made testing the import on a copy of production cheap and safe.
Python staging layer
Migration goes via a normalised xlsx staging file with one tab per logical table — Controle, Producten, Methodes, Oplosmiddelen, Preparations, DoseVolume. Pandas does the cleaning; a separate generator produces the SQL. Failures show up in staging, not in production.
Audit-friendly logbook
Bereidings_logboek stores Dose_Id, BereiderId, ApothekerId, DatumTijd, LotNummer, Vervaldatum_batch, BereidingsNummer, Teller and Opmerkingen for every batch. Updates land as explicit UPDATE statements scoped on teller + nummer + timestamp — never blind overwrites.
AD-bound permissions
Privileged operations — the licence CLI, certain admin views — run against AD identity. The CLI reads the current memberships before mutating, surfaces a preview, and only applies on explicit confirmation. Nothing happens implicitly.
Built for the pharmacy floor
Form design follows the existing paper rhythm: teller → product → preparation → solvent → volume → lot → expiry. The vocabulary is Dutch where the staff already uses Dutch (apotheker, bereider, oplosmiddel) and English where the codebase is English. Familiarity beats novelty.
What changed
The pharmacy moved off FileMaker without losing a single historical record. Migration runs are reproducible — running the pipeline twice against the same target is a no-op, which made staging-to-production switches uneventful.
Day-to-day, the bereidings-logboek is now the single source of truth across roles. Filters and sorts that used to require an Excel export run in the UI; preparations are recorded against the same teller / lot numbers that the audit trail uses.
The supporting tools — licence CLI, endpoint installer, service desk dashboard — absorbed the manual work that used to live in a tribal-knowledge runbook. A new endpoint is now a typed command, not a click-path.
This is internal software. It will never have a marketing site, a launch tweet or a public roadmap. Its measure is whether the pharmacy team notices it less over time — and that's the bar it's held to.
The patterns underneath — repository-style stored procedures, idempotent migrations, audit-first data model — are general enough that the same scaffold has carried into the surrounding tools (service desk dashboard, endpoint installer, licence CLI). The case study above documents what's visible; the rest stays internal where it belongs.