This project didn't start with a client brief. It started at Red Cross Clinic, where I was working and had a front-row seat to both the capabilities and the gaps of the clinical management system already in place. I spent time observing how every department operated — what the software handled well, where staff worked around it, and what was simply missing. That observation became the specification for everything I built next.

Clinic System v2 is my own implementation, built to do everything the existing system did — and to fix what it didn't. No compromises from third-party constraints, no waiting for a vendor to add a feature. Every module exists because a real workflow demanded it.

Starting with the workflow, not the database

Because I had worked inside the clinic, I already understood the flow before writing a single line of code. The receptionist registers a patient. A nurse records vitals. A doctor writes a consultation note and a prescription. That prescription travels — physically, as a paper slip — to the pharmacy window.

That paper slip is the most important object in the building. Every module in Clinic System v2 maps back to replacing or tracking that slip digitally, with a full audit trail at every step.

The database schema that made everything possible

The core of Clinic System v2 is a single patients table with a UUID primary key, linked to a visits table that captures every encounter. Everything else — vitals, prescriptions, lab requests, billing — hangs off a visit record.

schema.sql
CREATE TABLE visits (
  id         CHAR(36)     PRIMARY KEY,
  patient_id CHAR(36)     NOT NULL,
  opened_at  DATETIME     DEFAULT CURRENT_TIMESTAMP,
  closed_at  DATETIME     NULL,
  status     ENUM('open','billing','closed') DEFAULT 'open',
  doctor_id  INT          NOT NULL,
  FOREIGN KEY (patient_id) REFERENCES patients(id)
);

The status column drives the entire patient journey. When a doctor finalises their notes, the visit moves to billing. The cashier closes it. This simple state machine replaced a physical tray system the clinic had been using for twelve years.

The pharmacy module was the hardest part

Every pharmacy has stock. But clinic pharmacy stock behaves differently from retail stock: drugs are dispensed in quantities that don't always match purchase units, expiry dates matter for legal compliance, and you need to know exactly who dispensed what to which patient and when.

I built a stock_batches table — each delivery of a drug gets its own row with a batch number and expiry date. Dispensing always consumes from the oldest non-expired batch first (FEFO: First Expiry, First Out).

Medical aid claims integration

This was the feature that took the longest. Medical aid societies in Zimbabwe each have their own tariff codes, claim submission formats, and rejection reasons. I built a claims module with a configurable tariff table per medical aid, a submission queue, and a rejection workflow that lets the billing clerk resubmit with corrections.

Rejections are the real money. A system that doesn't handle them loses the clinic thousands of dollars every month in uncollected revenue.

What I'd do differently

The session-based auth works, but if I were building v3 I'd separate the API from the UI more cleanly. Right now PHP templates and business logic are tightly coupled. That's fast to build but hard to extend. I'd also invest earlier in a proper audit log — every change to patient data, every stock movement, every billing edit needs to be traceable.

The most valuable thing you can build into a healthcare system isn't a feature. It's the ability to answer "who did what, and when?" three months from now.

The result

Clinic System v2 now handles admissions, vitals, doctor consultations, lab request and result workflows, pharmacy dispensing, billing, medical aid claims, and QR-code patient identity — all from a browser, on a self-managed VPS, with zero external dependencies or subscription fees.

You can request a guided walkthrough via the contact section or see the live demo at novara.co.zw/demo.