diff --git a/.serena/.gitignore b/.serena/.gitignore new file mode 100644 index 0000000..14d86ad --- /dev/null +++ b/.serena/.gitignore @@ -0,0 +1 @@ +/cache diff --git a/.serena/project.yml b/.serena/project.yml new file mode 100644 index 0000000..024bb1a --- /dev/null +++ b/.serena/project.yml @@ -0,0 +1,89 @@ +# list of languages for which language servers are started; choose from: +# al bash clojure cpp csharp +# csharp_omnisharp dart elixir elm erlang +# fortran fsharp go groovy haskell +# java julia kotlin lua markdown +# matlab nix pascal perl php +# powershell python python_jedi r rego +# ruby ruby_solargraph rust scala swift +# terraform toml typescript typescript_vts vue +# yaml zig +# (This list may be outdated. For the current list, see values of Language enum here: +# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py +# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.) +# Note: +# - For C, use cpp +# - For JavaScript, use typescript +# - For Free Pascal/Lazarus, use pascal +# Special requirements: +# - csharp: Requires the presence of a .sln file in the project folder. +# - pascal: Requires Free Pascal Compiler (fpc) and optionally Lazarus. +# When using multiple languages, the first language server that supports a given file will be used for that file. +# The first language is the default language and the respective language server will be used as a fallback. +# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored. +languages: +- php + +# the encoding used by text files in the project +# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings +encoding: "utf-8" + +# whether to use project's .gitignore files to ignore files +ignore_all_files_in_gitignore: true + +# list of additional paths to ignore in all projects +# same syntax as gitignore, so you can use * and ** +ignored_paths: [] + +# whether the project is in read-only mode +# If set to true, all editing tools will be disabled and attempts to use them will result in an error +# Added on 2025-04-18 +read_only: false + +# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details. +# Below is the complete list of tools for convenience. +# To make sure you have the latest list of tools, and to view their descriptions, +# execute `uv run scripts/print_tool_overview.py`. +# +# * `activate_project`: Activates a project by name. +# * `check_onboarding_performed`: Checks whether project onboarding was already performed. +# * `create_text_file`: Creates/overwrites a file in the project directory. +# * `delete_lines`: Deletes a range of lines within a file. +# * `delete_memory`: Deletes a memory from Serena's project-specific memory store. +# * `execute_shell_command`: Executes a shell command. +# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced. +# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type). +# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type). +# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes. +# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file. +# * `initial_instructions`: Gets the initial instructions for the current project. +# Should only be used in settings where the system prompt cannot be set, +# e.g. in clients you have no control over, like Claude Desktop. +# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol. +# * `insert_at_line`: Inserts content at a given line in a file. +# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol. +# * `list_dir`: Lists files and directories in the given directory (optionally with recursion). +# * `list_memories`: Lists memories in Serena's project-specific memory store. +# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building). +# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context). +# * `read_file`: Reads a file within the project directory. +# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store. +# * `remove_project`: Removes a project from the Serena configuration. +# * `replace_lines`: Replaces a range of lines within a file with new content. +# * `replace_symbol_body`: Replaces the full definition of a symbol. +# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen. +# * `search_for_pattern`: Performs a search for a pattern in the project. +# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase. +# * `switch_modes`: Activates modes by providing a list of their names +# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information. +# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task. +# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed. +# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store. +excluded_tools: [] + +# initial prompt for the project. It will always be given to the LLM upon activating the project +# (contrary to the memories, which are loaded on demand). +initial_prompt: "" + +project_name: "clqms01" +included_optional_tools: [] diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index fe4396c..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,235 +0,0 @@ -# CLQMS Backend - Agent Instructions - -**Project:** Clinical Laboratory Quality Management System (CLQMS) Backend -**Framework:** CodeIgniter 4 (PHP 8.1+) -**Platform:** Windows - Use PowerShell or CMD for terminal commands -**Frontend:** Alpine.js (views/v2 directory) - -## Build / Test Commands - -```bash -# Install dependencies -composer install - -# Run all tests -composer test -php vendor/bin/phpunit - -# Run single test file -php vendor/bin/phpunit tests/feature/Patients/PatientIndexTest.php - -# Run single test method -php vendor/bin/phpunit tests/feature/Patients/PatientIndexTest.php --filter=testIndexWithoutParams - -# Run tests with coverage -php vendor/bin/phpunit --coverage-html build/logs/html - -# Run tests in verbose mode -php vendor/bin/phpunit --verbose -``` - -**Test Structure:** -- Feature tests: `tests/feature/` - API endpoint testing with `FeatureTestTrait` -- Unit tests: `tests/unit/` - Model/Logic testing -- Base test case: `Tests\Support\v2\MasterTestCase.php` - Provides JWT auth and helper methods - -## Code Style Guidelines - -### PHP Standards -- **PHP Version:** 8.1 minimum -- **PSR-4 Autoloading:** Follow namespace-to-path conventions (`App\Controllers\*`, `App\Models\*`) -- **Line endings:** Unix-style (LF) - configure editor accordingly - -### Naming Conventions -| Element | Convention | Examples | -|---------|------------|----------| -| Classes | PascalCase | `PatientController`, `BaseModel` | -| Methods | camelCase | `getPatient()`, `createPatient()` | -| Variables | camelCase | `$internalPID`, `$patientData` | -| Constants | UPPER_SNAKE_CASE | `ORDER_PRIORITY`, `TEST_TYPE` | -| Table names | snake_case | `patient`, `pat_idt`, `valueset` | -| Column names | PascalCase (original DB) | `InternalPID`, `PatientID` | - -### File Organization -``` -app/ -├── Controllers/{Domain}/ -│ └── DomainController.php -├── Models/{Domain}/ -│ └── DomainModel.php -├── Libraries/ -│ └── ValueSet.php # Base lookup class (loads from JSON) -│ └── Lookups.php # Extends ValueSet - use this for lookups -└── Views/v2/ -``` - -### Imports and Namespaces -```php -db = \Config\Database::connect(); - $this->model = new PatientModel(); - $this->rules = [...]; // Validation rules - } - - public function index() { - try { - $data = $this->model->findAll(); - return $this->respond([...], 200); - } catch (\Exception $e) { - return $this->failServerError($e->getMessage()); - } - } -} -``` - -### Model Patterns -```php -class PatientModel extends BaseModel { - protected $table = 'patient'; - protected $primaryKey = 'InternalPID'; - protected $allowedFields = [...]; - protected $useSoftDeletes = true; - protected $deletedField = 'DelDate'; - - public function getPatients(array $filters = []): array { - // Query builder chain - $this->select('...'); - $this->join(...); - if (!empty($filters['key'])) { - $this->where('key', $filters['key']); - } - return $this->findAll(); - } -} -``` - -### Error Handling -- Controllers: Use try-catch with `failServerError()`, `failValidationErrors()`, `failNotFound()` -- Models: Throw `\Exception` with descriptive messages -- Database errors: Check `$db->error()` after operations -- Always validate input before DB operations - -### Validation Rules -```php -protected $rules = [ - 'PatientID' => 'required|regex_match[/^[A-Za-z0-9]+$/]|max_length[30]', - 'EmailAddress' => 'permit_empty|valid_email|max_length[100]', - 'Phone' => 'permit_empty|regex_match[/^\+?[0-9]{8,15}$/]', -]; -``` - -### Date Handling -- All dates stored/retrieved in UTC via `BaseModel` callbacks -- Use `utc` helper functions: `convert_array_to_utc()`, `convert_array_to_utc_iso()` -- Format: ISO 8601 (`Y-m-d\TH:i:s\Z`) for API responses - -### API Response Format -```php -// Success -return $this->respond([ - 'status' => 'success', - 'message' => 'Data fetched successfully', - 'data' => $rows -], 200); - -// Created -return $this->respondCreated([ - 'status' => 'success', - 'message' => 'Record created' -]); - -// Error -return $this->failServerError('Something went wrong: ' . $e->getMessage()); -``` - -### Database Transactions -```php -$db->transBegin(); -try { - $this->insert($data); - $this->checkDbError($db, 'Insert operation'); - $db->transCommit(); - return $insertId; -} catch (\Exception $e) { - $db->transRollback(); - throw $e; -} -``` - -### Frontend Integration (Alpine.js) -- API calls use `BASEURL` global variable -- Include `credentials: 'include'` for authenticated requests -- Modals use `x-show` with `@click.self` backdrop close - -### Lookups Library -Use `App\Libraries\Lookups` (extends `ValueSet`) for all static lookup values. Data is loaded from JSON files in `app/Libraries/Data/valuesets/`: -```php -use App\Libraries\Lookups; - -// Get formatted for frontend dropdowns [{value: 'X', label: 'Y'}, ...] -$gender = Lookups::get('gender'); -$priorities = Lookups::get('order_priority'); - -// Get raw data [{key: 'X', value: 'Y'}, ...] -$raw = Lookups::getRaw('gender'); - -// Get single label by key -$label = Lookups::getLabel('gender', '1'); // Returns 'Female' - -// Get options with key/value pairs -$options = Lookups::getOptions('gender'); -// Returns: [['key' => '1', 'value' => 'Female'], ...] - -// Transform data with lookup labels -$patients = Lookups::transformLabels($patients, [ - 'Sex' => 'gender', - 'Priority' => 'order_priority' -]); - -// Clear cache after data changes -Lookups::clearCache(); -``` - -### Important Notes -- **Soft deletes:** Use `DelDate` field instead of hard delete -- **UTC timezone:** All dates normalized to UTC automatically -- **JWT auth:** API endpoints require Bearer token in `Authorization` header -- **No comments:** Do not add comments unless explicitly requested diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..1c0eade --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,200 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +--- + +## Tool Preference: Use Serena MCP + +**Always prioritize Serena MCP tools for code operations to minimize tool calls:** + +- **Use `find_symbol` / `get_symbols_overview`** instead of `Read` for exploring code structure +- **Use `replace_symbol_body`** instead of `Edit` for modifying functions, methods, classes +- **Use `insert_before_symbol` / `insert_after_symbol`** for adding new code symbols +- **Use `search_for_pattern`** instead of `Grep` for searching code patterns +- **Use `list_dir` / `find_file`** instead of `Glob` for file discovery + +This leverages semantic code understanding for this PHP codebase. + +--- + +## Common Commands + +### Testing +```bash +# Run all tests +vendor/bin/phpunit + +# Run specific test file +vendor/bin/phpunit tests/feature/UniformShowTest.php + +# Run tests in a directory +vendor/bin/phpunit app/Models + +# Generate code coverage report +vendor/bin/phpunit --colors --coverage-text=tests/coverage.txt --coverage-html=tests/coverage/ -d memory_limit=1024m +``` + +### Database +```bash +# Run migrations +spark migrate + +# Rollback migrations +spark migrate:rollback + +# Seed database +spark db:seed DBSeeder + +# Refresh all migrations and seed +spark migrate:refresh --seed +``` + +### CodeIgniter CLI +```bash +# General help +spark help + +# Generate code (scaffolding) +spark make:model ModelName +spark make:controller ControllerName +spark make:migration MigrationName +``` + +### API Documentation +```bash +# Keep api-docs.yaml updated whenever controllers change +# File: public/api-docs.yaml +# After modifying controllers, update the OpenAPI schema definitions +# to reflect new field names, types, and response formats +``` + +--- + +## Architecture Overview + +### Core Pattern: MVC + Libraries + +CLQMS follows CodeIgniter 4 conventions with these key architectural patterns: + +**Controllers** (`app/Controllers/`) +- Organized by domain: `Patient/`, `Organization/`, `Specimen/`, `Test/`, `Contact/` +- Root-level controllers for cross-domain concerns: `AuthController`, `EdgeController`, `ValueSetController` +- All controllers extend `BaseController` which provides request/response helpers + +**Models** (`app/Models/`) +- Organized by domain matching controllers: `Patient/`, `Organization/`, `Specimen/`, `Test/`, etc. +- All models extend `BaseModel` which provides automatic UTC date normalization +- Model callbacks: `beforeInsert/Update` normalize dates to UTC, `afterFind/Insert/Update` convert to UTC ISO format + +**Libraries** (`app/Libraries/`) +- `ValueSet` - Static lookup system using JSON files (see "Lookup System" below) + +### Lookup System: ValueSet Library + +The system uses a **JSON file-based lookup system** instead of database queries for static values. + +**Location:** `app/Libraries/Data/*.json` (44+ JSON files) + +**Key Files:** +- `_meta.json` - Metadata about lookup definitions +- `sex.json`, `order_status.json`, `specimen_type.json`, `test_type.json`, etc. + +**Usage:** +```php +use App\Libraries\ValueSet; + +// Get dropdown-formatted options +$options = ValueSet::get('sex'); // [["value"=>"1","label"=>"Female"],...] + +// Get raw data +$raw = ValueSet::getRaw('sex'); // [["key"=>"1","value"=>"Female"],...] + +// Get label for a specific key +$label = ValueSet::getLabel('sex', '1'); // "Female" + +// Transform database records with lookup text labels +$data = ValueSet::transformLabels($patients, ['Sex' => 'sex']); + +// Clear cache after modifying JSON files +ValueSet::clearCache(); +``` + +**When to use ValueSet vs API:** +- **ValueSet library** - Static values that rarely change (fast, cached) +- **API `/api/valueset*`** - Dynamic values managed by admins at runtime + +### Database Migrations Structure + +Migrations are numbered sequentially starting with `2026-01-01-`: + +| Migration | Tables Created | +|-----------|----------------| +| 000001 | valueset, counter, containerdef, occupation, specialty | +| 000002 | account, site, location, discipline, department | +| 000003 | patient, patidentifier, pataddress, patcontact | +| 000004 | contact, contactdetail, userdevices, loginattempts | +| 000005 | patvisit, patinsurance | +| 000006 | porder, orderitem | +| 000007 | specimen, specmenactivity, containerdef | +| 000008 | testdefinition, testactivity, refnum, reftxt | +| 000009 | patresult, patresultdetail, patresultcomment | +| 000010 | edgeres, edgestatus, edgeack, workstation | + +### Authentication & Authorization + +- **JWT-based authentication** using `firebase/php-jwt` +- **AuthFilter** - Route filter protecting API endpoints +- **AuthController** - Login/logout endpoints (`POST /api/login`, `POST /api/logout`) +- **AuthV2Controller** - V2 authentication endpoints + +### Edge API (Instrument Integration) + +The `EdgeController` provides endpoints for laboratory instrument integration via `tiny-edge` middleware: + +- `POST /api/edge/results` - Receive instrument results (stored in `edgeres` table) +- `GET /api/edge/orders` - Fetch pending orders for instruments +- `POST /api/edge/orders/:id/ack` - Acknowledge order delivery +- `POST /api/edge/status` - Log instrument status updates + +**Workflow:** `Instrument → tiny-edge → edgeres table → [Processing] → patresult table` + +### Test Types System + +Tests in `testdefinition` table support multiple types via `TestType` field: + +| Code | Type | Description | +|------|------|-------------| +| TEST | Technical | Individual lab test with specs | +| PARAM | Parameter | Non-lab measurement | +| CALC | Calculated | Test with formula | +| GROUP | Panel/Profile | Contains multiple tests | +| TITLE | Section | Report organization header | + +### Reference Range Architecture + +Reference ranges support multiple types for result validation: + +| Type | Table | Purpose | +|------|-------|---------| +| Numeric | `refnum` | Ranges with age/sex criteria | +| Threshold | `refthold` | Critical values | +| Text | `reftxt` | Text-based references | +| Value Set | `refvset` | Coded references | + +### Routes Organization + +Routes are defined in `app/Config/Routes.php`: +- API routes: `/api/{resource}` +- Auth-protected routes use `AuthFilter` + +--- + +## Project Structure Notes + +- **Language:** PHP 8.1+ (PSR-compliant) +- **Framework:** CodeIgniter 4 +- **Database:** MySQL with migration-based schema management +- **Testing:** PHPUnit 10.5+ (tests in `tests/` directory) +- **Entry point:** `public/index.php` (web), `spark` (CLI) +- **Environment config:** `.env` file (copy from `env` template) diff --git a/PRD.md b/PRD.md new file mode 100644 index 0000000..8e06b6a --- /dev/null +++ b/PRD.md @@ -0,0 +1,747 @@ +# CLQMS Product Requirements Document + +> Clinical Laboratory Quality Management System - Minimum Viable Product +> +> Version: 1.0 +> Last Updated: 2026-01-28 + +--- + +## 1. Executive Summary + +CLQMS is a **REST API backend** for a Clinical Laboratory Quality Management System designed to manage the complete laboratory workflow from order creation to result reporting. The system integrates with laboratory instruments via the Edge API and provides comprehensive specimen tracking, result management, and quality control capabilities. + +### Product Vision +To provide a **headless, API-first** laboratory information system that serves as the backend for any frontend client (web, mobile, desktop) while streamlining laboratory operations, ensuring regulatory compliance, and integrating seamlessly with laboratory instruments. + +### Core Value Propositions +- **API-First Design**: Pure REST API with comprehensive endpoints for all laboratory operations +- **Complete Workflow Coverage**: Order → Collection → Reception → Preparation → Analysis → Verification → Review → Reporting +- **Instrument Integration**: Standard Edge API for bidirectional communication with laboratory analyzers +- **Quality Management**: Built-in QC, calibration tracking, and audit trails +- **Modern Architecture**: RESTful API, JWT authentication, UTC timestamp normalization +- **Frontend Agnostic**: No view layer - clients consume JSON API responses + +--- + +## 2. Product Goals + +### MVP Success Criteria +| Goal | Description | Success Metric | +|------|-------------|----------------| +| Patient Registration | Register patients with demographics | Patient can be created and retrieved | +| Test Ordering | Generate valid order IDs and test mappings | Order ID format: LLYYMMDDXXXXX | +| Specimen Tracking | Track specimens through entire lifecycle | Status updates: Collection→Transport→Reception→Prep→Analysis | +| Result Entry | Enter results with reference range validation | Numeric, text, valueset, and range results supported | +| Result Verification | Multi-level verification workflow | VER → REV → REP status tracking | +| Instrument Integration | Receive results via Edge API | Results stored and processed to patient results | +| Master Data Management | Complete lookup and reference data | All value sets and test definitions manageable | + +--- + +## 3. Target Users + +### API Consumers + +| Consumer Type | Description | Key API Needs | +|---------------|-------------|---------------| +| Frontend Applications | Web apps, mobile apps, desktop clients consuming the API | Full CRUD operations, authentication, real-time updates | +| Laboratory Instruments | Analyzers and middleware systems | Edge API for orders/results, status monitoring | +| Third-Party Systems | HIS, EMR, billing systems | Patient data sync, order/results integration | +| Integration Partners | External lab networks, reference labs | Order referral, result reporting APIs | + +### API Client Types +- **Web Clients**: Single-page applications (React, Vue, Angular) consuming REST endpoints +- **Mobile Clients**: iOS/Android apps for mobile laboratory workflows +- **Desktop Clients**: Native applications for result entry and verification +- **Middleware Systems**: Instrument integration layer (tiny-edge) +- **Batch Processors**: Scheduled jobs, data import/export utilities + +--- + +## 4. Functional Requirements + +### 4.1 Priority Framework + +| Priority | Definition | Timeline | +|----------|------------|----------| +| **P0** | Must-have for MVP | Phase 1 | +| **P1** | Should-have for MVP | Phase 2 | +| **P2** | Nice-to-have for production | Phase 3 | +| **P3** | Future enhancement | Phase 4+ | + +### 4.2 Master Data Requirements (P0 - Foundation) + +> **Rationale:** All transactional API workflows depend on complete master data. Master data must be completed before order/test/result API operations can function properly. + +#### 4.2.1 Value Sets & Lookups ✅ +| Requirement | Description | Status | +|-------------|-------------|--------| +| VS-001 | Value set definitions management | Complete | +| VS-002 | Value set values management | Complete | +| VS-003 | JSON-based static lookup system | Complete | +| VS-004 | Lookup caching for performance | Complete | + +**Key Value Sets:** +- Demographics: sex, marital_status, race, ethnic +- Orders: order_status, order_priority, priority +- Specimens: specimen_type, specimen_status, specimen_condition +- Results: result_type, result_status, result_unit +- Organizations: site_type, location_type +- Technical: test_type, unit, formula_language + +#### 4.2.2 Test Definitions ✅ +| Requirement | Description | Status | +|-------------|-------------|--------| +| TD-001 | Test site definitions (testdefsite) | Complete | +| TD-002 | Technical specifications (testdeftech) | Complete | +| TD-003 | Calculated test formulas (testdefcal) | Complete | +| TD-004 | Test groups/panels (testdefgrp) | Complete | +| TD-005 | Test parameters | Complete | +| TD-006 | Instrument test mapping (testmap) | Complete | + +**Test Types Supported:** +| Code | Type | Description | +|------|------|-------------| +| TEST | Technical | Individual lab test with specs | +| PARAM | Parameter | Non-lab measurement | +| CALC | Calculated | Test with formula | +| GROUP | Panel/Profile | Contains multiple tests | +| TITLE | Section | Report organization header | + +#### 4.2.3 Reference Ranges ⚠️ +| Requirement | Description | Status | +|-------------|-------------|--------| +| RR-001 | Numeric reference ranges (refnum) | Complete | +| RR-002 | Threshold ranges (refthold) | Missing migration | +| RR-003 | Text reference ranges (reftxt) | Complete | +| RR-004 | Value set reference ranges (refvset) | Missing migration | +| RR-005 | Age/sex-based criteria | Complete (refnum) | +| RR-006 | Critical value flagging | Needs implementation | + +**Gap Analysis:** Missing database migrations for `refthold` and `refvset` tables. + +#### 4.2.4 Organization Structure ✅ +| Requirement | Description | Status | +|-------------|-------------|--------| +| ORG-001 | Account management | Complete | +| ORG-002 | Site management | Complete | +| ORG-003 | Location management | Complete | +| ORG-004 | Discipline management | Complete | +| ORG-005 | Department management | Complete | +| ORG-006 | Workstation management | Complete | + +#### 4.2.5 Specimen Types ✅ +| Requirement | Description | Status | +|-------------|-------------|--------| +| SPEC-001 | Container definitions (containerdef) | Complete | +| SPEC-002 | Specimen type catalog | Complete | +| SPEC-003 | Collection methods | Complete | +| SPEC-004 | Container classes and sizes | Complete | + +### 4.3 Order Management (P0) + +| Requirement | Description | Acceptance Criteria | +|-------------|-------------|---------------------| +| ORD-001 | Create order with patient linkage | OrderID generated in LLYYMMDDXXXXX format | +| ORD-002 | Update order details | All fields editable before verification | +| ORD-003 | Delete order (soft delete) | DelDate set, order not displayed | +| ORD-004 | Order status tracking | ORD → SCH → ANA → VER → REV → REP | +| ORD-005 | Priority assignment | Stat/Routine/ASAP | +| ORD-006 | Order attachment handling | Support for ordercom, orderatt tables | +| ORD-007 | Test-to-order mapping | Multiple tests per order | +| ORD-008 | Calculated test auto-selection | Parameters auto-added based on formulas | +| ORD-009 | Provider identification | Ordering provider captured | +| ORD-010 | Order date/time tracking | UTC timestamps normalized | + +**Minimum Data Required for Order Creation:** +```sql +-- Patient (at least 1 required) +-- Order Status values (VSetID=11): ORD, SCH, ANA, VER, REV, REP +-- Priority values (VSetID=10): S, R, A +-- Counter for Order ID generation +``` + +### 4.4 Specimen Management (P0) + +| Requirement | Description | Acceptance Criteria | +|-------------|-------------|---------------------| +| SPM-001 | Create specimen from order | SpecimenID = OrderID + SSS + C | +| SPM-002 | Collection status | Specimen marked as collected | +| SPM-003 | In-transit status | Track specimen transport | +| SPM-004 | Reception | Received or Rejected status | +| SPM-005 | Preparation | Centrifuge, Aliquot, Pre-treatment | +| SPM-006 | Storage | Stored status with location | +| SPM-007 | Dispatch | Dispatch status for referral | +| SPM-008 | Condition tracking | HEM, ITC, LIP flag support | +| SPM-009 | Activity logging | Full audit trail via specmenactivity | +| SPM-010 | Specimen-test linking | Tests mapped to specimens | + +**Specimen ID Format:** `OrderID + SpecimenTypeCode(3) + ContainerCode(1)` + +**Specimen Conditions:** +| Code | Description | +|------|-------------| +| HEM | Hemolysis | +| ITC | Insufficient | +| LIP | Lipemic | + +### 4.5 Result Management (P0) + +| Requirement | Description | Acceptance Criteria | +|-------------|-------------|---------------------| +| RES-001 | Numeric result entry | Value with unit, range validation | +| RES-002 | Text result entry | Free text or coded values | +| RES-003 | Value set result entry | Dropdown selection from valueset | +| RES-004 | Range result entry | Min/max with reference | +| RES-005 | Reference range validation | Auto-flag abnormal results | +| RES-006 | Critical value flagging | Threshold-based alerts | +| RES-007 | Result verification (Technical) | First-level verification (VER status) | +| RES-008 | Result verification (Clinical) | Pathologist review (REV status) | +| RES-009 | Result reporting | Final report status (REP) | +| RES-010 | Result rerun | AspCnt tracking for reruns | +| RES-011 | Result comments | Support for patresultcomment | +| RES-012 | Result details | Multiple details per result (patresultdetail) | + +**Result Verification Workflow:** +``` +Entry → VER (Technical) → REV (Clinical) → REP (Reported) +``` + +### 4.6 Patient Visit (P1) + +| Requirement | Description | Acceptance Criteria | +|-------------|-------------|---------------------| +| VIS-001 | Create patient visit | Visit linked to patient | +| VIS-002 | Visit-to-order linkage | Orders associated with visits | +| VIS-003 | ADT tracking | Admission, Discharge, Transfer | +| VIS-004 | Diagnosis linking | patdiag table support | +| VIS-005 | Insurance tracking | patinsurance table support | + +### 4.7 Instrument Integration (P1) + +| Requirement | Description | Acceptance Criteria | +|-------------|-------------|---------------------| +| EDGE-001 | Receive instrument results | POST /api/edge/results | +| EDGE-002 | Store raw results | edgeres table | +| EDGE-003 | Status logging | edgestatus tracking | +| EDGE-004 | Order acknowledgment | edgeack confirmation | +| EDGE-005 | Fetch pending orders | GET /api/edge/orders | +| EDGE-006 | Acknowledge order delivery | POST /api/edge/orders/:id/ack | +| EDGE-007 | Instrument status updates | POST /api/edge/status | + +**Edge API Flow:** +``` +Instrument → tiny-edge → /api/edge/results → edgeres table → Processing → patresult table +``` + +### 4.8 Quality Control (P2) + +| Requirement | Description | Acceptance Criteria | +|-------------|-------------|---------------------| +| QC-001 | QC result entry | calres table storage | +| QC-002 | Levey-Jennings data | Data preparation endpoints | +| QC-003 | QC validation | 2SD auto-validation rules | +| QC-004 | Sigma metrics | Calculation endpoint | +| QC-005 | QC history | Trend analysis support | + +### 4.9 Calibration (P2) + +| Requirement | Description | Acceptance Criteria | +|-------------|-------------|---------------------| +| CAL-001 | Calibration result entry | Factor tracking | +| CAL-002 | Calibration history | Historical query endpoint | +| CAL-003 | Calibration validation | Rule-based validation | + +### 4.10 Audit Trail (P2) + +| Requirement | Description | Acceptance Criteria | +|-------------|-------------|---------------------| +| AUD-001 | Audit logging middleware | Automatic change capture | +| AUD-002 | What/who/when tracking | Complete change record | +| AUD-003 | Security logging | Authentication attempts | +| AUD-004 | Audit query endpoint | Searchable audit logs | + +### 4.11 Inventory & Billing (P3) + +| Requirement | Description | Acceptance Criteria | +|-------------|-------------|---------------------| +| INV-001 | Counter management | ORDER, SID, etc. | +| INV-002 | Product catalog | Reagents, consumables | +| INV-003 | Reagent tracking | Lot numbers, expiration | +| INV-004 | Consumables usage | Usage logging | +| INV-005 | Billing export | Tariff-based billing | +| INV-006 | Service class selection | Billing tiers | + +--- + +## 5. Non-Functional Requirements + +### 5.1 Security +| Requirement | Description | +|-------------|-------------| +| AUTH-001 | JWT-based authentication for all API endpoints | +| AUTH-002 | Role-based access control (RBAC) | +| AUTH-003 | Password hashing with bcrypt | +| AUTH-004 | Login attempt tracking | +| AUTH-005 | Device registration (userdevices) | + +### 5.2 Data Integrity +| Requirement | Description | +|-------------|-------------| +| DATA-001 | Soft delete (DelDate) on all transactional tables | +| DATA-002 | UTC timezone normalization for all datetime fields | +| DATA-003 | Automatic date conversion (UTC ↔ ISO 8601) | +| DATA-004 | Referential integrity via foreign keys | +| DATA-005 | Transactional data consistency | + +### 5.3 Performance +| Requirement | Description | Target | +|-------------|-------------|--------| +| PERF-001 | Standard query response time | < 2 seconds | +| PERF-002 | API endpoint latency | < 500ms (p95) | +| PERF-003 | Lookup cache hit rate | > 95% | +| PERF-004 | Database indexing | All foreign keys indexed | + +### 5.4 Reliability +| Requirement | Description | +|-------------|-------------| +| REL-001 | Graceful error handling | +| REL-002 | Database transaction support | +| REL-003 | API rate limiting | +| REL-004 | Request validation | + +### 5.5 Maintainability +| Requirement | Description | +|-------------|-------------| +| MAIN-001 | PSR-12 coding standards | +| MAIN-002 | RESTful API design | +| MAIN-003 | Comprehensive documentation | +| MAIN-004 | Unit test coverage > 80% | +| MAIN-005 | Migration-based schema management | + +--- + +## 6. API Workflow Examples + +### 6.1 Core Laboratory API Workflow + +``` +POST /api/patient → Create patient +POST /api/ordertest → Create order with tests +POST /api/specimen → Create specimen from order +PATCH /api/specimen/status → Update specimen status +POST /api/patresult → Enter results +PATCH /api/patresult/status → Verify results (VER → REV → REP) +GET /api/patresult/:id → Retrieve final report +``` + +### 6.2 Order Creation API Sequence + +```bash +# 1. Create/Register Patient +POST /api/patient +{ + "NameFirst": "John", + "NameLast": "Doe", + "Sex": "1", + "Birthdate": "1990-05-15" +} +# Response: { "InternalPID": 123, "PatientID": "PT001" } + +# 2. Create Order with Tests +POST /api/ordertest +{ + "InternalPID": 123, + "Priority": "R", + "OrderingProvider": "Dr. Smith", + "Tests": [{ "TestID": 10 }, { "TestID": 15 }] +} +# Response: { "OrderID": "00250112000001", "OrderStatus": "ORD" } + +# 3. Create Specimens (if applicable) +POST /api/specimen +{ + "OrderID": "00250112000001", + "SpecimenType": "BLD", + "SpecimenStatus": "COLLECTED" +} +# Response: { "SID": "00250112000001BLDA" } +``` + +### 6.3 Result Entry & Verification API Sequence + +```bash +# 1. Enter Results +POST /api/patresult +{ + "OrderID": "00250112000001", + "TestID": 10, + "ResultValue": "5.2", + "ResultUnit": "g/dL", + "ResultStatus": "ENTRY" +} + +# 2. Technical Verification +PATCH /api/patresult/status +{ + "ResultID": 456, + "ResultStatus": "VER", + "VerifierID": 7 +} + +# 3. Clinical Review +PATCH /api/patresult/status +{ + "ResultID": 456, + "ResultStatus": "REV", + "VerifierID": 8 +} + +# 4. Report Finalization +PATCH /api/patresult/status +{ + "ResultID": 456, + "ResultStatus": "REP" +} +``` + +### 6.4 Instrument Integration API Workflow + +```bash +# 1. Fetch Pending Orders for Instrument +GET /api/edge/orders?instrument=coulter_counter + +# 2. Instrument Acknowledges Order +POST /api/edge/orders/123/ack + +# 3. Instrument Sends Results +POST /api/edge/results +{ + "SampleID": "00250112000001", + "Results": [ + { "TestCode": "HGB", "Value": "14.5", "Unit": "g/dL" }, + { "TestCode": "WBC", "Value": "7.2", "Unit": "x10^9/L" } + ] +} + +# 4. System Processes Results (automatic or manual) +# Results stored in edgeres → processed to patresult +``` + +--- + +## 7. Technical Architecture + +### 7.1 Technology Stack + +| Layer | Technology | Version | +|-------|-----------|---------| +| Backend | PHP | 8.1+ | +| Framework | CodeIgniter | 4.x | +| Database | MySQL | 8.0+ | +| Authentication | JWT (firebase/php-jwt) | Latest | +| Testing | PHPUnit | 10.5+ | +| API Format | JSON | RESTful | + +### 7.2 Architecture Pattern + +``` +┌─────────────────────────────────────────────────────────┐ +│ API Consumers │ +│ (Web Apps, Mobile Apps, Desktop Clients, Instruments) │ +└────────────────────┬────────────────────────────────────┘ + │ HTTP/HTTPS (JSON) +┌────────────────────┴────────────────────────────────────┐ +│ REST API Layer │ +│ (Controllers: Patient, Order, Specimen, Result, etc.) │ +│ - JWT Authentication Filter │ +│ - Request Validation │ +│ - Response Formatting │ +└────────────────────┬────────────────────────────────────┘ + │ +┌────────────────────┴────────────────────────────────────┐ +│ Business Logic Layer │ +│ (Models + Libraries + Services) │ +│ - ValueSet Library (JSON-based lookups) │ +│ - Base Model (UTC normalization) │ +│ - Edge Processing Service │ +└────────────────────┬────────────────────────────────────┘ + │ +┌────────────────────┴────────────────────────────────────┐ +│ Data Access Layer │ +│ (CodeIgniter Query Builder) │ +└────────────────────┬────────────────────────────────────┘ + │ +┌────────────────────┴────────────────────────────────────┐ +│ MySQL Database │ +│ (Migration-managed schema) │ +└─────────────────────────────────────────────────────────┘ +``` + +### 7.3 API Design Principles + +| Principle | Implementation | +|-----------|----------------| +| **Resource-Based** | URLs represent resources (patients, orders, specimens) | +| **HTTP Methods** | GET (read), POST (create), PATCH (update), DELETE (soft delete) | +| **Stateless** | Each request contains all necessary context (JWT token) | +| **JSON Responses** | All API responses use consistent JSON format | +| **Versioning** | API versioning via URL prefix (/api/v1/, /api/v2/) | +| **Error Handling** | Standardized error responses with status codes | +| **Pagination** | List endpoints support pagination parameters | + +### 7.3 Database Schema + +**Core Transaction Tables:** +| Table | Purpose | Key Fields | +|-------|---------|------------| +| patient | Patient registry | InternalPID, PatientID, NameFirst, NameLast, Sex, Birthdate | +| porder | Laboratory orders | OrderID, InternalPID, OrderStatus, Priority | +| orderitem | Order tests | OrderID, TestID | +| specimen | Specimens | SID, SpecimenID, SpecimenStatus | +| patresult | Patient results | ResultID, OrderID, TestID, ResultValue | +| patresultdetail | Result details | ResultID, ParameterID, Value | + +**Master Data Tables:** +| Table | Purpose | +|-------|---------| +| valueset | Static lookup values | +| valuesetdef | Value set definitions | +| testdefsite | Test definitions by site | +| testdeftech | Technical test specifications | +| testdefcal | Calculated test formulas | +| testdefgrp | Test groups/panels | +| refnum | Numeric reference ranges | +| reftxt | Text reference ranges | + +**Integration Tables:** +| Table | Purpose | +|-------|---------| +| edgeres | Raw instrument results | +| edgestatus | Instrument status | +| edgeack | Order acknowledgment | + +### 7.4 API Specifications + +**Authentication Endpoint:** +``` +POST /api/login +Request: { "username": "...", "password": "..." } +Response: { "token": "jwt-token", "expires": 3600 } +``` + +**Demo Order Endpoint (No Auth):** +``` +POST /api/demo/order +Request: { + "PatientID": "PT001", + "NameFirst": "John", + "NameLast": "Doe", + "Sex": "1", + "Birthdate": "1990-05-15", + "Priority": "R", + "OrderingProvider": "Dr. Smith" +} +Response: { + "status": "success", + "data": { + "PatientID": "DEMO1736689600", + "InternalPID": 1, + "OrderID": "00250112000001", + "OrderStatus": "ORD" + } +} +``` + +**Order Endpoints:** +``` +GET /api/ordertest - List orders +POST /api/ordertest - Create order +GET /api/ordertest/:id - Get order +PUT /api/ordertest/:id - Update order +DELETE /api/ordertest/:id - Delete order +POST /api/ordertest/status - Update status +``` + +**Edge API Endpoints:** +``` +POST /api/edge/results - Receive results +GET /api/edge/orders - Fetch pending orders +POST /api/edge/orders/:id/ack - Acknowledge order +POST /api/edge/status - Log status +``` + +--- + +## 8. Implementation Roadmap + +### Phase 0: Master Data Completion (P0) +> **Prerequisite:** All master data must be complete before transactional workflows + +| Task | Effort | Dependencies | +|------|--------|--------------| +| Complete reference range migrations | 2 days | None | +| Create reference range CRUD controllers | 3 days | Migrations | +| Verify all value sets populated | 1 day | None | +| Complete test definitions data entry | 2 days | None | +| **Total** | **8 days** | - | + +### Phase 1: Core Lab Workflow (P0) +| Task | Effort | Dependencies | +|------|--------|--------------| +| Order CRUD complete | 5 days | Phase 0 | +| Order ID generation | 2 days | Order CRUD | +| Order status tracking | 2 days | Order CRUD | +| Specimen API complete | 5 days | Phase 0 | +| Specimen status workflow | 3 days | Specimen API | +| Result CRUD | 5 days | Phase 0 | +| Result entry with validation | 4 days | Result CRUD | +| Result verification workflow | 4 days | Result CRUD | +| Result report generation | 3 days | Result verification | +| **Total** | **33 days** | Phase 0 | + +### Phase 2: Instrument Integration (P1) +| Task | Effort | Dependencies | +|------|--------|--------------| +| Edge API results endpoint | 4 days | Phase 1 | +| Edge API orders endpoint | 3 days | Phase 1 | +| Test mapping management | 3 days | Phase 0 | +| Order acknowledgment | 2 days | Edge orders | +| Status logging | 2 days | Edge results | +| **Total** | **14 days** | Phase 1 | + +### Phase 3: Quality Management (P2) +| Task | Effort | Dependencies | +|------|--------|--------------| +| QC result entry | 4 days | Phase 2 | +| QC validation rules | 3 days | QC entry | +| Calibration tracking | 3 days | None | +| Audit trail implementation | 5 days | Phase 1 | +| **Total** | **15 days** | Phase 2 | + +### Phase 4: Additional Features (P3) +| Task | Effort | Dependencies | +|------|--------|--------------| +| Inventory management | 8 days | None | +| Billing integration | 6 days | Phase 1 | +| Advanced reporting | 5 days | Phase 1 | +| **Total** | **19 days** | Phase 1 | + +**Total MVP Timeline:** ~89 days (~4 months) +- Phase 0: 2 weeks +- Phase 1: 7 weeks +- Phase 2: 3 weeks +- Phase 3: 3 weeks +- Phase 4: 4 weeks + +--- + +## 9. Success Criteria + +### 9.1 API Endpoint Acceptance + +A REST API endpoint is considered complete when: +- [ ] HTTP methods implemented (GET, POST, PATCH, DELETE) +- [ ] Request validation implemented (input sanitization, type checking) +- [ ] JSON response format consistent with API standards +- [ ] Error responses include status code and error message +- [ ] JWT authentication/authorization configured +- [ ] Soft delete implemented (where applicable) +- [ ] UTC date normalization working +- [ ] Unit tests written (PHPUnit) +- [ ] API documented (request/response examples) +- [ ] Pagination support (list endpoints) +- [ ] Filtering/sorting support (where applicable) + +### 9.2 MVP Definition of Done + +The MVP is considered complete when: +1. ✅ Patient can be registered and retrieved +2. ✅ Order can be created with valid OrderID +3. ✅ Specimen can be tracked through all statuses +4. ✅ Results can be entered with range validation +5. ✅ Results can be verified (VER → REV → REP) +6. ✅ Instruments can send results via Edge API +7. ✅ All master data is manageable +8. ✅ JWT authentication protects all endpoints +9. ✅ Soft delete works on all transactions +10. ✅ UTC timestamps are normalized + +### 9.3 Performance Benchmarks + +| Metric | Target | Measurement | +|--------|--------|-------------| +| API response time (p95) | < 500ms | Apache Bench | +| Database query time | < 200ms | EXPLAIN analysis | +| Lookup cache hit rate | > 95% | Application metrics | +| Test coverage | > 80% | PHPUnit --coverage | + +--- + +## 10. Open Questions & Risks + +### 10.1 Open Questions +| Question | Impact | Target Date | +|----------|--------|-------------| +| Reference range types (refthold, refvset) - are they needed for MVP? | Medium | Phase 0 | +| Multi-site deployment requirements? | High | Phase 0 | +| Specific instrument integrations needed? | High | Phase 2 | +| Report format requirements (PDF/HTML)? | Medium | Phase 1 | +| HL7/FHIR integration requirements? | Low | Phase 4 | + +### 10.2 Technical Risks + +| Risk | Probability | Impact | Mitigation | +|------|-------------|--------|------------| +| Edge API protocol mismatches | Medium | High | Early instrument testing | +| Reference range complexity | Low | Medium | Start with refnum only | +| Performance at scale | Low | Medium | Database indexing, caching | +| Test definition data entry | High | Medium | Provide templates/bulk import | + +### 10.3 Business Risks + +| Risk | Probability | Impact | Mitigation | +|------|-------------|--------|------------| +| Scope creep | High | High | Strict MVP definition | +| Resource constraints | Medium | High | Phased delivery | +| Instrument compatibility | Medium | High | Edge API abstraction layer | + +--- + +## 11. Appendix + +### 11.1 Glossary + +| Term | Definition | +|------|------------| +| **OrderID** | Laboratory order identifier: LLYYMMDDXXXXX (Lab+Date+Sequence) | +| **SID** | Specimen identifier: OrderID + SpecimenType + Container | +| **VSetID** | Value set definition ID | +| **VID** | Value set value ID | +| **Edge API** | Standardized interface for laboratory instrument integration | +| **ADT** | Admission, Discharge, Transfer | +| **QC** | Quality Control | +| **LLYYMMDD** | Lab location (LL) + Year (YY) + Month (MM) + Day (DD) | +| **UTC** | Coordinated Universal Time | + +### 11.2 Reference Documents + +| Document | Location | +|----------|----------| +| Technical Architecture | `CLAUDE.md` | +| Implementation Tasks | `TODO.md` | +| API Documentation | `README.md` | +| Database Migrations | `app/Database/Migrations/` | + +### 11.3 Revision History + +| Version | Date | Author | Changes | +|---------|------|--------|---------| +| 1.0 | 2026-01-28 | Claude | Initial PRD from TODO.md | + +--- + +**Document Status:** Draft for Review +**Next Review:** Upon Phase 0 completion +**Approvals:** Pending diff --git a/README.md b/README.md index 6fb829e..4016225 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,40 @@ # CLQMS (Clinical Laboratory Quality Management System) -> **The core backend engine for modern clinical laboratory workflows.** +> **A REST API backend for modern clinical laboratory workflows.** -CLQMS is a robust, mission-critical API suite designed to streamline laboratory operations, ensure data integrity, and manage complex diagnostic workflows. Built on a foundation of precision and regulatory compliance, this system handles everything from patient registration to high-throughput test resulting. +--- + +## 🤖 For Claude Code + +**When working in this repository, prioritize Serena MCP tools for code operations:** + +- **Use `find_symbol` / `get_symbols_overview`** instead of `Read` for exploring code structure +- **Use `replace_symbol_body`** instead of `Edit` for modifying functions, methods, classes +- **Use `insert_before_symbol` / `insert_after_symbol`** for adding new code symbols +- **Use `search_for_pattern`** instead of `Grep` for searching code patterns +- **Use `list_dir` / `find_file`** instead of `Glob` for file discovery + +This minimizes tool calls and leverages semantic code understanding for this PHP codebase. + +--- + +CLQMS is a **headless REST API backend** designed to streamline laboratory operations, ensure data integrity, and manage complex diagnostic workflows. Built on a foundation of precision and regulatory compliance, this system provides comprehensive JSON endpoints for laboratory operations. + +**Key Characteristic:** This is an **API-only system** with no view layer. Frontend applications (web, mobile, desktop) consume these REST endpoints to build laboratory information systems. --- ## 🏛️ Core Architecture & Design -The system is currently undergoing a strategic **Architectural Redesign** to consolidate legacy structures into a high-performance, maintainable schema. This design, spearheaded by leadership, focuses on reducing technical debt and improving data consistency across: +CLQMS is a **headless REST API system** following a clean architecture pattern. The system is designed to be consumed by any frontend client (web, mobile, desktop) through comprehensive JSON endpoints. + +**API-First Architecture:** +- **No View Layer:** This system provides REST APIs only - no HTML views, no server-side rendering +- **Frontend Agnostic:** Any client can consume these APIs (React, Vue, Angular, mobile apps, desktop apps) +- **JSON-First:** All requests/responses use JSON format +- **Stateless:** Each API request is independent with JWT authentication + +The system is currently undergoing a strategic **Architectural Redesign** to consolidate legacy structures into a high-performance, maintainable schema. This design focuses on reducing technical debt and improving data consistency across: - **Unified Test Definitions:** Consolidating technical, calculated, and site-specific test data. - **Reference Range Centralization:** A unified engine for numeric, threshold, text, and coded results. @@ -30,23 +56,177 @@ The system is currently undergoing a strategic **Architectural Redesign** to con | Component | Specification | | :------------- | :------------ | | **Language** | PHP 8.1+ (PSR-compliant) | -| **Framework** | CodeIgniter 4 | +| **Framework** | CodeIgniter 4 (API-only mode) | | **Security** | JWT (JSON Web Tokens) Authorization | | **Database** | MySQL (Optimized Schema Migration in progress) | +| **API Format** | RESTful JSON | +| **Testing** | PHPUnit 10.5+ | --- ## 📂 Documentation & Specifications -For detailed architectural blueprints and API specifications, please refer to the internal documentation: +### Key Documents -👉 **[Internal Documentation Index](./docs/README.md)** +| Document | Location | Description | +|----------|----------|-------------| +| **PRD** | `PRD.md` | Complete Product Requirements Document (API-focused) | +| **Technical Guide** | `CLAUDE.md` | Architecture, coding standards, common commands | +| **API Overview** | This file | REST API documentation and endpoints | +| **Database Migrations** | `app/Database/Migrations/` | Database schema history | -Key documents: -- [Database Schema Redesign Proposal](./docs/20251216002-Test_OrderTest_RefRange_schema_redesign_proposal.md) -- [API Contract: Patient Registration](./docs/api_contract_patient_registration.md) -- [Database Design Review (Reference)](./docs/20251212001-database_design_review_sonnet.md) +### API Documentation + +All API endpoints follow REST conventions: + +**Base URL:** `/api` + +**Authentication:** JWT token required for most endpoints (except `/api/login`, `/api/demo/*`) + +**Response Format:** +```json +{ + "status": "success|error", + "message": "Human-readable message", + "data": { ... } +} +``` + +--- + +## 🔌 REST API Overview + +### API Endpoint Categories + +#### Authentication & Authorization + +| Method | Endpoint | Description | Auth Required | +|--------|----------|-------------|---------------| +| `POST` | `/api/login` | User login, returns JWT token | No | +| `POST` | `/api/logout` | Invalidate JWT token | Yes | +| `POST` | `/api/refresh` | Refresh JWT token | Yes | + +#### Patient Management + +| Method | Endpoint | Description | Auth Required | +|--------|----------|-------------|---------------| +| `GET` | `/api/patient` | List patients with pagination | Yes | +| `GET` | `/api/patient/{id}` | Get patient details | Yes | +| `POST` | `/api/patient` | Create new patient | Yes | +| `PATCH` | `/api/patient/{id}` | Update patient | Yes | +| `DELETE` | `/api/patient/{id}` | Soft delete patient | Yes | + +#### Order Management + +| Method | Endpoint | Description | Auth Required | +|--------|----------|-------------|---------------| +| `GET` | `/api/ordertest` | List orders | Yes | +| `GET` | `/api/ordertest/{id}` | Get order details | Yes | +| `POST` | `/api/ordertest` | Create order | Yes | +| `PATCH` | `/api/ordertest/{id}` | Update order | Yes | +| `DELETE` | `/api/ordertest/{id}` | Delete order | Yes | +| `POST` | `/api/ordertest/status` | Update order status | Yes | + +#### Demo/Test Endpoints (No Auth) + +| Method | Endpoint | Description | Auth Required | +|--------|----------|-------------|---------------| +| `POST` | `/api/demo/order` | Create demo order with patient | No | + +#### Specimen Management + +| Method | Endpoint | Description | Auth Required | +|--------|----------|-------------|---------------| +| `GET` | `/api/specimen` | List specimens | Yes | +| `GET` | `/api/specimen/{id}` | Get specimen details | Yes | +| `POST` | `/api/specimen` | Create specimen | Yes | +| `PATCH` | `/api/specimen/{id}` | Update specimen | Yes | +| `POST` | `/api/specimen/status` | Update specimen status | Yes | + +#### Result Management + +| Method | Endpoint | Description | Auth Required | +|--------|----------|-------------|---------------| +| `GET` | `/api/patresult` | List patient results | Yes | +| `GET` | `/api/patresult/{id}` | Get result details | Yes | +| `POST` | `/api/patresult` | Enter new result | Yes | +| `PATCH` | `/api/patresult/{id}` | Update result | Yes | +| `POST` | `/api/patresult/status` | Verify result (VER/REV/REP) | Yes | + +#### Edge API (Instrument Integration) + +| Method | Endpoint | Description | Auth Required | +|--------|----------|-------------|---------------| +| `POST` | `/api/edge/results` | Receive instrument results | API Key | +| `GET` | `/api/edge/orders` | Fetch pending orders | API Key | +| `POST` | `/api/edge/orders/{id}/ack` | Acknowledge order | API Key | +| `POST` | `/api/edge/status` | Log instrument status | API Key | + +### API Response Format + +All API endpoints return JSON in this format: + +**Success Response:** +```json +{ + "status": "success", + "message": "Operation completed successfully", + "data": { + // Response data here + } +} +``` + +**Error Response:** +```json +{ + "status": "error", + "message": "Error description", + "errors": [ + { + "field": "field_name", + "message": "Validation error message" + } + ] +} +``` + +### Authentication + +Most endpoints require JWT authentication: + +**Request Headers:** +``` +Authorization: Bearer {jwt_token} +Content-Type: application/json +``` + +**Login Request Example:** +```bash +POST /api/login +{ + "username": "labuser", + "password": "password123" +} +``` + +**Login Response:** +```json +{ + "status": "success", + "message": "Login successful", + "data": { + "token": "eyJ0eXAiOiJKV1QiLCJhbGc...", + "expires_in": 3600, + "user": { + "id": 1, + "username": "labuser", + "name": "Lab User" + } + } +} +``` --- @@ -151,32 +331,12 @@ $labeled = Lookups::transformLabels($patients, [ Lookups::clearCache(); ``` -### Frontend Usage (Alpine.js) - -```php - - -``` - ### When to Use | Approach | Use Case | |----------|----------| -| **Lookups Library** | Static values that rarely change (gender, status, types) - fast, cached | -| **API `/api/valueset*`** | Dynamic values managed by admins at runtime | +| **Lookups Library** | Server-side static values that rarely change (gender, status, types) - fast, cached | +| **API `/api/valueset*`** | Dynamic values managed by admins at runtime, or for frontend clients needing lookup data | ### Adding New Lookups @@ -199,9 +359,9 @@ const genderDropdown = LOOKUPS.gender.values.map(v => ({value: v.key, label: v.v ## 📋 Master Data Management -CLQMS provides comprehensive master data management for laboratory operations. All master data is accessible via the V2 UI at `/v2/master/*` endpoints. +CLQMS provides comprehensive master data management for laboratory operations. All master data is accessible via REST API endpoints. -### 🧪 Laboratory Tests (`/v2/master/tests`) +### 🧪 Laboratory Tests The Test Definitions module manages all laboratory test configurations including parameters, calculated tests, and test panels. @@ -254,7 +414,7 @@ The Test Definitions module manages all laboratory test configurations including } ``` -### 📏 Reference Ranges (`/v2/master/refrange`) +### 📏 Reference Ranges Reference Ranges define normal and critical values for test results. The system supports multiple reference range types based on patient demographics. @@ -292,7 +452,7 @@ Reference Ranges define normal and critical values for test results. The system | `PATCH` | `/api/refnum` | Update reference range | | `DELETE` | `/api/refnum` | Soft delete reference range | -### 📑 Value Sets (`/v2/master/valuesets`) +### 📑 Value Sets Value Sets are configurable dropdown options used throughout the system. Each Value Set Definition (VSetDef) contains multiple Value Set Values (ValueSet). @@ -386,6 +546,9 @@ Instrument → tiny-edge → POST /api/edge/results → edgeres table → [Manua --- ### 📜 Usage Notice + +**This is an API-only backend system.** There are no views, HTML templates, or server-side rendering components. Frontend applications should consume these REST endpoints to build user interfaces for laboratory operations. + This repository contains proprietary information intended for the 5Panda Team and authorized collaborators. --- diff --git a/USER_STORIES.md b/USER_STORIES.md new file mode 100644 index 0000000..4a4d91a --- /dev/null +++ b/USER_STORIES.md @@ -0,0 +1,1607 @@ +# CLQMS User Stories + +> Clinical Laboratory Quality Management System - User Stories +> +> Version: 1.0 +> Last Updated: 2026-01-28 + +--- + +## How to Read These Stories + +Each user story follows this format: + +**Story ID**: [Module]-[Number] +**Priority**: P0 (Must-have) | P1 (Should-have) | P2 (Nice-to-have) | P3 (Future) + +``` +As a [actor/role], +I want [feature/action], +So that [benefit/value]. +``` + +**Acceptance Criteria:** +- [ ] Criteria 1 +- [ ] Criteria 2 + +--- + +## Table of Contents + +1. [Master Data Management (P0)](#1-master-data-management-p0) +2. [Order Management (P0)](#2-order-management-p0) +3. [Specimen Management (P0)](#3-specimen-management-p0) +4. [Result Management (P0)](#4-result-management-p0) +5. [Patient Visit (P1)](#5-patient-visit-p1) +6. [Instrument Integration (P1)](#6-instrument-integration-p1) +7. [Quality Control (P2)](#7-quality-control-p2) +8. [Calibration (P2)](#8-calibration-p2) +9. [Audit Trail (P2)](#9-audit-trail-p2) +10. [Inventory & Billing (P3)](#10--inventory--billing-p3) + +--- + +## 1. Master Data Management (P0) + +> **Note**: All master data stories are P0 because they are foundational prerequisites for all transactional workflows. + +### VS-001: Manage Value Set Definitions +**Priority**: P0 + +As a **system administrator**, +I want **to manage value set definitions**, +So that **I can define and maintain lookup value categories used throughout the system**. + +**Acceptance Criteria:** +- [ ] Create new value set definition with name, description, and metadata +- [ ] Update existing value set definition +- [ ] Delete value set definition (soft delete) +- [ ] List all value set definitions with pagination +- [ ] Retrieve single value set definition by ID +- [ ] Value set definitions include: name, description, category, active status +- [ ] API endpoints: GET/POST/PATCH/DELETE `/api/valuesetdef` + +--- + +### VS-002: Manage Value Set Values +**Priority**: P0 + +As a **system administrator**, +I want **to manage individual values within value sets**, +So that **I can maintain the lookup options available for dropdown selections**. + +**Acceptance Criteria:** +- [ ] Create new value set value with key, label, and sort order +- [ ] Update existing value set value +- [ ] Delete value set value (soft delete) +- [ ] List all values for a specific value set definition +- [ ] Filter values by active/inactive status +- [ ] Values can be marked as default +- [ ] Sort order controls display sequence in dropdowns +- [ ] API endpoints: GET/POST/PATCH/DELETE `/api/valueset` + +--- + +### VS-003: Use JSON-Based Static Lookups +**Priority**: P0 + +As a **developer**, +I want **to access static lookup values from JSON files**, +So that **I can quickly load commonly-used reference data without database queries**. + +**Acceptance Criteria:** +- [ ] ValueSet library provides `get($key)` method returning dropdown options +- [ ] ValueSet library provides `getRaw($key)` method returning raw JSON data +- [ ] ValueSet library provides `getLabel($key, $value)` to get text label +- [ ] ValueSet library provides `transformLabels($records, $mappings)` for batch label conversion +- [ ] JSON files stored in `app/Libraries/Data/*.json` +- [ ] Lookup results cached for performance (>95% cache hit rate) +- [ ] Cache can be cleared via `ValueSet::clearCache()` +- [ ] Common lookups include: sex, marital_status, race, ethnic, order_status, specimen_type, result_type + +--- + +### VS-004: Refresh Value Set Cache +**Priority**: P0 + +As a **system administrator**, +I want **to refresh the value set cache after making changes**, +So that **the system immediately reflects updated lookup values**. + +**Acceptance Criteria:** +- [ ] API endpoint: POST `/api/valueset/refresh` +- [ ] Cache refresh requires authentication +- [ ] Cache refresh returns success status +- [ ] All ValueSet library calls return fresh data after refresh + +--- + +### TD-001: Manage Test Site Definitions +**Priority**: P0 + +As a **lab manager**, +I want **to define which tests are available at each laboratory site**, +So that **orders can only include tests appropriate for the testing location**. + +**Acceptance Criteria:** +- [ ] Create test definition for a site +- [ ] Update test site definition +- [ ] Delete test site definition +- [ ] List all tests available at a specific site +- [ ] Filter tests by discipline, department, or specimen type +- [ ] Mark tests as active/inactive per site +- [ ] API endpoints: GET/POST/PATCH/DELETE `/api/testdefsite` + +--- + +### TD-002: Manage Technical Test Specifications +**Priority**: P0 + +As a **lab technical specialist**, +I want **to define technical specifications for each test**, +So that **the system knows how to process, validate, and report test results**. + +**Acceptance Criteria:** +- [ ] Define specimen type requirements +- [ ] Define container requirements +- [ ] Define result type (numeric, text, valueset, range) +- [ ] Define expected units of measure +- [ ] Define decimal precision +- [ ] Define result format options +- [ ] Link to reference range definitions +- [ ] API endpoints: GET/POST/PATCH/DELETE `/api/testdeftech` + +--- + +### TD-003: Define Calculated Test Formulas +**Priority**: P0 + +As a **lab technical specialist**, +I want **to create formulas for calculated tests**, +So that **the system can automatically compute derived values from other test results**. + +**Acceptance Criteria:** +- [ ] Create formula for calculated test +- [ ] Formula language supports basic arithmetic (+, -, *, /) +- [ ] Formula can reference other test results by TestID +- [ ] Formula can include constants and parentheses +- [ ] Formula validation catches circular references +- [ ] Formula calculation triggered when all dependencies are available +- [ ] Calculated results flagged as "CALC" type +- [ ] API endpoints: GET/POST/PATCH/DELETE `/api/testdefcal` + +--- + +### TD-004: Manage Test Groups and Panels +**Priority**: P0 + +As a **lab manager**, +I want **to group individual tests into panels and profiles**, +So that **clinicians can order commonly-related tests as a single group**. + +**Acceptance Criteria:** +- [ ] Create test group with name and description +- [ ] Add multiple tests to a group +- [ ] Remove tests from a group +- [ ] Mark group as type "GROUP" (panel) or "TITLE" (section header) +- [ ] Groups can include other groups (nested panels) +- [ ] Ordering a group automatically orders all component tests +- [ ] API endpoints: GET/POST/PATCH/DELETE `/api/testdefgrp` + +--- + +### TD-005: Map Instruments to Tests +**Priority**: P0 + +As a **lab technical specialist**, +I want **to map laboratory instruments to specific tests**, +So that **instrument results can be correctly matched to patient tests**. + +**Acceptance Criteria:** +- [ ] Create instrument-to-test mapping +- [ ] Define instrument test code (from analyzer) +- [ ] Define corresponding CLQMS TestID +- [ ] Define result transformation rules (if needed) +- [ ] Specify unit conversions (if needed) +- [ ] One instrument test can map to multiple CLQMS tests +- [ ] API endpoints: GET/POST/PATCH/DELETE `/api/testmap` + +--- + +### TD-006: Auto-Select Calculated Test Parameters +**Priority**: P0 + +As a **laboratory information system**, +I want **to automatically add parameters required for calculated tests**, +So that **all necessary data is collected when calculated tests are ordered**. + +**Acceptance Criteria:** +- [ ] System analyzes formulas for all calculated tests in order +- [ ] System identifies parameter dependencies +- [ ] System automatically adds missing parameters to order +- [ ] User notified of auto-added parameters +- [ ] Parameters cannot be removed if required by calculated test +- [ ] Dependency chain is recursive (parameters may have their own dependencies) + +--- + +### RR-001: Manage Numeric Reference Ranges +**Priority**: P0 + +As a **lab technical specialist**, +I want **to define numeric reference ranges with age and sex criteria**, +So that **the system can automatically flag abnormal test results**. + +**Acceptance Criteria:** +- [ ] Create reference range for a test +- [ ] Define minimum and maximum normal values +- [ ] Define age range (birth to death) +- [ ] Define sex criteria (male, female, both) +- [ ] Define race/ethnicity criteria (optional) +- [ ] System auto-flags results outside range as "abnormal" +- [ ] System finds matching range based on patient demographics +- [ ] Supports critical value thresholds +- [ ] API endpoints: GET/POST/PATCH/DELETE `/api/refnum` + +--- + +### RR-002: Manage Threshold Reference Ranges +**Priority**: P0 + +As a **lab technical specialist**, +I want **to define critical value thresholds**, +So that **the system can generate urgent alerts for dangerous results**. + +**Acceptance Criteria:** +- [ ] Create threshold range for a test +- [ ] Define critical low and critical high values +- [ ] Define panic values requiring immediate notification +- [ ] Threshold overrides standard reference range +- [ ] System flags threshold results as "critical" +- [ ] API endpoints: GET/POST/PATCH/DELETE `/api/refthold` + +--- + +### RR-003: Manage Text Reference Ranges +**Priority**: P0 + +As a **lab technical specialist**, +I want **to define acceptable text values for qualitative tests**, +So that **the system can validate text-based results**. + +**Acceptance Criteria:** +- [ ] Create text reference range for a test +- [ ] Define list of acceptable text values +- [ ] Define default/normal value +- [ ] Define abnormal values +- [ ] System validates entered text against acceptable values +- [ ] API endpoints: GET/POST/PATCH/DELETE `/api/reftxt` + +--- + +### RR-004: Manage Value Set Reference Ranges +**Priority**: P0 + +As a **lab technical specialist**, +I want **to define reference ranges based on value set codes**, +So that **dropdown-based results can be validated**. + +**Acceptance Criteria:** +- [ ] Create value set reference range +- [ ] Link to specific value set definition +- [ ] Mark which values are normal vs abnormal +- [ ] Define default normal value +- [ ] System flags non-default values as abnormal (optional) +- [ ] API endpoints: GET/POST/PATCH/DELETE `/api/refvset` + +--- + +### ORG-001: Manage Accounts +**Priority**: P0 + +As a **system administrator**, +I want **to manage laboratory accounts**, +So that **the system can track orders and results by customer organization**. + +**Acceptance Criteria:** +- [ ] Create account with name, code, and contact information +- [ ] Update account details +- [ ] Soft delete account +- [ ] List all accounts with pagination +- [ ] Assign sites to accounts +- [ ] API endpoints: GET/POST/PATCH/DELETE `/api/account` + +--- + +### ORG-002: Manage Sites +**Priority**: P0 + +As a **system administrator**, +I want **to manage laboratory sites**, +So that **multi-location laboratories can track data by physical location**. + +**Acceptance Criteria:** +- [ ] Create site with name, type, and location +- [ ] Define site type (hospital, clinic, reference lab, etc.) +- [ ] Link site to parent account +- [ ] Define site address and contact +- [ ] Update and soft delete sites +- [ ] API endpoints: GET/POST/PATCH/DELETE `/api/site` + +--- + +### ORG-003: Manage Locations +**Priority**: P0 + +As a **system administrator**, +I want **to manage physical locations within sites**, +So that **specimens and orders can be tracked to specific areas**. + +**Acceptance Criteria:** +- [ ] Create location within a site +- [ ] Define location type (phlebotomy, reception, lab, storage, etc.) +- [ ] Define location hierarchy (building, floor, room) +- [ ] Update and soft delete locations +- [ ] API endpoints: GET/POST/PATCH/DELETE `/api/location` + +--- + +### ORG-004: Manage Disciplines +**Priority**: P0 + +As a **lab manager**, +I want **to manage laboratory disciplines**, +So that **tests can be organized by clinical discipline**. + +**Acceptance Criteria:** +- [ ] Create discipline (e.g., Chemistry, Hematology, Microbiology) +- [ ] Define discipline code and description +- [ ] Link departments to disciplines +- [ ] Update and soft delete disciplines +- [ ] API endpoints: GET/POST/PATCH/DELETE `/api/discipline` + +--- + +### ORG-005: Manage Departments +**Priority**: P0 + +As a **lab manager**, +I want **to manage laboratory departments**, +So that **tests and staff can be organized by sub-discipline**. + +**Acceptance Criteria:** +- [ ] Create department within a discipline +- [ ] Define department code and description +- [ ] Link department to parent discipline +- [ ] Update and soft delete departments +- [ ] API endpoints: GET/POST/PATCH/DELETE `/api/department` + +--- + +### ORG-006: Manage Workstations +**Priority**: P0 + +As a **lab manager**, +I want **to manage laboratory workstations**, +So that **orders and results can be tracked to specific instruments or work areas**. + +**Acceptance Criteria:** +- [ ] Create workstation with name and type +- [ ] Link workstation to department +- [ ] Define workstation as manual or automated +- [ ] Link automated workstations to instruments +- [ ] Update and soft delete workstations +- [ ] API endpoints: GET/POST/PATCH/DELETE `/api/workstation` + +--- + +### SPEC-001: Manage Container Definitions +**Priority**: P0 + +As a **lab technical specialist**, +I want **to define specimen container types**, +So that **the system can track appropriate containers for each specimen type**. + +**Acceptance Criteria:** +- [ ] Create container definition +- [ ] Define container class (tube, cup, slide, etc.) +- [ ] Define container size +- [ ] Define cap color +- [ ] Define additive requirements +- [ ] Define minimum and maximum volumes +- [ ] Update and soft delete containers +- [ ] API endpoints: GET/POST/PATCH/DELETE `/api/containerdef` + +--- + +### SPEC-002: Manage Specimen Types +**Priority**: P0 + +As a **lab technical specialist**, +I want **to define specimen types**, +So that **orders can specify the required biological material**. + +**Acceptance Criteria:** +- [ ] Create specimen type (Blood, Urine, Tissue, etc.) +- [ ] Define specimen type code +- [ ] Link to appropriate container types +- [ ] Define collection methods +- [ ] Define storage requirements +- [ ] Update and soft delete specimen types +- [ ] API endpoints: GET/POST/PATCH/DELETE `/api/specimentype` + +--- + +### SPEC-003: Manage Collection Methods +**Priority**: P0 + +As a **lab technical specialist**, +I want **to define specimen collection methods**, +So that **phlebotomy procedures can be standardized**. + +**Acceptance Criteria:** +- [ ] Create collection method (Venipuncture, Catheter, etc.) +- [ ] Define method code and description +- [ ] Link to appropriate specimen types +- [ ] Update and soft delete collection methods +- [ ] API endpoints: GET/POST/PATCH/DELETE `/api/collectionmethod` + +--- + +### SPEC-004: Manage Container Classes and Sizes +**Priority**: P0 + +As a **lab technical specialist**, +I want **to define container classes and sizes**, +So that **specimen containers can be consistently categorized**. + +**Acceptance Criteria:** +- [ ] Container classes: tube, cup, vial, slide, bag, etc. +- [ ] Container sizes with volume capacity +- [ ] Size units (mL, L, etc.) +- [ ] Link sizes to container classes +- [ ] Manage cap color options +- [ ] API endpoints: GET/POST/PATCH/DELETE for container class and size lookup tables + +--- + +## 2. Order Management (P0) + +### ORD-001: Create Laboratory Order +**Priority**: P0 + +As a **ordering provider or clinical staff**, +I want **to create a laboratory order for a patient**, +So that **the laboratory can perform requested tests**. + +**Acceptance Criteria:** +- [ ] Create order via POST `/api/ordertest` +- [ ] Order requires patient identification (InternalPID) +- [ ] Order requires priority (Stat/Routine/ASAP) +- [ ] Order requires ordering provider identification +- [ ] System generates unique OrderID in format: LLYYMMDDXXXXX + - LL: Lab location code + - YYMMDD: Date + - XXXXX: Sequential number +- [ ] Order includes one or more tests +- [ ] Initial order status is "ORD" (ordered) +- [ ] Order date/time recorded in UTC +- [ ] Response includes: OrderID, InternalPID, OrderStatus, list of tests + +--- + +### ORD-002: Update Order Details +**Priority**: P0 + +As a **laboratory staff**, +I want **to modify order details before verification**, +So that **the order can be corrected if information was entered incorrectly**. + +**Acceptance Criteria:** +- [ ] Update order via PATCH `/api/ordertest/:id` +- [ ] Can update priority +- [ ] Can update ordering provider +- [ ] Can add tests to order +- [ ] Can remove tests from order (before specimen collection) +- [ ] Updates prevented if order is verified +- [ ] Update timestamp recorded in UTC + +--- + +### ORD-003: Delete Order +**Priority**: P0 + +As a **laboratory staff**, +I want **to cancel an order**, +So that **tests are not performed on cancelled orders**. + +**Acceptance Criteria:** +- [ ] Delete order via DELETE `/api/ordertest/:id` +- [ ] Delete is soft delete (DelDate set, not actual deletion) +- [ ] Deleted orders not displayed in standard queries +- [ ] Delete prevented if specimens have been collected +- [ ] Delete reason must be recorded +- [ ] Deleted orders can be retrieved with include_deleted flag + +--- + +### ORD-004: Track Order Status +**Priority**: P0 + +As a **laboratory staff and clinician**, +I want **to track order status through the testing workflow**, +So that **I know the current state of the order**. + +**Acceptance Criteria:** +- [ ] Order status workflow: ORD → SCH → ANA → VER → REV → REP + - ORD: Ordered + - SCH: Scheduled + - ANA: Analyzing + - VER: Technical verification complete + - REV: Clinical review complete + - REP: Reported (final) +- [ ] Status can be updated via PATCH `/api/ordertest/:id/status` +- [ ] Status update requires authentication +- [ ] Status change recorded with user ID and timestamp +- [ ] Status history tracked via audit trail +- [ ] Current status included in all order responses + +--- + +### ORD-005: Assign Order Priority +**Priority**: P0 + +As a **ordering provider or lab staff**, +I want **to mark orders as urgent**, +So that **critical tests are performed first**. + +**Acceptance Criteria:** +- [ ] Priority options: Stat (S), Routine (R), ASAP (A) +- [ ] Stat orders appear at top of worklists +- [ ] Priority can be updated before specimen collection +- [ ] Priority displayed on all order views +- [ ] Default priority is Routine + +--- + +### ORD-006: Attach Documents to Order +**Priority**: P0 + +As a **clinical staff**, +I want **to attach documents or images to an order**, +So that **relevant clinical information is available to laboratory staff**. + +**Acceptance Criteria:** +- [ ] Upload attachments via POST `/api/ordertest/:id/attachments` +- [ ] Supported file types: PDF, images (PNG, JPG), documents +- [ ] File size limits enforced +- [ ] Attachments linked to ordercom/orderatt tables +- [ ] Attachment metadata includes: filename, type, size, uploader, timestamp +- [ ] Attachments can be retrieved via GET `/api/ordertest/:id/attachments` + +--- + +### ORD-007: Map Multiple Tests to Order +**Priority**: P0 + +As a **ordering provider**, +I want **to include multiple tests in a single order**, +So that **related tests are performed on the same specimen**. + +**Acceptance Criteria:** +- [ ] Order can include multiple tests +- [ ] Tests can be individual tests or panels +- [ ] Panels expand to component tests +- [ ] Orderitem table links OrderID to TestID +- [ ] All tests in order initially have same status +- [ ] Individual test status can be tracked independently + +--- + +### ORD-008: Auto-Select Calculated Test Parameters +**Priority**: P0 + +As a **laboratory information system**, +I want **to automatically add required parameters for calculated tests**, +So that **all necessary data is collected**. + +**Acceptance Criteria:** +- [ ] System detects calculated tests in order +- [ ] System analyzes formulas for calculated tests +- [ ] System adds missing parameters to order +- [ ] User notified of auto-added parameters +- [ ] Parameters cannot be removed if required +- [ ] Dependency tracking is recursive + +--- + +### ORD-009: Identify Ordering Provider +**Priority**: P0 + +As a **laboratory system**, +I want **to track which provider ordered each test**, +So that **results can be sent back and questions can be directed appropriately**. + +**Acceptance Criteria:** +- [ ] Order requires ordering provider identification +- [ ] Provider can be identified by ID or name +- [ ] Provider contact information stored +- [ ] Provider included in result reports +- [ ] Results can be filtered by ordering provider + +--- + +### ORD-010: Track Order Date/Time in UTC +**Priority**: P0 + +As a **laboratory system**, +I want **to normalize all timestamps to UTC**, +So that **times are consistent across multiple time zones**. + +**Acceptance Criteria:** +- [ ] Order date stored in UTC in database +- [ ] Order dates returned as UTC ISO 8601 format in API +- [ ] Input dates automatically converted to UTC +- [ ] Timezone information preserved in metadata +- [ ] Display can show local time based on user preference + +--- + +## 3. Specimen Management (P0) + +### SPM-001: Create Specimen from Order +**Priority**: P0 + +As a **phlebotomist or lab staff**, +I want **to create a specimen record for an order**, +So that **the specimen can be tracked through the laboratory workflow**. + +**Acceptance Criteria:** +- [ ] Create specimen via POST `/api/specimen` +- [ ] Specimen requires OrderID +- [ ] Specimen requires SpecimenType +- [ ] System generates unique SpecimenID = OrderID + SpecimenTypeCode(3) + ContainerCode(1) +- [ ] Specimen linked to order +- [ ] Initial specimen status is "COLLECTED" +- [ ] Collection date/time recorded in UTC + +--- + +### SPM-002: Mark Specimen as Collected +**Priority**: P0 + +As a **phlebotomist**, +I want **to record specimen collection**, +So that **the laboratory knows the specimen is ready for processing**. + +**Acceptance Criteria:** +- [ ] Update specimen status to "COLLECTED" +- [ ] Record collection date/time +- [ ] Record collector ID +- [ ] Record collection location +- [ ] Record collection method (venipuncture, catheter, etc.) +- [ ] Collection activity logged to specmenactivity table + +--- + +### SPM-003: Track Specimen In-Transit +**Priority**: P0 + +As a **courier or lab staff**, +I want **to mark specimen as in-transit**, +So that **the specimen location is known during transport**. + +**Acceptance Criteria:** +- [ ] Update specimen status to "IN_TRANSIT" +- [ ] Record dispatch date/time +- [ ] Record courier/staff ID +- [ ] Record destination location +- [ ] Expected arrival time can be recorded +- [ ] Transit activity logged to specmenactivity table + +--- + +### SPM-004: Receive or Reject Specimen +**Priority**: P0 + +As a **laboratory reception staff**, +I want **to receive specimens and verify acceptability**, +So that **unacceptable specimens are not processed**. + +**Acceptance Criteria:** +- [ ] Update specimen status to "RECEIVED" or "REJECTED" +- [ ] Record reception date/time +- [ ] Record receiver ID +- [ ] If rejected, record rejection reason +- [ ] Rejection reasons: insufficient quantity, hemolysis, clotting, wrong container, etc. +- [ ] Rejected specimens cannot proceed to analysis +- [ ] Reception activity logged to specmenactivity table + +--- + +### SPM-005: Prepare Specimen for Analysis +**Priority**: P0 + +As a **lab technician**, +I want **to record specimen preparation steps**, +So that **the specimen processing history is documented**. + +**Acceptance Criteria:** +- [ ] Update specimen status to "PREPARATION" +- [ ] Preparation types: centrifuge, aliquot, pretreatment, stain, etc. +- [ ] Record preparation date/time +- [ ] Record technician ID +- [ ] Preparation activity logged to specmenactivity table +- [ ] Multiple preparation steps can be recorded + +--- + +### SPM-006: Store Specimen +**Priority**: P0 + +As a **lab technician**, +I want **to record specimen storage location**, +So that **the specimen can be retrieved if needed**. + +**Acceptance Criteria:** +- [ ] Update specimen status to "STORED" +- [ ] Record storage date/time +- [ ] Record storage location (freezer, refrigerator, room) +- [ ] Record technician ID +- [ ] Storage activity logged to specmenactivity table +- [ ] Storage conditions (temperature) can be recorded + +--- + +### SPM-007: Dispatch Specimen for Referral +**Priority**: P0 + +As a **lab staff**, +I want **to dispatch specimen to reference laboratory**, +So that **tests not performed in-house can be completed**. + +**Acceptance Criteria:** +- [ ] Update specimen status to "DISPATCHED" +- [ ] Record dispatch date/time +- [ ] Record destination (reference lab) +- [ ] Record courier/tracking information +- [ ] Dispatch activity logged to specmenactivity table +- [ ] Dispatched specimens can be tracked + +--- + +### SPM-008: Track Specimen Condition +**Priority**: P0 + +As a **lab staff**, +I want **to flag specimen condition issues**, +So that **result quality issues can be identified**. + +**Acceptance Criteria:** +- [ ] Condition flags can be recorded: HEM (hemolysis), ITC (insufficient), LIP (lipemic) +- [ ] Multiple conditions can be flagged +- [ ] Conditions can be recorded at any stage +- [ ] Conditions displayed on specimen and result views +- [ ] Conditions included in result reports +- [ ] Critical conditions prevent analysis + +--- + +### SPM-009: Log Specimen Activity +**Priority**: P0 + +As a **laboratory system**, +I want **to maintain complete audit trail for specimen**, +So that **all specimen handling is documented**. + +**Acceptance Criteria:** +- [ ] All status changes logged to specmenactivity table +- [ ] Activity record includes: SID, activity type, timestamp, user ID +- [ ] Activity history can be retrieved via GET `/api/specimen/:id/activity` +- [ ] Activity history displayed chronologically +- [ ] Activity cannot be deleted or modified + +--- + +### SPM-010: Link Tests to Specimens +**Priority**: P0 + +As a **laboratory system**, +I want **to map tests to specific specimens**, +So that **results can be linked to the correct biological material**. + +**Acceptance Criteria:** +- [ ] Tests from order can be assigned to specimen(s) +- [ ] One order can have multiple specimens +- [ ] Tests can be split across multiple specimens +- [ ] Test-to-specimen mapping stored in specimen table +- [ ] Results reference the specimen ID +- [ ] Result reports show specimen information for each test + +--- + +## 4. Result Management (P0) + +### RES-001: Enter Numeric Results +**Priority**: P0 + +As a **lab technician or instrument**, +I want **to enter numeric test results**, +So that **quantitative measurements are recorded**. + +**Acceptance Criteria:** +- [ ] Create result via POST `/api/patresult` +- [ ] Result type: numeric +- [ ] Requires OrderID, TestID, ResultValue, ResultUnit +- [ ] ResultValue must be valid number +- [ ] ResultUnit must match test definition +- [ ] Decimal precision enforced +- [ ] Initial status is "ENTRY" +- [ ] UTC timestamp recorded + +--- + +### RES-002: Enter Text Results +**Priority**: P0 + +As a **lab technician or pathologist**, +I want **to enter text-based results**, +So that **qualitative or descriptive findings can be recorded**. + +**Acceptance Criteria:** +- [ ] Create result via POST `/api/patresult` +- [ ] Result type: text +- [ ] Requires OrderID, TestID, ResultValue (text) +- [ ] Text can be free-form or coded +- [ ] Text length limits enforced +- [ ] Initial status is "ENTRY" +- [ ] UTC timestamp recorded + +--- + +### RES-003: Enter Value Set Results +**Priority**: P0 + +As a **lab technician**, +I want **to select results from dropdown options**, +So that **coded results ensure consistency**. + +**Acceptance Criteria:** +- [ ] Create result via POST `/api/patresult` +- [ ] Result type: valueset +- [ ] Dropdown populated from linked value set +- [ ] Requires OrderID, TestID, ResultValue (code) +- [ ] Code must be valid value from value set +- [ ] Initial status is "ENTRY" +- [ ] UTC timestamp recorded + +--- + +### RES-004: Enter Range Results +**Priority**: P0 + +As a **lab technician**, +I want **to enter range-based results**, +So that **measurements with min/max can be recorded**. + +**Acceptance Criteria:** +- [ ] Create result via POST `/api/patresult` +- [ ] Result type: range +- [ ] Requires OrderID, TestID, ResultMin, ResultMax, ResultUnit +- [ ] Both min and max must be valid numbers +- [ ] ResultUnit must match test definition +- [ ] Initial status is "ENTRY" +- [ ] UTC timestamp recorded + +--- + +### RES-005: Validate Against Reference Ranges +**Priority**: P0 + +As a **laboratory system**, +I want **to automatically flag results outside reference ranges**, +So that **abnormal results are highlighted for review**. + +**Acceptance Criteria:** +- [ ] System retrieves reference range for test based on patient age/sex +- [ ] Numeric results compared to min/max +- [ ] Text results compared to acceptable values +- [ ] Value set results compared to normal/abnormal flags +- [ ] Results outside range flagged as "abnormal" +- [ ] Flag displayed on result entry and review screens +- [ ] Flag included in result reports + +--- + +### RES-006: Flag Critical Values +**Priority**: P0 + +As a **laboratory system**, +I want **to identify critical values**, +So that **urgent results are immediately brought to attention**. + +**Acceptance Criteria:** +- [ ] Critical values defined in threshold reference ranges +- [ ] Results exceeding critical thresholds flagged as "critical" +- [ ] Critical flag visually prominent in UI +- [ ] Critical results require acknowledgment +- [ ] Critical results require notification to ordering provider +- [ ] Critical notification logged + +--- + +### RES-007: Technical Verification +**Priority**: P0 + +As a **lab technician or technologist**, +I want **to verify results for technical accuracy**, +So that **results are validated before clinical review**. + +**Acceptance Criteria:** +- [ ] Update result status to "VER" via PATCH `/api/patresult/:id/status` +- [ ] Verification requires authenticated user +- [ ] Verifier ID recorded +- [ ] Verification timestamp recorded in UTC +- [ ] Verification requires review of reference range flags +- [ ] Verification requires review of critical values +- [ ] Bulk verification allowed for multiple results +- [ ] Verification can be reversed before clinical review + +--- + +### RES-008: Clinical Review +**Priority**: P0 + +As a **pathologist or clinical reviewer**, +I want **to review results for clinical interpretation**, +So that **results are validated before reporting**. + +**Acceptance Criteria:** +- [ ] Update result status to "REV" via PATCH `/api/patresult/:id/status` +- [ ] Review requires authenticated user with clinical role +- [ ] Reviewer ID recorded +- [ ] Review timestamp recorded in UTC +- [ ] Review can add clinical comments +- [ ] Review can override reference range flags with justification +- [ ] Bulk review allowed for multiple results +- [ ] Review can be reversed before reporting + +--- + +### RES-009: Report Final Results +**Priority**: P0 + +As a **laboratory staff**, +I want **to finalize results for reporting**, +So that **results can be sent to clinicians**. + +**Acceptance Criteria:** +- [ ] Update result status to "REP" via PATCH `/api/patresult/:id/status` +- [ ] Finalization requires clinical review complete +- [ ] Finalization timestamp recorded in UTC +- [ ] Finalizer ID recorded +- [ ] Finalized results cannot be modified (only addendum) +- [ ] Finalized results trigger report generation +- [ ] Finalized results sent to ordering provider +- [ ] Bulk finalization allowed for multiple results + +--- + +### RES-010: Track Result Reruns +**Priority**: P0 + +As a **lab technician**, +I want **to record result reruns**, +So that **quality issues and repeated measurements are documented**. + +**Acceptance Criteria:** +- [ ] Rerun count tracked via AspCnt field +- [ ] Rerun reason can be recorded +- [ ] Rerun timestamp recorded +- [ ] All reruns preserved (no overwriting) +- [ ] Final result selected from reruns +- [ ] Rerun history displayed with result +- [ ] Rerun cannot occur after result verification + +--- + +### RES-011: Add Result Comments +**Priority**: P0 + +As a **lab technician or pathologist**, +I want **to add comments to results**, +So that **additional context and notes are preserved**. + +**Acceptance Criteria:** +- [ ] Add comment via POST `/api/patresult/:id/comments` +- [ ] Comment requires text and user ID +- [ ] Comment timestamp recorded in UTC +- [ ] Comment type flag: technical, clinical, administrative +- [ ] Comments displayed with result +- [ ] Comments included in result reports +- [ ] Comments cannot be deleted (only appended) + +--- + +### RES-012: Manage Result Details +**Priority**: P0 + +As a **lab technician**, +I want **to add multiple detail parameters to a result**, +So that **complex multi-part results can be recorded**. + +**Acceptance Criteria:** +- [ ] Add detail via POST `/api/patresult/:id/details` +- [ ] Detail requires ParameterID and Value +- [ ] Multiple details can be added to one result +- [ ] Details can be numeric, text, or coded +- [ ] Details linked to patresultdetail table +- [ ] Details displayed with main result +- [ ] Details included in result reports + +--- + +## 5. Patient Visit (P1) + +### VIS-001: Create Patient Visit +**Priority**: P1 + +As a **admission or registration staff**, +I want **to create a patient visit record**, +So that **orders and results can be linked to an encounter**. + +**Acceptance Criteria:** +- [ ] Create visit via POST `/api/patvisit` +- [ ] Visit requires patient identification (InternalPID) +- [ ] Visit type: inpatient, outpatient, emergency +- [ ] Visit number generated +- [ ] Admission date/time recorded in UTC +- [ ] Discharge date/time nullable (open until discharged) +- [ ] API endpoints: GET/POST/PATCH/DELETE `/api/patvisit` + +--- + +### VIS-002: Link Orders to Visit +**Priority**: P1 + +As a **laboratory or clinical staff**, +I want **to associate orders with a patient visit**, +So that **laboratory work is organized by encounter**. + +**Acceptance Criteria:** +- [ ] Orders can be linked to visit during creation +- [ ] Orders can be updated to link to visit +- [ ] Visit ID included in order record +- [ ] Orders can be queried by visit +- [ ] Visit details displayed with orders + +--- + +### VIS-003: Track ADT Events +**Priority**: P1 + +As a **hospital staff**, +I want **to record patient admission, discharge, and transfer**, +So that **patient location is known throughout the stay**. + +**Acceptance Criteria:** +- [ ] Record admission event +- [ ] Record discharge event +- [ ] Record transfer event +- [ ] Each event requires timestamp (UTC) and location +- [ ] ADT events logged to patvisit table +- [ ] Current location always available +- [ ] ADT history queryable + +--- + +### VIS-004: Link Diagnosis to Visit +**Priority**: P1 + +As a **clinical staff**, +I want **to record patient diagnoses**, +So that **laboratory results can be correlated with clinical conditions**. + +**Acceptance Criteria:** +- [ ] Add diagnosis via POST `/api/patvisit/:id/diagnosis` +- [ ] Diagnosis requires ICD code or text +- [ ] Diagnosis type: admission, principal, secondary +- [ ] Multiple diagnoses can be added +- [ ] Diagnoses stored in patdiag table +- [ ] Diagnoses displayed with visit + +--- + +### VIS-005: Track Insurance Information +**Priority**: P1 + +As a **registration or billing staff**, +I want **to record patient insurance**, +So that **billing information is available**. + +**Acceptance Criteria:** +- [ ] Add insurance via POST `/api/patvisit/:id/insurance` +- [ ] Insurance requires: company, policy number, group number +- [ ] Insurance type: primary, secondary, tertiary +- [ ] Multiple insurance policies can be added +- [ ] Insurance stored in patinsurance table +- [ ] Insurance displayed with visit + +--- + +## 6. Instrument Integration (P1) + +### EDGE-001: Receive Instrument Results +**Priority**: P1 + +As a **laboratory instrument via tiny-edge**, +I want **to send results to CLQMS**, +So that **results are automatically recorded in the system**. + +**Acceptance Criteria:** +- [ ] Receive results via POST `/api/edge/results` +- [ ] Request includes: instrument ID, sample ID, results array +- [ ] Each result includes: test code, value, unit, timestamp +- [ ] Raw results stored in edgeres table +- [ ] Results queued for processing +- [ ] Acknowledgment returned to instrument +- [ ] Authentication required (API key or JWT) + +--- + +### EDGE-002: Store Raw Edge Results +**Priority**: P1 + +As a **laboratory system**, +I want **to preserve raw instrument results**, +So that **original data is available for troubleshooting and audit**. + +**Acceptance Criteria:** +- [ ] Raw results stored in edgeres table +- [ ] Raw data includes: instrument, sample, test code, value, unit, flags +- [ ] Raw data is immutable (no updates or deletes) +- [ ] Raw data linked to processed result +- [ ] Raw data queryable for troubleshooting + +--- + +### EDGE-003: Log Instrument Status +**Priority**: P1 + +As a **laboratory instrument**, +I want **to send status updates**, +So that **instrument health is monitored**. + +**Acceptance Criteria:** +- [ ] Send status via POST `/api/edge/status` +- [ ] Status includes: instrument ID, status code, message, timestamp +- [ ] Status stored in edgestatus table +- [ ] Status types: ready, busy, error, maintenance, offline +- [ ] Status history queryable +- [ ] Alerts triggered for error status + +--- + +### EDGE-004: Acknowledge Order Delivery +**Priority**: P1 + +As a **laboratory instrument**, +I want **to acknowledge order receipt**, +So that **the system knows the order was received**. + +**Acceptance Criteria:** +- [ ] Acknowledge via POST `/api/edge/orders/:id/ack` +- [ ] Acknowledgment includes: order ID, instrument ID, timestamp +- [ ] Acknowledgment stored in edgeack table +- [ ] Order marked as "acknowledged" in system +- [ ] Missing acknowledgments flagged + +--- + +### EDGE-005: Fetch Pending Orders +**Priority**: P1 + +As a **laboratory instrument via tiny-edge**, +I want **to retrieve pending orders**, +So that **the instrument knows which samples to process**. + +**Acceptance Criteria:** +- [ ] Fetch orders via GET `/api/edge/orders` +- [ ] Can filter by instrument ID +- [ ] Can filter by specimen status +- [ ] Response includes: OrderID, SampleID, tests, priority +- [ ] Only orders not yet acknowledged returned +- [ ] Orders marked as "sent" after retrieval + +--- + +### EDGE-006: Acknowledge Order Delivery +**Priority**: P1 + +As a **laboratory instrument**, +I want **to confirm order receipt**, +So that **the system can track order delivery**. + +**Acceptance Criteria:** +- [ ] Acknowledge via POST `/api/edge/orders/:id/ack` +- [ ] Acknowledgment requires instrument ID +- [ ] Acknowledgment timestamp recorded +- [ ] Acknowledgment stored in edgeack table +- [ ] Failed acknowledgments retried + +--- + +### EDGE-007: Process Edge Results to Patient Results +**Priority**: P1 + +As a **laboratory system**, +I want **to automatically process raw edge results into patient results**, +So that **instrument results become part of the patient record**. + +**Acceptance Criteria:** +- [ ] System polls edgeres table for unprocessed results +- [ ] Test code mapped to TestID via testmap +- [ ] Sample ID mapped to OrderID/SpecimenID +- [ ] Result value and unit converted if needed +- [ ] Reference range validation performed +- [ ] Result created in patresult table with status "ENTRY" +- [ ] Raw result marked as processed +- [ ] Processing errors logged + +--- + +## 7. Quality Control (P2) + +### QC-001: Enter QC Results +**Priority**: P2 + +As a **lab technician**, +I want **to enter quality control results**, +So that **instrument performance is monitored**. + +**Acceptance Criteria:** +- [ ] Create QC result via POST `/api/qcresult` +- [ ] QC result requires: instrument, lot number, level, test, value +- [ ] QC results stored in calres table +- [ ] QC result timestamp recorded in UTC +- [ ] QC result linked to QC material lot +- [ ] API endpoints: GET/POST/PATCH/DELETE `/api/qcresult` + +--- + +### QC-002: Prepare Levey-Jennings Data +**Priority**: P2 + +As a **lab manager or quality officer**, +I want **to retrieve Levey-Jennings chart data**, +So that **QC trends can be visualized**. + +**Acceptance Criteria:** +- [ ] Get QC data via GET `/api/qcresult/levey-jennings` +- [ ] Requires instrument, test, lot, level, date range +- [ ] Returns: mean, SD, values, timestamps +- [ ] Data formatted for charting libraries +- [ ] Includes control limits (±2SD, ±3SD) + +--- + +### QC-003: Validate QC Results +**Priority**: P2 + +As a **laboratory system**, +I want **to automatically validate QC results**, +So that **out-of-control QC is flagged**. + +**Acceptance Criteria:** +- [ ] System applies Westgard rules (or 2SD rule) +- [ ] Rules: 1:2s warning, 1:3s rejection, 2:2s rejection, etc. +- [ ] Out-of-control QC flagged +- [ ] Flagged QC prevents patient result release +- [ ] QC override requires justification + +--- + +### QC-004: Calculate Sigma Metrics +**Priority**: P2 + +As a **lab manager or quality officer**, +I want **to calculate sigma metrics**, +So that **method performance can be evaluated**. + +**Acceptance Criteria:** +- [ ] Calculate via GET `/api/qcresult/sigma` +- [ ] Requires: test, total allowable error (TEa), CV, bias +- [ ] Formula: Sigma = (TEa - bias) / CV +- [ ] Returns sigma value and quality ranking +- [ ] Rankings: world class (>6), excellent (5), good (4), marginal (3), poor (<3) + +--- + +### QC-005: Query QC History +**Priority**: P2 + +As a **lab manager or quality officer**, +I want **to review QC history**, +So that **long-term trends can be analyzed**. + +**Acceptance Criteria:** +- [ ] Query via GET `/api/qcresult/history` +- [ ] Can filter by instrument, test, lot, date range +- [ ] Returns all QC results with timestamps +- [ ] Includes calculated statistics (mean, SD, CV) +- [ ] Results downloadable as CSV + +--- + +## 8. Calibration (P2) + +### CAL-001: Enter Calibration Results +**Priority**: P2 + +As a **lab technician**, +I want **to record calibration results**, +So that **instrument calibration is documented**. + +**Acceptance Criteria:** +- [ ] Create calibration via POST `/api/calibration` +- [ ] Calibration requires: instrument, test, calibrator, factor, result +- [ ] Calibration stored in calres table +- [ ] Calibration timestamp recorded in UTC +- [ ] Calibration type: initial, periodic, after maintenance +- [ ] API endpoints: GET/POST/PATCH/DELETE `/api/calibration` + +--- + +### CAL-002: Query Calibration History +**Priority**: P2 + +As a **lab manager or quality officer**, +I want **to review calibration history**, +So that **instrument performance over time is documented**. + +**Acceptance Criteria:** +- [ ] Query via GET `/api/calibration/history` +- [ ] Can filter by instrument, test, date range +- [ ] Returns all calibrations with factors and results +- [ ] Includes calibration type and technician +- [ ] Results downloadable as CSV + +--- + +### CAL-003: Validate Calibration +**Priority**: P2 + +As a **laboratory system**, +I want **to validate calibration results**, +So that **out-of-spec calibration is flagged**. + +**Acceptance Criteria:** +- [ ] Calibration result compared to acceptable range +- [ ] Out-of-spec calibration flagged +- [ ] Flagged calibration prevents patient testing +- [ ] Calibration override requires justification +- [ ] Calibration failure triggers maintenance request + +--- + +## 9. Audit Trail (P2) + +### AUD-001: Automatic Audit Logging +**Priority**: P2 + +As a **laboratory system**, +I want **to automatically log all data changes**, +So that **complete audit trail is maintained**. + +**Acceptance Criteria:** +- [ ] Middleware intercepts all create/update/delete operations +- [ ] Audit record includes: table, record ID, action, user, timestamp +- [ ] Before and after values captured +- [ ] IP address and user agent logged +- [ ] Audit records stored in audit table +- [ ] Audit records are immutable + +--- + +### AUD-002: Track What/Who/When +**Priority**: P2 + +As a **compliance officer or auditor**, +I want **to see complete change history**, +So that **data provenance is documented**. + +**Acceptance Criteria:** +- [ ] Query via GET `/api/audit` +- [ ] Can filter by table, record, user, date range, action +- [ ] Returns: action, table, record, user, timestamp, before, after +- [ ] Results sorted chronologically +- [ ] Supports pagination + +--- + +### AUD-003: Log Security Events +**Priority**: P2 + +As a **laboratory system**, +I want **to log authentication and authorization events**, +So that **security incidents can be investigated**. + +**Acceptance Criteria:** +- [ ] Login attempts logged (success and failure) +- [ ] Logout events logged +- [ ] Failed authorization attempts logged +- [ ] Password changes logged +- [ ] Permission changes logged +- [ ] Events include: user, IP, timestamp, result + +--- + +### AUD-004: Query Audit Logs +**Priority**: P2 + +As a **compliance officer or auditor**, +I want **to search audit logs**, +So that **specific events can be investigated**. + +**Acceptance Criteria:** +- [ ] Query via GET `/api/audit/search` +- [ ] Search parameters: table, record, user, action, date range, keyword +- [ ] Returns matching audit records +- [ ] Results sorted by relevance or chronology +- [ ] Supports export to CSV/JSON + +--- + +## 10. Inventory & Billing (P3) + +### INV-001: Manage Counters +**Priority**: P3 + +As a **system administrator**, +I want **to manage system counters**, +So that **sequential IDs can be generated**. + +**Acceptance Criteria:** +- [ ] Create counter via POST `/api/counter` +- [ ] Counter requires: name, prefix, current value +- [ ] Counter can be incremented +- [ ] Counter can be reset +- [ ] Counters used for OrderID, SpecimenID, ResultID generation +- [ ] API endpoints: GET/POST/PATCH/DELETE `/api/counter` + +--- + +### INV-002: Manage Product Catalog +**Priority**: P3 + +As a **inventory manager**, +I want **to maintain product catalog**, +So that **reagents and consumables are tracked**. + +**Acceptance Criteria:** +- [ ] Create product via POST `/api/product` +- [ ] Product requires: name, code, category, unit +- [ ] Product includes: manufacturer, catalog number +- [ ] Products can be marked as active/inactive +- [ ] API endpoints: GET/POST/PATCH/DELETE `/api/product` + +--- + +### INV-003: Track Reagent Lots +**Priority**: P3 + +As a **inventory manager**, +I want **to track reagent lot numbers and expiration**, +So that **QC can be linked to specific lots**. + +**Acceptance Criteria:** +- [ ] Create lot via POST `/api/reagentlot` +- [ ] Lot requires: product, lot number, expiration date +- [ ] Lot includes: received date, quantity, location +- [ ] Lots can be marked as open/closed +- [ ] Expired lots flagged +- [ ] API endpoints: GET/POST/PATCH/DELETE `/api/reagentlot` + +--- + +### INV-004: Track Consumable Usage +**Priority**: P3 + +As a **lab technician**, +I want **to record consumable usage**, +So that **inventory is accurate**. + +**Acceptance Criteria:** +- [ ] Create usage record via POST `/api/consumableusage` +- [ ] Usage requires: product, quantity, test or order +- [ ] Usage timestamp recorded +- [ ] Usage linked to user ID +- [ ] Usage updates inventory quantity +- [ ] Low inventory alerts triggered + +--- + +### INV-005: Export Billing Data +**Priority**: P3 + +As a **billing staff**, +I want **to export completed orders for billing**, +So that **charges can be generated**. + +**Acceptance Criteria:** +- [ ] Export via GET `/api/billing/export` +- [ ] Can filter by date range, account, order status +- [ ] Export includes: OrderID, PatientID, tests, dates +- [ ] Export format: CSV, JSON, or HL7 +- [ ] Billing codes included (CPT, LOINC) +- [ ] Export marks orders as "billed" + +--- + +### INV-006: Select Service Class +**Priority**: P3 + +As a **ordering provider**, +I want **to select service class for billing**, +So that **appropriate pricing is applied**. + +**Acceptance Criteria:** +- [ ] Service class options: routine, urgent, stat, research, etc. +- [ ] Service class selected during order creation +- [ ] Service class affects billing tier +- [ ] Service class included in billing export +- [ ] Service class can be updated before billing + +--- + +## Summary Statistics + +**Total User Stories**: 103 + +**By Priority:** +- P0 (Must-have): 75 +- P1 (Should-have): 10 +- P2 (Nice-to-have): 13 +- P3 (Future): 5 + +**By Module:** +- Master Data: 28 stories +- Order Management: 10 stories +- Specimen Management: 10 stories +- Result Management: 12 stories +- Patient Visit: 5 stories +- Instrument Integration: 7 stories +- Quality Control: 5 stories +- Calibration: 3 stories +- Audit Trail: 4 stories +- Inventory & Billing: 6 stories + +--- + +## User Story Mapping + +### MVP Sprint 1: Foundation (Weeks 1-4) +- All Master Data stories (28 stories) + +### MVP Sprint 2: Orders & Specimens (Weeks 5-8) +- Order Management stories (10 stories) +- Specimen Management stories (10 stories) + +### MVP Sprint 3: Results (Weeks 9-12) +- Result Management stories (12 stories) + +### MVP Sprint 4: Integration (Weeks 13-16) +- Patient Visit stories (5 stories) +- Instrument Integration stories (7 stories) + +### Post-MVP: Enhancement (Weeks 17+) +- Quality Control stories (5 stories) +- Calibration stories (3 stories) +- Audit Trail stories (4 stories) +- Inventory & Billing stories (6 stories) + +--- + +**Document Status**: Draft for Review +**Last Updated**: 2026-01-28 +**Next Review**: Prior to Sprint 1 kickoff diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 2a83766..c95b6b5 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -22,6 +22,9 @@ $routes->group('api', ['filter' => 'auth'], function ($routes) { // Public Routes (no auth required) $routes->get('/v2/login', 'PagesController::login'); +// Swagger API Documentation (public - no filters) +$routes->add('swagger', 'PagesController::swagger'); + // V2 Auth API Routes (public - no auth required) $routes->group('v2/auth', function ($routes) { $routes->post('login', 'AuthV2Controller::login'); diff --git a/app/Controllers/AuthController.php b/app/Controllers/AuthController.php index 25e0dba..389881f 100644 --- a/app/Controllers/AuthController.php +++ b/app/Controllers/AuthController.php @@ -16,6 +16,8 @@ class AuthController extends Controller { use ResponseTrait; + protected $db; + // ok public function __construct() { @@ -170,19 +172,22 @@ class AuthController extends Controller try { // Melakukan Hash terhadap Payload dengan Kunci .env menggunakan Algortima HMAC + SHA-256 $jwt = JWT::encode($payload, $key, 'HS256'); - } catch (Exception $e) { + } catch (\Exception $e) { return $this->fail('Error generating JWT: ' . $e->getMessage(), 500); } + // Detect if HTTPS is being used + $isSecure = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on'; + // Kirim Respon ke HttpOnly yg akan disimpan di browser dan tidak akan dapat diakses oleh siapapun $this->response->setCookie([ 'name' => 'token', // nama token 'value' => $jwt, // value dari jwt yg sudah di hash 'expire' => 864000, // 10 hari 'path' => '/', // valid untuk semua path - 'secure' => true, // set true kalau sudah HTTPS + 'secure' => $isSecure, 'httponly' => true, // dipakai agar cookie berikut tidak dapat diakses oleh javascript - 'samesite' => Cookie::SAMESITE_NONE + 'samesite' => $isSecure ? Cookie::SAMESITE_NONE : Cookie::SAMESITE_LAX ]); // Response tanpa token di body @@ -214,15 +219,18 @@ class AuthController extends Controller // } public function logout() { + // Detect if HTTPS is being used + $isSecure = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on'; + // Definisikan ini pada cookies browser, harus sama dengan cookies login return $this->response->setCookie([ 'name' => 'token', 'value' => '', 'expire' => time() - 3600, 'path' => '/', - 'secure' => true, + 'secure' => $isSecure, 'httponly' => true, - 'samesite' => Cookie::SAMESITE_NONE + 'samesite' => $isSecure ? Cookie::SAMESITE_NONE : Cookie::SAMESITE_LAX ])->setJSON([ 'status' => 'success', diff --git a/app/Controllers/Contact/ContactController.php b/app/Controllers/Contact/ContactController.php index 6fb0706..945f430 100644 --- a/app/Controllers/Contact/ContactController.php +++ b/app/Controllers/Contact/ContactController.php @@ -3,7 +3,7 @@ namespace App\Controllers\Contact; use CodeIgniter\API\ResponseTrait; use App\Controllers\BaseController; - +use App\Libraries\ValueSet; use App\Models\Contact\ContactModel; class ContactController extends BaseController { @@ -23,11 +23,25 @@ class ContactController extends BaseController { $ContactName = $this->request->getVar('ContactName'); $Specialty = $this->request->getVar('Specialty'); $rows = $this->model->getContacts($ContactName, $Specialty); - + if (empty($rows)) { return $this->respond([ 'status' => 'success', 'message' => "no Data.", 'data' => [] ], 200); } + // Transform Specialty and Occupation + foreach ($rows as &$row) { + if (isset($row['Specialty'])) { + $row['specialty'] = $row['Specialty']; + $row['specialtyLabel'] = ValueSet::getLabel('specialty', $row['Specialty']) ?? ''; + unset($row['Specialty']); + } + if (isset($row['Occupation'])) { + $row['occupation'] = $row['Occupation']; + $row['occupationLabel'] = ValueSet::getLabel('occupation', $row['Occupation']) ?? ''; + unset($row['Occupation']); + } + } + return $this->respond([ 'status' => 'success', 'message'=> "fetch success", 'data' => $rows ], 200); } @@ -39,6 +53,18 @@ class ContactController extends BaseController { return $this->respond([ 'status' => 'success', 'message' => "no Data.", 'data' => null ], 200); } + // Transform Specialty and Occupation + if (isset($row['Specialty'])) { + $row['specialty'] = $row['Specialty']; + $row['specialtyLabel'] = ValueSet::getLabel('specialty', $row['Specialty']) ?? ''; + unset($row['Specialty']); + } + if (isset($row['Occupation'])) { + $row['occupation'] = $row['Occupation']; + $row['occupationLabel'] = ValueSet::getLabel('occupation', $row['Occupation']) ?? ''; + unset($row['Occupation']); + } + return $this->respond([ 'status' => 'success', 'message'=> "fetch success", 'data' => $row ], 200); } diff --git a/app/Controllers/OrderTestController.php b/app/Controllers/OrderTestController.php index 2565720..fb2f87f 100644 --- a/app/Controllers/OrderTestController.php +++ b/app/Controllers/OrderTestController.php @@ -3,9 +3,10 @@ namespace App\Controllers; use CodeIgniter\API\ResponseTrait; use CodeIgniter\Controller; -use App\Models\OrderTest\OrderTestModel; +use App\Libraries\ValueSet; +use App\Models\OrderTestModel; use App\Models\Patient\PatientModel; -use App\Models\PatVisit\PatVisitModel; +use App\Models\Patient\PatVisitModel; class OrderTestController extends Controller { use ResponseTrait; @@ -40,6 +41,20 @@ class OrderTestController extends Controller { ->getResultArray(); } + // Transform Priority and OrderStatus + foreach ($rows as &$row) { + if (isset($row['Priority'])) { + $row['priority'] = $row['Priority']; + $row['priorityLabel'] = ValueSet::getLabel('priority', $row['Priority']) ?? ''; + unset($row['Priority']); + } + if (isset($row['OrderStatus'])) { + $row['orderStatus'] = $row['OrderStatus']; + $row['orderStatusLabel'] = ValueSet::getLabel('order_status', $row['OrderStatus']) ?? ''; + unset($row['OrderStatus']); + } + } + return $this->respond([ 'status' => 'success', 'message' => 'Data fetched successfully', @@ -60,6 +75,19 @@ class OrderTestController extends Controller { 'data' => null ], 200); } + + // Transform Priority and OrderStatus + if (isset($row['Priority'])) { + $row['priority'] = $row['Priority']; + $row['priorityLabel'] = ValueSet::getLabel('priority', $row['Priority']) ?? ''; + unset($row['Priority']); + } + if (isset($row['OrderStatus'])) { + $row['orderStatus'] = $row['OrderStatus']; + $row['orderStatusLabel'] = ValueSet::getLabel('order_status', $row['OrderStatus']) ?? ''; + unset($row['OrderStatus']); + } + return $this->respond([ 'status' => 'success', 'message' => 'Data fetched successfully', diff --git a/app/Controllers/PagesController.php b/app/Controllers/PagesController.php index e930108..9330317 100644 --- a/app/Controllers/PagesController.php +++ b/app/Controllers/PagesController.php @@ -175,4 +175,12 @@ class PagesController extends BaseController 'activePage' => '' ]); } + + /** + * API Documentation / Swagger UI page + */ + public function swagger() + { + return view('swagger'); + } } diff --git a/app/Controllers/Patient/PatientController.php b/app/Controllers/Patient/PatientController.php index 666775d..f66f9c0 100644 --- a/app/Controllers/Patient/PatientController.php +++ b/app/Controllers/Patient/PatientController.php @@ -13,6 +13,7 @@ class PatientController extends Controller { protected $model; protected $rules; +use App\Libraries\ValueSet; public function __construct() { $this->db = \Config\Database::connect(); $this->model = new PatientModel(); @@ -58,6 +59,16 @@ class PatientController extends Controller { try { $rows = $this->model->getPatients($filters); + + // Transform Sex to sex and sexLabel + foreach ($rows as &$row) { + if (isset($row['Sex'])) { + $row['sex'] = $row['Sex']; + $row['sexLabel'] = ValueSet::getLabel('sex', $row['Sex']) ?? ''; + unset($row['Sex']); + } + } + return $this->respond([ 'status' => 'success', 'message'=> "data fetched successfully", 'data' => $rows ], 200); } catch (\Exception $e) { return $this->failServerError('Exception : '.$e->getMessage()); @@ -68,6 +79,14 @@ class PatientController extends Controller { try { $row = $this->model->getPatient($InternalPID); if (empty($row)) { return $this->respond([ 'status' => 'success', 'message' => "data not found.", 'data' => null ], 200); } + + // Transform Sex to sex and sexLabel + if (isset($row['Sex'])) { + $row['sex'] = $row['Sex']; + $row['sexLabel'] = ValueSet::getLabel('sex', $row['Sex']) ?? ''; + unset($row['Sex']); + } + return $this->respond([ 'status' => 'success', 'message' => "data fetched successfully", 'data' => $row ], 200); } catch (\Exception $e) { return $this->failServerError('Something went wrong: ' . $e->getMessage()); diff --git a/app/Controllers/Specimen/ContainerDefController.php b/app/Controllers/Specimen/ContainerDefController.php index 4f1cb48..1d7a448 100644 --- a/app/Controllers/Specimen/ContainerDefController.php +++ b/app/Controllers/Specimen/ContainerDefController.php @@ -4,6 +4,7 @@ namespace App\Controllers\Specimen; use CodeIgniter\API\ResponseTrait; use App\Controllers\BaseController; +use App\Libraries\ValueSet; use App\Models\Specimen\ContainerDefModel; class ContainerDefController extends BaseController { @@ -29,6 +30,26 @@ class ContainerDefController extends BaseController { 'ConName' => $this->request->getVar('ConName') ]; $rows = $this->model->getContainers($filter); + + // Transform ConCategory, CapColor, ConSize + foreach ($rows as &$row) { + if (isset($row['ConCategory'])) { + $row['conCategory'] = $row['ConCategory']; + $row['conCategoryLabel'] = ValueSet::getLabel('container_class', $row['ConCategory']) ?? ''; + unset($row['ConCategory']); + } + if (isset($row['CapColor'])) { + $row['capColor'] = $row['CapColor']; + $row['capColorLabel'] = ValueSet::getLabel('container_cap_color', $row['CapColor']) ?? ''; + unset($row['CapColor']); + } + if (isset($row['ConSize'])) { + $row['conSize'] = $row['ConSize']; + $row['conSizeLabel'] = ValueSet::getLabel('container_size', $row['ConSize']) ?? ''; + unset($row['ConSize']); + } + } + return $this->respond([ 'status' => 'success', 'message'=> "data fetched successfully", 'data' => $rows ], 200); } catch (\Exception $e) { return $this->failServerError('Exception : '.$e->getMessage()); @@ -41,6 +62,24 @@ class ContainerDefController extends BaseController { if (empty($row)) { return $this->respond([ 'status' => 'success', 'message'=> "data not found", 'data' => null ], 200); } + + // Transform ConCategory, CapColor, ConSize + if (isset($row['ConCategory'])) { + $row['conCategory'] = $row['ConCategory']; + $row['conCategoryLabel'] = ValueSet::getLabel('container_class', $row['ConCategory']) ?? ''; + unset($row['ConCategory']); + } + if (isset($row['CapColor'])) { + $row['capColor'] = $row['CapColor']; + $row['capColorLabel'] = ValueSet::getLabel('container_cap_color', $row['CapColor']) ?? ''; + unset($row['CapColor']); + } + if (isset($row['ConSize'])) { + $row['conSize'] = $row['ConSize']; + $row['conSizeLabel'] = ValueSet::getLabel('container_size', $row['ConSize']) ?? ''; + unset($row['ConSize']); + } + return $this->respond([ 'status' => 'success', 'message'=> "data fetched successfully", 'data' => $row ], 200); } catch (\Exception $e) { return $this->failServerError('Exception : '.$e->getMessage()); diff --git a/app/Controllers/Specimen/SpecimenCollectionController.php b/app/Controllers/Specimen/SpecimenCollectionController.php index 69d38ac..1c5a1f1 100644 --- a/app/Controllers/Specimen/SpecimenCollectionController.php +++ b/app/Controllers/Specimen/SpecimenCollectionController.php @@ -4,6 +4,7 @@ namespace App\Controllers\Specimen; use CodeIgniter\API\ResponseTrait; use App\Controllers\BaseController; +use App\Libraries\ValueSet; use App\Models\Specimen\SpecimenCollectionModel; class SpecimenCollectionController extends BaseController { @@ -22,6 +23,26 @@ class SpecimenCollectionController extends BaseController { public function index() { try { $rows = $this->model->findAll(); + + // Transform CollectionMethod, Additive, SpecimenRole + foreach ($rows as &$row) { + if (isset($row['CollectionMethod'])) { + $row['collectionMethod'] = $row['CollectionMethod']; + $row['collectionMethodLabel'] = ValueSet::getLabel('collection_method', $row['CollectionMethod']) ?? ''; + unset($row['CollectionMethod']); + } + if (isset($row['Additive'])) { + $row['additive'] = $row['Additive']; + $row['additiveLabel'] = ValueSet::getLabel('additive', $row['Additive']) ?? ''; + unset($row['Additive']); + } + if (isset($row['SpecimenRole'])) { + $row['specimenRole'] = $row['SpecimenRole']; + $row['specimenRoleLabel'] = ValueSet::getLabel('specimen_role', $row['SpecimenRole']) ?? ''; + unset($row['SpecimenRole']); + } + } + return $this->respond([ 'status' => 'success', 'message'=> "data fetched successfully", 'data' => $rows ], 200); } catch (\Exception $e) { return $this->failServerError('Exception : '.$e->getMessage()); @@ -34,6 +55,24 @@ class SpecimenCollectionController extends BaseController { if (empty($row)) { return $this->respond([ 'status' => 'success', 'message'=> "data not found", 'data' => null ], 200); } + + // Transform CollectionMethod, Additive, SpecimenRole + if (isset($row['CollectionMethod'])) { + $row['collectionMethod'] = $row['CollectionMethod']; + $row['collectionMethodLabel'] = ValueSet::getLabel('collection_method', $row['CollectionMethod']) ?? ''; + unset($row['CollectionMethod']); + } + if (isset($row['Additive'])) { + $row['additive'] = $row['Additive']; + $row['additiveLabel'] = ValueSet::getLabel('additive', $row['Additive']) ?? ''; + unset($row['Additive']); + } + if (isset($row['SpecimenRole'])) { + $row['specimenRole'] = $row['SpecimenRole']; + $row['specimenRoleLabel'] = ValueSet::getLabel('specimen_role', $row['SpecimenRole']) ?? ''; + unset($row['SpecimenRole']); + } + return $this->respond([ 'status' => 'success', 'message'=> "data fetched successfully", 'data' => $row ], 200); } catch (\Exception $e) { return $this->failServerError('Exception : '.$e->getMessage()); diff --git a/app/Controllers/Specimen/SpecimenController.php b/app/Controllers/Specimen/SpecimenController.php index 6eba3d0..76322c0 100644 --- a/app/Controllers/Specimen/SpecimenController.php +++ b/app/Controllers/Specimen/SpecimenController.php @@ -4,6 +4,7 @@ namespace App\Controllers\Specimen; use CodeIgniter\API\ResponseTrait; use App\Controllers\BaseController; +use App\Libraries\ValueSet; use App\Models\Specimen\SpecimenModel; class SpecimenController extends BaseController { @@ -22,6 +23,26 @@ class SpecimenController extends BaseController { public function index() { try { $rows = $this->model->findAll(); + + // Transform SpecimenType, SpecimenStatus, BodySite + foreach ($rows as &$row) { + if (isset($row['SpecimenType'])) { + $row['specimenType'] = $row['SpecimenType']; + $row['specimenTypeLabel'] = ValueSet::getLabel('specimen_type', $row['SpecimenType']) ?? ''; + unset($row['SpecimenType']); + } + if (isset($row['SpecimenStatus'])) { + $row['specimenStatus'] = $row['SpecimenStatus']; + $row['specimenStatusLabel'] = ValueSet::getLabel('specimen_status', $row['SpecimenStatus']) ?? ''; + unset($row['SpecimenStatus']); + } + if (isset($row['BodySite'])) { + $row['bodySite'] = $row['BodySite']; + $row['bodySiteLabel'] = ValueSet::getLabel('body_site', $row['BodySite']) ?? ''; + unset($row['BodySite']); + } + } + return $this->respond([ 'status' => 'success', 'message'=> "data fetched successfully", 'data' => $rows ], 200); } catch (\Exception $e) { return $this->failServerError('Exception : '.$e->getMessage()); @@ -34,6 +55,24 @@ class SpecimenController extends BaseController { if (empty($row)) { return $this->respond([ 'status' => 'success', 'message'=> "data not found", 'data' => null ], 200); } + + // Transform SpecimenType, SpecimenStatus, BodySite + if (isset($row['SpecimenType'])) { + $row['specimenType'] = $row['SpecimenType']; + $row['specimenTypeLabel'] = ValueSet::getLabel('specimen_type', $row['SpecimenType']) ?? ''; + unset($row['SpecimenType']); + } + if (isset($row['SpecimenStatus'])) { + $row['specimenStatus'] = $row['SpecimenStatus']; + $row['specimenStatusLabel'] = ValueSet::getLabel('specimen_status', $row['SpecimenStatus']) ?? ''; + unset($row['SpecimenStatus']); + } + if (isset($row['BodySite'])) { + $row['bodySite'] = $row['BodySite']; + $row['bodySiteLabel'] = ValueSet::getLabel('body_site', $row['BodySite']) ?? ''; + unset($row['BodySite']); + } + return $this->respond([ 'status' => 'success', 'message'=> "data fetched successfully", 'data' => $row ], 200); } catch (\Exception $e) { return $this->failServerError('Exception : '.$e->getMessage()); diff --git a/app/Controllers/Specimen/SpecimenStatusController.php b/app/Controllers/Specimen/SpecimenStatusController.php index 2444135..d9cb606 100644 --- a/app/Controllers/Specimen/SpecimenStatusController.php +++ b/app/Controllers/Specimen/SpecimenStatusController.php @@ -4,6 +4,7 @@ namespace App\Controllers\Specimen; use CodeIgniter\API\ResponseTrait; use App\Controllers\BaseController; +use App\Libraries\ValueSet; use App\Models\Specimen\SpecimenStatusModel; class ContainerDef extends BaseController { @@ -22,6 +23,21 @@ class ContainerDef extends BaseController { public function index() { try { $rows = $this->model->findAll(); + + // Transform Status and Activity + foreach ($rows as &$row) { + if (isset($row['Status'])) { + $row['status'] = $row['Status']; + $row['statusLabel'] = ValueSet::getLabel('specimen_status', $row['Status']) ?? ''; + unset($row['Status']); + } + if (isset($row['Activity'])) { + $row['activity'] = $row['Activity']; + $row['activityLabel'] = ValueSet::getLabel('specimen_activity', $row['Activity']) ?? ''; + unset($row['Activity']); + } + } + return $this->respond([ 'status' => 'success', 'message'=> "data fetched successfully", 'data' => $rows ], 200); } catch (\Exception $e) { return $this->failServerError('Exception : '.$e->getMessage()); @@ -34,6 +50,19 @@ class ContainerDef extends BaseController { if (empty($row)) { return $this->respond([ 'status' => 'success', 'message'=> "data not found", 'data' => null ], 200); } + + // Transform Status and Activity + if (isset($row['Status'])) { + $row['status'] = $row['Status']; + $row['statusLabel'] = ValueSet::getLabel('specimen_status', $row['Status']) ?? ''; + unset($row['Status']); + } + if (isset($row['Activity'])) { + $row['activity'] = $row['Activity']; + $row['activityLabel'] = ValueSet::getLabel('specimen_activity', $row['Activity']) ?? ''; + unset($row['Activity']); + } + return $this->respond([ 'status' => 'success', 'message'=> "data fetched successfully", 'data' => $row ], 200); } catch (\Exception $e) { return $this->failServerError('Exception : '.$e->getMessage()); diff --git a/app/Controllers/Test/DemoOrderController.php b/app/Controllers/Test/DemoOrderController.php index 5d2cb6f..eeabf40 100644 --- a/app/Controllers/Test/DemoOrderController.php +++ b/app/Controllers/Test/DemoOrderController.php @@ -3,8 +3,9 @@ namespace App\Controllers\Test; use CodeIgniter\API\ResponseTrait; use CodeIgniter\Controller; +use App\Libraries\ValueSet; use App\Models\Patient\PatientModel; -use App\Models\OrderTest\OrderTestModel; +use App\Models\OrderTestModel; class DemoOrderController extends Controller { use ResponseTrait; @@ -69,6 +70,20 @@ class DemoOrderController extends Controller { ->get() ->getResultArray(); + // Transform Priority and OrderStatus + foreach ($orders as &$order) { + if (isset($order['Priority'])) { + $order['priority'] = $order['Priority']; + $order['priorityLabel'] = ValueSet::getLabel('priority', $order['Priority']) ?? ''; + unset($order['Priority']); + } + if (isset($order['OrderStatus'])) { + $order['orderStatus'] = $order['OrderStatus']; + $order['orderStatusLabel'] = ValueSet::getLabel('order_status', $order['OrderStatus']) ?? ''; + unset($order['OrderStatus']); + } + } + return $this->respond([ 'status' => 'success', 'message' => 'Data fetched successfully', diff --git a/app/Controllers/Test/TestMapController.php b/app/Controllers/Test/TestMapController.php index 8d9677c..fc77d19 100644 --- a/app/Controllers/Test/TestMapController.php +++ b/app/Controllers/Test/TestMapController.php @@ -3,6 +3,7 @@ namespace App\Controllers\Test; use CodeIgniter\API\ResponseTrait; use App\Controllers\BaseController; +use App\Libraries\ValueSet; use App\Models\Test\TestMapModel; class TestMapController extends BaseController { @@ -20,13 +21,41 @@ class TestMapController extends BaseController { public function index() { $rows = $this->model->findAll(); if (empty($rows)) { return $this->respond([ 'status' => 'success', 'message' => "no Data.", 'data' => [] ], 200); } - return $this->respond([ 'status' => 'success', 'message'=> "Data fetched successfully", 'data' => $rows ], 200); + + // Transform HostType and ClientType + foreach ($rows as &$row) { + if (isset($row['HostType'])) { + $row['hostType'] = $row['HostType']; + $row['hostTypeLabel'] = ValueSet::getLabel('entity_type', $row['HostType']) ?? ''; + unset($row['HostType']); + } + if (isset($row['ClientType'])) { + $row['clientType'] = $row['ClientType']; + $row['clientTypeLabel'] = ValueSet::getLabel('entity_type', $row['ClientType']) ?? ''; + unset($row['ClientType']); + } + } + + return $this->respond([ 'status' => 'success', 'message'=> "Data fetched successfully", 'data' => $rows ], 200); } public function show($id = null) { $row = $this->model->where('TestMapID',$id)->first(); if (empty($row)) { return $this->respond([ 'status' => 'success', 'message' => "no Data.", 'data' => null ], 200); } - return $this->respond([ 'status' => 'success', 'message'=> "Data fetched successfully", 'data' => $row ], 200); + + // Transform HostType and ClientType + if (isset($row['HostType'])) { + $row['hostType'] = $row['HostType']; + $row['hostTypeLabel'] = ValueSet::getLabel('entity_type', $row['HostType']) ?? ''; + unset($row['HostType']); + } + if (isset($row['ClientType'])) { + $row['clientType'] = $row['ClientType']; + $row['clientTypeLabel'] = ValueSet::getLabel('entity_type', $row['ClientType']) ?? ''; + unset($row['ClientType']); + } + + return $this->respond([ 'status' => 'success', 'message'=> "Data fetched successfully", 'data' => $row ], 200); } public function create() { diff --git a/app/Libraries/Data/activity_result.json b/app/Libraries/Data/activity_result.json index 238647a..22c3c08 100644 --- a/app/Libraries/Data/activity_result.json +++ b/app/Libraries/Data/activity_result.json @@ -1,6 +1,6 @@ {"name": "activity_result", "VSName": "Activity Result", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "0", "value": "Failed"}, {"key": "1", "value": "Success with note"}, diff --git a/app/Libraries/Data/additive.json b/app/Libraries/Data/additive.json index adfe244..b4a04f4 100644 --- a/app/Libraries/Data/additive.json +++ b/app/Libraries/Data/additive.json @@ -1,6 +1,6 @@ {"name": "additive", "VSName": "Additive", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "Hep", "value": "Heparin ammonium"}, {"key": "Apro", "value": "Aprotinin"}, diff --git a/app/Libraries/Data/adt_event.json b/app/Libraries/Data/adt_event.json index a0938ff..939edc8 100644 --- a/app/Libraries/Data/adt_event.json +++ b/app/Libraries/Data/adt_event.json @@ -1,6 +1,6 @@ {"name": "adt_event", "VSName": "ADT Event", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "A01", "value": "Admit"}, {"key": "A02", "value": "Transfer"}, diff --git a/app/Libraries/Data/area_class.json b/app/Libraries/Data/area_class.json index 30d7415..dd6c9bb 100644 --- a/app/Libraries/Data/area_class.json +++ b/app/Libraries/Data/area_class.json @@ -1,6 +1,6 @@ {"name": "area_class", "VSName": "Area Class", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "PROP", "value": "Propinsi"}, {"key": "KAB", "value": "Kabupaten"}, diff --git a/app/Libraries/Data/body_site.json b/app/Libraries/Data/body_site.json index 1812af4..abb4b69 100644 --- a/app/Libraries/Data/body_site.json +++ b/app/Libraries/Data/body_site.json @@ -1,6 +1,6 @@ {"name": "body_site", "VSName": "Body Site", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "LA", "value": "Left Arm"}, {"key": "RA", "value": "Right Arm"}, diff --git a/app/Libraries/Data/collection_method.json b/app/Libraries/Data/collection_method.json index aeb5181..3bed5eb 100644 --- a/app/Libraries/Data/collection_method.json +++ b/app/Libraries/Data/collection_method.json @@ -1,6 +1,6 @@ {"name": "collection_method", "VSName": "Collection Method", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "pcntr", "value": "Puncture"}, {"key": "fprk", "value": "Finger-prick sampling"}, diff --git a/app/Libraries/Data/container_cap_color.json b/app/Libraries/Data/container_cap_color.json index 2963b3a..2d60188 100644 --- a/app/Libraries/Data/container_cap_color.json +++ b/app/Libraries/Data/container_cap_color.json @@ -1,6 +1,6 @@ {"name": "container_cap_color", "VSName": "Container Cap Color", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "PRPL", "value": "Purple"}, {"key": "RED", "value": "Red"}, diff --git a/app/Libraries/Data/container_class.json b/app/Libraries/Data/container_class.json index 14f7e24..d6627a4 100644 --- a/app/Libraries/Data/container_class.json +++ b/app/Libraries/Data/container_class.json @@ -1,6 +1,6 @@ {"name": "container_class", "VSName": "Container Class", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "Pri", "value": "Primary"}, {"key": "Sec", "value": "Secondary"}, diff --git a/app/Libraries/Data/container_size.json b/app/Libraries/Data/container_size.json index dbc4d4c..b45ffed 100644 --- a/app/Libraries/Data/container_size.json +++ b/app/Libraries/Data/container_size.json @@ -1,6 +1,6 @@ {"name": "container_size", "VSName": "Container Size", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "5ml", "value": "5 mL"}, {"key": "7ml", "value": "7 mL"}, diff --git a/app/Libraries/Data/country.json b/app/Libraries/Data/country.json index 632aac9..015e850 100644 --- a/app/Libraries/Data/country.json +++ b/app/Libraries/Data/country.json @@ -1,6 +1,6 @@ {"name": "country", "VSName": "Country", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "AFG", "value": "Afghanistan"}, {"key": "ALA", "value": "Åland Islands"}, diff --git a/app/Libraries/Data/ethnic.json b/app/Libraries/Data/ethnic.json index 966eaea..c417cb6 100644 --- a/app/Libraries/Data/ethnic.json +++ b/app/Libraries/Data/ethnic.json @@ -1,6 +1,6 @@ {"name": "ethnic", "VSName": "Ethnic", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "PPMLN", "value": "Papua Melanezoid"}, {"key": "NGRID", "value": "Negroid"}, diff --git a/app/Libraries/Data/fasting_status.json b/app/Libraries/Data/fasting_status.json index f7b3414..03e6314 100644 --- a/app/Libraries/Data/fasting_status.json +++ b/app/Libraries/Data/fasting_status.json @@ -1,6 +1,6 @@ {"name": "fasting_status", "VSName": "Fasting Status", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "F", "value": "Fasting"}, {"key": "NF", "value": "Not Fasting"}, diff --git a/app/Libraries/Data/formula_language.json b/app/Libraries/Data/formula_language.json index b7bce89..9bc3513 100644 --- a/app/Libraries/Data/formula_language.json +++ b/app/Libraries/Data/formula_language.json @@ -1,6 +1,6 @@ {"name": "formula_language", "VSName": "Formula Language", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "Phyton", "value": "Phyton"}, {"key": "CQL", "value": "Clinical Quality Language"}, diff --git a/app/Libraries/Data/identifier_type.json b/app/Libraries/Data/identifier_type.json index 4d38c15..a4cd258 100644 --- a/app/Libraries/Data/identifier_type.json +++ b/app/Libraries/Data/identifier_type.json @@ -1,6 +1,6 @@ {"name": "identifier_type", "VSName": "Identifier Type", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "KTP", "value": "Kartu Tanda Penduduk"}, {"key": "PASS", "value": "Passport"}, diff --git a/app/Libraries/Data/location_type.json b/app/Libraries/Data/location_type.json index 3374b9e..7c38fe4 100644 --- a/app/Libraries/Data/location_type.json +++ b/app/Libraries/Data/location_type.json @@ -1,6 +1,6 @@ {"name": "location_type", "VSName": "Location Type", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "FCLT", "value": "Facility"}, {"key": "BLDG", "value": "Building"}, diff --git a/app/Libraries/Data/marital_status.json b/app/Libraries/Data/marital_status.json index 582ecc3..ff6f2cc 100644 --- a/app/Libraries/Data/marital_status.json +++ b/app/Libraries/Data/marital_status.json @@ -1,6 +1,6 @@ {"name": "marital_status", "VSName": "Marital Status", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "A", "value": "Separated"}, {"key": "D", "value": "Divorced"}, diff --git a/app/Libraries/Data/math_sign.json b/app/Libraries/Data/math_sign.json index 9197639..9e5f1c1 100644 --- a/app/Libraries/Data/math_sign.json +++ b/app/Libraries/Data/math_sign.json @@ -1,6 +1,6 @@ {"name": "math_sign", "VSName": "Math Sign", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "=", "value": "Equal"}, {"key": "<", "value": "Less than"}, diff --git a/app/Libraries/Data/numeric_ref_type.json b/app/Libraries/Data/numeric_ref_type.json index 4533bc4..fb5afd3 100644 --- a/app/Libraries/Data/numeric_ref_type.json +++ b/app/Libraries/Data/numeric_ref_type.json @@ -1,6 +1,6 @@ {"name": "numeric_ref_type", "VSName": "Numeric Reference Type", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "RANGE", "value": "Range"}, {"key": "THOLD", "value": "Threshold"} diff --git a/app/Libraries/Data/order_priority.json b/app/Libraries/Data/order_priority.json index 0864f8f..ed23871 100644 --- a/app/Libraries/Data/order_priority.json +++ b/app/Libraries/Data/order_priority.json @@ -1,6 +1,6 @@ {"name": "order_priority", "VSName": "Order Priority", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "S", "value": "Stat"}, {"key": "A", "value": "ASAP"}, diff --git a/app/Libraries/Data/order_status.json b/app/Libraries/Data/order_status.json index 9fb4ce2..49b7395 100644 --- a/app/Libraries/Data/order_status.json +++ b/app/Libraries/Data/order_status.json @@ -1,6 +1,6 @@ {"name": "order_status", "VSName": "Order Status", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "A", "value": "Some, not all results available"}, {"key": "CA", "value": "Order is cancelled"}, diff --git a/app/Libraries/Data/priority.json b/app/Libraries/Data/priority.json index 4ed812f..60fcac6 100644 --- a/app/Libraries/Data/priority.json +++ b/app/Libraries/Data/priority.json @@ -1,6 +1,6 @@ {"name": "priority", "VSName": "Priority", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "S", "value": "Stat"}, {"key": "A", "value": "ASAP"}, diff --git a/app/Libraries/Data/race.json b/app/Libraries/Data/race.json index 7dee89a..5f29061 100644 --- a/app/Libraries/Data/race.json +++ b/app/Libraries/Data/race.json @@ -1,6 +1,6 @@ {"name": "race", "VSName": "Race (Ethnicity)", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "JAWA", "value": "Jawa"}, {"key": "SUNDA", "value": "Sunda"}, diff --git a/app/Libraries/Data/range_type.json b/app/Libraries/Data/range_type.json index 8e187d7..31a7d76 100644 --- a/app/Libraries/Data/range_type.json +++ b/app/Libraries/Data/range_type.json @@ -1,6 +1,6 @@ {"name": "range_type", "VSName": "Range Type", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "REF", "value": "Reference Range"}, {"key": "CRTC", "value": "Critical Range"}, diff --git a/app/Libraries/Data/reference_type.json b/app/Libraries/Data/reference_type.json index 639a3ee..f6fe909 100644 --- a/app/Libraries/Data/reference_type.json +++ b/app/Libraries/Data/reference_type.json @@ -1,6 +1,6 @@ {"name": "reference_type", "VSName": "Reference Type", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "NMRC", "value": "Numeric"}, {"key": "TEXT", "value": "Text"} diff --git a/app/Libraries/Data/religion.json b/app/Libraries/Data/religion.json index 7cf774d..a16915a 100644 --- a/app/Libraries/Data/religion.json +++ b/app/Libraries/Data/religion.json @@ -1,6 +1,6 @@ {"name": "religion", "VSName": "Religion", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "ISLAM", "value": "Islam"}, {"key": "KRSTN", "value": "Kristen"}, diff --git a/app/Libraries/Data/request_status.json b/app/Libraries/Data/request_status.json index 53cdd52..44646df 100644 --- a/app/Libraries/Data/request_status.json +++ b/app/Libraries/Data/request_status.json @@ -1,6 +1,6 @@ {"name": "request_status", "VSName": "Request Status", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "STC", "value": "To be collected"}, {"key": "SCFld", "value": "Collection failed"}, diff --git a/app/Libraries/Data/result_status.json b/app/Libraries/Data/result_status.json index d1e7895..299da23 100644 --- a/app/Libraries/Data/result_status.json +++ b/app/Libraries/Data/result_status.json @@ -1,6 +1,6 @@ {"name": "result_status", "VSName": "Result Status", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "PRELIMINARY", "value": "Preliminary"}, {"key": "FINAL", "value": "Final"}, diff --git a/app/Libraries/Data/result_type.json b/app/Libraries/Data/result_type.json index d090590..09081da 100644 --- a/app/Libraries/Data/result_type.json +++ b/app/Libraries/Data/result_type.json @@ -1,6 +1,6 @@ {"name": "result_type", "VSName": "Result Type", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "NMRIC", "value": "Numeric"}, {"key": "RANGE", "value": "Range"}, diff --git a/app/Libraries/Data/result_unit.json b/app/Libraries/Data/result_unit.json index 97d4553..ae45dc5 100644 --- a/app/Libraries/Data/result_unit.json +++ b/app/Libraries/Data/result_unit.json @@ -1,6 +1,6 @@ {"name": "result_unit", "VSName": "Result Unit", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "g/dL", "value": "g/dL"}, {"key": "g/L", "value": "g/L"}, diff --git a/app/Libraries/Data/sex.json b/app/Libraries/Data/sex.json index 11a3d16..70b4f0e 100644 --- a/app/Libraries/Data/sex.json +++ b/app/Libraries/Data/sex.json @@ -1,6 +1,6 @@ {"name": "sex", "VSName": "Sex", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "1", "value": "Female"}, {"key": "2", "value": "Male"}, diff --git a/app/Libraries/Data/site_class.json b/app/Libraries/Data/site_class.json index cbe9988..ea33086 100644 --- a/app/Libraries/Data/site_class.json +++ b/app/Libraries/Data/site_class.json @@ -1,6 +1,6 @@ {"name": "site_class", "VSName": "Site Class", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "A", "value": "Kelas A"}, {"key": "B", "value": "Kelas B"}, diff --git a/app/Libraries/Data/site_type.json b/app/Libraries/Data/site_type.json index e86ed50..f094d91 100644 --- a/app/Libraries/Data/site_type.json +++ b/app/Libraries/Data/site_type.json @@ -1,6 +1,6 @@ {"name": "site_type", "VSName": "Site Type", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "GH", "value": "Government Hospital"}, {"key": "PH", "value": "Private Hospital"}, diff --git a/app/Libraries/Data/specimen_activity.json b/app/Libraries/Data/specimen_activity.json index 66aac84..4670c15 100644 --- a/app/Libraries/Data/specimen_activity.json +++ b/app/Libraries/Data/specimen_activity.json @@ -1,6 +1,6 @@ {"name": "specimen_activity", "VSName": "Specimen Activity", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "SColl", "value": "Collection"}, {"key": "STran", "value": "Transport"}, diff --git a/app/Libraries/Data/specimen_condition.json b/app/Libraries/Data/specimen_condition.json index ec4c31a..9bc4ee0 100644 --- a/app/Libraries/Data/specimen_condition.json +++ b/app/Libraries/Data/specimen_condition.json @@ -1,6 +1,6 @@ {"name": "specimen_condition", "VSName": "Specimen Condition", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "HEM", "value": "Hemolyzed"}, {"key": "ITC", "value": "Icteric"}, diff --git a/app/Libraries/Data/specimen_role.json b/app/Libraries/Data/specimen_role.json index 98dcd20..a378b8d 100644 --- a/app/Libraries/Data/specimen_role.json +++ b/app/Libraries/Data/specimen_role.json @@ -1,6 +1,6 @@ {"name": "specimen_role", "VSName": "Specimen Role", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "P", "value": "Patient"}, {"key": "B", "value": "Blind Sample"}, diff --git a/app/Libraries/Data/specimen_status.json b/app/Libraries/Data/specimen_status.json index 2f0b3a4..fe9d7ea 100644 --- a/app/Libraries/Data/specimen_status.json +++ b/app/Libraries/Data/specimen_status.json @@ -1,6 +1,6 @@ {"name": "specimen_status", "VSName": "Specimen Status", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "STC", "value": "To be collected"}, {"key": "SCFld", "value": "Collection failed"}, diff --git a/app/Libraries/Data/specimen_type.json b/app/Libraries/Data/specimen_type.json index 4a391e7..28f6c66 100644 --- a/app/Libraries/Data/specimen_type.json +++ b/app/Libraries/Data/specimen_type.json @@ -1,6 +1,6 @@ {"name": "specimen_type", "VSName": "Specimen Type", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "BLD", "value": "Whole blood"}, {"key": "BLDA", "value": "Blood arterial"}, diff --git a/app/Libraries/Data/test_activity.json b/app/Libraries/Data/test_activity.json index 2af369d..0282514 100644 --- a/app/Libraries/Data/test_activity.json +++ b/app/Libraries/Data/test_activity.json @@ -1,6 +1,6 @@ {"name": "test_activity", "VSName": "Test Activity", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "ORD", "value": "Order"}, {"key": "ANA", "value": "Analyse"}, diff --git a/app/Libraries/Data/test_status.json b/app/Libraries/Data/test_status.json index df4e306..31d1cdf 100644 --- a/app/Libraries/Data/test_status.json +++ b/app/Libraries/Data/test_status.json @@ -1,6 +1,6 @@ {"name": "test_status", "VSName": "Test Status", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "PENDING", "value": "Waiting for Results"}, {"key": "IN_PROCESS", "value": "Analyzing"}, diff --git a/app/Libraries/Data/test_type.json b/app/Libraries/Data/test_type.json index ed91007..5bf50e1 100644 --- a/app/Libraries/Data/test_type.json +++ b/app/Libraries/Data/test_type.json @@ -1,6 +1,6 @@ {"name": "test_type", "VSName": "Test Type", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "TEST", "value": "Test"}, {"key": "PARAM", "value": "Parameter"}, diff --git a/app/Libraries/Data/text_ref_type.json b/app/Libraries/Data/text_ref_type.json index 8b60c70..9dfef96 100644 --- a/app/Libraries/Data/text_ref_type.json +++ b/app/Libraries/Data/text_ref_type.json @@ -1,6 +1,6 @@ {"name": "text_ref_type", "VSName": "Text Reference Type", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "VSET", "value": "Value Set"}, {"key": "TEXT", "value": "Text"} diff --git a/app/Libraries/Data/unit.json b/app/Libraries/Data/unit.json index c0facfc..9527c1f 100644 --- a/app/Libraries/Data/unit.json +++ b/app/Libraries/Data/unit.json @@ -1,6 +1,6 @@ {"name": "unit", "VSName": "Unit", - "VCategory": "User-defined", + "VCategory": "System", "values": [ {"key": "L", "value": "Liter"}, {"key": "mL", "value": "Mili Liter"}, diff --git a/app/Views/swagger.php b/app/Views/swagger.php new file mode 100644 index 0000000..b9e45ac --- /dev/null +++ b/app/Views/swagger.php @@ -0,0 +1,405 @@ + + +
+ + +Keyboard shortcuts:
-