diff --git a/.serena/memories/code_conventions.md b/.serena/memories/code_conventions.md new file mode 100644 index 0000000..01fe9a5 --- /dev/null +++ b/.serena/memories/code_conventions.md @@ -0,0 +1,97 @@ +# CLQMS Code Conventions + +## PHP Standards +- **Version**: PHP 8.1+ +- **Autoloading**: PSR-4 +- **Coding Style**: PSR-12 (where applicable) + +## Naming Conventions + +| Element | Convention | Example | +|---------|-----------|---------| +| Classes | PascalCase | `PatientController` | +| Methods | camelCase | `createPatient()` | +| Properties | snake_case (legacy) / camelCase (new) | `$patient_id` / `$patientId` | +| Constants | UPPER_SNAKE_CASE | `MAX_RETRY_COUNT` | +| Database Tables | snake_case | `patient_visits` | +| Database Columns | PascalCase (legacy) | `PatientID`, `NameFirst` | +| JSON Fields | PascalCase | `"PatientID": "123"` | + +## Imports & Namespaces +- Fully qualified namespaces at top of file +- Group imports: Framework first, then App, then external +- Alphabetical order within groups + +```php +model = new \App\Models\ExampleModel(); + } +} +``` + +## Response Format +All API responses use standardized format: +```php +// Success +return $this->respond([ + 'status' => 'success', + 'message' => 'Operation completed', + 'data' => $data +], 200); + +// Error +return $this->respond([ + 'status' => 'failed', + 'message' => 'Error description', + 'data' => [] +], 400); +``` + +## Database Operations +- Use CodeIgniter Query Builder or Model methods +- Use `helper('utc')` for UTC date conversion +- Wrap multi-table operations in transactions + +```php +$this->db->transStart(); +// ... operations +$this->db->transComplete(); + +if ($this->db->transStatus() === false) { + return $this->respond(['status' => 'error', ...], 500); +} +``` + +## Test Naming Convention +Format: `test` +Examples: `testCreatePatientValidationFail`, `testUpdatePatientSuccess` + +## HTTP Status Codes +- 200: GET/PATCH success +- 201: POST success +- 400: Validation error +- 401: Unauthorized +- 404: Not found +- 500: Server error + +## Legacy Field Naming +Database uses PascalCase: `PatientID`, `NameFirst`, `Birthdate`, `CreatedAt` diff --git a/.serena/memories/project_overview.md b/.serena/memories/project_overview.md index 20c7f63..05b1b53 100644 --- a/.serena/memories/project_overview.md +++ b/.serena/memories/project_overview.md @@ -1,55 +1,54 @@ # CLQMS Project Overview ## Project Purpose +CLQMS (Clinical Laboratory Quality Management System) is a headless REST API backend for clinical laboratory workflows. It provides comprehensive JSON endpoints for: +- Patient management +- Order/test management +- Specimen tracking +- Result management and verification +- Reference ranges +- Laboratory instrument integration (Edge API) -CLQMS (Clinical Laboratory Quality Management System) is a **headless REST API backend** designed for modern clinical laboratory workflows. This API-only system provides comprehensive JSON endpoints for laboratory operations without any view layer. Frontend applications (web, mobile, desktop) consume these REST endpoints to build laboratory information systems. +## Tech Stack +- **Language**: PHP 8.1+ +- **Framework**: CodeIgniter 4 (API-only mode) +- **Database**: MySQL with MySQLi driver +- **Authentication**: JWT (JSON Web Tokens) +- **Testing**: PHPUnit 10.5+ +- **Documentation**: OpenAPI/Swagger YAML -### Core Features -- Patient registration and management -- Laboratory test ordering and tracking -- Specimen lifecycle management (collection → transport → reception → prep → analysis) -- Result entry with reference range validation -- Multi-level result verification (Technical → Clinical → Reporting) -- Instrument integration via Edge API (tiny-edge middleware) -- Master data management (value sets, test definitions, reference ranges) -- Quality control and calibration tracking +## Architecture +- **API-First**: No view layer, headless REST API only +- **Stateless**: JWT-based authentication per request +- **UTC Dates**: All dates stored in UTC, converted for display +- **PSR-4 Autoloading**: `App\` → `app/`, `Config\` → `app/Config/` -### Product Vision -To provide a headless, API-first laboratory information system serving as the backend for any frontend client while streamlining laboratory operations, ensuring regulatory compliance, and integrating seamlessly with laboratory instruments. +## Key Directories +``` +app/ + Controllers/ # API endpoint handlers + Models/ # Database models + Libraries/ # Helper classes (Lookups, ValueSet) + Database/ + Migrations/ # Schema migrations + Seeds/ # Test data seeders + Helpers/ # json_helper.php, utc_helper.php + Traits/ # ResponseTrait + Config/ # Configuration files + Filters/ # AuthFilter, CORS -## Technology Stack +public/ # Web root + paths/ # OpenAPI path definitions + components/schemas/ # OpenAPI schemas -| Component | Specification | -|-----------|---------------| -| **Language** | PHP 8.1+ (PSR-compliant) | -| **Framework** | CodeIgniter 4 (API-only mode) | -| **Database** | MySQL 8.0+ | -| **Security** | JWT (JSON Web Tokens) via firebase/php-jwt | -| **Testing** | PHPUnit 10.5+ | -| **API Format** | RESTful JSON | -| **Architecture** | Clean architecture, API-first design | +tests/ + feature/ # Feature/integration tests + unit/ # Unit tests + _support/ # Test support files +``` -### Key Dependencies -- `codeigniter4/framework` - Main framework +## Key Dependencies +- `codeigniter4/framework` - Core framework - `firebase/php-jwt` - JWT authentication -- `fakerphp/faker` - Test data generation -- `phpunit/phpunit` - Unit/feature testing - -## Strategic Pillars -- **Precision & Accuracy**: Strict validation for all laboratory parameters and reference ranges -- **Scalability**: Optimized for high-volume diagnostic environments -- **Compliance**: Built-in audit trails and status history for full traceability -- **Interoperability**: Modular architecture designed for LIS, HIS, and analyzer integrations - -## Key Characteristics -- **API-Only**: No view layer, no server-side rendering -- **Frontend Agnostic**: Any client can consume these APIs -- **JSON-First**: All requests/responses use JSON format -- **Stateless**: Each API request is independent with JWT authentication -- **UTC Normalization**: Automatic UTC timestamp handling via BaseModel - -## Current Architecture Status -The system is undergoing an **Architectural Redesign** to consolidate legacy structures into a high-performance, maintainable schema focusing on: -- Unified Test Definitions (consolidating technical, calculated, and site-specific test data) -- Reference Range Centralization (unified engine for numeric, threshold, text, and coded results) -- Ordered Workflow Management (precise tracking of orders from collection to verification) +- `fakerphp/faker` - Test data generation (dev) +- `phpunit/phpunit` - Testing (dev) diff --git a/.serena/memories/suggested_commands.md b/.serena/memories/suggested_commands.md index f3b66e2..a3b0392 100644 --- a/.serena/memories/suggested_commands.md +++ b/.serena/memories/suggested_commands.md @@ -1,393 +1,100 @@ # CLQMS Suggested Commands -## Database Commands - -### Migrations -```bash -# Run all pending migrations -php spark migrate - -# Run specific migration -php spark migrate [migration_file] - -# Rollback last batch of migrations -php spark migrate:rollback - -# Rollback and re-run migrations -php spark migrate:refresh - -# Refresh and seed database -php spark migrate:refresh --seed - -# Check migration status -php spark migrate:status -``` - -### Database Seeding -```bash -# Run all seeders -php spark db:seed - -# Run specific seeder -php spark db:seed DBSeeder - -# Create new database -php spark db:create -``` - -### Database Query -```bash -# Get table information -php spark db:table [table_name] -``` - -## Testing Commands - -### PHPUnit Tests +## Testing ```bash # Run all tests -vendor/bin/phpunit +./vendor/bin/phpunit -# Run specific test file (IMPORTANT for debugging) -vendor/bin/phpunit tests/feature/UniformShowTest.php -vendor/bin/phpunit tests/feature/Patient/PatientCreateTest.php - -# Run tests in specific directory -vendor/bin/phpunit tests/feature/Patient/ - -# Run tests with coverage (text output) -vendor/bin/phpunit --coverage-text=tests/coverage.txt -d memory_limit=1024m - -# Run tests with coverage (HTML report) -vendor/bin/phpunit --coverage-text=tests/coverage.txt --coverage-html=tests/coverage/ -d memory_limit=1024m - -# Run with verbose output -vendor/bin/phpunit --verbose +# Run specific test file +./vendor/bin/phpunit tests/feature/Patients/PatientCreateTest.php # Run specific test method -vendor/bin/phpunit --filter testCanCreatePatient -``` +./vendor/bin/phpunit --filter testCreatePatientSuccess tests/feature/Patients/PatientCreateTest.php -## Code Generation Commands +# Run tests with coverage +./vendor/bin/phpunit --coverage-html build/logs/html -### Generate Code Files -```bash -# Generate new model -php spark make:model ModelName +# Run tests by suite +./vendor/bin/phpunit --testsuite App -# Generate new controller -php spark make:controller ControllerName - -# Generate new migration -php spark make:migration MigrationName - -# Generate new seeder -php spark make:seeder SeederName - -# Generate new test -php spark make:test TestName - -# Generate scaffold (complete set) -php spark make:scaffold ModelName +# Run via composer +composer test ``` ## Development Server - -### Start Development Server ```bash -# Start CodeIgniter dev server (default port 8080) +# Start PHP development server php spark serve -# Start on specific port -php spark serve --port=8080 - -# Start with specific host -php spark serve --host=0.0.0.0 +# Or specify port +php spark serve --port 8080 ``` -## Cache Management - -### Clear Caches +## Database ```bash -# Clear application cache -php spark cache:clear +# Run migrations +php spark migrate -# Show cache information -php spark cache:info +# Rollback migrations +php spark migrate:rollback -# Clear debug bar JSON files -php spark debugbar:clear +# Create new migration +php spark make:migration CreateUsersTable -# Clear log files -php spark logs:clear +# Run database seeds +php spark db:seed DBSeeder +php spark db:seed PatientSeeder ``` -## System Utilities - -### Configuration & Environment +## Code Generation (Scaffolding) ```bash -# Get current environment -php spark env +# Create controller +php spark make:controller Users -# Set environment -php spark env development +# Create model +php spark make:model UserModel -# Check configuration values -php spark config:check +# Create migration +php spark make:migration CreateUsersTable -# Check php.ini values (for production) -php spark phpini:check - -# Verify namespaces -php spark namespaces +# Create seeder +php spark make:seeder UserSeeder ``` -### Routes +## API Documentation ```bash -# Display all routes -php spark routes +# After updating YAML files, regenerate bundled docs +node public/bundle-api-docs.js -# Check filters for a route -php spark filter:check +# Produces: public/api-docs.bundled.yaml ``` -### Optimization +## Utilities (Windows) ```bash -# Optimize for production -php spark optimize -``` - -## Utility Commands - -### Encryption -```bash -# Generate new encryption key (writes to .env) -php spark key:generate -``` - -### Publish -```bash -# Publish predefined resources -php spark publish -``` - -## General Utility Commands (Windows) - -### File & Directory Operations -```bash -# List files in current directory +# List files dir -# List files with details -dir /a +# Search in files (PowerShell) +Select-String -Path "app\*.php" -Pattern "PatientModel" -# Change directory -cd path\to\directory +# Or using git bash (if available) +grep -r "PatientModel" app/ -# Go to parent directory -cd .. - -# Create directory -mkdir directory_name - -# Remove directory (empty) -rmdir directory_name - -# Remove directory with contents -rmdir /s /q directory_name - -# Copy file -copy source_file destination - -# Move/Rename file -move source destination - -# Delete file -del filename - -# Search for file -dir /s filename +# Clear writable cache +del /q writable\cache\* ``` -### File Content Operations +## Git Commands ```bash -# Display file content -type filename - -# Display file content page by page -more filename - -# Search for text in file -findstr "pattern" filename - -# Search recursively -findstr /s /i "pattern" *.php -``` - -### Git Commands -```bash -# Check git status +# Check status git status -# View changes -git diff - -# View staged changes -git diff --staged - -# Add files to staging +# Add files git add . -# Add specific file -git add path/to/file +# Commit (only when explicitly asked) +git commit -m "message" -# Commit changes -git commit -m "commit message" - -# Push to remote -git push - -# Pull from remote -git pull - -# View commit history -git log - -# View commit history with graph -git log --graph --oneline - -# Create new branch -git checkout -b branch_name - -# Switch branch -git checkout branch_name - -# Merge branch -git merge branch_name - -# View branches -git branch +# View recent commits +git log --oneline -10 ``` - -### Process Management -```bash -# List running processes -tasklist - -# Kill process by PID -taskkill /PID pid_number - -# Kill process by name -taskkill /IM process_name.exe - -# Kill process forcefully -taskkill /F /PID pid_number -``` - -### Network Operations -```bash -# Check port usage -netstat -an | findstr :port - -# Test network connectivity -ping hostname - -# View active connections -netstat -an -``` - -## After Task Completion Checklist - -When completing a task, run the following commands to ensure code quality: - -### 1. Run Tests -```bash -# Run all tests -vendor/bin/phpunit - -# If tests fail, run specific test file for debugging -vendor/bin/phpunit tests/feature/[SpecificTestFile].php -``` - -### 2. Check for Linting Issues (if configured) -```bash -# Check for PHP syntax errors -php -l app/Controllers/YourController.php -php -l app/Models/YourModel.php - -# Run any custom linting tools if configured in composer.json -composer test # if 'test' script includes linting -``` - -### 3. Type Checking (if configured) -```bash -# Run static analysis tools if configured -vendor/bin/phpstan analyse # if phpstan is installed -vendor/bin/psalm # if psalm is installed -``` - -### 4. Database Verification -```bash -# Check migration status -php spark migrate:status - -# If you created a migration, run it -php spark migrate -``` - -### 5. API Documentation Update (CRITICAL) -```bash -# After modifying ANY controller, MUST update api-docs.yaml -# This is a manual process - edit public/api-docs.yaml - -# Verify YAML syntax (optional, if yamllint is available) -yamllint public/api-docs.yaml -``` - -### 6. Code Review Checklist -- [ ] All tests passing -- [ ] No PHP syntax errors -- [ ] Database migrations applied (if any) -- [ ] API documentation updated (`public/api-docs.yaml`) -- [ ] Code follows style conventions (see `code_style_conventions.md`) -- [ ] Proper error handling in place -- [ ] Input validation implemented -- [ ] JWT authentication required where needed -- [ ] UTC date handling via BaseModel -- [ ] Soft delete using `DelDate` where applicable -- [ ] ValueSet lookups properly used and cached cleared if modified - -### 7. Verify API Endpoints (if applicable) -```bash -# If you have curl or a REST client, test the endpoints -# Example using curl: -curl -X GET http://localhost:8080/api/patient -H "Cookie: token=your_jwt_token" -``` - -## Common Workflows - -### Creating a New API Endpoint -1. Create controller: `php spark make:controller ControllerName` -2. Create model: `php spark make:model ModelName` -3. Create migration (if new table): `php spark make:migration MigrationName` -4. Run migration: `php spark migrate` -5. Add routes in `app/Config/Routes.php` -6. Implement controller methods following pattern -7. Create tests: `php spark make:test Feature/EndpointNameTest` -8. Run tests: `vendor/bin/phpunit tests/feature/EndpointNameTest.php` -9. Update `public/api-docs.yaml` -10. Verify with test client - -### Debugging a Failing Test -1. Run specific test file: `vendor/bin/phpunit tests/feature/SpecificTest.php` -2. Add debug output: `var_dump($result); die();` temporarily -3. Check database state: View with database client -4. Check logs: `writable/logs/` directory -5. Run with verbose: `vendor/bin/phpunit --verbose` -6. Isolate specific test: `vendor/bin/phpunit --filter testMethodName` - -### Working with ValueSets -1. Create/edit JSON file: `app/Libraries/Data/valuesets/{name}.json` -2. Clear cache: `ValueSet::clearCache();` (in code) or via code -3. Verify via API: GET `/api/valueset/{name}` -4. Test lookup: `ValueSet::get('name')` in controller/model diff --git a/.serena/memories/task_completion.md b/.serena/memories/task_completion.md index 64b653d..1374db8 100644 --- a/.serena/memories/task_completion.md +++ b/.serena/memories/task_completion.md @@ -1,129 +1,67 @@ -# CLQMS Task Completion Guidelines +# CLQMS Task Completion Checklist -## When a Task is Completed - -### 1. Run Tests -Always run relevant tests after making changes: +When completing a task, ensure: +## 1. Tests Pass ```bash -# Run all tests ./vendor/bin/phpunit - -# Run specific test file -./vendor/bin/phpunit tests/feature/Patients/PatientCreateTest.php - -# Run specific test method -./vendor/bin/phpunit --filter testCreatePatientSuccess tests/feature/Patients/PatientCreateTest.php ``` +- All existing tests must pass +- Add new tests for new features +- Test naming: `test` -### 2. Verify Code Quality +## 2. API Documentation Updated (CRITICAL) +When updating ANY controller, update corresponding OpenAPI YAML: -#### Check for Syntax Errors +| Controller | YAML Path File | YAML Schema File | +|-----------|----------------|------------------| +| `PatientController` | `paths/patients.yaml` | `components/schemas/patient.yaml` | +| `PatVisitController` | `paths/patient-visits.yaml` | `components/schemas/patient-visit.yaml` | +| `OrderTestController` | `paths/orders.yaml` | `components/schemas/orders.yaml` | +| `SpecimenController` | `paths/specimen.yaml` | `components/schemas/specimen.yaml` | +| `TestsController` | `paths/tests.yaml` | `components/schemas/tests.yaml` | +| `AuthController` | `paths/authentication.yaml` | `components/schemas/authentication.yaml` | +| `ResultController` | `paths/results.yaml` | `components/schemas/*.yaml` | +| `EdgeController` | `paths/edge-api.yaml` | `components/schemas/edge-api.yaml` | +| `LocationController` | `paths/locations.yaml` | `components/schemas/master-data.yaml` | +| `ValueSetController` | `paths/valuesets.yaml` | `components/schemas/valuesets.yaml` | +| `ContactController` | `paths/contact.yaml` | (inline schemas) | + +After updating YAML files: ```bash -php -l app/Controllers/Patient/PatientController.php +node public/bundle-api-docs.js ``` -#### Check Test Results -Ensure: -- All existing tests still pass -- New tests (if any) pass -- No unexpected warnings or errors - -### 3. Code Review Checklist - -#### Controller Changes -- [ ] Validation rules are properly defined -- [ ] Error handling uses try-catch blocks -- [ ] Responses use standardized format with `ResponseTrait` -- [ ] Authentication filter applied where needed -- [ ] Input sanitization via validation rules -- [ ] Database operations wrapped in transactions (if multi-table) - -#### Model Changes -- [ ] Extends `BaseModel` for UTC handling -- [ ] Table name, primary key, allowed fields defined -- [ ] Uses `checkDbError()` for error detection -- [ ] Audit logging via `AuditService::logData()` for data changes -- [ ] Soft delete fields configured if using soft deletes -- [ ] Nested data properly handled (extracted before filtering) - -#### New Routes -- [ ] Added to `app/Config/Routes.php` -- [ ] Follows REST conventions (GET, POST, PATCH, DELETE) -- [ ] Grouped appropriately under `/api/` -- [ ] Auth filter applied if needed - -#### New Tests -- [ ] Test name follows `test` pattern -- [ ] Uses `FeatureTestTrait` -- [ ] Uses `Factory::create('id_ID')` for Indonesian test data -- [ ] Asserts correct status codes (200, 201, 400, 401, 404, 500) -- [ ] Tests both success and failure scenarios - -### 4. Common Issues to Check - -#### Database Operations -- Multi-table operations should use transactions -- Use parameterized queries (Query Builder handles this) -- Check for empty/null arrays before processing - -#### Nested Data -- Extract nested arrays before filtering/processing -- Handle empty/null nested data appropriately +## 3. Code Quality Checks +- PSR-12 compliance where applicable +- No database queries in controllers - Use transactions for multi-table operations +- Proper error handling with try-catch for JWT/external calls +- Log errors: `log_message('error', $message)` -#### Date Handling -- All dates stored in UTC -- Use `helper('utc')` for conversions -- BaseModel extends with automatic UTC conversion - -#### Security -- Input validation rules defined -- SQL injection prevention via parameterized queries -- JWT validation on protected endpoints -- No sensitive data logged or exposed - -### 5. What NOT to Do - -- **Do NOT commit** unless explicitly asked by the user -- **Do NOT push** to remote repository unless asked -- **Do NOT** skip running tests -- **Do NOT** add comments unless specifically requested -- **Do NOT** modify `.env` (database credentials, secrets) -- **Do NOT** include hardcoded secrets in code - -### 6. Common Test Status Codes Reference - -| Code | Usage | Example | -|------|-------|---------| -| 200 | GET/PATCH success | `->assertStatus(200)` | -| 201 | POST success (created) | `->assertStatus(201)` | -| 400 | Validation error | `->assertStatus(400)` | -| 401 | Unauthorized | `->assertStatus(401)` | -| 404 | Not found | `->assertStatus(404)` | -| 500 | Server error | `->assertStatus(500)` | - -### 7. After Completing Tasks - -Simply inform the user the task is complete. For example: -``` -Task completed. The patient create endpoint now validates the identifier type dynamically. +## 4. Response Format Verification +Ensure all responses follow the standard format: +```php +return $this->respond([ + 'status' => 'success|failed', + 'message' => 'Description', + 'data' => $data +], $httpStatus); ``` -Or if tests were run: -``` -Task completed. All tests passing: -- testCreatePatientSuccess ✓ -- testCreatePatientValidationFail ✓ -``` +## 5. Security Checklist +- Use `auth` filter for protected routes +- Sanitize user inputs +- Use parameterized queries +- No secrets committed to repo (use .env) -### 8. When Something Goes Wrong +## 6. Naming Conventions +- Classes: PascalCase +- Methods: camelCase +- Properties: snake_case (legacy) / camelCase (new) +- Database columns: PascalCase (legacy convention) -If tests fail or errors occur: -1. Check the error message carefully -2. Review the code against the patterns in this guide -3. Check database connection and configuration -4. Verify all required dependencies are installed -5. Review log files in `writable/logs/` - -If unable to resolve, inform the user with details of the issue. +## 7. Do NOT Commit Unless Explicitly Asked +- Check status: `git status` +- Never commit .env files +- Never commit secrets diff --git a/.serena/project.yml b/.serena/project.yml index e9ee168..6f908a6 100644 --- a/.serena/project.yml +++ b/.serena/project.yml @@ -125,3 +125,8 @@ language_backend: # list of regex patterns which, when matched, mark a memory entry as read‑only. # Extends the list from the global configuration, merging the two lists. read_only_memory_patterns: [] + +# line ending convention to use when writing source files. +# Possible values: unset (use global setting), "lf", "crlf", or "native" (platform default) +# This does not affect Serena's own files (e.g. memories and configuration files), which always use native line endings. +line_ending: diff --git a/app/Controllers/Organization/SiteController.php b/app/Controllers/Organization/SiteController.php index 6010689..47c66d4 100644 --- a/app/Controllers/Organization/SiteController.php +++ b/app/Controllers/Organization/SiteController.php @@ -56,6 +56,17 @@ class SiteController extends BaseController { public function create() { $input = $this->request->getJSON(true); + + $validation = service('validation'); + $validation->setRules([ + 'SiteCode' => 'required|regex_match[/^[A-Z0-9]{2}$/]', + 'SiteName' => 'required', + ]); + + if (!$validation->run($input)) { + return $this->failValidationErrors($validation->getErrors()); + } + try { $id = $this->model->insert($input,true); return $this->respondCreated([ 'status' => 'success', 'message' => 'data created successfully', 'data' => $id ], 201); @@ -66,9 +77,22 @@ class SiteController extends BaseController { public function update() { $input = $this->request->getJSON(true); + + $id = $input['SiteID']; + if (!$id) { return $this->failValidationErrors('ID is required.'); } + + if (!empty($input['SiteCode'])) { + $validation = service('validation'); + $validation->setRules([ + 'SiteCode' => 'regex_match[/^[A-Z0-9]{2}$/]', + ]); + + if (!$validation->run($input)) { + return $this->failValidationErrors($validation->getErrors()); + } + } + try { - $id = $input['SiteID']; - if (!$id) { return $this->failValidationErrors('ID is required.'); } $this->model->update($id, $input); return $this->respondCreated([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 201); } catch (\Throwable $e) { diff --git a/app/Controllers/Test/TestsController.php b/app/Controllers/Test/TestsController.php index b1658a7..a6e79ab 100644 --- a/app/Controllers/Test/TestsController.php +++ b/app/Controllers/Test/TestsController.php @@ -11,7 +11,6 @@ class TestsController extends BaseController { use ResponseTrait; - protected $db; protected $model; protected $modelCal; protected $modelGrp; @@ -23,7 +22,6 @@ class TestsController extends BaseController public function __construct() { - $this->db = \Config\Database::connect(); $this->model = new \App\Models\Test\TestDefSiteModel; $this->modelCal = new \App\Models\Test\TestDefCalModel; $this->modelGrp = new \App\Models\Test\TestDefGrpModel; @@ -81,9 +79,7 @@ class TestsController extends BaseController return $this->failValidationErrors('TestSiteID is required'); } - $row = $this->model->select('testdefsite.*') - ->where('testdefsite.TestSiteID', $id) - ->find($id); + $row = $this->model->getTestById($id); if (!$row) { return $this->respond([ @@ -93,25 +89,14 @@ class TestsController extends BaseController ], 200); } - $row = ValueSet::transformLabels([$row], [ - 'TestType' => 'test_type', - ])[0]; - $typeCode = $row['TestType'] ?? ''; if ($typeCode === 'CALC') { $row['testdefcal'] = $this->modelCal->getByTestSiteID($id); } elseif ($typeCode === 'GROUP') { $row['testdefgrp'] = $this->modelGrp->getGroupMembers($id); - } elseif ($typeCode === 'TITLE') { - } else { - $row['testdeftech'] = $this->db->table('testdefsite') - ->select('testdefsite.*, d.DisciplineName, dept.DepartmentName') - ->join('discipline d', 'd.DisciplineID=testdefsite.DisciplineID', 'left') - ->join('department dept', 'dept.DepartmentID=testdefsite.DepartmentID', 'left') - ->where('testdefsite.TestSiteID', $id) - ->where('testdefsite.EndDate IS NULL') - ->get()->getResultArray(); + } elseif ($typeCode !== 'TITLE') { + $row['testdeftech'] = $this->model->getTestTechWithRelations($id); if (!empty($row['testdeftech'])) { $techData = $row['testdeftech'][0]; @@ -119,47 +104,11 @@ class TestsController extends BaseController $resultType = $techData['ResultType'] ?? ''; if (TestValidationService::usesRefNum($resultType, $refType)) { - $refnumData = $this->modelRefNum->getActiveByTestSiteID($id); - - $row['refnum'] = array_map(function ($r) { - return [ - 'RefNumID' => $r['RefNumID'], - 'NumRefType' => $r['NumRefType'], - 'NumRefTypeLabel' => $r['NumRefType'] ? ValueSet::getLabel('numeric_ref_type', $r['NumRefType']) : '', - 'RangeType' => $r['RangeType'], - 'RangeTypeLabel' => $r['RangeType'] ? ValueSet::getLabel('range_type', $r['RangeType']) : '', - 'Sex' => $r['Sex'], - 'SexLabel' => $r['Sex'] ? ValueSet::getLabel('gender', $r['Sex']) : '', - 'LowSign' => $r['LowSign'], - 'LowSignLabel' => $r['LowSign'] ? ValueSet::getLabel('math_sign', $r['LowSign']) : '', - 'HighSign' => $r['HighSign'], - 'HighSignLabel' => $r['HighSign'] ? ValueSet::getLabel('math_sign', $r['HighSign']) : '', - 'High' => $r['High'] !== null ? (float) $r['High'] : null, - 'Low' => $r['Low'] !== null ? (float) $r['Low'] : null, - 'AgeStart' => (int) $r['AgeStart'], - 'AgeEnd' => (int) $r['AgeEnd'], - 'Flag' => $r['Flag'], - 'Interpretation' => $r['Interpretation'], - ]; - }, $refnumData ?? []); + $row['refnum'] = $this->modelRefNum->getFormattedByTestSiteID($id); } if (TestValidationService::usesRefTxt($resultType, $refType)) { - $reftxtData = $this->modelRefTxt->getActiveByTestSiteID($id); - - $row['reftxt'] = array_map(function ($r) { - return [ - 'RefTxtID' => $r['RefTxtID'], - 'TxtRefType' => $r['TxtRefType'], - 'TxtRefTypeLabel'=> $r['TxtRefType'] ? ValueSet::getLabel('text_ref_type', $r['TxtRefType']) : '', - 'Sex' => $r['Sex'], - 'SexLabel' => $r['Sex'] ? ValueSet::getLabel('gender', $r['Sex']) : '', - 'AgeStart' => (int) $r['AgeStart'], - 'AgeEnd' => (int) $r['AgeEnd'], - 'RefTxt' => $r['RefTxt'], - 'Flag' => $r['Flag'], - ]; - }, $reftxtData ?? []); + $row['reftxt'] = $this->modelRefTxt->getFormattedByTestSiteID($id); } } } @@ -199,7 +148,8 @@ class TestsController extends BaseController } } - $this->db->transStart(); + $db = \Config\Database::connect(); + $db->transStart(); try { $testSiteData = [ @@ -225,9 +175,9 @@ class TestsController extends BaseController $this->handleDetails($id, $input, 'insert'); - $this->db->transComplete(); + $db->transComplete(); - if ($this->db->transStatus() === false) { + if ($db->transStatus() === false) { return $this->failServerError('Transaction failed'); } @@ -237,7 +187,7 @@ class TestsController extends BaseController 'data' => ['TestSiteId' => $id], ]); } catch (\Exception $e) { - $this->db->transRollback(); + $db->transRollback(); return $this->failServerError('Something went wrong: ' . $e->getMessage()); } @@ -279,7 +229,8 @@ class TestsController extends BaseController } } - $this->db->transStart(); + $db = \Config\Database::connect(); + $db->transStart(); try { $testSiteData = []; @@ -310,9 +261,9 @@ class TestsController extends BaseController $this->handleDetails($id, $input, 'update'); - $this->db->transComplete(); + $db->transComplete(); - if ($this->db->transStatus() === false) { + if ($db->transStatus() === false) { return $this->failServerError('Transaction failed'); } @@ -322,7 +273,7 @@ class TestsController extends BaseController 'data' => ['TestSiteId' => $id], ]); } catch (\Exception $e) { - $this->db->transRollback(); + $db->transRollback(); return $this->failServerError('Something went wrong: ' . $e->getMessage()); } @@ -348,7 +299,8 @@ class TestsController extends BaseController return $this->failValidationErrors('Test is already disabled'); } - $this->db->transStart(); + $db = \Config\Database::connect(); + $db->transStart(); try { $now = date('Y-m-d H:i:s'); @@ -363,8 +315,8 @@ class TestsController extends BaseController } elseif (TestValidationService::isGroup($typeCode)) { $this->modelGrp->disableByTestSiteID($id); } elseif (TestValidationService::isTechnicalTest($typeCode)) { - $this->modelRefNum->where('TestSiteID', $id)->set('EndDate', $now)->update(); - $this->modelRefTxt->where('TestSiteID', $id)->set('EndDate', $now)->update(); + $this->modelRefNum->disableByTestSiteID($id); + $this->modelRefTxt->disableByTestSiteID($id); } // Disable testmap by test code @@ -372,17 +324,14 @@ class TestsController extends BaseController if ($testSiteCode) { $existingMaps = $this->modelMap->getMappingsByTestCode($testSiteCode); foreach ($existingMaps as $existingMap) { - $this->modelMapDetail->where('TestMapID', $existingMap['TestMapID']) - ->where('EndDate', null) - ->set('EndDate', $now) - ->update(); + $this->modelMapDetail->disableByTestMapID($existingMap['TestMapID']); $this->modelMap->update($existingMap['TestMapID'], ['EndDate' => $now]); } } - $this->db->transComplete(); + $db->transComplete(); - if ($this->db->transStatus() === false) { + if ($db->transStatus() === false) { return $this->failServerError('Transaction failed'); } @@ -392,7 +341,7 @@ class TestsController extends BaseController 'data' => ['TestSiteId' => $id, 'EndDate' => $now], ]); } catch (\Exception $e) { - $this->db->transRollback(); + $db->transRollback(); return $this->failServerError('Something went wrong: ' . $e->getMessage()); } @@ -488,53 +437,19 @@ class TestsController extends BaseController private function saveRefNumRanges($testSiteID, $ranges, $action, $siteID) { if ($action === 'update') { - $this->modelRefNum->where('TestSiteID', $testSiteID) - ->set('EndDate', date('Y-m-d H:i:s')) - ->update(); + $this->modelRefNum->disableByTestSiteID($testSiteID); } - foreach ($ranges as $index => $range) { - $this->modelRefNum->insert([ - 'TestSiteID' => $testSiteID, - 'SiteID' => $siteID, - 'NumRefType' => $range['NumRefType'], - 'RangeType' => $range['RangeType'], - 'Sex' => $range['Sex'], - 'AgeStart' => (int) ($range['AgeStart'] ?? 0), - 'AgeEnd' => (int) ($range['AgeEnd'] ?? 150), - 'LowSign' => !empty($range['LowSign']) ? $range['LowSign'] : null, - 'Low' => !empty($range['Low']) ? (float) $range['Low'] : null, - 'HighSign' => !empty($range['HighSign']) ? $range['HighSign'] : null, - 'High' => !empty($range['High']) ? (float) $range['High'] : null, - 'Flag' => $range['Flag'] ?? null, - 'Interpretation'=> $range['Interpretation'] ?? null, - 'Display' => $index, - 'CreateDate' => date('Y-m-d H:i:s'), - ]); - } + $this->modelRefNum->batchInsert($testSiteID, $siteID, $ranges); } private function saveRefTxtRanges($testSiteID, $ranges, $action, $siteID) { if ($action === 'update') { - $this->modelRefTxt->where('TestSiteID', $testSiteID) - ->set('EndDate', date('Y-m-d H:i:s')) - ->update(); + $this->modelRefTxt->disableByTestSiteID($testSiteID); } - foreach ($ranges as $range) { - $this->modelRefTxt->insert([ - 'TestSiteID' => $testSiteID, - 'SiteID' => $siteID, - 'TxtRefType' => $range['TxtRefType'], - 'Sex' => $range['Sex'], - 'AgeStart' => (int) ($range['AgeStart'] ?? 0), - 'AgeEnd' => (int) ($range['AgeEnd'] ?? 150), - 'RefTxt' => $range['RefTxt'] ?? '', - 'Flag' => $range['Flag'] ?? null, - 'CreateDate' => date('Y-m-d H:i:s'), - ]); - } + $this->modelRefTxt->batchInsert($testSiteID, $siteID, $ranges); } private function saveCalcDetails($testSiteID, $data, $action) @@ -595,10 +510,7 @@ class TestsController extends BaseController $existingMaps = $this->modelMap->getMappingsByTestCode($testSiteCode); foreach ($existingMaps as $existingMap) { - $this->modelMapDetail->where('TestMapID', $existingMap['TestMapID']) - ->where('EndDate', null) - ->set('EndDate', date('Y-m-d H:i:s')) - ->update(); + $this->modelMapDetail->disableByTestMapID($existingMap['TestMapID']); } // Soft delete the testmap headers diff --git a/app/Database/Migrations/2026-01-01-000001_CreateValueSet.php b/app/Database/Migrations/2026-01-01-000001_CreateValueSet.php index e50ea77..d820094 100644 --- a/app/Database/Migrations/2026-01-01-000001_CreateValueSet.php +++ b/app/Database/Migrations/2026-01-01-000001_CreateValueSet.php @@ -36,6 +36,7 @@ class CreateLookups extends Migration { 'CounterValue' => ['type' => 'INT', 'null' => false], 'CounterStart' => ['type' => 'INT', 'null' => false], 'CounterEnd' => ['type' => 'INT', 'null' => false], + 'CounterName' => [ 'type' => 'VARCHAR', 'constraint' => 50, 'null' => true, 'after' => 'CounterID' ], 'CounterDesc' => ['type' => 'varchar', 'constraint' => 255, 'null' => true], 'CounterReset' => ['type' => 'varchar', 'constraint' => 1, 'null' => true], 'CreateDate' => ['type' => 'Datetime', 'null' => true], diff --git a/app/Database/Migrations/2026-01-01-000002_CreateOrganization.php b/app/Database/Migrations/2026-01-01-000002_CreateOrganization.php index d6b849c..da3d915 100644 --- a/app/Database/Migrations/2026-01-01-000002_CreateOrganization.php +++ b/app/Database/Migrations/2026-01-01-000002_CreateOrganization.php @@ -31,12 +31,13 @@ class CreateOrganization extends Migration { $this->forge->addField([ 'SiteID' => ['type' => 'INT', 'unsigned' => true, 'auto_increment' => true], - 'SiteCode' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => false], + 'ExtSiteID' => ['type' => 'int', 'null' => true], + 'SiteCode' => ['type' => 'VARCHAR', 'constraint' => 2, 'null' => false], 'SiteName' => ['type' => 'VARCHAR', 'constraint' => 100, 'null' => false], 'AccountID' => ['type' => 'int', 'null' => true], - 'SiteTypeID' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true], + 'SiteType' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true], 'Parent' => ['type' => 'int', 'null' => true], - 'SiteClassID' => ['type' => 'VARCHAR', 'constraint' => 50, 'null' => true], + 'SiteClass' => ['type' => 'VARCHAR', 'constraint' => 50, 'null' => true], 'ME' => ['type' => 'VARCHAR', 'constraint' => 50, 'null' => true], 'CreateDate' => ['type' => 'datetime', 'null'=> true], 'EndDate' => ['type' => 'datetime', 'null'=> true] @@ -81,6 +82,8 @@ class CreateOrganization extends Migration { 'DisciplineCode' => ['type' => 'varchar', 'constraint'=> 10, 'null'=> false], 'DisciplineName' => ['type' => 'varchar', 'constraint'=> 150, 'null'=> true], 'Parent' => ['type' => 'int', 'null'=> true], + 'SeqScr' => ['type' => 'int', 'null' => true], + 'SeqRpt' => ['type' => 'int', 'null' => true], 'CreateDate' => ['type'=>'DATETIME', 'null' => true], 'EndDate' => ['type'=>'DATETIME', 'null' => true] ]); diff --git a/app/Database/Seeds/OrganizationSeeder.php b/app/Database/Seeds/OrganizationSeeder.php index c7076d4..ecd556d 100644 --- a/app/Database/Seeds/OrganizationSeeder.php +++ b/app/Database/Seeds/OrganizationSeeder.php @@ -20,25 +20,25 @@ class OrganizationSeeder extends Seeder $this->db->table('account')->insertBatch($data); $data = [ - [ 'SiteID' => 1, 'SiteCode' => 'QSIT', 'SiteName' => 'Dummy Site', 'AccountID' => 1, 'Parent' => null, 'CreateDate' => "$now" ], - [ 'SiteID' => 2, 'SiteCode' => 'XSIT', 'SiteName' => 'Dummy Child Site', 'AccountID' => 1, 'Parent' => 1, 'CreateDate' => "$now" ], + [ 'SiteID' => 1, 'SiteCode' => 'Q1', 'SiteName' => 'Dummy Site', 'AccountID' => 1, 'Parent' => null, 'CreateDate' => "$now" ], + [ 'SiteID' => 2, 'SiteCode' => 'X1', 'SiteName' => 'Dummy Child Site', 'AccountID' => 1, 'Parent' => 1, 'CreateDate' => "$now" ], ]; $this->db->table('site')->insertBatch($data); $data = [ - ['DisciplineID' => '1', 'DisciplineCode' => 'HEMA', 'DisciplineName' => 'Hematology', 'Parent' => null, 'CreateDate' => "$now"], - ['DisciplineID' => '2', 'DisciplineCode' => 'CHEM', 'DisciplineName' => 'Clinical Chemistry', 'Parent' => null, 'CreateDate' => "$now"], - ['DisciplineID' => '3', 'DisciplineCode' => 'IMSR', 'DisciplineName' => 'Immunology/Serology', 'Parent' => null, 'CreateDate' => "$now"], - ['DisciplineID' => '4', 'DisciplineCode' => 'URIN', 'DisciplineName' => 'Urinalysis', 'Parent' => null, 'CreateDate' => "$now"], - ['DisciplineID' => '5', 'DisciplineCode' => 'FECAL', 'DisciplineName' => 'Fecal Analysis', 'Parent' => null, 'CreateDate' => "$now"], - ['DisciplineID' => '6', 'DisciplineCode' => 'HC', 'DisciplineName' => 'Pathology/Cytology', 'Parent' => null, 'CreateDate' => "$now"], - ['DisciplineID' => '7', 'DisciplineCode' => 'MICRO', 'DisciplineName' => 'Microbiology', 'Parent' => null, 'CreateDate' => "$now"], - ['DisciplineID' => '8', 'DisciplineCode' => 'TXC', 'DisciplineName' => 'Toxicology', 'Parent' => null, 'CreateDate' => "$now"], - ['DisciplineID' => '9', 'DisciplineCode' => 'LF', 'DisciplineName' => 'Life Sciences', 'Parent' => null, 'CreateDate' => "$now"], - ['DisciplineID' => '10', 'DisciplineCode' => 'ND', 'DisciplineName' => 'Non-discipline', 'Parent' => null, 'CreateDate' => "$now"], - ['DisciplineID' => '11', 'DisciplineCode' => 'HEMO', 'DisciplineName' => 'Hemostasis', 'Parent' => '1', 'CreateDate' => "$now"], - ['DisciplineID' => '12', 'DisciplineCode' => 'BLGLU', 'DisciplineName' => 'Blood Glucose', 'Parent' => '2', 'CreateDate' => "$now"], - ['DisciplineID' => '13', 'DisciplineCode' => 'KIDF', 'DisciplineName' => 'Kidney Function', 'Parent' => '2', 'CreateDate' => "$now"], + ['DisciplineID' => '1', 'DisciplineCode' => 'HEMA', 'DisciplineName' => 'Hematology', 'Parent' => null, 'SeqScr' => 10, 'SeqRpt' => 10, 'CreateDate' => "$now"], + ['DisciplineID' => '2', 'DisciplineCode' => 'CHEM', 'DisciplineName' => 'Clinical Chemistry', 'Parent' => null, 'SeqScr' => 20, 'SeqRpt' => 20, 'CreateDate' => "$now"], + ['DisciplineID' => '3', 'DisciplineCode' => 'IMSR', 'DisciplineName' => 'Immunology/Serology', 'Parent' => null, 'SeqScr' => 30, 'SeqRpt' => 30, 'CreateDate' => "$now"], + ['DisciplineID' => '4', 'DisciplineCode' => 'URIN', 'DisciplineName' => 'Urinalysis', 'Parent' => null, 'SeqScr' => 40, 'SeqRpt' => 40, 'CreateDate' => "$now"], + ['DisciplineID' => '5', 'DisciplineCode' => 'FECAL', 'DisciplineName' => 'Fecal Analysis', 'Parent' => null, 'SeqScr' => 50, 'SeqRpt' => 50, 'CreateDate' => "$now"], + ['DisciplineID' => '6', 'DisciplineCode' => 'HC', 'DisciplineName' => 'Pathology/Cytology', 'Parent' => null, 'SeqScr' => 60, 'SeqRpt' => 60, 'CreateDate' => "$now"], + ['DisciplineID' => '7', 'DisciplineCode' => 'MICRO', 'DisciplineName' => 'Microbiology', 'Parent' => null, 'SeqScr' => 70, 'SeqRpt' => 70, 'CreateDate' => "$now"], + ['DisciplineID' => '8', 'DisciplineCode' => 'TXC', 'DisciplineName' => 'Toxicology', 'Parent' => null, 'SeqScr' => 80, 'SeqRpt' => 80, 'CreateDate' => "$now"], + ['DisciplineID' => '9', 'DisciplineCode' => 'LF', 'DisciplineName' => 'Life Sciences', 'Parent' => null, 'SeqScr' => 90, 'SeqRpt' => 90, 'CreateDate' => "$now"], + ['DisciplineID' => '10', 'DisciplineCode' => 'ND', 'DisciplineName' => 'Non-discipline', 'Parent' => null, 'SeqScr' => 100, 'SeqRpt' => 100, 'CreateDate' => "$now"], + ['DisciplineID' => '11', 'DisciplineCode' => 'HEMO', 'DisciplineName' => 'Hemostasis', 'Parent' => '1', 'SeqScr' => 15, 'SeqRpt' => 15, 'CreateDate' => "$now"], + ['DisciplineID' => '12', 'DisciplineCode' => 'BLGLU', 'DisciplineName' => 'Blood Glucose', 'Parent' => '2', 'SeqScr' => 25, 'SeqRpt' => 25, 'CreateDate' => "$now"], + ['DisciplineID' => '13', 'DisciplineCode' => 'KIDF', 'DisciplineName' => 'Kidney Function', 'Parent' => '2', 'SeqScr' => 26, 'SeqRpt' => 26, 'CreateDate' => "$now"], ]; $this->db->table('discipline')->insertBatch($data); diff --git a/app/Database/Seeds/TestSeeder.php b/app/Database/Seeds/TestSeeder.php index f446f9c..d71fd9d 100644 --- a/app/Database/Seeds/TestSeeder.php +++ b/app/Database/Seeds/TestSeeder.php @@ -65,128 +65,51 @@ class TestSeeder extends Seeder }; // ======================================== - // TEST TYPE - Actual Laboratory Tests + // DEPARTMENT 1 - HEMATOLOGY (Discipline 1) + // Order: GROUP (<100) → PARAM (<100) → TEST (100+) → CALC (100+) // ======================================== - // Hematology Tests - Technical details merged into testdefsite - $data = ['SiteID' => '1', 'TestSiteCode' => 'HB', 'TestSiteName' => 'Hemoglobin', 'TestType' => 'TEST', 'Description' => '', 'SeqScr' => '2', 'SeqRpt' => '2', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '1', 'DepartmentID' => '1', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '500', 'ReqQtyUnit' => 'uL', 'Unit1' => 'g/dL', 'Factor' => '', 'Unit2' => '', 'Decimal' => '1', 'Method' => 'CBC Analyzer', 'CreateDate' => "$now"]; + + // GROUP: CBC (Complete Blood Count) + $data = ['SiteID' => '1', 'TestSiteCode' => 'CBC', 'TestSiteName' => 'Complete Blood Count', 'TestType' => 'GROUP', 'Description' => 'Darah Lengkap', 'SeqScr' => '10', 'SeqRpt' => '10', 'IndentLeft' => '0', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'ResultType' => 'NORES', 'RefType' => 'NOREF', 'CreateDate' => "$now"]; + $this->db->table('testdefsite')->insert($data); + $tIDs['CBC'] = $this->db->insertID(); + + // PARAM: (none for Hematology) + + // TEST: Hematology Tests + $data = ['SiteID' => '1', 'TestSiteCode' => 'HB', 'TestSiteName' => 'Hemoglobin', 'TestType' => 'TEST', 'Description' => '', 'SeqScr' => '100', 'SeqRpt' => '100', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '1', 'DepartmentID' => '1', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '500', 'ReqQtyUnit' => 'uL', 'Unit1' => 'g/dL', 'Factor' => '', 'Unit2' => '', 'Decimal' => '1', 'Method' => 'CBC Analyzer', 'CreateDate' => "$now"]; $this->db->table('testdefsite')->insert($data); $tIDs['HB'] = $this->db->insertID(); - $data = ['SiteID' => '1', 'TestSiteCode' => 'HCT', 'TestSiteName' => 'Hematocrit', 'TestType' => 'TEST', 'Description' => '', 'SeqScr' => '3', 'SeqRpt' => '3', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '1', 'DepartmentID' => '1', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '500', 'ReqQtyUnit' => 'uL', 'Unit1' => '%', 'Factor' => '', 'Unit2' => '', 'Decimal' => '1', 'Method' => 'CBC Analyzer', 'CreateDate' => "$now"]; + $data = ['SiteID' => '1', 'TestSiteCode' => 'HCT', 'TestSiteName' => 'Hematocrit', 'TestType' => 'TEST', 'Description' => '', 'SeqScr' => '110', 'SeqRpt' => '110', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '1', 'DepartmentID' => '1', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '500', 'ReqQtyUnit' => 'uL', 'Unit1' => '%', 'Factor' => '', 'Unit2' => '', 'Decimal' => '1', 'Method' => 'CBC Analyzer', 'CreateDate' => "$now"]; $this->db->table('testdefsite')->insert($data); $tIDs['HCT'] = $this->db->insertID(); - $data = ['SiteID' => '1', 'TestSiteCode' => 'RBC', 'TestSiteName' => 'Red Blood Cell', 'TestType' => 'TEST', 'Description' => 'Eritrosit', 'SeqScr' => '4', 'SeqRpt' => '4', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '1', 'DepartmentID' => '1', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '500', 'ReqQtyUnit' => 'uL', 'Unit1' => 'x10^6/uL', 'Factor' => '', 'Unit2' => '', 'Decimal' => '2', 'Method' => 'CBC Analyzer', 'CreateDate' => "$now"]; + $data = ['SiteID' => '1', 'TestSiteCode' => 'RBC', 'TestSiteName' => 'Red Blood Cell', 'TestType' => 'TEST', 'Description' => 'Eritrosit', 'SeqScr' => '120', 'SeqRpt' => '120', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '1', 'DepartmentID' => '1', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '500', 'ReqQtyUnit' => 'uL', 'Unit1' => 'x10^6/uL', 'Factor' => '', 'Unit2' => '', 'Decimal' => '2', 'Method' => 'CBC Analyzer', 'CreateDate' => "$now"]; $this->db->table('testdefsite')->insert($data); $tIDs['RBC'] = $this->db->insertID(); - $data = ['SiteID' => '1', 'TestSiteCode' => 'WBC', 'TestSiteName' => 'White Blood Cell', 'TestType' => 'TEST', 'Description' => 'Leukosit', 'SeqScr' => '5', 'SeqRpt' => '5', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '1', 'DepartmentID' => '1', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '500', 'ReqQtyUnit' => 'uL', 'Unit1' => 'x10^3/uL', 'Factor' => '', 'Unit2' => '', 'Decimal' => '2', 'Method' => 'CBC Analyzer', 'CreateDate' => "$now"]; + $data = ['SiteID' => '1', 'TestSiteCode' => 'WBC', 'TestSiteName' => 'White Blood Cell', 'TestType' => 'TEST', 'Description' => 'Leukosit', 'SeqScr' => '130', 'SeqRpt' => '130', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '1', 'DepartmentID' => '1', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '500', 'ReqQtyUnit' => 'uL', 'Unit1' => 'x10^3/uL', 'Factor' => '', 'Unit2' => '', 'Decimal' => '2', 'Method' => 'CBC Analyzer', 'CreateDate' => "$now"]; $this->db->table('testdefsite')->insert($data); $tIDs['WBC'] = $this->db->insertID(); - $data = ['SiteID' => '1', 'TestSiteCode' => 'PLT', 'TestSiteName' => 'Platelet', 'TestType' => 'TEST', 'Description' => 'Trombosit', 'SeqScr' => '6', 'SeqRpt' => '6', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '1', 'DepartmentID' => '1', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '500', 'ReqQtyUnit' => 'uL', 'Unit1' => 'x10^3/uL', 'Factor' => '', 'Unit2' => '', 'Decimal' => '0', 'Method' => 'CBC Analyzer', 'CreateDate' => "$now"]; + $data = ['SiteID' => '1', 'TestSiteCode' => 'PLT', 'TestSiteName' => 'Platelet', 'TestType' => 'TEST', 'Description' => 'Trombosit', 'SeqScr' => '140', 'SeqRpt' => '140', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '1', 'DepartmentID' => '1', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '500', 'ReqQtyUnit' => 'uL', 'Unit1' => 'x10^3/uL', 'Factor' => '', 'Unit2' => '', 'Decimal' => '0', 'Method' => 'CBC Analyzer', 'CreateDate' => "$now"]; $this->db->table('testdefsite')->insert($data); $tIDs['PLT'] = $this->db->insertID(); - $data = ['SiteID' => '1', 'TestSiteCode' => 'MCV', 'TestSiteName' => 'MCV', 'TestType' => 'TEST', 'Description' => 'Mean Corpuscular Volume', 'SeqScr' => '7', 'SeqRpt' => '7', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '1', 'DepartmentID' => '1', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '500', 'ReqQtyUnit' => 'uL', 'Unit1' => 'fL', 'Factor' => '', 'Unit2' => '', 'Decimal' => '1', 'Method' => 'CBC Analyzer', 'CreateDate' => "$now"]; + $data = ['SiteID' => '1', 'TestSiteCode' => 'MCV', 'TestSiteName' => 'MCV', 'TestType' => 'TEST', 'Description' => 'Mean Corpuscular Volume', 'SeqScr' => '150', 'SeqRpt' => '150', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '1', 'DepartmentID' => '1', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '500', 'ReqQtyUnit' => 'uL', 'Unit1' => 'fL', 'Factor' => '', 'Unit2' => '', 'Decimal' => '1', 'Method' => 'CBC Analyzer', 'CreateDate' => "$now"]; $this->db->table('testdefsite')->insert($data); $tIDs['MCV'] = $this->db->insertID(); - $data = ['SiteID' => '1', 'TestSiteCode' => 'MCH', 'TestSiteName' => 'MCH', 'TestType' => 'TEST', 'Description' => 'Mean Corpuscular Hemoglobin', 'SeqScr' => '8', 'SeqRpt' => '8', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '1', 'DepartmentID' => '1', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '500', 'ReqQtyUnit' => 'uL', 'Unit1' => 'pg', 'Factor' => '', 'Unit2' => '', 'Decimal' => '1', 'Method' => 'CBC Analyzer', 'CreateDate' => "$now"]; + $data = ['SiteID' => '1', 'TestSiteCode' => 'MCH', 'TestSiteName' => 'MCH', 'TestType' => 'TEST', 'Description' => 'Mean Corpuscular Hemoglobin', 'SeqScr' => '160', 'SeqRpt' => '160', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '1', 'DepartmentID' => '1', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '500', 'ReqQtyUnit' => 'uL', 'Unit1' => 'pg', 'Factor' => '', 'Unit2' => '', 'Decimal' => '1', 'Method' => 'CBC Analyzer', 'CreateDate' => "$now"]; $this->db->table('testdefsite')->insert($data); $tIDs['MCH'] = $this->db->insertID(); - $data = ['SiteID' => '1', 'TestSiteCode' => 'MCHC', 'TestSiteName' => 'MCHC', 'TestType' => 'TEST', 'Description' => 'Mean Corpuscular Hemoglobin Concentration', 'SeqScr' => '9', 'SeqRpt' => '9', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '1', 'DepartmentID' => '1', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '500', 'ReqQtyUnit' => 'uL', 'Unit1' => 'g/dL', 'Factor' => '', 'Unit2' => '', 'Decimal' => '1', 'Method' => 'CBC Analyzer', 'CreateDate' => "$now"]; + $data = ['SiteID' => '1', 'TestSiteCode' => 'MCHC', 'TestSiteName' => 'MCHC', 'TestType' => 'TEST', 'Description' => 'Mean Corpuscular Hemoglobin Concentration', 'SeqScr' => '170', 'SeqRpt' => '170', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '1', 'DepartmentID' => '1', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '500', 'ReqQtyUnit' => 'uL', 'Unit1' => 'g/dL', 'Factor' => '', 'Unit2' => '', 'Decimal' => '1', 'Method' => 'CBC Analyzer', 'CreateDate' => "$now"]; $this->db->table('testdefsite')->insert($data); $tIDs['MCHC'] = $this->db->insertID(); - // Chemistry Tests - $data = ['SiteID' => '1', 'TestSiteCode' => 'GLU', 'TestSiteName' => 'Glucose', 'TestType' => 'TEST', 'Description' => 'Glukosa Sewaktu', 'SeqScr' => '11', 'SeqRpt' => '11', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '2', 'DepartmentID' => '2', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '300', 'ReqQtyUnit' => 'uL', 'Unit1' => 'mg/dL', 'Factor' => '0.0555', 'Unit2' => 'mmol/L', 'Decimal' => '0', 'Method' => 'Hexokinase', 'CreateDate' => "$now"]; - $this->db->table('testdefsite')->insert($data); - $tIDs['GLU'] = $this->db->insertID(); - - $data = ['SiteID' => '1', 'TestSiteCode' => 'CREA', 'TestSiteName' => 'Creatinine', 'TestType' => 'TEST', 'Description' => 'Kreatinin', 'SeqScr' => '12', 'SeqRpt' => '12', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '2', 'DepartmentID' => '2', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '300', 'ReqQtyUnit' => 'uL', 'Unit1' => 'mg/dL', 'Factor' => '88.4', 'Unit2' => 'umol/L', 'Decimal' => '2', 'Method' => 'Enzymatic', 'CreateDate' => "$now"]; - $this->db->table('testdefsite')->insert($data); - $tIDs['CREA'] = $this->db->insertID(); - - $data = ['SiteID' => '1', 'TestSiteCode' => 'UREA', 'TestSiteName' => 'Blood Urea Nitrogen', 'TestType' => 'TEST', 'Description' => 'BUN', 'SeqScr' => '13', 'SeqRpt' => '13', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '2', 'DepartmentID' => '2', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '300', 'ReqQtyUnit' => 'uL', 'Unit1' => 'mg/dL', 'Factor' => '', 'Unit2' => '', 'Decimal' => '1', 'Method' => 'Urease-GLDH', 'CreateDate' => "$now"]; - $this->db->table('testdefsite')->insert($data); - $tIDs['UREA'] = $this->db->insertID(); - - $data = ['SiteID' => '1', 'TestSiteCode' => 'SGOT', 'TestSiteName' => 'AST (SGOT)', 'TestType' => 'TEST', 'Description' => 'Aspartate Aminotransferase', 'SeqScr' => '14', 'SeqRpt' => '14', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '2', 'DepartmentID' => '2', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '300', 'ReqQtyUnit' => 'uL', 'Unit1' => 'U/L', 'Factor' => '0.017', 'Unit2' => 'ukat/L', 'Decimal' => '0', 'Method' => 'IFCC', 'CreateDate' => "$now"]; - $this->db->table('testdefsite')->insert($data); - $tIDs['SGOT'] = $this->db->insertID(); - - $data = ['SiteID' => '1', 'TestSiteCode' => 'SGPT', 'TestSiteName' => 'ALT (SGPT)', 'TestType' => 'TEST', 'Description' => 'Alanine Aminotransferase', 'SeqScr' => '15', 'SeqRpt' => '15', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '2', 'DepartmentID' => '2', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '300', 'ReqQtyUnit' => 'uL', 'Unit1' => 'U/L', 'Factor' => '0.017', 'Unit2' => 'ukat/L', 'Decimal' => '0', 'Method' => 'IFCC', 'CreateDate' => "$now"]; - $this->db->table('testdefsite')->insert($data); - $tIDs['SGPT'] = $this->db->insertID(); - - $data = ['SiteID' => '1', 'TestSiteCode' => 'CHOL', 'TestSiteName' => 'Total Cholesterol', 'TestType' => 'TEST', 'Description' => 'Kolesterol Total', 'SeqScr' => '16', 'SeqRpt' => '16', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '2', 'DepartmentID' => '2', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '300', 'ReqQtyUnit' => 'uL', 'Unit1' => 'mg/dL', 'Factor' => '', 'Unit2' => '', 'Decimal' => '0', 'Method' => 'Enzymatic', 'CreateDate' => "$now"]; - $this->db->table('testdefsite')->insert($data); - $tIDs['CHOL'] = $this->db->insertID(); - - $data = ['SiteID' => '1', 'TestSiteCode' => 'TG', 'TestSiteName' => 'Triglycerides', 'TestType' => 'TEST', 'Description' => 'Trigliserida', 'SeqScr' => '17', 'SeqRpt' => '17', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '2', 'DepartmentID' => '2', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '300', 'ReqQtyUnit' => 'uL', 'Unit1' => 'mg/dL', 'Factor' => '', 'Unit2' => '', 'Decimal' => '0', 'Method' => 'GPO-PAP', 'CreateDate' => "$now"]; - $this->db->table('testdefsite')->insert($data); - $tIDs['TG'] = $this->db->insertID(); - - $data = ['SiteID' => '1', 'TestSiteCode' => 'HDL', 'TestSiteName' => 'HDL Cholesterol', 'TestType' => 'TEST', 'Description' => 'Kolesterol HDL', 'SeqScr' => '18', 'SeqRpt' => '18', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '2', 'DepartmentID' => '2', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '300', 'ReqQtyUnit' => 'uL', 'Unit1' => 'mg/dL', 'Factor' => '', 'Unit2' => '', 'Decimal' => '0', 'Method' => 'Direct', 'CreateDate' => "$now"]; - $this->db->table('testdefsite')->insert($data); - $tIDs['HDL'] = $this->db->insertID(); - - $data = ['SiteID' => '1', 'TestSiteCode' => 'LDL', 'TestSiteName' => 'LDL Cholesterol', 'TestType' => 'TEST', 'Description' => 'Kolesterol LDL', 'SeqScr' => '19', 'SeqRpt' => '19', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '2', 'DepartmentID' => '2', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '300', 'ReqQtyUnit' => 'uL', 'Unit1' => 'mg/dL', 'Factor' => '', 'Unit2' => '', 'Decimal' => '0', 'Method' => 'Direct', 'CreateDate' => "$now"]; - $this->db->table('testdefsite')->insert($data); - $tIDs['LDL'] = $this->db->insertID(); - - // ======================================== - // PARAM TYPE - Parameters (non-lab values) - // ======================================== - $data = ['SiteID' => '1', 'TestSiteCode' => 'HEIGHT', 'TestSiteName' => 'Height', 'TestType' => 'PARAM', 'Description' => 'Tinggi Badan', 'SeqScr' => '40', 'SeqRpt' => '40', 'IndentLeft' => '0', 'VisibleScr' => '1', 'VisibleRpt' => '0', 'CountStat' => '0', 'DisciplineID' => '10', 'DepartmentID' => '', 'ResultType' => 'NMRIC', 'RefType' => '', 'VSet' => '', 'ReqQty' => '', 'ReqQtyUnit' => '', 'Unit1' => 'cm', 'Factor' => '', 'Unit2' => '', 'Decimal' => '0', 'Method' => '', 'CreateDate' => "$now"]; - $this->db->table('testdefsite')->insert($data); - $tIDs['HEIGHT'] = $this->db->insertID(); - - $data = ['SiteID' => '1', 'TestSiteCode' => 'WEIGHT', 'TestSiteName' => 'Weight', 'TestType' => 'PARAM', 'Description' => 'Berat Badan', 'SeqScr' => '41', 'SeqRpt' => '41', 'IndentLeft' => '0', 'VisibleScr' => '1', 'VisibleRpt' => '0', 'CountStat' => '0', 'DisciplineID' => '10', 'DepartmentID' => '', 'ResultType' => 'NMRIC', 'RefType' => '', 'VSet' => '', 'ReqQty' => '', 'ReqQtyUnit' => '', 'Unit1' => 'kg', 'Factor' => '', 'Unit2' => '', 'Decimal' => '1', 'Method' => '', 'CreateDate' => "$now"]; - $this->db->table('testdefsite')->insert($data); - $tIDs['WEIGHT'] = $this->db->insertID(); - - $data = ['SiteID' => '1', 'TestSiteCode' => 'AGE', 'TestSiteName' => 'Age', 'TestType' => 'PARAM', 'Description' => 'Usia', 'SeqScr' => '42', 'SeqRpt' => '42', 'IndentLeft' => '0', 'VisibleScr' => '1', 'VisibleRpt' => '0', 'CountStat' => '0', 'DisciplineID' => '10', 'DepartmentID' => '', 'ResultType' => 'NMRIC', 'RefType' => '', 'VSet' => '', 'ReqQty' => '', 'ReqQtyUnit' => '', 'Unit1' => 'years', 'Factor' => '', 'Unit2' => '', 'Decimal' => '0', 'Method' => '', 'CreateDate' => "$now"]; - $this->db->table('testdefsite')->insert($data); - $tIDs['AGE'] = $this->db->insertID(); - - $data = ['SiteID' => '1', 'TestSiteCode' => 'SYSTL', 'TestSiteName' => 'Systolic BP', 'TestType' => 'PARAM', 'Description' => 'Tekanan Darah Sistolik', 'SeqScr' => '43', 'SeqRpt' => '43', 'IndentLeft' => '0', 'VisibleScr' => '1', 'VisibleRpt' => '0', 'CountStat' => '0', 'DisciplineID' => '10', 'DepartmentID' => '', 'ResultType' => 'NMRIC', 'RefType' => '', 'VSet' => '', 'ReqQty' => '', 'ReqQtyUnit' => '', 'Unit1' => 'mmHg', 'Factor' => '', 'Unit2' => '', 'Decimal' => '0', 'Method' => '', 'CreateDate' => "$now"]; - $this->db->table('testdefsite')->insert($data); - $tIDs['SYSTL'] = $this->db->insertID(); - - $data = ['SiteID' => '1', 'TestSiteCode' => 'DIASTL', 'TestSiteName' => 'Diastolic BP', 'TestType' => 'PARAM', 'Description' => 'Tekanan Darah Diastolik', 'SeqScr' => '44', 'SeqRpt' => '44', 'IndentLeft' => '0', 'VisibleScr' => '1', 'VisibleRpt' => '0', 'CountStat' => '0', 'DisciplineID' => '10', 'DepartmentID' => '', 'ResultType' => 'NMRIC', 'RefType' => '', 'VSet' => '', 'ReqQty' => '', 'ReqQtyUnit' => '', 'Unit1' => 'mmHg', 'Factor' => '', 'Unit2' => '', 'Decimal' => '0', 'Method' => '', 'CreateDate' => "$now"]; - $this->db->table('testdefsite')->insert($data); - $tIDs['DIASTL'] = $this->db->insertID(); - - // ======================================== - // CALC TYPE - Calculated Tests - // ======================================== - $data = ['SiteID' => '1', 'TestSiteCode' => 'BMI', 'TestSiteName' => 'Body Mass Index', 'TestType' => 'CALC', 'Description' => 'Indeks Massa Tubuh - weight/(height^2)', 'SeqScr' => '45', 'SeqRpt' => '45', 'IndentLeft' => '0', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '0', 'CreateDate' => "$now"]; - $this->db->table('testdefsite')->insert($data); - $tIDs['BMI'] = $this->db->insertID(); - $data = ['TestSiteID' => $tIDs['BMI'], 'DisciplineID' => '10', 'DepartmentID' => '', 'FormulaInput' => 'WEIGHT,HEIGHT', 'FormulaCode' => 'WEIGHT / ((HEIGHT/100) * (HEIGHT/100))', 'RefType' => 'RANGE', 'Unit1' => 'kg/m2', 'Factor' => '', 'Unit2' => '', 'Decimal' => '1', 'CreateDate' => "$now"]; - $this->db->table('testdefcal')->insert($data); - - $data = ['SiteID' => '1', 'TestSiteCode' => 'EGFR', 'TestSiteName' => 'eGFR (CKD-EPI)', 'TestType' => 'CALC', 'Description' => 'Estimated Glomerular Filtration Rate', 'SeqScr' => '20', 'SeqRpt' => '20', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '0', 'CreateDate' => "$now"]; - $this->db->table('testdefsite')->insert($data); - $tIDs['EGFR'] = $this->db->insertID(); - $data = ['TestSiteID' => $tIDs['EGFR'], 'DisciplineID' => '2', 'DepartmentID' => '2', 'FormulaInput' => 'CREA,AGE,GENDER', 'FormulaCode' => 'CKD_EPI(CREA,AGE,GENDER)', 'RefType' => 'RANGE', 'Unit1' => 'mL/min/1.73m2', 'Factor' => '', 'Unit2' => '', 'Decimal' => '0', 'CreateDate' => "$now"]; - $this->db->table('testdefcal')->insert($data); - - $data = ['SiteID' => '1', 'TestSiteCode' => 'LDLCALC', 'TestSiteName' => 'LDL Cholesterol (Calculated)', 'TestType' => 'CALC', 'Description' => 'Friedewald formula: TC - HDL - (TG/5)', 'SeqScr' => '21', 'SeqRpt' => '21', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '0', 'CreateDate' => "$now"]; - $this->db->table('testdefsite')->insert($data); - $tIDs['LDLCALC'] = $this->db->insertID(); - $data = ['TestSiteID' => $tIDs['LDLCALC'], 'DisciplineID' => '2', 'DepartmentID' => '2', 'FormulaInput' => 'CHOL,HDL,TG', 'FormulaCode' => 'CHOL - HDL - (TG/5)', 'RefType' => 'RANGE', 'Unit1' => 'mg/dL', 'Factor' => '', 'Unit2' => '', 'Decimal' => '0', 'CreateDate' => "$now"]; - $this->db->table('testdefcal')->insert($data); - - // ======================================== - // GROUP TYPE - Panel/Profile Tests - // ======================================== - $data = ['SiteID' => '1', 'TestSiteCode' => 'CBC', 'TestSiteName' => 'Complete Blood Count', 'TestType' => 'GROUP', 'Description' => 'Darah Lengkap', 'SeqScr' => '50', 'SeqRpt' => '50', 'IndentLeft' => '0', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'ResultType' => 'NORES', 'RefType' => 'NOREF', 'CreateDate' => "$now"]; - $this->db->table('testdefsite')->insert($data); - $tIDs['CBC'] = $this->db->insertID(); + // Add CBC members now that tests are defined $this->db->table('testdefgrp')->insertBatch([ ['TestSiteID' => $tIDs['CBC'], 'Member' => $tIDs['HB'], 'CreateDate' => "$now"], ['TestSiteID' => $tIDs['CBC'], 'Member' => $tIDs['HCT'], 'CreateDate' => "$now"], @@ -198,9 +121,79 @@ class TestSeeder extends Seeder ['TestSiteID' => $tIDs['CBC'], 'Member' => $tIDs['MCHC'], 'CreateDate' => "$now"] ]); - $data = ['SiteID' => '1', 'TestSiteCode' => 'LIPID', 'TestSiteName' => 'Lipid Profile', 'TestType' => 'GROUP', 'Description' => 'Profil Lipid', 'SeqScr' => '51', 'SeqRpt' => '51', 'IndentLeft' => '0', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'ResultType' => 'NORES', 'RefType' => 'NOREF', 'CreateDate' => "$now"]; + // CALC: (none for Hematology) + + // ======================================== + // DEPARTMENT 2 - CHEMISTRY (Discipline 2) + // Order: GROUP (<100) → PARAM (<100) → TEST (100+) → CALC (100+) + // ======================================== + + // GROUP: LIPID, LFT, RFT + $data = ['SiteID' => '1', 'TestSiteCode' => 'LIPID', 'TestSiteName' => 'Lipid Profile', 'TestType' => 'GROUP', 'Description' => 'Profil Lipid', 'SeqScr' => '10', 'SeqRpt' => '10', 'IndentLeft' => '0', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'ResultType' => 'NORES', 'RefType' => 'NOREF', 'CreateDate' => "$now"]; $this->db->table('testdefsite')->insert($data); $tIDs['LIPID'] = $this->db->insertID(); + + $data = ['SiteID' => '1', 'TestSiteCode' => 'LFT', 'TestSiteName' => 'Liver Function Test', 'TestType' => 'GROUP', 'Description' => 'Fungsi Hati', 'SeqScr' => '20', 'SeqRpt' => '20', 'IndentLeft' => '0', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'ResultType' => 'NORES', 'RefType' => 'NOREF', 'CreateDate' => "$now"]; + $this->db->table('testdefsite')->insert($data); + $tIDs['LFT'] = $this->db->insertID(); + + $data = ['SiteID' => '1', 'TestSiteCode' => 'RFT', 'TestSiteName' => 'Renal Function Test', 'TestType' => 'GROUP', 'Description' => 'Fungsi Ginjal', 'SeqScr' => '30', 'SeqRpt' => '30', 'IndentLeft' => '0', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'ResultType' => 'NORES', 'RefType' => 'NOREF', 'CreateDate' => "$now"]; + $this->db->table('testdefsite')->insert($data); + $tIDs['RFT'] = $this->db->insertID(); + + // PARAM: (none for Chemistry) + + // TEST: Chemistry Tests + $data = ['SiteID' => '1', 'TestSiteCode' => 'GLU', 'TestSiteName' => 'Glucose', 'TestType' => 'TEST', 'Description' => 'Glukosa Sewaktu', 'SeqScr' => '100', 'SeqRpt' => '100', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '2', 'DepartmentID' => '2', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '300', 'ReqQtyUnit' => 'uL', 'Unit1' => 'mg/dL', 'Factor' => '0.0555', 'Unit2' => 'mmol/L', 'Decimal' => '0', 'Method' => 'Hexokinase', 'CreateDate' => "$now"]; + $this->db->table('testdefsite')->insert($data); + $tIDs['GLU'] = $this->db->insertID(); + + $data = ['SiteID' => '1', 'TestSiteCode' => 'CREA', 'TestSiteName' => 'Creatinine', 'TestType' => 'TEST', 'Description' => 'Kreatinin', 'SeqScr' => '110', 'SeqRpt' => '110', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '2', 'DepartmentID' => '2', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '300', 'ReqQtyUnit' => 'uL', 'Unit1' => 'mg/dL', 'Factor' => '88.4', 'Unit2' => 'umol/L', 'Decimal' => '2', 'Method' => 'Enzymatic', 'CreateDate' => "$now"]; + $this->db->table('testdefsite')->insert($data); + $tIDs['CREA'] = $this->db->insertID(); + + $data = ['SiteID' => '1', 'TestSiteCode' => 'UREA', 'TestSiteName' => 'Blood Urea Nitrogen', 'TestType' => 'TEST', 'Description' => 'BUN', 'SeqScr' => '120', 'SeqRpt' => '120', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '2', 'DepartmentID' => '2', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '300', 'ReqQtyUnit' => 'uL', 'Unit1' => 'mg/dL', 'Factor' => '', 'Unit2' => '', 'Decimal' => '1', 'Method' => 'Urease-GLDH', 'CreateDate' => "$now"]; + $this->db->table('testdefsite')->insert($data); + $tIDs['UREA'] = $this->db->insertID(); + + $data = ['SiteID' => '1', 'TestSiteCode' => 'SGOT', 'TestSiteName' => 'AST (SGOT)', 'TestType' => 'TEST', 'Description' => 'Aspartate Aminotransferase', 'SeqScr' => '130', 'SeqRpt' => '130', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '2', 'DepartmentID' => '2', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '300', 'ReqQtyUnit' => 'uL', 'Unit1' => 'U/L', 'Factor' => '0.017', 'Unit2' => 'ukat/L', 'Decimal' => '0', 'Method' => 'IFCC', 'CreateDate' => "$now"]; + $this->db->table('testdefsite')->insert($data); + $tIDs['SGOT'] = $this->db->insertID(); + + $data = ['SiteID' => '1', 'TestSiteCode' => 'SGPT', 'TestSiteName' => 'ALT (SGPT)', 'TestType' => 'TEST', 'Description' => 'Alanine Aminotransferase', 'SeqScr' => '140', 'SeqRpt' => '140', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '2', 'DepartmentID' => '2', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '300', 'ReqQtyUnit' => 'uL', 'Unit1' => 'U/L', 'Factor' => '0.017', 'Unit2' => 'ukat/L', 'Decimal' => '0', 'Method' => 'IFCC', 'CreateDate' => "$now"]; + $this->db->table('testdefsite')->insert($data); + $tIDs['SGPT'] = $this->db->insertID(); + + $data = ['SiteID' => '1', 'TestSiteCode' => 'CHOL', 'TestSiteName' => 'Total Cholesterol', 'TestType' => 'TEST', 'Description' => 'Kolesterol Total', 'SeqScr' => '150', 'SeqRpt' => '150', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '2', 'DepartmentID' => '2', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '300', 'ReqQtyUnit' => 'uL', 'Unit1' => 'mg/dL', 'Factor' => '', 'Unit2' => '', 'Decimal' => '0', 'Method' => 'Enzymatic', 'CreateDate' => "$now"]; + $this->db->table('testdefsite')->insert($data); + $tIDs['CHOL'] = $this->db->insertID(); + + $data = ['SiteID' => '1', 'TestSiteCode' => 'TG', 'TestSiteName' => 'Triglycerides', 'TestType' => 'TEST', 'Description' => 'Trigliserida', 'SeqScr' => '160', 'SeqRpt' => '160', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '2', 'DepartmentID' => '2', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '300', 'ReqQtyUnit' => 'uL', 'Unit1' => 'mg/dL', 'Factor' => '', 'Unit2' => '', 'Decimal' => '0', 'Method' => 'GPO-PAP', 'CreateDate' => "$now"]; + $this->db->table('testdefsite')->insert($data); + $tIDs['TG'] = $this->db->insertID(); + + $data = ['SiteID' => '1', 'TestSiteCode' => 'HDL', 'TestSiteName' => 'HDL Cholesterol', 'TestType' => 'TEST', 'Description' => 'Kolesterol HDL', 'SeqScr' => '170', 'SeqRpt' => '170', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '2', 'DepartmentID' => '2', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '300', 'ReqQtyUnit' => 'uL', 'Unit1' => 'mg/dL', 'Factor' => '', 'Unit2' => '', 'Decimal' => '0', 'Method' => 'Direct', 'CreateDate' => "$now"]; + $this->db->table('testdefsite')->insert($data); + $tIDs['HDL'] = $this->db->insertID(); + + $data = ['SiteID' => '1', 'TestSiteCode' => 'LDL', 'TestSiteName' => 'LDL Cholesterol', 'TestType' => 'TEST', 'Description' => 'Kolesterol LDL', 'SeqScr' => '180', 'SeqRpt' => '180', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '2', 'DepartmentID' => '2', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '300', 'ReqQtyUnit' => 'uL', 'Unit1' => 'mg/dL', 'Factor' => '', 'Unit2' => '', 'Decimal' => '0', 'Method' => 'Direct', 'CreateDate' => "$now"]; + $this->db->table('testdefsite')->insert($data); + $tIDs['LDL'] = $this->db->insertID(); + + // CALC: Chemistry Calculated Tests + $data = ['SiteID' => '1', 'TestSiteCode' => 'EGFR', 'TestSiteName' => 'eGFR (CKD-EPI)', 'TestType' => 'CALC', 'Description' => 'Estimated Glomerular Filtration Rate', 'SeqScr' => '190', 'SeqRpt' => '190', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '0', 'CreateDate' => "$now"]; + $this->db->table('testdefsite')->insert($data); + $tIDs['EGFR'] = $this->db->insertID(); + $data = ['TestSiteID' => $tIDs['EGFR'], 'DisciplineID' => '2', 'DepartmentID' => '2', 'FormulaInput' => 'CREA,AGE,GENDER', 'FormulaCode' => 'CKD_EPI(CREA,AGE,GENDER)', 'RefType' => 'RANGE', 'Unit1' => 'mL/min/1.73m2', 'Factor' => '', 'Unit2' => '', 'Decimal' => '0', 'CreateDate' => "$now"]; + $this->db->table('testdefcal')->insert($data); + + $data = ['SiteID' => '1', 'TestSiteCode' => 'LDLCALC', 'TestSiteName' => 'LDL Cholesterol (Calculated)', 'TestType' => 'CALC', 'Description' => 'Friedewald formula: TC - HDL - (TG/5)', 'SeqScr' => '200', 'SeqRpt' => '200', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '0', 'CreateDate' => "$now"]; + $this->db->table('testdefsite')->insert($data); + $tIDs['LDLCALC'] = $this->db->insertID(); + $data = ['TestSiteID' => $tIDs['LDLCALC'], 'DisciplineID' => '2', 'DepartmentID' => '2', 'FormulaInput' => 'CHOL,HDL,TG', 'FormulaCode' => 'CHOL - HDL - (TG/5)', 'RefType' => 'RANGE', 'Unit1' => 'mg/dL', 'Factor' => '', 'Unit2' => '', 'Decimal' => '0', 'CreateDate' => "$now"]; + $this->db->table('testdefcal')->insert($data); + + // Add Chemistry Group members now that tests are defined $this->db->table('testdefgrp')->insertBatch([ ['TestSiteID' => $tIDs['LIPID'], 'Member' => $tIDs['CHOL'], 'CreateDate' => "$now"], ['TestSiteID' => $tIDs['LIPID'], 'Member' => $tIDs['TG'], 'CreateDate' => "$now"], @@ -209,48 +202,87 @@ class TestSeeder extends Seeder ['TestSiteID' => $tIDs['LIPID'], 'Member' => $tIDs['LDLCALC'], 'CreateDate' => "$now"] ]); - $data = ['SiteID' => '1', 'TestSiteCode' => 'LFT', 'TestSiteName' => 'Liver Function Test', 'TestType' => 'GROUP', 'Description' => 'Fungsi Hati', 'SeqScr' => '52', 'SeqRpt' => '52', 'IndentLeft' => '0', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'ResultType' => 'NORES', 'RefType' => 'NOREF', 'CreateDate' => "$now"]; - $this->db->table('testdefsite')->insert($data); - $tIDs['LFT'] = $this->db->insertID(); $this->db->table('testdefgrp')->insertBatch([ ['TestSiteID' => $tIDs['LFT'], 'Member' => $tIDs['SGOT'], 'CreateDate' => "$now"], ['TestSiteID' => $tIDs['LFT'], 'Member' => $tIDs['SGPT'], 'CreateDate' => "$now"] ]); - $data = ['SiteID' => '1', 'TestSiteCode' => 'RFT', 'TestSiteName' => 'Renal Function Test', 'TestType' => 'GROUP', 'Description' => 'Fungsi Ginjal', 'SeqScr' => '53', 'SeqRpt' => '53', 'IndentLeft' => '0', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'ResultType' => 'NORES', 'RefType' => 'NOREF', 'CreateDate' => "$now"]; - $this->db->table('testdefsite')->insert($data); - $tIDs['RFT'] = $this->db->insertID(); $this->db->table('testdefgrp')->insertBatch([ ['TestSiteID' => $tIDs['RFT'], 'Member' => $tIDs['UREA'], 'CreateDate' => "$now"], ['TestSiteID' => $tIDs['RFT'], 'Member' => $tIDs['CREA'], 'CreateDate' => "$now"], ['TestSiteID' => $tIDs['RFT'], 'Member' => $tIDs['EGFR'], 'CreateDate' => "$now"] ]); - // Urinalysis Tests (with valueset result type) - $data = ['SiteID' => '1', 'TestSiteCode' => 'UCOLOR', 'TestSiteName' => 'Urine Color', 'TestType' => 'TEST', 'Description' => 'Warna Urine', 'SeqScr' => '31', 'SeqRpt' => '31', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '4', 'DepartmentID' => '4', 'ResultType' => 'VSET', 'RefType' => 'VSET', 'VSet' => '1001', 'ReqQty' => '10', 'ReqQtyUnit' => 'mL', 'Unit1' => '', 'Factor' => '', 'Unit2' => '', 'Decimal' => '', 'Method' => 'Visual', 'CreateDate' => "$now"]; + // ======================================== + // DEPARTMENT 4 - URINALYSIS (Discipline 4) + // Order: GROUP (<100) → PARAM (<100) → TEST (100+) → CALC (100+) + // ======================================== + + // GROUP: (none for Urinalysis in current setup) + + // PARAM: (none for Urinalysis) + + // TEST: Urinalysis Tests + $data = ['SiteID' => '1', 'TestSiteCode' => 'UCOLOR', 'TestSiteName' => 'Urine Color', 'TestType' => 'TEST', 'Description' => 'Warna Urine', 'SeqScr' => '100', 'SeqRpt' => '100', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '4', 'DepartmentID' => '4', 'ResultType' => 'VSET', 'RefType' => 'VSET', 'VSet' => '1001', 'ReqQty' => '10', 'ReqQtyUnit' => 'mL', 'Unit1' => '', 'Factor' => '', 'Unit2' => '', 'Decimal' => '', 'Method' => 'Visual', 'CreateDate' => "$now"]; $this->db->table('testdefsite')->insert($data); $tIDs['UCOLOR'] = $this->db->insertID(); - $data = ['SiteID' => '1', 'TestSiteCode' => 'UGLUC', 'TestSiteName' => 'Urine Glucose', 'TestType' => 'TEST', 'Description' => 'Glukosa Urine', 'SeqScr' => '32', 'SeqRpt' => '32', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '4', 'DepartmentID' => '4', 'ResultType' => 'VSET', 'RefType' => 'VSET', 'VSet' => '1002', 'ReqQty' => '10', 'ReqQtyUnit' => 'mL', 'Unit1' => '', 'Factor' => '', 'Unit2' => '', 'Decimal' => '', 'Method' => 'Dipstick', 'CreateDate' => "$now"]; + $data = ['SiteID' => '1', 'TestSiteCode' => 'UGLUC', 'TestSiteName' => 'Urine Glucose', 'TestType' => 'TEST', 'Description' => 'Glukosa Urine', 'SeqScr' => '110', 'SeqRpt' => '110', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '4', 'DepartmentID' => '4', 'ResultType' => 'VSET', 'RefType' => 'VSET', 'VSet' => '1002', 'ReqQty' => '10', 'ReqQtyUnit' => 'mL', 'Unit1' => '', 'Factor' => '', 'Unit2' => '', 'Decimal' => '', 'Method' => 'Dipstick', 'CreateDate' => "$now"]; $this->db->table('testdefsite')->insert($data); $tIDs['UGLUC'] = $this->db->insertID(); - $data = ['SiteID' => '1', 'TestSiteCode' => 'UPROT', 'TestSiteName' => 'Urine Protein', 'TestType' => 'TEST', 'Description' => 'Protein Urine', 'SeqScr' => '33', 'SeqRpt' => '33', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '4', 'DepartmentID' => '4', 'ResultType' => 'VSET', 'RefType' => 'VSET', 'VSet' => '1003', 'ReqQty' => '10', 'ReqQtyUnit' => 'mL', 'Unit1' => '', 'Factor' => '', 'Unit2' => '', 'Decimal' => '', 'Method' => 'Dipstick', 'CreateDate' => "$now"]; + $data = ['SiteID' => '1', 'TestSiteCode' => 'UPROT', 'TestSiteName' => 'Urine Protein', 'TestType' => 'TEST', 'Description' => 'Protein Urine', 'SeqScr' => '120', 'SeqRpt' => '120', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '4', 'DepartmentID' => '4', 'ResultType' => 'VSET', 'RefType' => 'VSET', 'VSet' => '1003', 'ReqQty' => '10', 'ReqQtyUnit' => 'mL', 'Unit1' => '', 'Factor' => '', 'Unit2' => '', 'Decimal' => '', 'Method' => 'Dipstick', 'CreateDate' => "$now"]; $this->db->table('testdefsite')->insert($data); $tIDs['UPROT'] = $this->db->insertID(); - $data = ['SiteID' => '1', 'TestSiteCode' => 'PH', 'TestSiteName' => 'Urine pH', 'TestType' => 'TEST', 'Description' => 'pH Urine', 'SeqScr' => '34', 'SeqRpt' => '34', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '4', 'DepartmentID' => '4', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '10', 'ReqQtyUnit' => 'mL', 'Unit1' => '', 'Factor' => '', 'Unit2' => '', 'Decimal' => '1', 'Method' => 'Dipstick', 'CreateDate' => "$now"]; + $data = ['SiteID' => '1', 'TestSiteCode' => 'PH', 'TestSiteName' => 'Urine pH', 'TestType' => 'TEST', 'Description' => 'pH Urine', 'SeqScr' => '130', 'SeqRpt' => '130', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '1', 'DisciplineID' => '4', 'DepartmentID' => '4', 'ResultType' => 'NMRIC', 'RefType' => 'RANGE', 'VSet' => '', 'ReqQty' => '10', 'ReqQtyUnit' => 'mL', 'Unit1' => '', 'Factor' => '', 'Unit2' => '', 'Decimal' => '1', 'Method' => 'Dipstick', 'CreateDate' => "$now"]; $this->db->table('testdefsite')->insert($data); $tIDs['PH'] = $this->db->insertID(); + // CALC: (none for Urinalysis) + + // ======================================== + // DEPARTMENT 10 - GENERAL (Discipline 10) + // Order: GROUP (<100) → PARAM (<100) → TEST (100+) → CALC (100+) + // ======================================== + + // GROUP: (none for General) + + // PARAM: General Parameters (< 100) + $data = ['SiteID' => '1', 'TestSiteCode' => 'HEIGHT', 'TestSiteName' => 'Height', 'TestType' => 'PARAM', 'Description' => 'Tinggi Badan', 'SeqScr' => '10', 'SeqRpt' => '10', 'IndentLeft' => '0', 'VisibleScr' => '1', 'VisibleRpt' => '0', 'CountStat' => '0', 'DisciplineID' => '10', 'DepartmentID' => '', 'ResultType' => 'NMRIC', 'RefType' => '', 'VSet' => '', 'ReqQty' => '', 'ReqQtyUnit' => '', 'Unit1' => 'cm', 'Factor' => '', 'Unit2' => '', 'Decimal' => '0', 'Method' => '', 'CreateDate' => "$now"]; + $this->db->table('testdefsite')->insert($data); + $tIDs['HEIGHT'] = $this->db->insertID(); + + $data = ['SiteID' => '1', 'TestSiteCode' => 'WEIGHT', 'TestSiteName' => 'Weight', 'TestType' => 'PARAM', 'Description' => 'Berat Badan', 'SeqScr' => '20', 'SeqRpt' => '20', 'IndentLeft' => '0', 'VisibleScr' => '1', 'VisibleRpt' => '0', 'CountStat' => '0', 'DisciplineID' => '10', 'DepartmentID' => '', 'ResultType' => 'NMRIC', 'RefType' => '', 'VSet' => '', 'ReqQty' => '', 'ReqQtyUnit' => '', 'Unit1' => 'kg', 'Factor' => '', 'Unit2' => '', 'Decimal' => '1', 'Method' => '', 'CreateDate' => "$now"]; + $this->db->table('testdefsite')->insert($data); + $tIDs['WEIGHT'] = $this->db->insertID(); + + $data = ['SiteID' => '1', 'TestSiteCode' => 'AGE', 'TestSiteName' => 'Age', 'TestType' => 'PARAM', 'Description' => 'Usia', 'SeqScr' => '30', 'SeqRpt' => '30', 'IndentLeft' => '0', 'VisibleScr' => '1', 'VisibleRpt' => '0', 'CountStat' => '0', 'DisciplineID' => '10', 'DepartmentID' => '', 'ResultType' => 'NMRIC', 'RefType' => '', 'VSet' => '', 'ReqQty' => '', 'ReqQtyUnit' => '', 'Unit1' => 'years', 'Factor' => '', 'Unit2' => '', 'Decimal' => '0', 'Method' => '', 'CreateDate' => "$now"]; + $this->db->table('testdefsite')->insert($data); + $tIDs['AGE'] = $this->db->insertID(); + + $data = ['SiteID' => '1', 'TestSiteCode' => 'SYSTL', 'TestSiteName' => 'Systolic BP', 'TestType' => 'PARAM', 'Description' => 'Tekanan Darah Sistolik', 'SeqScr' => '40', 'SeqRpt' => '40', 'IndentLeft' => '0', 'VisibleScr' => '1', 'VisibleRpt' => '0', 'CountStat' => '0', 'DisciplineID' => '10', 'DepartmentID' => '', 'ResultType' => 'NMRIC', 'RefType' => '', 'VSet' => '', 'ReqQty' => '', 'ReqQtyUnit' => '', 'Unit1' => 'mmHg', 'Factor' => '', 'Unit2' => '', 'Decimal' => '0', 'Method' => '', 'CreateDate' => "$now"]; + $this->db->table('testdefsite')->insert($data); + $tIDs['SYSTL'] = $this->db->insertID(); + + $data = ['SiteID' => '1', 'TestSiteCode' => 'DIASTL', 'TestSiteName' => 'Diastolic BP', 'TestType' => 'PARAM', 'Description' => 'Tekanan Darah Diastolik', 'SeqScr' => '50', 'SeqRpt' => '50', 'IndentLeft' => '0', 'VisibleScr' => '1', 'VisibleRpt' => '0', 'CountStat' => '0', 'DisciplineID' => '10', 'DepartmentID' => '', 'ResultType' => 'NMRIC', 'RefType' => '', 'VSet' => '', 'ReqQty' => '', 'ReqQtyUnit' => '', 'Unit1' => 'mmHg', 'Factor' => '', 'Unit2' => '', 'Decimal' => '0', 'Method' => '', 'CreateDate' => "$now"]; + $this->db->table('testdefsite')->insert($data); + $tIDs['DIASTL'] = $this->db->insertID(); + + // TEST: (none for General) + + // CALC: BMI (>= 100) + $data = ['SiteID' => '1', 'TestSiteCode' => 'BMI', 'TestSiteName' => 'Body Mass Index', 'TestType' => 'CALC', 'Description' => 'Indeks Massa Tubuh - weight/(height^2)', 'SeqScr' => '100', 'SeqRpt' => '100', 'IndentLeft' => '0', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '0', 'CreateDate' => "$now"]; + $this->db->table('testdefsite')->insert($data); + $tIDs['BMI'] = $this->db->insertID(); + $data = ['TestSiteID' => $tIDs['BMI'], 'DisciplineID' => '10', 'DepartmentID' => '', 'FormulaInput' => 'WEIGHT,HEIGHT', 'FormulaCode' => 'WEIGHT / ((HEIGHT/100) * (HEIGHT/100))', 'RefType' => 'RANGE', 'Unit1' => 'kg/m2', 'Factor' => '', 'Unit2' => '', 'Decimal' => '1', 'CreateDate' => "$now"]; + $this->db->table('testdefcal')->insert($data); + // ======================================== // TEST MAP - Specimen Mapping - // Maps tests to workstations and containers - // New structure: testmap (header) + testmapdetail (details) // ======================================== // Get container IDs (inserted by SpecimenSeeder in order) - // ConCode order: 1, 11, 12, 13, 14, 15, 16, 20, 101, 150, 200, 290, 295, 900 $conSST = 1; // SST (Serum Separator Tube) $conEDTA = 9; // EDTA - Hematology $conCitrate = 10; // Citrate - Koagulasi @@ -299,10 +331,7 @@ class TestSeeder extends Seeder ]; }; - // ============================================ - // TEST MAP CONFIGURATION - // Grouped by discipline for maintainability - // ============================================ + // Test mappings configuration $testMappings = [ // Hematology: Site → HAUTO → Sysmex (EDTA) [ @@ -347,7 +376,7 @@ class TestSeeder extends Seeder $entityInst = $this->getEntityType('INST') ?? 'INST'; foreach ($testMappings as $mapping) { - // Site → Workstation mapping (one header per workstation) + // Site → Workstation mapping $testMapSiteWsID = $getTestMapID($entitySite, '1', $entityWst, (string)$mapping['siteToWs']['ws']); foreach ($mapping['tests'] as $testCode) { $addDetail($testMapSiteWsID, $testCode, $testCode, $mapping['siteToWs']['con'], $testCode, $testCode); @@ -356,7 +385,7 @@ class TestSeeder extends Seeder $addDetail($testMapSiteWsID, $panelCode, $panelCode, $mapping['siteToWs']['con'], $panelCode, $panelCode); } - // Workstation → Instrument mapping (one header per instrument) + // Workstation → Instrument mapping if ($mapping['wsToInst'] !== null) { $testMapWsInstID = $getTestMapID($entityWst, (string)$mapping['wsToInst']['ws'], $entityInst, (string)$mapping['wsToInst']['inst']); foreach ($mapping['tests'] as $testCode) { diff --git a/app/Models/CounterModel.php b/app/Models/CounterModel.php index 59f8dec..341d92f 100644 --- a/app/Models/CounterModel.php +++ b/app/Models/CounterModel.php @@ -5,7 +5,7 @@ use App\Models\BaseModel; class CounterModel extends BaseModel { protected $table = 'counter'; protected $primaryKey = 'CounterID'; - protected $allowedFields = ['CounterValue', 'CounterStart', 'CounterEnd', 'CounterReset', 'CreateDate', 'EndDate']; + protected $allowedFields = ['CounterValue', 'CounterStart', 'CounterEnd', 'CounterReset', 'CounterName', 'CreateDate', 'EndDate']; protected $useTimestamps = true; protected $createdField = "CreateDate"; diff --git a/app/Models/OrderTest/OrderTestModel.php b/app/Models/OrderTest/OrderTestModel.php index 8150c68..0b94f36 100644 --- a/app/Models/OrderTest/OrderTestModel.php +++ b/app/Models/OrderTest/OrderTestModel.php @@ -60,7 +60,7 @@ class OrderTestModel extends BaseModel { $this->db->transStart(); try { - $orderID = $data['OrderID'] ?? $this->generateOrderID($data['SiteCode'] ?? '00'); + $orderID = !empty($data['OrderID']) ? $data['OrderID'] : $this->generateOrderID($data['SiteCode'] ?? '00'); $orderData = [ 'OrderID' => $orderID, diff --git a/app/Models/Organization/DisciplineModel.php b/app/Models/Organization/DisciplineModel.php index 8be8fb6..d4c6c57 100644 --- a/app/Models/Organization/DisciplineModel.php +++ b/app/Models/Organization/DisciplineModel.php @@ -5,7 +5,7 @@ use App\Models\BaseModel; class DisciplineModel extends BaseModel { protected $table = 'discipline'; protected $primaryKey = 'DisciplineID'; - protected $allowedFields = ['DisciplineCode', 'DisciplineName', 'SiteID', 'Parent', 'CreateDate', 'EndDate']; + protected $allowedFields = ['DisciplineCode', 'DisciplineName', 'SiteID', 'Parent', 'SeqScr', 'SeqRpt', 'CreateDate', 'EndDate']; protected $useTimestamps = true; protected $createdField = 'CreateDate'; @@ -15,7 +15,7 @@ class DisciplineModel extends BaseModel { public function getDisciplines($filter) { $builder = $this->select('discipline.DisciplineID, discipline.DisciplineCode, discipline.DisciplineName, discipline.SiteID, - discipline.Parent, d.DisciplineCode as ParentCode,d.DisciplineName as ParentName') + discipline.Parent, discipline.SeqScr, discipline.SeqRpt, d.DisciplineCode as ParentCode,d.DisciplineName as ParentName') ->join('discipline as d', 'd.DisciplineID = discipline.Parent', 'left'); if (!empty($filter['DisciplineCode'])) { diff --git a/app/Models/Organization/SiteModel.php b/app/Models/Organization/SiteModel.php index 35a764f..2e348f6 100644 --- a/app/Models/Organization/SiteModel.php +++ b/app/Models/Organization/SiteModel.php @@ -6,7 +6,7 @@ use App\Libraries\ValueSet; class SiteModel extends BaseModel { protected $table = 'site'; protected $primaryKey = 'SiteID'; - protected $allowedFields = ['SiteCode', 'SiteName', 'AccountID', 'SiteTypeID', 'Parent', 'SiteClassID', 'ME', + protected $allowedFields = ['SiteCode', 'ExtSiteID', 'SiteName', 'AccountID', 'SiteType', 'Parent', 'SiteClass', 'ME', 'CreateDate', 'EndDate']; protected $useTimestamps = true; diff --git a/app/Models/RefRange/RefNumModel.php b/app/Models/RefRange/RefNumModel.php index c2bbe61..820c850 100644 --- a/app/Models/RefRange/RefNumModel.php +++ b/app/Models/RefRange/RefNumModel.php @@ -46,4 +46,82 @@ class RefNumModel extends BaseModel ->orderBy('Display', 'ASC') ->findAll(); } + + + /** + * Get formatted numeric reference ranges with labels + * + * @param int $testSiteID + * @return array + */ + public function getFormattedByTestSiteID($testSiteID) + { + $rows = $this->getActiveByTestSiteID($testSiteID); + + return array_map(function ($r) { + return [ + 'RefNumID' => $r['RefNumID'], + 'NumRefType' => $r['NumRefType'], + 'NumRefTypeLabel' => $r['NumRefType'] ? \App\Libraries\ValueSet::getLabel('numeric_ref_type', $r['NumRefType']) : '', + 'RangeType' => $r['RangeType'], + 'RangeTypeLabel' => $r['RangeType'] ? \App\Libraries\ValueSet::getLabel('range_type', $r['RangeType']) : '', + 'Sex' => $r['Sex'], + 'SexLabel' => $r['Sex'] ? \App\Libraries\ValueSet::getLabel('gender', $r['Sex']) : '', + 'LowSign' => $r['LowSign'], + 'LowSignLabel' => $r['LowSign'] ? \App\Libraries\ValueSet::getLabel('math_sign', $r['LowSign']) : '', + 'HighSign' => $r['HighSign'], + 'HighSignLabel' => $r['HighSign'] ? \App\Libraries\ValueSet::getLabel('math_sign', $r['HighSign']) : '', + 'High' => $r['High'] !== null ? (float) $r['High'] : null, + 'Low' => $r['Low'] !== null ? (float) $r['Low'] : null, + 'AgeStart' => (int) $r['AgeStart'], + 'AgeEnd' => (int) $r['AgeEnd'], + 'Flag' => $r['Flag'], + 'Interpretation' => $r['Interpretation'], + ]; + }, $rows ?? []); + } + + /** + * Disable all numeric reference ranges for a test + * + * @param int $testSiteID + * @return void + */ + public function disableByTestSiteID($testSiteID) + { + $this->where('TestSiteID', $testSiteID) + ->set('EndDate', date('Y-m-d H:i:s')) + ->update(); + } + + /** + * Batch insert numeric reference ranges + * + * @param int $testSiteID + * @param int $siteID + * @param array $ranges + * @return void + */ + public function batchInsert($testSiteID, $siteID, $ranges) + { + foreach ($ranges as $index => $range) { + $this->insert([ + 'TestSiteID' => $testSiteID, + 'SiteID' => $siteID, + 'NumRefType' => $range['NumRefType'], + 'RangeType' => $range['RangeType'], + 'Sex' => $range['Sex'], + 'AgeStart' => (int) ($range['AgeStart'] ?? 0), + 'AgeEnd' => (int) ($range['AgeEnd'] ?? 150), + 'LowSign' => !empty($range['LowSign']) ? $range['LowSign'] : null, + 'Low' => !empty($range['Low']) ? (float) $range['Low'] : null, + 'HighSign' => !empty($range['HighSign']) ? $range['HighSign'] : null, + 'High' => !empty($range['High']) ? (float) $range['High'] : null, + 'Flag' => $range['Flag'] ?? null, + 'Interpretation'=> $range['Interpretation'] ?? null, + 'Display' => $index, + 'CreateDate' => date('Y-m-d H:i:s'), + ]); + } + } } diff --git a/app/Models/RefRange/RefTxtModel.php b/app/Models/RefRange/RefTxtModel.php index b61c7cf..27e4b54 100644 --- a/app/Models/RefRange/RefTxtModel.php +++ b/app/Models/RefRange/RefTxtModel.php @@ -40,4 +40,68 @@ class RefTxtModel extends BaseModel ->orderBy('RefTxtID', 'ASC') ->findAll(); } + + + /** + * Get formatted text reference ranges with labels + * + * @param int $testSiteID + * @return array + */ + public function getFormattedByTestSiteID($testSiteID) + { + $rows = $this->getActiveByTestSiteID($testSiteID); + + return array_map(function ($r) { + return [ + 'RefTxtID' => $r['RefTxtID'], + 'TxtRefType' => $r['TxtRefType'], + 'TxtRefTypeLabel'=> $r['TxtRefType'] ? \App\Libraries\ValueSet::getLabel('text_ref_type', $r['TxtRefType']) : '', + 'Sex' => $r['Sex'], + 'SexLabel' => $r['Sex'] ? \App\Libraries\ValueSet::getLabel('gender', $r['Sex']) : '', + 'AgeStart' => (int) $r['AgeStart'], + 'AgeEnd' => (int) $r['AgeEnd'], + 'RefTxt' => $r['RefTxt'], + 'Flag' => $r['Flag'], + ]; + }, $rows ?? []); + } + + /** + * Disable all text reference ranges for a test + * + * @param int $testSiteID + * @return void + */ + public function disableByTestSiteID($testSiteID) + { + $this->where('TestSiteID', $testSiteID) + ->set('EndDate', date('Y-m-d H:i:s')) + ->update(); + } + + /** + * Batch insert text reference ranges + * + * @param int $testSiteID + * @param int $siteID + * @param array $ranges + * @return void + */ + public function batchInsert($testSiteID, $siteID, $ranges) + { + foreach ($ranges as $range) { + $this->insert([ + 'TestSiteID' => $testSiteID, + 'SiteID' => $siteID, + 'TxtRefType' => $range['TxtRefType'], + 'Sex' => $range['Sex'], + 'AgeStart' => (int) ($range['AgeStart'] ?? 0), + 'AgeEnd' => (int) ($range['AgeEnd'] ?? 150), + 'RefTxt' => $range['RefTxt'] ?? '', + 'Flag' => $range['Flag'] ?? null, + 'CreateDate' => date('Y-m-d H:i:s'), + ]); + } + } } diff --git a/app/Models/Test/TestDefGrpModel.php b/app/Models/Test/TestDefGrpModel.php index f1d9e05..1cd584a 100644 --- a/app/Models/Test/TestDefGrpModel.php +++ b/app/Models/Test/TestDefGrpModel.php @@ -11,6 +11,8 @@ class TestDefGrpModel extends BaseModel { protected $allowedFields = [ 'TestSiteID', 'Member', + 'SeqScr', + 'SeqRpt', 'CreateDate', 'EndDate' ]; @@ -25,10 +27,12 @@ class TestDefGrpModel extends BaseModel { $db = \Config\Database::connect(); $rows = $db->table('testdefgrp') - ->select('testdefgrp.*, t.TestSiteID as MemberTestSiteID, t.TestSiteCode, t.TestSiteName, t.TestType') + ->select('t.TestSiteID as TestSiteID, t.TestSiteCode, t.TestSiteName, t.TestType, t.SeqScr, t. SeqRpt') ->join('testdefsite t', 't.TestSiteID=testdefgrp.Member', 'left') ->where('testdefgrp.TestSiteID', $testSiteID) ->where('testdefgrp.EndDate IS NULL') + ->orderBy('t.SeqScr', 'ASC') + ->orderBy('t.SeqRpt', 'ASC') ->orderBy('testdefgrp.TestGrpID', 'ASC') ->get()->getResultArray(); diff --git a/app/Models/Test/TestDefSiteModel.php b/app/Models/Test/TestDefSiteModel.php index fccf15f..85aeae6 100644 --- a/app/Models/Test/TestDefSiteModel.php +++ b/app/Models/Test/TestDefSiteModel.php @@ -158,17 +158,8 @@ class TestDefSiteModel extends BaseModel { $row['testmap'] = $testMapModel->getMappingsByTestCode($row['TestSiteCode']); } elseif (TestValidationService::isGroup($typeCode)) { - $row['testdefgrp'] = $db->table('testdefgrp') - ->select('testdefgrp.*, t.TestSiteID as MemberTestSiteID, t.TestSiteCode, t.TestSiteName, t.TestType') - ->join('testdefsite t', 't.TestSiteID=testdefgrp.Member', 'left') - ->where('testdefgrp.TestSiteID', $TestSiteID) - ->where('testdefgrp.EndDate IS NULL') - ->orderBy('testdefgrp.TestGrpID', 'ASC') - ->get()->getResultArray(); - - $row['testdefgrp'] = ValueSet::transformLabels($row['testdefgrp'], [ - 'TestType' => 'test_type', - ]); + $testDefGrpModel = new \App\Models\Test\TestDefGrpModel(); + $row['testdefgrp'] = $testDefGrpModel->getGroupMembers($TestSiteID); $testMapModel = new \App\Models\Test\TestMapModel(); $row['testmap'] = $testMapModel->getMappingsByTestCode($row['TestSiteCode']); @@ -230,4 +221,45 @@ class TestDefSiteModel extends BaseModel { { return TestValidationService::getReferenceTable($resultType, $refType); } + + /** + * Get test by ID with ValueSet transformation + * + * @param int $id + * @return array|null + */ + public function getTestById($id) + { + $row = $this->select('testdefsite.*') + ->where('testdefsite.TestSiteID', $id) + ->find($id); + + if (!$row) { + return null; + } + + $rows = ValueSet::transformLabels([$row], [ + 'TestType' => 'test_type', + ]); + + return $rows[0]; + } + + /** + * Get technical test with discipline and department relations + * + * @param int $testSiteID + * @return array + */ + public function getTestTechWithRelations($testSiteID) + { + return $this->db->table('testdefsite') + ->select('testdefsite.*, d.DisciplineName, dept.DepartmentName') + ->join('discipline d', 'd.DisciplineID=testdefsite.DisciplineID', 'left') + ->join('department dept', 'dept.DepartmentID=testdefsite.DepartmentID', 'left') + ->where('testdefsite.TestSiteID', $testSiteID) + ->where('testdefsite.EndDate IS NULL') + ->get() + ->getResultArray(); + } } diff --git a/app/Models/Test/TestMapDetailModel.php b/app/Models/Test/TestMapDetailModel.php index 94a48f8..92af1ef 100644 --- a/app/Models/Test/TestMapDetailModel.php +++ b/app/Models/Test/TestMapDetailModel.php @@ -50,4 +50,19 @@ class TestMapDetailModel extends BaseModel { ->where('EndDate IS NULL') ->findAll(); } + + + /** + * Disable all details for a test map + * + * @param int $testMapID + * @return void + */ + public function disableByTestMapID($testMapID) + { + $this->where('TestMapID', $testMapID) + ->where('EndDate', null) + ->set('EndDate', date('Y-m-d H:i:s')) + ->update(); + } } diff --git a/public/api-docs.bundled.yaml b/public/api-docs.bundled.yaml index d7f0c03..9d35699 100644 --- a/public/api-docs.bundled.yaml +++ b/public/api-docs.bundled.yaml @@ -5519,6 +5519,8 @@ components: type: string SiteCode: type: string + maxLength: 2 + pattern: ^[A-Z0-9]{2}$ AccountID: type: integer Discipline: diff --git a/public/components/schemas/organization.yaml b/public/components/schemas/organization.yaml index cd56bcb..3fe3170 100644 --- a/public/components/schemas/organization.yaml +++ b/public/components/schemas/organization.yaml @@ -19,6 +19,8 @@ Site: type: string SiteCode: type: string + maxLength: 2 + pattern: '^[A-Z0-9]{2}$' AccountID: type: integer @@ -31,6 +33,12 @@ Discipline: type: string DisciplineCode: type: string + SeqScr: + type: integer + description: Display order on screen + SeqRpt: + type: integer + description: Display order in reports Department: type: object diff --git a/public/paths/organization.yaml b/public/paths/organization.yaml index 0093e04..0a51d23 100644 --- a/public/paths/organization.yaml +++ b/public/paths/organization.yaml @@ -150,6 +150,12 @@ type: string DisciplineCode: type: string + SeqScr: + type: integer + description: Display order on screen + SeqRpt: + type: integer + description: Display order in reports responses: '200': description: Discipline updated