# 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 - `ResultController` only returns JWT payload (no CRUD) - `PatResultModel` has 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 names - `getByPatient($internalPID)` - Get cumulative patient results - `updateWithValidation($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_id` or `patient_id` query 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: ```php // 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 print` support **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): ```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) ```php // 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 1. `app/Controllers/ReportController.php` - HTML report controller 2. `app/Views/reports/lab_report.php` - HTML report template with dynamic flag calculation 3. `app/Database/Migrations/2026-03-04-073950_RemoveFlagColumnFromPatRes.php` - Remove Flag column 4. `public/paths/reports.yaml` - OpenAPI documentation for reports endpoint ### Modify 1. `app/Models/PatResultModel.php` - Add validation methods (validateAndFlag, getByOrder, getByPatient, updateWithValidation, getWithRelations, softDelete) 2. `app/Controllers/ResultController.php` - Full CRUD (index, show, update, delete) 3. `app/Config/Routes.php` - New routes for results and reports 4. `public/paths/results.yaml` - Updated OpenAPI documentation 5. `public/api-docs.yaml` - Added Reports tag 6. 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*