Case study 02 / Quality systems
An internal audit system where the evidence survives the audit.
Audit scheduling, role-based reviews, evidence collection, scoring, and exports a director can read — built so that six months later you can still prove what was checked and who signed it off.
- Role
- Sole developer — data model, permissions, backend, frontend, reporting
- Stack
- Laravel (PHP) · MySQL · Blade · Tailwind
- Type
- Quality management / compliance system
- Repository
- github.com/HINCHEU ↗
The problem
Internal audits usually start life as a spreadsheet template, a shared folder of photos, and a chain of emails. Each of those works individually. Together, they lose the one thing an audit exists to produce: a defensible record that a specific person checked a specific thing on a specific date.
The failure shows up months later, when someone asks why a finding was closed and the answer is spread across three inboxes and a folder nobody can find.
What the system does
- Audit scheduling — recurring audits planned ahead with assigned auditors and departments, so nothing depends on somebody remembering.
- Role-based reviews — auditors, reviewers, and managers see different screens and have different powers. A reviewer can approve; an auditor cannot approve their own work.
- Evidence collection — files and notes attached directly to the finding they support, rather than living in a folder with a filename that made sense to one person.
- Scoring — consistent criteria applied across departments, so results can actually be compared instead of merely collected.
- Executive exports — reports that summarise for a director without stripping out the trail that supports the numbers.
How it is built
Laravel with MySQL. This is exactly the kind of system Laravel is good at: heavy on relationships, heavy on permissions, and heavy on the boring correctness work that makes a compliance tool trustworthy.
Permissions as a first-class part of the data model
Role checks are attached to the data itself rather than scattered through the interface. Hiding a button is a user experience decision; it is not security. Every action verifies on the server that this user, in this role, may act on this specific audit — which also means a new role can be added later without auditing every screen for a missed check.
Findings are immutable once submitted
A submitted finding cannot be quietly edited. Changes happen through a documented revision that keeps the original visible. This is unpopular for about a week and then becomes the feature people trust the system for — an audit record that can be rewritten is not an audit record.
Reusable pattern. Any system where someone's sign-off carries weight — approvals, inspections, incident reports — needs the same property: the record of what was approved must be separable from the record as it exists today.
What I would change
The export layer was built for one report format and then extended for others, which made it harder to change than it needed to be. A template-driven approach, where the report structure is data rather than code, would have let the organisation adjust their own formats without a developer.
Need something similar?
Approval flows, evidence trails, and role-based review are common requirements well beyond auditing. The internal tools service covers this kind of build, and the POS Flask case study shows a related approach to keeping history intact.
Approvals living in email?
If your compliance record is a folder and a chain of replies, it is one staff change away from being unusable. Let's fix that.
Start a conversation ↗