Lab tests without the paperwork
The Laboratory feature handles the journey of a lab test — from the doctor ordering it, to the result coming back, to the patient being told. It keeps everything connected to the patient's record.
What it does
- Offers a ready-made catalog of common tests to choose from.
- Lets you retire a test and bring it back without deleting it, so past orders and results stay intact.
- Can offer your tests as services in the departments catalogue, priced from here (see below).
- Lets doctors order tests, mark them urgent, and note fasting requirements.
- Records results against the patient's file.
- Ties each order to the visit's reference number, so the test sits alongside the same encounter's consultation, prescription and charges.
- Bills a test the moment it's ordered — from Laboratory itself, from the booking form, or from the APE form — using the price the order was placed at.
- Flags dangerously abnormal results so they are not missed.
Billed when ordered
Placing an order bills it to the visit right away, on every path a test can be ordered from: the Laboratory module (web and API), the appointment booking form, and the APE form's Order button. A test with no price set is left off the bill — the order itself still goes through — and a message names it so the price can be added, or the line added by hand at the cashier.
If the same test is already billed for this encounter — from an earlier order, or from a checklist service or package that includes it — the new order's line shows as billed on another line for this visit instead of charging again. See Billing & Cashier for how that rule works across every source. Orders created by the APE spreadsheet importer, and by seeding, are never billed — they describe results from elsewhere or from setup, not a charge to raise.
Keeping the catalog tidy
Tests you no longer run can be deactivated rather than deleted — from the test list or its edit page — so every order and result that referred to them stays readable. A deactivated test stays in the list, marked Inactive, and can be activated again at any time.
Selling tests as services
The tests here can be brought into Departments & Services with Import Lab Tests, so a test can be booked, packaged, assigned to a doctor and billed like any other service.
This catalog remains the source of truth for their price. Change a test's price here and the matching service follows automatically; the service's own price field is read-only to make that plain. Deactivating a test also retires its service, and reactivating restores it — so the two can never say different things.
Why it helps
For the clinic
Orders and results stay organized and traceable, reducing the chance of a test being forgotten or a result going astray.
For doctors
Ordering a test takes a moment, and results appear alongside the rest of the patient's history for easy comparison over time.
For staff
The lab team and front desk can see what has been ordered and what is still pending, keeping everyone on the same page.
For patients
Their tests are tracked carefully, and a result that needs urgent attention is highlighted so the clinic can act quickly.
When a result is critically out of range, the system raises an alert directed to the doctor who ordered the test, so it reaches the right person and follow-up happens without delay.
For the diagnostics whose result is a picture and a radiologist's opinion rather than a number — X-rays, scans, ultrasounds — see Imaging & Radiology, which works the same way.
This is an optional feature your clinic can switch on when needed.
Deploying this update
Order-time billing and the once-per-visit rule need tenant migration 089 (patient_charge_items.lab_claims / waived_lab_tests / base_discount, and laboratory added to the item-type list).
Migrate every tenant first, then deploy the code. The new code writes those three columns on every charge line it inserts or updates, not just on lab lines. Against a tenant that has not been migrated, every charge-line write fails: the cashier adding an item or creating a charge, changing a line's brand, pharmacy and imaging charges, consultation charges, the charge API, the accounting screens, the availed-items checklist and the billing simulator. Orders themselves are still saved (the front desk sees "add the charge at the cashier"), but nothing can be billed anywhere on that tenant until 089 runs.
The migration is additive — new nullable columns and a wider item-type list — so the code you are replacing keeps working on a migrated database. That is what makes "migrate all tenants, then deploy" safe.
Clear the compiled container after deploying (rm -rf storage/cache/container-*) — the new lab charge creator is wired through dependency injection.
Seeding and the APE spreadsheet importer never bill. Seeding creates its orders with the order-writing methods this feature leaves untouched (createOrder() for lab and imaging orders, ApeLabWriter::applyOrders() for APE forms), which write the order rows only; billing lives in separate sibling methods that only the interactive paths (booking form, Laboratory module, APE form) call. The APE spreadsheet importer does not go through either: ApeImportService builds its lab orders and order items itself and never calls a charge creator. A seeded or imported encounter is never charged for orders it never actually incurred.
An order placed without a visit reference is billed to the patient's open charge that carries no reference (a new one is opened if there is none). It is never put on — or checked against — some other visit's bill, so a test already paid for on a different visit cannot make this one free.