A live dispensing platform where label printing failed silently, page names decided who could open patient records, and migrated stock and refill data could not be trusted
01 — The brief
What was wrong
A live dispensing platform where label printing failed silently, page names decided who could open patient records, and migrated stock and refill data could not be trusted
What we did about it
Rebuilt authorisation around explicit per-action permissions, consolidated label printing behind one shared path, and gave staff on-screen tools to correct every imported drug and refill match

02 — The build
STEP 01 / 04
Access had been inferred from the name of the page being opened, so any screen called Create, Edit or List went through unchecked — patient screens included. Every action now declares the permission it requires, shared create/edit screens decide from the request rather than the route name, and permission caching was switched off so withdrawing access takes effect immediately.
Built with
03 — The sequence
17 months, in 4 phases
M1-2
Closed the patient-record access gap, labelled 77 files, and migrated the permission tables live across four environments
M3-6
Consolidated six print paths into one, trialled and withdrew a certificate-based helper, and added printer discovery and true status reporting
M5-8
Three-pass drug matching, reference-number resolution, and the Remap Workbench with bulk correction and audit logging
M2-8
Grouped spelling decisions, eCW patient and allergy import, and shared comparison logic to stop silent row loss
.NET 10, ASP.NET Core MVC, Entity Framework Core, Dapper
SQL Server, FuzzySharp, ClosedXML, CsvHelper
What this build actually ran on, grouped the way we scoped it.
eClinicalWorks, JSPrintManager, SignalR, Telerik UI for ASP.NET Core
In their words
Every one of these problems produced output that looked perfectly fine — a made-up date, a check that approved everything, a working printer reported as offline. Nothing crashed, so nothing got reported. The work was making the system say where its answers came from, and admit when it is guessing.
Keep reading