- Add full CRUD operations for results (index, show, update, delete)
- Implement result validation with reference range checking (L/H flags)
- Add cumulative patient results retrieval across all orders
- Create ReportController for HTML lab report generation
- Add lab report view with patient info, order details, and test results
- Implement soft delete for results with transaction safety
- Update API routes with /api/results/* endpoints for CRUD
- Add /api/reports/{orderID} endpoint for report viewing
- Update OpenAPI docs with results and reports schemas/paths
- Add documentation for manual result entry and MVP plan
7.5 KiB
CLQMS MVP Sprint Plan
Scope: Result Entry + Validation + Reporting (2-week sprint)
Removed: Edge API, Worklists, Calculated Tests, QC, Instrument Integration
Current State
What's Working
- Patient CRUD + Identifiers + Addresses
- Patient Visit + ADT
- Lab Orders (create with specimen generation)
- Test Definitions + Reference Ranges (refnum)
- Authentication (JWT)
- Master Data (ValueSets, Locations, etc.)
Critical Gap
ResultControlleronly returns JWT payload (no CRUD)PatResultModelhas no validation logic- No PDF generation capability
- Only 1 working route:
GET /api/result(returns auth check only)
Phase 1: Result CRUD + Validation (Week 1)
Day 1-2: PatResultModel Enhancement
Location: app/Models/PatResultModel.php
Add methods:
validateAndFlag($resultID, $value)- Compare result against refnum ranges- Check patient age, sex from order
- Match refnum criteria
- Return 'L', 'H', or null
getByOrder($orderID)- Fetch all results for an order with test namesgetByPatient($internalPID)- Get cumulative patient resultsupdateWithValidation($resultID, $data)- Update + auto-validate
Day 3-4: ResultController
Location: app/Controllers/ResultController.php
Replace placeholder with full CRUD:
| Method | Endpoint | Description |
|---|---|---|
index() |
GET /api/results |
List results (filter by order/patient) |
show($id) |
GET /api/results/{id} |
Get single result |
update($id) |
PATCH /api/results/{id} |
Update result + auto-validate |
delete($id) |
DELETE /api/results/{id} |
Soft delete result |
Features:
- Filter by
order_idorpatient_idquery param - Include test name from
testdefsite - Auto-calculate flags on update
- Return standardized ResponseTrait format
Day 5: Routes & Testing
Location: app/Config/Routes.php
Replace line 18:
// OLD
$routes->get('result', 'ResultController::index');
// NEW
$routes->group('results', function ($routes) {
$routes->get('/', 'ResultController::index');
$routes->get('(:num)', 'ResultController::show/$1');
$routes->patch('(:num)', 'ResultController::update/$1');
$routes->delete('(:num)', 'ResultController::delete/$1');
});
Testing: Manual API testing with Postman/Insomnia
Phase 2: HTML Report Viewing (Week 2)
Day 1-2: Report View & Controller
Create Report View: app/Views/reports/lab_report.php
Template sections:
- Patient header (Name, DOB, ID)
- Order info (OrderID, Date, Doctor)
- Results table (Test, Result, Units, Reference, Flag)
- Footer (Lab info, signature)
- Print-friendly CSS with
@media printsupport
Create ReportController: app/Controllers/ReportController.php
| Method | Endpoint | Description |
|---|---|---|
view($orderID) |
GET /api/reports/{orderID} |
Generate HTML lab report |
Logic:
- Fetch order with patient details
- Get all results for order (with flags)
- Include test definitions (name, units, ref range)
- Render HTML view using CodeIgniter's view() function
- Users can print/save as PDF via browser
Day 3-4: Routes & Polish
Routes (add to Routes.php):
$routes->get('reports/(:num)', 'ReportController::view/$1');
HTML Styling:
- Professional lab report format
- Show abnormal flags (L/H) highlighted
- Include reference ranges
- Signature line for pathologist
- Responsive design with print button
Testing:
- Generate report with actual data
- Verify formatting
- Test print functionality
- Test edge cases (no results, all normal, mix of flags)
Note: PDF generation deferred to post-MVP. Users can use browser's "Print to PDF" feature for now.
Database Schema Reference
patres (Results)
| Column | Type | Notes |
|---|---|---|
| ResultID | INT | PK |
| OrderID | INT | FK to ordertest |
| TestSiteID | INT | FK to testdefsite |
| Result | VARCHAR(255) | The actual value |
| ResultDateTime | DATETIME | When entered |
| RefNumID | INT | Applied reference range |
refnum (Reference Ranges)
| Column | Type | Notes |
|---|---|---|
| RefNumID | INT | PK |
| TestSiteID | INT | Which test |
| Sex | VARCHAR | M/F/ALL |
| AgeStart/AgeEnd | INT | Age criteria |
| Low/High | DECIMAL | Range values |
| LowSign/HighSign | VARCHAR | <=, <, etc |
API Endpoints Summary
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/api/results |
GET | Yes | List results (filter: ?order_id= or ?patient_id=) |
/api/results/{id} |
GET | Yes | Get single result |
/api/results/{id} |
PATCH | Yes | Update result + auto-flag |
/api/results/{id} |
DELETE | Yes | Soft delete result |
/api/reports/{orderID} |
GET | Yes | Generate PDF report |
Flag Logic (Reference Range Validation)
// Pseudo-code for validation
function validateResult($resultValue, $refNumID) {
$ref = getRefNum($refNumID);
$patient = getPatientFromOrder($orderID);
// Match criteria (sex, age)
if (!matchesCriteria($ref, $patient)) {
return null; // No flag if criteria don't match
}
$value = floatval($resultValue);
$low = floatval($ref['Low']);
$high = floatval($ref['High']);
// Check low
if ($ref['LowSign'] === '<=' && $value <= $low) return 'L';
if ($ref['LowSign'] === '<' && $value < $low) return 'L';
// Check high
if ($ref['HighSign'] === '>=' && $value >= $high) return 'H';
if ($ref['HighSign'] === '>' && $value > $high) return 'H';
return null; // Normal
}
Out of Scope (Post-MVP)
- Edge API: Instrument integration (
app/Controllers/EdgeController.php) - Worklist Generation: Technician worklists
- Calculated Tests: Formula execution (CALC type)
- Quality Control: QC samples, Levy-Jennings charts
- Calculated Test Execution: Deferred to later
- Delta Checking: Result trending
- Critical Result Alerts: Notification system
- Audit Trail: Complete audit logging
Success Criteria
- Can enter result values via API
- Results auto-validate against refnum ranges
- Abnormal results show L/H flags
- Can view all results for an order
- Can generate PDF lab report
- Report shows patient, order, results with flags
Files to Create/Modify
Create
app/Controllers/ReportController.php- HTML report controllerapp/Views/reports/lab_report.php- HTML report template with dynamic flag calculationapp/Database/Migrations/2026-03-04-073950_RemoveFlagColumnFromPatRes.php- Remove Flag columnpublic/paths/reports.yaml- OpenAPI documentation for reports endpoint
Modify
app/Models/PatResultModel.php- Add validation methods (validateAndFlag, getByOrder, getByPatient, updateWithValidation, getWithRelations, softDelete)app/Controllers/ResultController.php- Full CRUD (index, show, update, delete)app/Config/Routes.php- New routes for results and reportspublic/paths/results.yaml- Updated OpenAPI documentationpublic/api-docs.yaml- Added Reports tag- Regenerated
public/api-docs.bundled.yaml
Note: Flag is calculated dynamically at runtime, not stored in database. This allows for:
- Real-time validation against current reference ranges
- Support for reference range updates without re-processing historical results
- Reduced storage requirements
API Documentation: Remember to run node public/bundle-api-docs.js after updating YAML files to regenerate the bundled documentation.
Last Updated: 2026-03-04
Sprint Duration: 2 weeks
Team Size: 1 developer