# code_chunks_vec_auxiliary

value00: app/Controllers/Test/TestsController.php
value01:
                'VisibleScr'  => $input['VisibleScr'] ?? 1,
                'VisibleRpt'  => $input['VisibleRpt'] ?? 1,
                'CountStat'   => $input['CountStat'] ?? 1,
                'StartDate'   => $input['StartDate'] ?? date('Y-m-d H:i:s'),
            ];

            $id = $this->model->insert($testSiteData);
            if (!$id) {
                $dbError = $db->error();
                log_message('error', 'Test insert failed: ' . json_encode($dbError, JSON_UNESCAPED_SLASHES));
                $message = $dbError['message'] ?? 'Failed to insert main test definition';
                throw new \Exception('Failed to insert main test definition: ' . $message);
            }

            $this->handleDetails($id, $input, 'insert');

            $db->transComplete();


value00: app/Controllers/Infrastructure/EquipmentListController.php
value01:
        $rows = $this->model->getEquipmentLists($filter);

        if (empty($rows)) {
            return $this->respond([
                'status' => 'success',
                'message' => 'no Data.',
                'data' => []
            ], 200);
        }

        return $this->respond([
            'status' => 'success',
            'message' => 'fetch success',
            'data' => $rows
        ], 200);
    }


value00: app/Controllers/Test/TestsController.php
value01:
            return $this->respondCreated([
                'status'  => 'created',
                'message' => 'Test created successfully',
                'data'    => ['TestSiteId' => $id],
            ]);
        } catch (\Exception $e) {
            $db->transRollback();

            return $this->failServerError('Something went wrong: ' . $e->getMessage());
        }
    }


value00: app/Controllers/Test/TestsController.php
value01:
    private function saveTestMap($testSiteID, $testSiteCode, $mappings, $action)
    {
        if ($action === 'update' && $testSiteCode) {
            // Find existing mappings by test code through testmapdetail
            $existingMaps = $this->modelMap->getMappingsByTestCode($testSiteCode);

            foreach ($existingMaps as $existingMap) {
                $this->modelMapDetail->disableByTestMapID($existingMap['TestMapID']);
            }

            // Soft delete the testmap headers
            foreach ($existingMaps as $existingMap) {
                $this->modelMap->update($existingMap['TestMapID'], ['EndDate' => date('Y-m-d H:i:s')]);
            }
        }


value00: app/Controllers/Test/TestsController.php
value01:
            $this->handleDetails($id, $input, 'insert');

            $db->transComplete();

            if ($db->transStatus() === false) {
                $dbError = $db->error();
                $lastQuery = $db->showLastQuery();
                log_message('error', 'TestController transaction failed: ' . json_encode([
                    'error' => $dbError,
                    'last_query' => $lastQuery,
                ], JSON_UNESCAPED_SLASHES));
                return $this->failServerError('Transaction failed');
            }


value00: app/Controllers/Test/TestsController.php
value01:
elseif ($typeCode !== 'TITLE') {
            $row['testdeftech'] = $this->model->getTestTechWithRelations($id);

            if (!empty($row['testdeftech'])) {
                $techData = $row['testdeftech'][0];
                $refType = $techData['RefType'];
                $resultType = $techData['ResultType'] ?? '';

                if (TestValidationService::usesRefNum($resultType, $refType)) {
                    $row['refnum'] = $this->modelRefNum->getFormattedByTestSiteID($id);
                }

                if (TestValidationService::usesRefTxt($resultType, $refType)) {
                    $row['reftxt'] = $this->modelRefTxt->getFormattedByTestSiteID($id);
                }
            }
        }


value00: app/Controllers/Test/TestsController.php
value01:
                break;
        }

        if ((TestValidationService::isTechnicalTest($typeCode) || TestValidationService::isCalc($typeCode)) && isset($input['testmap']) && is_array($input['testmap'])) {
            $this->saveTestMap($testSiteID, $testSiteCode, $input['testmap'], $action);
        }
    }

    private function saveTechDetails($testSiteID, $data, $action, $typeCode)
    {
        $techData = [
            'DisciplineID'   => $data['DisciplineID'] ?? null,
            'DepartmentID'   => $data['DepartmentID'] ?? null,
            'ResultType'     => $data['ResultType'] ?? null


value00: app/Controllers/Test/TestsController.php
value01:
            // Disable testmap by test code
            $testSiteCode = $existing['TestSiteCode'] ?? null;
            if ($testSiteCode) {
                $existingMaps = $this->modelMap->getMappingsByTestCode($testSiteCode);
                foreach ($existingMaps as $existingMap) {
                    $this->modelMapDetail->disableByTestMapID($existingMap['TestMapID']);
                    $this->modelMap->update($existingMap['TestMapID'], ['EndDate' => $now]);
                }
            }

            $db->transComplete();

            if ($db->transStatus() === false) {
                return $this->failServerError('Transaction failed');
            }


value00: app/Controllers/Test/TestsController.php
value01:
,
            'DepartmentID'   => $data['DepartmentID'] ?? null,
            'ResultType'     => $data['ResultType'] ?? null,
            'RefType'        => $data['RefType'] ?? null,
            'VSet'           => $data['VSet'] ?? null,
            'ReqQty'         => $data['ReqQty'] ?? null,
            'ReqQtyUnit'     => $data['ReqQtyUnit'] ?? null,
            'Unit1'          => $data['Unit1'] ?? null,
            'Factor'         => $data['Factor'] ?? null,
            'Unit2'          => $data['Unit2'] ?? null,
            'Decimal'        => $data['Decimal'] ?? 2,
            'CollReq'        => $data['CollReq'] ?? null,
            'Method'         => $data['Method'] ?? null,
            'ExpectedTAT'    => $data['ExpectedTAT'] ?? null,
        ]


value00: app/Controllers/Test/TestsController.php
value01:
        $resultType = $details['ResultType'] ?? '';
        $refType = $details['RefType'] ?? '';

        if (TestValidationService::isCalc($testType)) {
            $resultType = 'NMRIC';
            $refType = $refType ?: 'RANGE';
        } elseif (TestValidationService::isGroup($testType) || TestValidationService::isTitle($testType)) {
            $resultType = 'NORES';
            $refType = 'NOREF';
        }

        if ($resultType && $refType) {
            $validation = TestValidationService::validate($testType, $resultType, $refType);
            if (!$validation['valid']) {
                return $this->failValidationErrors(['type_validation' => $validation['error']]);
            }
        }

        $db = \Config\Database::connect();


value00: app/Controllers/User/UserController.php
value01:
use App\Models\User\UserModel;
use App\Traits\ResponseTrait;

/**
 * User Management Controller
 * Handles CRUD operations for users
 */
class UserController extends BaseController
{
    use ResponseTrait;

    protected $model;
    protected $db;

    public function __construct()
    {
        $this->db = \Config\Database::connect();
        $this->model = new UserModel();
    }

    /**
     * List users with pagination and search
     * GET /api/user?page=1&per_page=20&search=term
     */
    public function index()
    {
        try {
            $page = (int)($this->request->getGet('page') ?? 1);
            $perPage = (int)($this->request->getGet('per_page') ?? 20);
            $search = $this->request->getGet('search');

            // Build query


value00: app/Controllers/User/UserController.php
value01:
            $search = $this->request->getGet('search');

            // Build query
            $builder = $this->model->where('DelDate', null);

            // Apply search if provided
            if ($search) {
                $builder->groupStart()
                    ->like('Username', $search)
                    ->orLike('Email', $search)
                    ->orLike('Name', $search)
                    ->groupEnd();
            }

            // Get total count for pagination
            $total = $builder->countAllResults(false);


value00: app/Controllers/User/UserController.php
value01:
catch (\Exception $e) {
            log_message('error', 'UserController::update error: ' . $e->getMessage());
            return $this->respond([
                'status' => 'failed',
                'message' => 'Failed to update user',
                'data' => null
            ], 500);
        }
    }


value00: app/Controllers/User/UserController.php
value01:
            $user = $this->model->where('UserID', $userId)
                ->where('DelDate', null)
                ->first();

            if (empty($user)) {
                return $this->respond([
                    'status' => 'failed',
                    'message' => 'User not found',
                    'data' => null
                ], 404);
            }

            // Remove UserID from data array
            unset($data['UserID']);

            // Don't allow updating these fields
            unset($data['CreatedAt']);
            unset($data['Username']); // Username should not change

            $data['UpdatedAt'] = date('Y-m-d H:i:s');

            $updated = $this->model->update($userId, $data);


value00: app/Controllers/User/UserController.php
value01:
{
            $data = $this->request->getJSON(true);

            // Validation rules
            $rules = [
                'Username' => 'required|min_length[3]|max_length[50]|is_unique[users.Username]',
                'Email' => 'required|valid_email|is_unique[users.Email]',
            ];

            if (!$this->validateData($data, $rules)) {
                return $this->respond([
                    'status' => 'failed',
                    'message' => 'Validation failed',
                    'data' => $this->validator->getErrors()
                ], 400);
            }

            // Set default values
            $data['IsActive'] = $data['IsActive'] ?? true;


value00: app/Controllers/User/UserController.php
value01:
            $data['UpdatedAt'] = date('Y-m-d H:i:s');

            $updated = $this->model->update($userId, $data);

            if (!$updated) {
                return $this->respond([
                    'status' => 'failed',
                    'message' => 'Failed to update user',
                    'data' => null
                ], 500);
            }

            return $this->respond([
                'status' => 'success',
                'message' => 'User updated successfully',
                'data' => [
                    'UserID' => $userId,
                    'updated_fields' => array_keys($data)
                ]
            ], 200);

        }


value00: app/Controllers/User/UserController.php
value01:
            if (!$deleted) {
                return $this->respond([
                    'status' => 'failed',
                    'message' => 'Failed to delete user',
                    'data' => null
                ], 500);
            }

            return $this->respond([
                'status' => 'success',
                'message' => 'User deleted successfully',
                'data' => ['UserID' => $id]
            ], 200);

        } catch (\Exception $e) {
            log_message('error', 'UserController::delete error: ' . $e->getMessage());
            return $this->respond([
                'status' => 'failed',
                'message' => 'Failed to delete user',
                'data' => null
            ], 500);
        }
    }
}


value00: app/Controllers/User/UserController.php
value01:
            if (isset($data['UserID']) && (string) $data['UserID'] !== (string) $id) {
                return $this->respond([
                    'status' => 'failed',
                    'message' => 'UserID in URL does not match body',
                    'data' => null
                ], 400);
            }

            $userId = (int) $id;

            // Check if user exists
            $user = $this->model->where('UserID', $userId)
                ->where('DelDate', null)
                ->first();


value00: app/Controllers/User/UserController.php
value01:
    /**
     * Update existing user
     * PATCH /api/user/(:num)
     */
    public function update($id)
    {
        try {
            $data = $this->request->getJSON(true);

            if (empty($id) || !ctype_digit((string) $id)) {
                return $this->respond([
                    'status' => 'failed',
                    'message' => 'UserID is required',
                    'data' => null
                ], 400);
            }

            if (isset($data['UserID']) && (string) $data['UserID'] !== (string) $id)


value00: app/Controllers/User/UserController.php
value01:
catch (\Exception $e) {
            log_message('error', 'UserController::create error: ' . $e->getMessage());
            return $this->respond([
                'status' => 'failed',
                'message' => 'Failed to create user',
                'data' => null
            ], 500);
        }
    }


value00: app/Controllers/Specimen/SpecimenPrepController.php
value01:
    public function update($SpcPrpID = null) {
        $input = $this->request->getJSON(true);
        if (!$SpcPrpID || !ctype_digit((string) $SpcPrpID)) {
            return $this->failValidationErrors('SpcPrpID is required.');
        }
        $input['SpcPrpID'] = (int) $SpcPrpID;
        if (!$this->validateData($input, $this->rules)) { return $this->failValidationErrors($this->validator->getErrors()); }
        try {
            $id = $this->model->update($input['SpcPrpID'], $input);
            return $this->respondCreated([ 'status'  => 'success', 'message' => "data $id updated successfully" ]);
        } catch (\Exception $e) {
            return $this->failServerError('Something went wrong: ' . $e->getMessage());
        }
    }

}


value00: app/Controllers/Specimen/SpecimenPrepController.php
value01:
    use ResponseTrait;

    protected $db;
    protected $model;
    protected $rules;

    public function __construct() {
        $this->db = \Config\Database::connect();
        $this->model = new SpecimenPrepModel();
        $this->rules = [];
    }

    public function index() {
        try {
            $rows = $this->model->findAll();
            return $this->respond([ 'status' => 'success', 'message'=> "data fetched successfully", 'data'   => $rows ], 200);
        } catch (\Exception $e) {
            return $this->failServerError('Exception : '.$e->getMessage());
        }
    }


value00: app/Controllers/User/UserController.php
value01:
            $data['IsActive'] = $data['IsActive'] ?? true;
            $data['CreatedAt'] = date('Y-m-d H:i:s');

            $userId = $this->model->insert($data);

            if (!$userId) {
                return $this->respond([
                    'status' => 'failed',
                    'message' => 'Failed to create user',
                    'data' => null
                ], 500);
            }

            return $this->respond([
                'status' => 'success',
                'message' => 'User created successfully',
                'data' => [
                    'UserID' => $userId,
                    'Username' => $data['Username'],
                    'Email' => $data['Email']
                ]
            ], 201);

        }


value00: app/Controllers/Specimen/SpecimenPrepController.php
value01:
<?php

namespace App\Controllers\Specimen;

use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Models\Specimen\SpecimenPrepModel;

class SpecimenPrepController extends BaseController {
    use ResponseTrait;

    protected $db;
    protected $model;
    protected $rules;


value00: app/Controllers/User/UserController.php
value01:
<?php

namespace App\Controllers\User;

use App\Controllers\BaseController;
use App\Models\User\UserModel;
use App\Traits\ResponseTrait;

/**
 * User Management Controller
 * Handles CRUD operations for users
 */


value00: app/Controllers/User/UserController.php
value01:
            $total = $builder->countAllResults(false);

            // Get paginated results
            $users = $builder
                ->orderBy('UserID', 'DESC')
                ->limit($perPage, ($page - 1) * $perPage)
                ->findAll();

            return $this->respond([
                'status' => 'success',
                'message' => 'Users retrieved successfully',
                'data' => [
                    'users' => $users,
                    'pagination' => [
                        'current_page' => $page,
                        'per_page' => $perPage,
                        'total' => $total,
                        'total_pages' => ceil($total / $perPage)
                    ]
                ]
            ], 200);

        }


value00: app/Controllers/User/UserController.php
value01:
    /**
     * Get single user by ID
     * GET /api/user/(:num)
     */
    public function show($id)
    {
        try {
            $user = $this->model->where('UserID', $id)
                ->where('DelDate', null)
                ->first();

            if (empty($user)) {
                return $this->respond([
                    'status' => 'failed',
                    'message' => 'User not found',
                    'data' => null
                ], 404);
            }

            return $this->respond([
                'status' => 'success',
                'message' => 'User retrieved successfully',
                'data' => $user
            ], 200);

        }


value00: app/Controllers/User/UserController.php
value01:
catch (\Exception $e) {
            log_message('error', 'UserController::index error: ' . $e->getMessage());
            return $this->respond([
                'status' => 'failed',
                'message' => 'Failed to retrieve users',
                'data' => null
            ], 500);
        }
    }


value00: app/Controllers/Specimen/SpecimenPrepController.php
value01:
    public function show($id) {
        try {
            $row = $this->model->where('SpcPrpID', $id)->first();
            if (empty($row)) {
                return $this->respond([ 'status' => 'success', 'message'=> "data not found", 'data' => null ], 200);
            }
            return $this->respond([ 'status' => 'success', 'message'=> "data fetched successfully", 'data'   => $row ], 200);
        } catch (\Exception $e) {
            return $this->failServerError('Exception : '.$e->getMessage());
        }
    }


value00: app/Controllers/User/UserController.php
value01:
    /**
     * Delete user (soft delete)
     * DELETE /api/user/(:num)
     */
    public function delete($id)
    {
        try {
            // Check if user exists
            $user = $this->model->where('UserID', $id)
                ->where('DelDate', null)
                ->first();

            if (empty($user)) {
                return $this->respond([
                    'status' => 'failed',
                    'message' => 'User not found',
                    'data' => null
                ], 404);
            }

            // Soft delete by setting DelDate
            $deleted = $this->model->update($id, [
                'DelDate' => date('Y-m-d H:i:s'),
                'IsActive' => false
            ]);


value00: app/Controllers/User/UserController.php
value01:
catch (\Exception $e) {
            log_message('error', 'UserController::show error: ' . $e->getMessage());
            return $this->respond([
                'status' => 'failed',
                'message' => 'Failed to retrieve user',
                'data' => null
            ], 500);
        }
    }

    /**
     * Create new user
     * POST /api/user
     */
    public function create()
    {
        try {
            $data = $this->request->getJSON(true);

            // Validation rules


value00: app/Controllers/Test/TestMapDetailController.php
value01:
            $this->model->update($id, ['EndDate' => date('Y-m-d H:i:s')]);
            
            return $this->respond([ 
                'status'  => 'success', 
                'message' => "data deleted successfully", 
                'data' => $id 
            ], 200);
        } catch (\Exception $e) {
            return $this->failServerError('Something went wrong: ' . $e->getMessage());
        }
    }


value00: app/Controllers/Test/TestMapDetailController.php
value01:
        $this->db->transComplete();
        
        return $this->respond([
            'status' => empty($results['failed']) ? 'success' : 'partial',
            'message' => 'Batch update completed',
            'data' => $results
        ], 200);
    }


value00: app/Controllers/Test/TestMapDetailController.php
value01:
    public function delete() {
        $input = $this->request->getJSON(true);
        $id = $input["TestMapDetailID"] ?? null;
        
        if (!$id) { 
            return $this->failValidationErrors('TestMapDetailID is required.'); 
        }
        
        try {
            $row = $this->model->where('TestMapDetailID', $id)->where('EndDate', null)->first();
            
            if (empty($row)) { 
                return $this->respond([ 
                    'status'  => 'failed', 
                    'message' => "Data not found or already deleted.", 
                    'data' => null 
                ], 404); 
            }
            
            $this->model->update($id, ['EndDate' => date('Y-m-d H:i:s')]);


value00: app/Controllers/Test/TestMapDetailController.php
value01:
    public function batchDelete() {
        $ids = $this->request->getJSON(true);
        
        if (!is_array($ids)) { 
            return $this->failValidationErrors('Expected array of TestMapDetailIDs'); 
        }
        
        $results = ['success' => [], 'failed' => []];
        $this->db->transStart();


value00: app/Controllers/Test/TestMapDetailController.php
value01:
    public function update($TestMapDetailID = null) {
        $input = $this->request->getJSON(true);
        if (!$TestMapDetailID || !ctype_digit((string) $TestMapDetailID)) { 
            return $this->failValidationErrors('TestMapDetailID is required.'); 
        }
        $id = (int) $TestMapDetailID;
        if (isset($input['TestMapDetailID']) && (string) $input['TestMapDetailID'] !== (string) $id) {
            return $this->failValidationErrors('TestMapDetailID in URL does not match body.');
        }
        $input['TestMapDetailID'] = $id;
        
        if (!$this->validateData($input, $this->rules)) { 
            return $this->failValidationErrors($this->validator->getErrors()); 
        }


value00: app/Controllers/Test/TestMapDetailController.php
value01:
            $id = $item['TestMapDetailID'] ?? null;
            
            if (!$id) {
                $results['failed'][] = ['index' => $index, 'error' => 'TestMapDetailID required'];
                continue;
            }
            
            if (!$this->validateData($item, $this->rules)) {
                $results['failed'][] = ['index' => $index, 'errors' => $this->validator->getErrors()];
                continue;
            }
            
            try {
                $this->model->update($id, $item);
                $results['success'][] = ['index' => $index, 'TestMapDetailID' => $id];
            } catch (\Exception $e) {
                $results['failed'][] = ['index' => $index, 'error' => $e->getMessage()];
            }
        }


value00: app/Controllers/Test/TestMapDetailController.php
value01:
        $results = ['success' => [], 'failed' => []];
        $this->db->transStart();
        
        foreach ($items as $index => $item) {
            if (!$this->validateData($item, $this->rules)) {
                $results['failed'][] = ['index' => $index, 'errors' => $this->validator->getErrors()];
                continue;
            }
            try {
                $id = $this->model->insert($item);
                $results['success'][] = ['index' => $index, 'TestMapDetailID' => $id];
            } catch (\Exception $e) {
                $results['failed'][] = ['index' => $index, 'error' => $e->getMessage()];
            }
        }
        
        $this->db->transComplete();


value00: app/Controllers/Test/TestMapDetailController.php
value01:
{
    use ResponseTrait;

    protected $db;
    protected $rules;
    protected $model;

    public function __construct() {
        $this->db = \Config\Database::connect();
        $this->model = new TestMapDetailModel;
        $this->rules = [
            'TestMapID' => 'required|integer',
            'HostTestCode' => 'permit_empty|max_length[10]',
            'HostTestName' => 'permit_empty|max_length[100]',
            'ConDefID' => 'permit_empty|integer',
            'ClientTestCode' => 'permit_empty|max_length[10]',
            'ClientTestName' => 'permit_empty|max_length[100]',
        ];
    }


value00: app/Controllers/Test/TestMapDetailController.php
value01:
        $results = ['success' => [], 'failed' => []];
        $this->db->transStart();
        
        foreach ($ids as $id) {
            try {
        $row = $this->model->where('TestMapDetailID', $id)->where('EndDate', null)->first();
                
                if (empty($row)) {
                    $results['failed'][] = ['TestMapDetailID' => $id, 'error' => 'Not found or already deleted'];
                    continue;
                }
                
                $this->model->update($id, ['EndDate' => date('Y-m-d H:i:s')]);
                $results['success'][] = $id;
            } catch (\Exception $e) {
                $results['failed'][] = ['TestMapDetailID' => $id, 'error' => $e->getMessage()];
            }
        }


value00: app/Controllers/Specimen/SpecimenStatusController.php
value01:
    use ResponseTrait;

    protected $db;
    protected $model;
    protected $rules;

    public function __construct() {
        $this->db = \Config\Database::connect();
        $this->model = new SpecimenStatusModel();
        $this->rules = [];
    }

    public function index() {
        try {
            $rows = $this->model->findAll();

            $rows = ValueSet::transformLabels($rows, [
                'Status' => 'specimen_status',
                'Activity' => 'specimen_activity',
            ]);

            return $this->respond([ 'status' => 'success', 'message'=> "data fetched successfully", 'data'   => $rows ], 200);
        } catch (\Exception $e) {
            return $this->failServerError('Exception : '.$e->getMessage());
        }
    }


value00: app/Controllers/Specimen/SpecimenStatusController.php
value01:
    public function update($SpcStaID = null) {
        $input = $this->request->getJSON(true);
        if (!$SpcStaID || !ctype_digit((string) $SpcStaID)) {
            return $this->failValidationErrors('SpcStaID is required.');
        }
        $input['SpcStaID'] = (int) $SpcStaID;
        if (!$this->validateData($input, $this->rules)) { return $this->failValidationErrors($this->validator->getErrors()); }
        try {
            $id = $this->model->update($input['SpcStaID'], $input);
            return $this->respondCreated([ 'status'  => 'success', 'message' => "data $id updated successfully" ]);
        } catch (\Exception $e) {
            return $this->failServerError('Something went wrong: ' . $e->getMessage());
        }
    }

}


value00: app/Controllers/Test/TestMapDetailController.php
value01:
        try {
            $this->model->update($id, $input);
            return $this->respond([
                'status'  => 'success', 
                'message' => "data updated successfully", 
                'data' => $id 
            ], 200);
        } catch (\Exception $e) {
            return $this->failServerError('Something went wrong: ' . $e->getMessage());
        }
    }


value00: app/Controllers/Test/TestMapDetailController.php
value01:
    public function batchCreate() {
        $items = $this->request->getJSON(true);
        
        if (!is_array($items)) { 
            return $this->failValidationErrors('Expected array of items'); 
        }
        
        $results = ['success' => [], 'failed' => []];
        $this->db->transStart();


value00: app/Controllers/Test/TestMapDetailController.php
value01:
    public function show($id = null) {
        if (!$id) {
            return $this->failValidationErrors('TestMapDetailID is required.');
        }
        
                $row = $this->model->where('TestMapDetailID', $id)->where('EndDate', null)->first();
        
        if (empty($row)) { 
            return $this->respond([ 'status'  => 'success', 'message' => "no Data.", 'data' => null ], 200); 
        }

        return $this->respond([ 'status' => 'success', 'message'=> "Data fetched successfully", 'data' => $row ], 200);
    }


value00: app/Controllers/Specimen/SpecimenStatusController.php
value01:
<?php

namespace App\Controllers\Specimen;

use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Libraries\ValueSet;
use App\Models\Specimen\SpecimenStatusModel;

class ContainerDef extends BaseController {
    use ResponseTrait;

    protected $db;
    protected $model;
    protected $rules;


value00: app/Controllers/Test/TestMapDetailController.php
value01:
<?php

namespace App\Controllers\Test;

use App\Controllers\BaseController;
use App\Traits\ResponseTrait;
use App\Models\Test\TestMapDetailModel;

class TestMapDetailController extends BaseController {
    use ResponseTrait;

    protected $db;
    protected $rules;
    protected $model;


value00: app/Controllers/Specimen/SpecimenStatusController.php
value01:
    public function show($id) {
        try {
            $row = $this->model->where('SpcStaID', $id)->first();
            if (empty($row)) {
                return $this->respond([ 'status' => 'success', 'message'=> "data not found", 'data' => null ], 200);
            }

            $row = ValueSet::transformLabels([$row], [
                'Status' => 'specimen_status',
                'Activity' => 'specimen_activity',
            ])[0];

            return $this->respond([ 'status' => 'success', 'message'=> "data fetched successfully", 'data'   => $row ], 200);
        } catch (\Exception $e) {
            return $this->failServerError('Exception : '.$e->getMessage());
        }
    }


value00: app/Controllers/Test/TestMapDetailController.php
value01:
    public function showByTestMap($testMapID = null) {
        if (!$testMapID) { 
            return $this->failValidationErrors('TestMapID is required.'); 
        }
        
        $rows = $this->model->getDetailsByTestMap($testMapID);
        
        if (empty($rows)) { 
            return $this->respond([ 'status'  => 'success', 'message' => "no Data.", 'data' => [] ], 200); 
        }

        return $this->respond([ 'status' => 'success', 'message'=> "Data fetched successfully", 'data' => $rows ], 200);
    }


value00: app/Controllers/Test/TestMapDetailController.php
value01:
    public function create() {
        $input = $this->request->getJSON(true);
        
        if (!$this->validateData($input, $this->rules)) { 
            return $this->failValidationErrors($this->validator->getErrors()); 
        }
        
        try {
            $id = $this->model->insert($input);
            return $this->respondCreated([ 
                'status'  => 'success', 
                'message' => "data created successfully", 
                'data' => $id 
            ]);
        } catch (\Exception $e) {
            return $this->failServerError('Something went wrong: ' . $e->getMessage());
        }
    }


value00: app/Controllers/Test/TestMapDetailController.php
value01:
    public function index() {
        $testMapID = $this->request->getGet('TestMapID');
        
        if ($testMapID) {
            $rows = $this->model->getDetailsByTestMap($testMapID);
        } else {
            $rows = $this->model->where('EndDate', null)->findAll();
        }
        
        if (empty($rows)) { 
            return $this->respond([ 'status'  => 'success', 'message' => "no Data.", 'data' => [] ], 200); 
        }

        return $this->respond([ 'status' => 'success', 'message'=> "Data fetched successfully", 'data' => $rows ], 200);
    }


value00: app/Controllers/Test/TestMapDetailController.php
value01:
        $this->db->transComplete();
        
        return $this->respond([
            'status' => empty($results['failed']) ? 'success' : 'partial',
            'message' => 'Batch create completed',
            'data' => $results
        ], 200);
    }


value00: app/Controllers/Test/TestMapDetailController.php
value01:
    public function batchUpdate() {
        $items = $this->request->getJSON(true);
        
        if (!is_array($items)) { 
            return $this->failValidationErrors('Expected array of items'); 
        }
        
        $results = ['success' => [], 'failed' => []];
        $this->db->transStart();
        
        foreach ($items as $index => $item) {
            $id = $item['TestMapDetailID'] ?? null;


value00: app/Controllers/Test/TestMapDetailController.php
value01:
        $this->db->transComplete();
        
        return $this->respond([
            'status' => empty($results['failed']) ? 'success' : 'partial',
            'message' => 'Batch delete completed',
            'data' => $results
        ], 200);
    }
}


value00: app/Controllers/Specimen/SpecimenCollectionController.php
value01:
<?php

namespace App\Controllers\Specimen;

use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Libraries\ValueSet;
use App\Models\Specimen\SpecimenCollectionModel;

class SpecimenCollectionController extends BaseController {
    use ResponseTrait;

    protected $db;
    protected $model;
    protected $rules;


value00: app/Controllers/Specimen/SpecimenCollectionController.php
value01:
    public function show($id) {
        try {
            $row = $this->model->where('SpcColID', $id)->first();
            if (empty($row)) {
                return $this->respond([ 'status' => 'success', 'message'=> "data not found", 'data' => null ], 200);
            }

            $row = ValueSet::transformLabels([$row], [
                'CollectionMethod' => 'collection_method',
                'Additive' => 'additive',
                'SpecimenRole' => 'specimen_role',
            ])[0];

            return $this->respond([ 'status' => 'success', 'message'=> "data fetched successfully", 'data'   => $row ], 200);
        } catch (\Exception $e) {
            return $this->failServerError('Exception : '.$e->getMessage());
        }
    }


value00: app/Controllers/Specimen/SpecimenCollectionController.php
value01:
    public function update($SpcColID = null) {
        $input = $this->request->getJSON(true);
        if (!$SpcColID || !ctype_digit((string) $SpcColID)) {
            return $this->failValidationErrors('SpcColID is required.');
        }
        $input['SpcColID'] = (int) $SpcColID;
        if (!$this->validateData($input, $this->rules)) { return $this->failValidationErrors($this->validator->getErrors()); }
        try {
            $id = $this->model->update($input['SpcColID'], $input);
            return $this->respondCreated([ 'status'  => 'success', 'message' => "data $id updated successfully" ]);
        } catch (\Exception $e) {
            return $this->failServerError('Something went wrong: ' . $e->getMessage());
        }
    }

}


value00: app/Controllers/ZonesController.php
value01:
<?php
/*
namespace App\Controllers;

use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Models\SyncCRM\ZonesModel;

class ZonesController extends BaseController {
	use ResponseTrait;
	
	protected $model;

	public function __construct() {	
		$this->model = new ZonesModel();
	}

    public function index() {
        try {
            $rows = $this->model->getZones();

            if(empty($rows)){return $this->respond(['status'=>'success', 'message'=>"no data found.", 'data'=>$rows], 200);}
            return $this->respond(['status'=>'success', 'message'=>"data fetched successfully", 'data'=>$rows], 200);
        } catch (\Exception $e) {
            return $this->respond([ 'status' => 'error', 'message' => $e->getMessage() ], 200);
        }


value00: app/Controllers/ZonesController.php
value01:
        } catch (\Exception $e) {
            return $this->respond([ 'status' => 'error', 'message' => $e->getMessage() ], 200);
        }

    }

    public function getProvinces() {
        $filters = [
            'zoneid' => $this->request->getVar('zoneid') ?? null,
            'zonename' => $this->request->getVar('zonename') ?? null


value00: app/Controllers/ZonesController.php
value01:
                'User-Agent' => 'Mozilla/5.0 (CI4 cURL Request)',
                'Accept' => 'application/json',
            ],
        ]);

        try {
            // Ambil data dari API pusat (CRM)
            $response = $client->get('http://services-summit.my.id/api/zones'); // Prod
            // $response = $client->get('https://services-summit.my.id/api/zones'); // Dev
            $result = json_decode($response->getBody(), true);
            
            if (!isset($result['data']) || !is_array($result['data'])) {
                return $this->response->setJSON([
                    'status'  => 'error',
                    'message' => 'Invalid or empty response from CRM API'


value00: app/Controllers/ZonesController.php
value01:
    }

    public function getCities() {
        $filter = [
            'zoneid' => $this->request->getVar('zoneid') ?? null
        ];

        $rows = $this->model->getCities($filter);
        
        if (empty($rows)) { return $this->respond([ 'status'  => 'success', 'message' => "data not found", 'data' => [] ], 200); }
        return $this->respond([ 'status' => 'success', 'message'=> "Data fetched successfully", 'data' => $rows ], 200); 
    }

    public function synchronize() {
        $client = \Config\Services::curlrequest([
            'headers' => [
                'User-Agent' => 'Mozilla/5.0 (CI4 cURL Request)',
                'Accept' => 'application/json',
            ],


value00: app/Controllers/ZonesController.php
value01:
            'zoneid' => $this->request->getVar('zoneid') ?? null,
            'zonename' => $this->request->getVar('zonename') ?? null
        ];

        $rows = $this->model->getProvinces();
        
        if (empty($rows)) { return $this->respond([ 'status'  => 'success', 'message' => "data not found", 'data' => [] ], 200); }
        return $this->respond([ 'status' => 'success', 'message'=> "Data fetched successfully", 'data' => $rows ], 200); 
    }

    public function getCities() {
        $filter = [
            'zoneid' => $this->request->getVar('zoneid') ?? null


value00: app/Controllers/ZonesController.php
value01:
                    'status'  => 'error',
                    'message' => 'Invalid or empty response from CRM API'
                ]);
            }

            $record = $this->model->synchronize($result['data']);

            return $this->response->setJSON([
                'status'  => 'success',
                'message' => 'Zones synchronized successfully',
                'summary' => [
                    'inserted' => $record['inserted'],
                    'updated'  => $record['updated'],
                    'deleted'  => $record['deleted'],
                    'skipped'  => $record['skipped']
                ]


value00: app/Controllers/ZonesController.php
value01:
                    'deleted'  => $record['deleted'],
                    'skipped'  => $record['skipped']
                ]
            ]);
        } catch (\Exception $e) {
            return $this->response->setJSON([
                'status'  => 'error',
                'message' => $e->getMessage()
            ]);
        }
    }

}
*/


value00: app/Controllers/Specimen/SpecimenCollectionController.php
value01:
    protected $rules;

    public function __construct() {
        $this->db = \Config\Database::connect();
        $this->model = new SpecimenCollectionModel();
        $this->rules = [];
    }

    public function index() {
        try {
            $rows = $this->model->findAll();

            $rows = ValueSet::transformLabels($rows, [
                'CollectionMethod' => 'collection_method',
                'Additive' => 'additive',
                'SpecimenRole' => 'specimen_role',
            ]);

            return $this->respond([ 'status' => 'success', 'message'=> "data fetched successfully", 'data'   => $rows ], 200);
        } catch (\Exception $e) {
            return $this->failServerError('Exception : '.$e->getMessage());
        }
    }


value00: app/Database/Migrations/2026-01-01-000001_CreateValueSet.php
value01:
<?php

namespace App\Database\Migrations;

use CodeIgniter\Database\Migration;

class CreateLookups extends Migration {
  public function up() {
    $this->forge->addField([
      'VID' => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
      'SiteID' => ['type' => 'INT', 'null' => true],
      'VSetID' => ['type' => 'INT', 'null' => true],
      'VOrder' => ['type' => 'INT', 'null' => true],
      'VValue' => ['type' => 'varchar', 'constraint' => 10],
      'VDesc' => ['type' => 'varchar', 'constraint' => 255, 'null' => true],
      'VCategory' => ['type' => 'int', 'null' => true],
      'CreateDate' => ['type' => 'Datetime', 'null' => true],
      'EndDate' => ['type' => 'DATETIME', 'null' => true]
    ]);
    $this->forge->addKey('VID', true);


value00: app/Database/Migrations/2026-01-01-000001_CreateValueSet.php
value01:
    $this->forge->addField([
      'ConDefID' => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
      'SiteID'  => ['type' => 'INT', 'null' => true],
      'ConCode' => ['type' => 'VARCHAR', 'constraint' => 3, 'null' => false],
      'ConName' => ['type' => 'varchar', 'constraint' => 50, 'null' => true],
      'ConDesc' => ['type' => 'varchar', 'constraint' => 50, 'null' => true],
      'Additive' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'ConClass' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'Color' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'CreateDate' => ['type' => 'Datetime', 'null' => true],
      'EndDate' => ['type' => 'DATETIME', 'null' => true]
    ]);


value00: app/Database/Migrations/2026-01-01-000001_CreateValueSet.php
value01:
    $this->forge->addKey('ConDefID', true);
    $this->forge->addUniqueKey('ConCode');
    $this->forge->createTable('containerdef');

    $this->forge->addField([
      'OccupationID' => [ 'type' => 'INT', 'constraint' => 11, 'auto_increment' => true],
      'OccCode'       => [ 'type' => 'VARCHAR', 'constraint' => 5, 'null' => true ],
      'OccText'     => [ 'type' => 'VARCHAR', 'constraint' => 255, 'null' => true ],
      'Description'  => [ 'type' => 'TEXT', 'null' => true ],
      'CreateDate' => ['type' => 'DATETIME', 'null' => true],
    ]);
    $this->forge->addKey('OccupationID', true);
    $this->forge->createTable('occupation');


value00: app/Database/Migrations/2026-01-01-000001_CreateValueSet.php
value01:
  public function down() {
    $this->forge->dropTable('areageo');
    $this->forge->dropTable('medicalspecialty');
    $this->forge->dropTable('occupation');
    $this->forge->dropTable('containerdef');
    $this->forge->dropTable('counter');
    $this->forge->dropTable('valuesetdef');
    $this->forge->dropTable('valueset');
  }
}


value00: app/Database/Migrations/2026-01-01-000001_CreateValueSet.php
value01:
    $this->forge->addField([
      'CounterID' => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
      '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],
      'EndDate' => ['type' => 'DATETIME', 'null' => true]
    ]);
    $this->forge->addKey('CounterID', true);
    $this->forge->createTable('counter');


value00: app/Database/Migrations/2026-01-01-000001_CreateValueSet.php
value01:
    $this->forge->addKey('OccupationID', true);
    $this->forge->createTable('occupation');

    $this->forge->addField([
      'SpecialtyID' => ['type' => 'INT', 'unsigned' => true, 'auto_increment' => true],
      'SpecialtyText' => ['type' => 'varchar', 'constraint' => 255, 'null' => true],
      'Parent' => ['type' => 'int', 'null' => true],
      'Title' => ['type' => 'VARCHAR', 'constraint' => 100,  'null' => false],
      'CreateDate' => ['type' => 'datetime', 'null' => true],
      'EndDate' => ['type' => 'datetime', 'null' => true],
    ]);
    $this->forge->addKey('SpecialtyID', true);
    $this->forge->createTable('medicalspecialty');


value00: app/Database/Migrations/2026-01-01-000001_CreateValueSet.php
value01:
    $this->forge->addKey('SpecialtyID', true);
    $this->forge->createTable('medicalspecialty');

    $this->forge->addField([
      'AreaGeoID' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
      'AreaCode' => ['type' => 'varchar', 'constraint' => 20, 'null' => true],
      'Class' => ['type' => 'int', 'null' => true],
      'AreaName' => ['type' => 'VARCHAR', 'constraint' => 100,  'null' => false],
      'Parent' => ['type' => 'int', 'null' => true],
    ]);
    $this->forge->addKey('AreaGeoID', true);
    $this->forge->createTable('areageo');
  }


value00: app/Database/Migrations/2026-01-01-000001_CreateValueSet.php
value01:
    $this->forge->addKey('VID', true);
    $this->forge->createTable('valueset');

    $this->forge->addField([
      'VSetID' => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
      'SiteID'  => ['type' => 'INT', 'null' => true],
      'VSName' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => false],
      'VSDesc' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => false],
      'CreateDate' => ['type' => 'Datetime', 'null' => true],
      'EndDate' => ['type' => 'DATETIME', 'null' => true]
    ]);
    $this->forge->addKey('VSetID', true);
    $this->forge->createTable('valuesetdef');


value00: app/Controllers/AuthV2Controller.php
value01:
/**
 * AuthV2 Controller
 * 
 * Handles authentication for V2 UI
 * Separate from the main Auth controller to avoid conflicts
 */
class AuthV2Controller extends Controller
{
    use ResponseTrait;

    protected $db;

    public function __construct()
    {
        $this->db = \Config\Database::connect();
    }

    /**
     * Check authentication status
     * GET /v2/auth/check
     */
    public function checkAuth()
    {
        $token = $this->request->getCookie('token');
        $key = getenv('JWT_SECRET');

        if (!$token) {
            return $this->respond([
                'status'  => 'failed',
                'message' => 'No token found'
            ], 401);
        }


value00: app/Controllers/AuthV2Controller.php
value01:
        $password = $this->request->getVar('password');
        $key = getenv('JWT_SECRET');

        // Validate username
        if (!$username) {
            return $this->respond([
                'status'  => 'failed',
                'message' => 'Username is required'
            ], 400);
        }

        // Find user
        $sql = "SELECT * FROM users WHERE username = " . $this->db->escape($username);


value00: app/Controllers/AuthV2Controller.php
value01:
        $hashedPassword = password_hash($password, PASSWORD_DEFAULT);

        // Insert user
        $this->db->transStart();
        $this->db->query(
            "INSERT INTO users(username, password, role_id) VALUES(?, ?, ?)",
            [$username, $hashedPassword, 1]
        );
        $this->db->transComplete();

        if ($this->db->transStatus() === false) {
            return $this->respond([
                'status'  => 'failed',
                'message' => 'Failed to create user'
            ], 500);
        }

        return $this->respond([
            'status'  => 'success',
            'message' => 'User ' . $username . ' successfully created'
        ], 201);
    }
}


value00: app/Controllers/Test/DemoOrderController.php
value01:
<?php
namespace App\Controllers\Test;

use App\Traits\ResponseTrait;
use CodeIgniter\Controller;
use App\Libraries\ValueSet;
use App\Models\Patient\PatientModel;
use App\Models\OrderTestModel;

class DemoOrderController extends Controller {
    use ResponseTrait;

    protected $db;
    protected $patientModel;
    protected $orderModel;

    public function __construct() {
        $this->db = \Config\Database::connect();
        $this->patientModel = new PatientModel();
        $this->orderModel = new OrderTestModel();
    }

    public function createDemoOrder() {
        $input = $this->request->getJSON(true);

        $patientData = [
            'PatientID' => $input['PatientID'] ?? 'DEMO' . time()


value00: app/Controllers/AuthV2Controller.php
value01:
        // Find user
        $sql = "SELECT * FROM users WHERE username = " . $this->db->escape($username);
        $query = $this->db->query($sql);
        $row = $query->getResultArray();

        if (!$row) {
            return $this->respond([
                'status'  => 'failed',
                'message' => 'User not found'
            ], 401);
        }

        $row = $row[0];

        // Verify password
        if (!password_verify($password, $row['password'])) {
            return $this->respond([
                'status'  => 'failed',
                'message' => 'Invalid password'
            ], 401);
        }

        // Create JWT payload
        $exp = time() + 864000; // 10 days


value00: app/Controllers/AuthV2Controller.php
value01:
        // Create JWT payload
        $exp = time() + 864000; // 10 days
        $payload = [
            'userid'   => $row['id'],
            'roleid'   => $row['role_id'],
            'username' => $row['username'],
            'exp'      => $exp
        ];

        try {
            $jwt = JWT::encode($payload, $key, 'HS256');
        } catch (\Exception $e) {
            return $this->respond([
                'status'  => 'failed',
                'message' => 'Error generating JWT: ' . $e->getMessage()
            ], 500);
        }

        // Detect if HTTPS is being used
        $isSecure = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on';

        // Set HTTP-only cookie


value00: app/Controllers/AuthV2Controller.php
value01:
        $exists = $this->db->query("SELECT id FROM users WHERE username = ?", [$username])->getRow();
        if ($exists) {
            return $this->respond([
                'status'  => 'failed',
                'message' => 'Username already exists'
            ], 409);
        }

        // Hash password
        $hashedPassword = password_hash($password, PASSWORD_DEFAULT);

        // Insert user
        $this->db->transStart();


value00: app/Controllers/AuthV2Controller.php
value01:
catch (BeforeValidException $e) {
            return $this->respond([
                'status'  => 'failed',
                'message' => 'Token not valid yet'
            ], 401);

        } catch (\Exception $e) {
            return $this->respond([
                'status'  => 'failed',
                'message' => 'Invalid token: ' . $e->getMessage()
            ], 401);
        }
    }

    /**
     * Login user
     * POST /v2/auth/login
     */
    public function login()
    {
        $username = $this->request->getVar('username');
        $password = $this->request->getVar('password');
        $key = getenv('JWT_SECRET');

        // Validate username


value00: app/Controllers/AuthV2Controller.php
value01:
        try {
            $decodedPayload = JWT::decode($token, new Key($key, 'HS256'));

            return $this->respond([
                'status'  => 'success',
                'message' => 'Authenticated',
                'data'    => $decodedPayload
            ], 200);

        } catch (ExpiredException $e) {
            return $this->respond([
                'status'  => 'failed',
                'message' => 'Token expired'
            ], 401);

        } catch (SignatureInvalidException $e) {
            return $this->respond([
                'status'  => 'failed',
                'message' => 'Invalid token signature'
            ], 401);

        }


value00: app/Controllers/AuthV2Controller.php
value01:
<?php

namespace App\Controllers;

use App\Traits\ResponseTrait;
use CodeIgniter\Controller;

use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Firebase\JWT\ExpiredException;
use Firebase\JWT\SignatureInvalidException;
use Firebase\JWT\BeforeValidException;
use CodeIgniter\Cookie\Cookie;

/**
 * AuthV2 Controller
 * 
 * Handles authentication for V2 UI
 * Separate from the main Auth controller to avoid conflicts
 */


value00: app/Controllers/AuthV2Controller.php
value01:
        $isSecure = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on';

        // Set HTTP-only cookie
        $this->response->setCookie([
            'name'     => 'token',
            'value'    => $jwt,
            'expire'   => 864000,
            'path'     => '/',
            'secure'   => $isSecure,  // false for localhost HTTP
            'httponly' => true,
            'samesite' => $isSecure ? Cookie::SAMESITE_NONE : Cookie::SAMESITE_LAX
        ]);

        return $this->respond([
            'status'  => 'success',
            'message' => 'Login successful',
            'data'    => [
                'username' => $row['username'],
                'role_id'  => $row['role_id']
            ]
        ], 200);
    }


value00: app/Controllers/Test/DemoOrderController.php
value01:
        $patient = $this->patientModel->where('PatientID', $patientData['PatientID'])->findAll();
        if (empty($patient)) {
            $internalPID = $this->patientModel->createPatient($patientData);
        } else {
            $internalPID = $patient[0]['InternalPID'];
        }

        $orderData = [
            'InternalPID' => $internalPID,
            'PatVisitID' => $input['PatVisitID'] ?? null,
            'Priority' => $input['Priority'] ?? 'R',
            'OrderingProvider' => $input['OrderingProvider'] ?? 'Dr. Demo',
            'DepartmentID' => $input['DepartmentID'] ?? 1,
            'Tests' => $input['Tests'] ?? []
        ];

        $orderID = $this->orderModel->createOrder($orderData);


value00: app/Controllers/Test/DemoOrderController.php
value01:
        $orderID = $this->orderModel->createOrder($orderData);

        return $this->respond([
            'status' => 'success',
            'message' => 'Demo order created successfully',
            'data' => [
                'PatientID' => $patientData['PatientID'],
                'InternalPID' => $internalPID,
                'OrderID' => $orderID,
                'OrderStatus' => 'ORD'
            ]
        ], 201);
    }


value00: app/Controllers/AuthV2Controller.php
value01:
    /**
     * Logout user
     * POST /v2/auth/logout
     */
    public function logout()
    {
        // Detect if HTTPS is being used
        $isSecure = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on';

        // Clear the token cookie
        return $this->response->setCookie([
            'name'     => 'token',
            'value'    => '',
            'expire'   => time() - 3600,
            'path'     => '/',
            'secure'   => $isSecure,
            'httponly' => true,
            'samesite' => $isSecure ? Cookie::SAMESITE_NONE : Cookie::SAMESITE_LAX
        ])->setJSON([
            'status'  => 'success',
            'message' => 'Logout successful'
        ]);
    }


value00: app/Controllers/AuthV2Controller.php
value01:
    /**
     * Register new user
     * POST /v2/auth/register
     */
    public function register()
    {
        $username = strtolower($this->request->getJsonVar('username'));
        $password = $this->request->getJsonVar('password');

        // Validate input
        if (empty($username) || empty($password)) {
            return $this->respond([
                'status'  => 'failed',
                'message' => 'Username and password are required'
            ], 400);
        }

        // Check for existing username
        $exists = $this->db->query("SELECT id FROM users WHERE username = ?", [$username])->getRow();


value00: app/Controllers/Test/DemoOrderController.php
value01:
{
        $input = $this->request->getJSON(true);

        $patientData = [
            'PatientID' => $input['PatientID'] ?? 'DEMO' . time(),
            'Gender' => $input['Gender'] ?? '1',
            'NameFirst' => $input['NameFirst'] ?? 'Demo',
            'NameLast' => $input['NameLast'] ?? 'Patient',
            'Birthdate' => $input['Birthdate'] ?? '1990-01-01'
        ];

        $patient = $this->patientModel->where('PatientID', $patientData['PatientID'])->findAll();


value00: app/Controllers/Test/DemoOrderController.php
value01:
    public function listDemoOrders() {
        $orders = $this->db->table('ordertest ot')
            ->select('ot.OrderID, ot.InternalPID, p.PatientID, ot.OrderDateTime, ot.Priority, ot.OrderStatus')
            ->join('patient p', 'p.InternalPID = ot.InternalPID')
            ->where('ot.DelDate', null)
            ->orderBy('ot.OrderDateTime', 'DESC')
            ->limit(50)
            ->get()
            ->getResultArray();

        $orders = ValueSet::transformLabels($orders, [
            'Priority' => 'order_priority',
            'OrderStatus' => 'order_status',
        ]);

        return $this->respond([
            'status' => 'success',
            'message' => 'Data fetched successfully',
            'data' => $orders
        ], 200);
    }
}


value00: app/Database/Migrations/2026-01-01-000005_CreateLabInfrastructure.php
value01:
      'Parity' => ['type' => 'varchar', 'constraint'=> 10, 'null'=> true],
      'Stop' => ['type' => 'varchar', 'constraint'=> 10, 'null'=> true],
      'CreateDate' => ['type'=>'DATETIME', 'null' => true],
      'EndDate' => ['type'=>'DATETIME', 'null' => true]
    ]);
    $this->forge->addKey('InterfaceID', true);
    $this->forge->createTable('comparameters');

    $this->forge->addField([
      'EquipmentID' => ['type' => 'int', 'unsigned' => true, 'auto_increment'=> true],
      'DeviceName' => ['type' => 'varchar', 'constraint' => 50,'null'=> false],
      'Description' => ['type' => 'varchar', 'constraint' => 50,'null'=> false],
      'SiteID' => ['type' => 'int', 'null'=> true]


value00: app/Database/Migrations/2026-01-01-000005_CreateLabInfrastructure.php
value01:
<?php

namespace App\Database\Migrations;

use CodeIgniter\Database\Migration;

class CreateLabInfrastructure extends Migration {
  public function up() {
    $this->forge->addField([
      'EID' => ['type' => 'int', 'unsigned' => true, 'auto_increment'=> true]


value00: app/Database/Migrations/2026-01-01-000005_CreateLabInfrastructure.php
value01:
,
      'Description' => ['type' => 'varchar', 'constraint' => 50,'null'=> false],
      'SiteID' => ['type' => 'int', 'null'=> true],
      'LocationID' => ['type' => 'int', 'null'=> true],
      'DIDType' => ['type' => 'varchar', 'constraint'=>10, 'null'=> true],
      'DID' => ['type' => 'varchar', 'constraint'=>100, 'null'=> true],
      'MachineID' => ['type' => 'varchar', 'constraint'=>100, 'null'=> true]


value00: app/Database/Migrations/2026-01-01-000005_CreateLabInfrastructure.php
value01:
      'DID' => ['type' => 'varchar', 'constraint'=>100, 'null'=> true],
      'MachineID' => ['type' => 'varchar', 'constraint'=>100, 'null'=> true],
      'IPAddress' => ['type' => 'varchar', 'constraint'=>25, 'null'=> true],
      'CreateDate' => ['type'=>'DATETIME', 'null' => true],
      'EndDate' => ['type'=>'DATETIME', 'null' => true]
    ]);
    $this->forge->addKey('EquipmentID', true);
    $this->forge->createTable('devicelist');
  }

  public function down() {
    $this->forge->dropTable('devicelist');
    $this->forge->dropTable('comparameters');
    $this->forge->dropTable('equipmentlist');
  }
}


value00: app/Database/Migrations/2026-01-01-000005_CreateLabInfrastructure.php
value01:
    $this->forge->addKey('EID', true);
    $this->forge->createTable('equipmentlist');

    $this->forge->addField([
      'InterfaceID' => ['type' => 'int', 'unsigned' => true, 'auto_increment'=> true],
      'InstrumentID' => ['type' => 'int', 'null'=> false],
      'SiteID' => ['type' => 'int', 'null'=> true],
      'InterfaceName' => ['type' => 'varchar', 'constraint'=> 150, 'null'=> true],
      'InterfaceDesc' => ['type' => 'varchar', 'constraint'=> 150, 'null'=> true],
      'Protocol' => ['type' => 'varchar', 'constraint'=> 50, 'null'=> true],
      'IPAddress' => ['type' => 'varchar', 'constraint'=> 50, 'null'=> true],
      'Port' => ['type' => 'varchar', 'constraint'=> 25, 'null'=> true],
      'COM' => ['type' => 'varchar', 'constraint'=> 5, 'null'=> true]


value00: app/Database/Migrations/2026-01-01-000005_CreateLabInfrastructure.php
value01:
,
      'Port' => ['type' => 'varchar', 'constraint'=> 25, 'null'=> true],
      'COM' => ['type' => 'varchar', 'constraint'=> 5, 'null'=> true],
      'Baud' => ['type' => 'varchar', 'constraint'=> 10, 'null'=> true],
      'Data' => ['type' => 'varchar', 'constraint'=> 10, 'null'=> true],
      'Parity' => ['type' => 'varchar', 'constraint'=> 10, 'null'=> true],
      'Stop' => ['type' => 'varchar', 'constraint'=> 10, 'null'=> true]


value00: app/Database/Migrations/2026-01-01-000005_CreateLabInfrastructure.php
value01:
{
    $this->forge->addField([
      'EID' => ['type' => 'int', 'unsigned' => true, 'auto_increment'=> true],
      'IEID' => ['type' => 'varchar', 'constraint' => 50],
      'DepartmentID' => ['type' => 'int', 'constraint'=> 10, 'null'=> false],
      'InstrumentID' => ['type' => 'varchar', 'constraint'=> 150, 'null'=> true],
      'InstrumentName' => ['type' => 'varchar', 'constraint'=> 150, 'null'=> true],
      'WorkstationID' => ['type' => 'int', 'unsigned' => true ],
      'Enable' => ['type' => 'bit', 'null'=> false],
      'EquipmentRole' => ['type' => 'varchar', 'constraint' => 1,  'null'=> false],
      'CreateDate' => ['type'=>'DATETIME', 'null' => true],
      'EndDate' => ['type'=>'DATETIME', 'null' => true]
    ]);
    $this->forge->addKey('EID', true);


value00: app/Database/Migrations/2026-01-01-000004_CreateTestDefinitions.php
value01:
[
      'RefTxtID' => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
      'SiteID'  => ['type' => 'INT', 'null' => true],
      'TestSiteID'  => ['type' => 'INT', 'null' => false],
      'SpcType'  => ['type' => 'varchar', 'constraint'=> 10, 'null' => false],
      'Sex'  => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'Criteria' => ['type' => 'varchar', 'constraint'=>100, 'null' => true],
      'AgeStart'  => ['type' => 'INT', 'null' => true],
      'AgeEnd'  => ['type' => 'int', 'null' => true],
      'TxtRefType' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'RefTxt'  => ['type' => 'varchar', 'constraint'=>255, 'null' => true],
      'Flag' => ['type' => 'varchar', 'constraint'=>10, 'null' => true]


value00: app/Database/Migrations/2026-01-01-000004_CreateTestDefinitions.php
value01:
<?php

namespace App\Database\Migrations;

use CodeIgniter\Database\Migration;

class CreateTestDefinitions extends Migration {
  public function up() {
    $this->forge->addField([
      'TestSiteID'  => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
      'SiteID' => ['type' => 'INT', 'null' => false],
      'TestSiteCode'  => ['type' => 'varchar', 'constraint'=> 6, 'null' => false],
      'TestSiteName'  => ['type' => 'varchar', 'constraint'=> 100, 'null' => false],
      'TestType'  => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => false],
      'Description'  => ['type' => 'varchar', 'constraint'=> 255, 'null' => true],
      'DisciplineID' => ['type' => 'INT', 'null' => true]


value00: app/Database/Migrations/2026-01-01-000004_CreateTestDefinitions.php
value01:
,
      'Unit2' => ['type' => 'varchar', 'constraint'=> 20, 'null' => true],
      'Decimal' => ['type' => 'int', 'null' => true, 'default' => 2],
      'CollReq' => ['type' => 'varchar', 'constraint'=> 255, 'null' => true],
      'Method' => ['type' => 'varchar', 'constraint'=> 50, 'null' => true],
      'ExpectedTAT' => ['type' => 'INT', 'null' => true],
      'SeqScr' => ['type' => 'int', 'null' => true],
      'SeqRpt' => ['type' => 'int', 'null' => true],
      'IndentLeft' => ['type' => 'int', 'null' => true, 'default' => 0],
      'FontStyle' => ['type' => 'varchar', 'constraint'=> 50, 'null' => true],
      'VisibleScr' => ['type' => 'int', 'null' => true, 'default' => 1],
      'VisibleRpt' => ['type' => 'int', 'null' => true, 'default' => 1]


value00: app/Database/Migrations/2026-01-01-000004_CreateTestDefinitions.php
value01:
,
      'VisibleScr' => ['type' => 'int', 'null' => true, 'default' => 1],
      'VisibleRpt' => ['type' => 'int', 'null' => true, 'default' => 1],
      'CountStat' => ['type' => 'int', 'null' => true, 'default' => 1],
      'Level' => ['type' => 'int', 'null' => true],
			'Requestable' => [ 'type' => 'TINYINT', 'constraint' => 1, 'null' => true, 'default' => 1, 'comment' => 'Flag indicating if test can be requested (1=yes, 0=no)' ],
      'CreateDate' => ['type' => 'Datetime', 'null' => true],
      'StartDate' => ['type' => 'Datetime', 'null' => true],
      'EndDate' => ['type' => 'Datetime', 'null' => true],
    ]


value00: app/Database/Migrations/2026-01-01-000004_CreateTestDefinitions.php
value01:
,
      'StartDate' => ['type' => 'Datetime', 'null' => true],
      'EndDate' => ['type' => 'Datetime', 'null' => true],
    ]);
    $this->forge->addKey('TestSiteID', true);
    $this->forge->createTable('testdefsite');

    // testdeftech table removed - merged into testdefsite

    $this->forge->addField([
      'TestCalID'  => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
      'TestSiteID'  => ['type' => 'INT', 'unsigned' => true, 'null' => false],
      'DisciplineID' => ['type' => 'INT', 'null' => true],
      'DepartmentID' => ['type' => 'INT', 'null' => true],
      'FormulaCode' => ['type' => 'varchar', 'constraint'=> 255, 'null' => true]


value00: app/Database/Migrations/2026-01-01-000002_CreateOrganization.php
value01:
([
      'SiteID' => ['type' => 'INT', 'unsigned' => true, 'auto_increment' => true],
      'ExtSiteID' => ['type' => 'int', 'null' => true],
      'SiteCode' => ['type' => 'VARCHAR', 'constraint' => 2, 'null' => false],
      'SiteName' => ['type' => 'VARCHAR', 'constraint' => 100, 'null' => false],
      'AccountID' => ['type' => 'int', 'null' => true],
      'SiteType' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'Parent' => ['type' => 'int', '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]
    ]);


value00: app/Database/Migrations/2026-01-01-000004_CreateTestDefinitions.php
value01:
    $this->forge->addForeignKey('Member', 'testdefsite', 'TestSiteID', 'CASCADE', 'CASCADE');
    $this->forge->createTable('testdefgrp');

    $this->forge->addField([
      'RefNumID' => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
      'SiteID'  => ['type' => 'INT', 'null' => true],
      'TestSiteID'  => ['type' => 'INT', 'null' => false],
      'SpcType'  => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'Sex'  => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'Criteria'  => ['type' => 'varchar', 'constraint'=>100, 'null' => true],
      'AgeStart'  => ['type' => 'INT', 'null' => true]


value00: app/Database/Migrations/2026-01-01-000004_CreateTestDefinitions.php
value01:
,
      'Display' => ['type' => 'INT', 'null' => true],
      'Flag' => ['type' => 'varchar', 'constraint'=>10, 'null' => true],
      'Interpretation' => ['type' => 'varchar', 'constraint'=>255, 'null' => true],
      'Notes' => ['type' => 'varchar', 'constraint'=>255, 'null' => true],
      'CreateDate' => ['type' => 'Datetime', 'null' => true],
      'StartDate' => ['type' => 'Datetime', 'null' => true],
      'EndDate' => ['type' => 'Datetime', 'null' => true],
    ]);
    $this->forge->addKey('RefNumID', true);
    $this->forge->createTable('refnum');

    $this->forge->addField([
      'RefTxtID' => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
      'SiteID'  => ['type' => 'INT', 'null' => true]


value00: app/Database/Migrations/2026-01-01-000004_CreateTestDefinitions.php
value01:
,
      'Decimal' => ['type' => 'int', 'null' => true, 'default' => 2],
      'Method' => ['type' => 'varchar', 'constraint'=> 50, 'null' => true],
      'CreateDate' => ['type' => 'Datetime', 'null' => true],
      'EndDate' => ['type' => 'Datetime', 'null' => true]
    ]);
    $this->forge->addKey('TestCalID', true);
    $this->forge->addForeignKey('TestSiteID', 'testdefsite', 'TestSiteID', 'CASCADE', 'CASCADE');


value00: app/Database/Migrations/2026-01-01-000002_CreateOrganization.php
value01:
      'Phone' => ['type' => 'VARCHAR', 'constraint' => 50, 'null' => true],
      'Fax' => ['type' => 'VARCHAR', 'constraint' => 50, 'null' => true],
      'CreateDate' => ['type' => 'datetime', 'null'=> true],
      'EndDate' => ['type' => 'datetime', 'null'=> true]
    ]);
    $this->forge->addKey('AccountID', true);
    $this->forge->createTable('account');

    $this->forge->addField([
      'SiteID' => ['type' => 'INT', 'unsigned' => true, 'auto_increment' => true],
      'ExtSiteID' => ['type' => 'int', 'null' => true]


value00: app/Database/Migrations/2026-01-01-000002_CreateOrganization.php
value01:
    $this->forge->addKey('DisciplineID', true);
    $this->forge->createTable('discipline');

    $this->forge->addField([
      'DepartmentID' => ['type' => 'int', 'unsigned' => true, 'auto_increment'=> true],
      'DisciplineID' => ['type' => 'int', 'null'=> false],
      'SiteID' => ['type' => 'int', 'null'=> false],
      'DepartmentCode' => ['type' => 'varchar', 'constraint'=>10, 'null'=> false],
      'DepartmentName' => ['type' => 'varchar', 'constraint'=> 150, 'null'=> true],
      'CreateDate' => ['type'=>'DATETIME', 'null' => true],
      'EndDate' => ['type'=>'DATETIME', 'null' => true]
    ]);
    $this->forge->addKey('DepartmentID', true);
    $this->forge->createTable('department');


value00: app/Database/Migrations/2026-01-01-000002_CreateOrganization.php
value01:
,
      'Street_3' => ['type' => 'VARCHAR', 'constraint' => 150, 'null' => true],
      'City' => ['type' => 'varchar', 'constraint' => 150, 'null' => true],
      'Province' => ['type' => 'varchar', 'constraint' => 150, 'null' => true],
      'ZIP' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'Country' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'AreaCode' => ['type' => 'VARCHAR', 'constraint' => 50, 'null' => true],
      'EmailAddress1' => ['type' => 'VARCHAR', 'constraint' => 50, 'null' => true],
      'EmailAddress2' => ['type' => 'VARCHAR', 'constraint' => 50, 'null' => true],
      'Phone' => ['type' => 'VARCHAR', 'constraint' => 50, 'null' => true],
      'Fax' => ['type' => 'VARCHAR', 'constraint' => 50, 'null' => true]


value00: app/Database/Migrations/2026-01-01-000004_CreateTestDefinitions.php
value01:
      'Criteria'  => ['type' => 'varchar', 'constraint'=>100, 'null' => true],
      'AgeStart'  => ['type' => 'INT', 'null' => true],
      'AgeEnd'  => ['type' => 'int', 'null' => true],
      'NumRefType' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'RangeType' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'LowSign' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'Low' => ['type' => 'DECIMAL', 'constraint' => '10,2', 'null' => true],
      'HighSign' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'High' => ['type' => 'DECIMAL', 'constraint' => '10,2', 'null' => true],
      'Display' => ['type' => 'INT', 'null' => true],
      'Flag' => ['type' => 'varchar', 'constraint'=>10, 'null' => true]


value00: app/Database/Migrations/2026-01-01-000004_CreateTestDefinitions.php
value01:
,
      'DepartmentID' => ['type' => 'INT', 'null' => true],
      'FormulaCode' => ['type' => 'varchar', 'constraint'=> 255, 'null' => true],
      'RefType' => ['type' => 'varchar', 'constraint'=> 10, 'null' => true, 'default' => 'NMRC'],
      'Unit1' => ['type' => 'varchar', 'constraint'=> 20, 'null' => true],
      'Factor' => ['type' => 'DECIMAL', 'constraint'=> '10,4', 'null' => true],
      'Unit2' => ['type' => 'varchar', 'constraint'=> 20, 'null' => true],
      'Decimal' => ['type' => 'int', 'null' => true, 'default' => 2],
      'Method' => ['type' => 'varchar', 'constraint'=> 50, 'null' => true]


value00: app/Database/Migrations/2026-01-01-000004_CreateTestDefinitions.php
value01:
      'Flag' => ['type' => 'varchar', 'constraint'=>10, 'null' => true],
      'Notes' => ['type' => 'varchar', 'constraint'=>255, 'null' => true],
      'CreateDate' => ['type' => 'Datetime', 'null' => true],
      'StartDate' => ['type' => 'Datetime', 'null' => true],
      'EndDate' => ['type' => 'Datetime', 'null' => true],
    ]);
    $this->forge->addKey('RefTxtID', true);
    $this->forge->createTable('reftxt');

    $this->forge->addField([
      'FlagDefID' => ['type' => 'INT', 'auto_increment' => true],
      'InstrumentName' => ['type' => 'varchar', 'constraint' => 100, 'null' => true],
      'Flag' => ['type' => 'varchar', 'constraint' => 50, 'null' => true],
    ]);
    $this->forge->addKey('FlagDefID', true);
    $this->forge->createTable('flagdef');


value00: app/Database/Migrations/2026-01-01-000004_CreateTestDefinitions.php
value01:
      'RefTxt'  => ['type' => 'varchar', 'constraint'=>255, 'null' => true],
      'Flag' => ['type' => 'varchar', 'constraint'=>10, 'null' => true],
      'Notes' => ['type' => 'varchar', 'constraint'=>255, 'null' => true]


value00: app/Database/Migrations/2026-01-01-000004_CreateTestDefinitions.php
value01:
,
      'DepartmentID' => ['type' => 'INT', 'null' => true],
      'ResultType' => ['type' => 'VARCHAR', 'constraint'=> 20, 'null' => true],
      'RefType' => ['type' => 'VARCHAR', 'constraint'=> 10, 'null' => true],
      'VSet' => ['type' => 'INT', 'null' => true],
      'ReqQty' => ['type' => 'DECIMAL', 'constraint'=> '10,2', 'null' => true],
      'ReqQtyUnit' => ['type' => 'varchar', 'constraint'=> 20, 'null' => true],
      'Unit1' => ['type' => 'varchar', 'constraint'=> 20, 'null' => true],
      'Factor' => ['type' => 'DECIMAL', 'constraint'=> '10,4', 'null' => true],
      'Unit2' => ['type' => 'varchar', 'constraint'=> 20, 'null' => true],
      'Decimal' => ['type' => 'int', 'null' => true, 'default' => 2]


value00: app/Database/Migrations/2026-01-01-000002_CreateOrganization.php
value01:
    $this->forge->addKey('LocationID', true);
    $this->forge->createTable('location');

    $this->forge->addField([
      'LocationID' => ['type' => 'INT', 'unsigned' => true],
      'Street1'  => ['type' => 'Varchar', 'constraint' => 255, 'null' => true],
      'Street2' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'City' => ['type' => 'int', 'null' => true],
      'Province' => ['type' => 'int', 'null' => true],
      'PostCode' => ['type' => 'varchar', 'constraint' => 255, 'null' => true]


value00: app/Database/Migrations/2026-01-01-000002_CreateOrganization.php
value01:
,
      'Province' => ['type' => 'int', 'null' => true],
      'PostCode' => ['type' => 'varchar', 'constraint' => 255, 'null' => true],
      'GeoLocationSystem' => ['type' => 'varchar', 'constraint' => 255, 'null' => true],
      'GeoLocationData' => ['type' => 'varchar', 'constraint' => 255, 'null' => true],
      'Phone' => ['type' => 'varchar', 'constraint' => 100, 'null' => true],
      'Email' => ['type' => 'varchar', 'constraint' => 150, 'null' => true],
      'CreateDate' => ['type' => 'DATETIME', 'null' => true],
      'EndDate' => ['type' => 'DATETIME', 'null' => true]
    ]);
    $this->forge->addKey('LocationID', true);
    $this->forge->createTable('locationaddress');


value00: app/Database/Migrations/2026-01-01-000004_CreateTestDefinitions.php
value01:
  public function down() {
    $this->forge->dropTable('testmapdetail');
    $this->forge->dropTable('testmap');
    $this->forge->dropTable('flagdef');
    $this->forge->dropTable('reftxt');
    $this->forge->dropTable('refnum');
    $this->forge->dropTable('testdefgrp');
    $this->forge->dropTable('testdefcal');
    $this->forge->dropTable('testdefsite');
  }
}


value00: app/Database/Migrations/2026-01-01-000004_CreateTestDefinitions.php
value01:
    $this->forge->addKey('FlagDefID', true);
    $this->forge->createTable('flagdef');

    $this->forge->addField([
      'TestMapID'  => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
      'HostType' => ['type' => 'varchar', 'constraint'=> 20, 'null' => true],
      'HostID' => ['type' => 'varchar', 'constraint'=> 50, 'null' => true],
      'ClientType' => ['type' => 'varchar', 'constraint'=> 20, 'null' => true],
      'ClientID' => ['type' => 'varchar', 'constraint'=> 50, 'null' => true],
      'CreateDate' => ['type' => 'Datetime', 'null' => true],
      'EndDate' => ['type' => 'Datetime', 'null' => true]
    ]);
    $this->forge->addKey('TestMapID', true);
    $this->forge->createTable('testmap');


value00: app/Database/Migrations/2026-01-01-000002_CreateOrganization.php
value01:
    $this->forge->addField([
      'WorkstationID' => ['type' => 'int', 'unsigned' => true, 'auto_increment'=> true],
      'DepartmentID' => ['type' => 'int', 'null'=> false],
      'WorkstationCode' => ['type' => 'varchar', 'constraint'=>10, 'null'=> false],
      'WorkstationName' => ['type' => 'varchar', 'constraint'=> 150, 'null'=> true],
      'Type' => ['type' => 'VARCHAR', 'constraint' => 10, 'null'=> true],
      'LinkTo' => ['type' => 'int', 'null'=> true],
      'Enable' => ['type' => 'VARCHAR', 'constraint' => 10, 'null'=> true],
      'CreateDate' => ['type'=>'DATETIME', 'null' => true],
      'EndDate' => ['type'=>'DATETIME', 'null' => true]
    ]);
    $this->forge->addKey('WorkstationID', true);
    $this->forge->createTable('workstation');
  }


value00: app/Database/Migrations/2026-01-01-000002_CreateOrganization.php
value01:
    $this->forge->addKey('SiteID', true);
    $this->forge->createTable('site');

    $this->forge->addField([
      'LocationID' => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
      'SiteID'  => ['type' => 'INT', 'null' => true],
      'LocCode' => ['type' => 'VARCHAR', 'constraint' => 6, 'null' => false],
      'Parent' => ['type' => 'INT', 'null' => true],
      'LocFull' => ['type' => 'varchar', 'constraint' => 255, 'null' => true],
      'Description' => ['type' => 'varchar', 'constraint' => 255, 'null' => true],
      'LocType' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'CreateDate' => ['type' => 'DATETIME', 'null' => true],
      'EndDate' => ['type' => 'DATETIME', 'null' => true]
    ]);


value00: app/Database/Migrations/2026-01-01-000004_CreateTestDefinitions.php
value01:
    $this->forge->addField([
      'TestMapDetailID'  => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
      'TestMapID'  => ['type' => 'INT'],
      'HostTestCode' => ['type' => 'varchar', 'constraint'=> 10, 'null' => true],
      'HostTestName' => ['type' => 'varchar', 'constraint'=> 100, 'null' => true],
      'ConDefID' => ['type' => 'INT', 'null' => true],
      'ClientTestCode' => ['type' => 'varchar', 'constraint'=> 10, 'null' => true],
      'ClientTestName' => ['type' => 'varchar', 'constraint'=> 100, 'null' => true],
      'CreateDate' => ['type' => 'Datetime', 'null' => true],
      'EndDate' => ['type' => 'Datetime', 'null' => true]
    ]);
    $this->forge->addKey('TestMapDetailID', true);
    $this->forge->createTable('testmapdetail');
  }


value00: app/Database/Migrations/2026-01-01-000004_CreateTestDefinitions.php
value01:
,
      'Description'  => ['type' => 'varchar', 'constraint'=> 255, 'null' => true],
      'DisciplineID' => ['type' => 'INT', 'null' => true],
      'DepartmentID' => ['type' => 'INT', 'null' => true],
      'ResultType' => ['type' => 'VARCHAR', 'constraint'=> 20, 'null' => true]


value00: app/Database/Migrations/2026-01-01-000002_CreateOrganization.php
value01:
    $this->forge->addKey('LocationID', true);
    $this->forge->createTable('locationaddress');

    $this->forge->addField([
      'DisciplineID' => ['type' => 'int', 'unsigned' => true, 'auto_increment'=> true],
      'SiteID' => ['type' => 'int', 'null'=> false],
      '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]
    ]);
    $this->forge->addKey('DisciplineID', true);


value00: app/Database/Migrations/2026-01-01-000002_CreateOrganization.php
value01:
<?php

namespace App\Database\Migrations;

use CodeIgniter\Database\Migration;

class CreateOrganization extends Migration {
  public function up() {
    $this->forge->addField([
      'AccountID' => ['type' => 'INT', 'unsigned' => true, 'auto_increment' => true],
      'Parent' => ['type' => 'INT', 'null' => true],
      'AccountName' => ['type' => 'VARCHAR', 'constraint' => 100, 'null' => false],
      'Initial' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => false],
      'Street_1' => ['type' => 'VARCHAR', 'constraint' => 150, 'null' => true],
      'Street_2' => ['type' => 'VARCHAR', 'constraint' => 150, 'null' => true],
      'Street_3' => ['type' => 'VARCHAR', 'constraint' => 150, 'null' => true]


value00: app/Database/Migrations/2026-01-01-000002_CreateOrganization.php
value01:
  public function down() {
    $this->forge->dropTable('workstation');
    $this->forge->dropTable('department');
    $this->forge->dropTable('discipline');
    $this->forge->dropTable('locationaddress');
    $this->forge->dropTable('location');
    $this->forge->dropTable('site');
    $this->forge->dropTable('account');
  }
}


value00: app/Database/Migrations/2026-01-01-000004_CreateTestDefinitions.php
value01:
    $this->forge->addForeignKey('TestSiteID', 'testdefsite', 'TestSiteID', 'CASCADE', 'CASCADE');
    $this->forge->createTable('testdefcal');

    $this->forge->addField([
      'TestGrpID'  => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
      'TestSiteID'  => ['type' => 'INT', 'unsigned' => true, 'null' => false],
      'Member' => ['type' => 'INT', 'unsigned' => true, 'null' => true],
      'CreateDate' => ['type' => 'Datetime', 'null' => true],
      'EndDate' => ['type' => 'Datetime', 'null' => true]
    ]);
    $this->forge->addKey('TestGrpID', true);
    $this->forge->addForeignKey('TestSiteID', 'testdefsite', 'TestSiteID', 'CASCADE', 'CASCADE');
    $this->forge->addForeignKey('Member', 'testdefsite', 'TestSiteID', 'CASCADE', 'CASCADE');


value00: app/Database/Migrations/2026-01-01-000006_CreateSpecimens.php
value01:
,
      'GenerateBy' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'SchDateTime' => ['type' => 'datetime', 'null' => true],
      'CreateDate' => ['type' => 'Datetime', 'null' => true],
      'EndDate' => ['type' => 'DATETIME', 'null' => true],
      'ArchiveDate' => ['type' => 'DATETIME', 'null' => true]
    ]);
    $this->forge->addKey('InternalSID', true);
    $this->forge->addUniqueKey('SID');
    $this->forge->createTable('specimen');

    $this->forge->addField([
      'SpcStaID' => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
      'SID' => ['type' => 'VARCHAR', 'constraint' => 30,  'null' => false],
      'OrderID' => ['type' => 'int', 'null' => false]


value00: app/Database/Migrations/2026-01-01-000006_CreateSpecimens.php
value01:
      'DIDType' => ['type' => 'varchar', 'constraint'=>10, 'null' => true ],
      'DID' => ['type' => 'varchar', 'constraint'=>10, 'null' => true ],
      'UserID' => ['type' => 'int', 'null' => true ],
      'CreateDate' => ['type' => 'Datetime', 'null' => true],
      'EndDate' => ['type' => 'DATETIME', 'null' => true],
      'ArchiveDate' => ['type' => 'DATETIME', 'null' => true]
    ]);
    $this->forge->addKey('SpcStaID', true);
    $this->forge->createTable('specimenstatus');

    $this->forge->addField([
      'SpcColID' => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
      'SpcStaID' => ['type' => 'INT', 'unsigned' => true]


value00: app/Database/Migrations/2026-01-01-000006_CreateSpecimens.php
value01:
,
      'SID' => ['type' => 'VARCHAR', 'constraint' => 30,  'null' => false],
      'OrderID' => ['type' => 'int', 'null' => false],
      'SpcAct' => ['type' => 'varchar', 'constraint' => 15, 'null' => true],
      'ActRes' => ['type' => 'INT', 'null' => true],
      'SpcStatus' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'Qty' => ['type' => 'INT', 'null' => true],
      'Unit' => ['type' => 'INT', 'null' => true],
      'SpcCon' => ['type' => 'INT', 'null' => true],
      'Comment' => ['type' => 'varchar', 'constraint'=>150, 'null' => true],
      'CurrSiteID' => ['type' => 'int', 'null' => true ]


value00: app/Database/Migrations/2026-01-01-000006_CreateSpecimens.php
value01:
[
      'SpcColID' => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
      'SpcStaID' => ['type' => 'INT', 'unsigned' => true],
      'SpcRole' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'ColMethod' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'BodySite' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'CntSize' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'FastingVolume' => ['type' => 'varchar', 'constraint'=> 2, 'null' => true],
      'ColStart' => ['type' => 'datetime', 'null' => true]


value00: app/Database/Migrations/2026-01-01-000006_CreateSpecimens.php
value01:
,
      'Comment' => ['type' => 'varchar', 'constraint'=>150, 'null' => true],
      'CurrSiteID' => ['type' => 'int', 'null' => true ],
      'CurrLocID' => ['type' => 'int', 'null' => true ],
      'Origin' => ['type' => 'varchar', 'constraint'=>50, 'null' => true ],
      'GeoLocationSystem' => ['type' => 'varchar', 'constraint'=>10, 'null' => true ],
      'GeoLocationData' => ['type' => 'varchar', 'constraint'=>10, 'null' => true ],
      'DIDType' => ['type' => 'varchar', 'constraint'=>10, 'null' => true ],
      'DID' => ['type' => 'varchar', 'constraint'=>10, 'null' => true ]


value00: app/Database/Migrations/2026-01-01-000006_CreateSpecimens.php
value01:
[
      'SpcPrpID' => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
      'SpcStaID' => ['type' => 'INT', 'unsigned' => true],
      'Description' => ['type' => 'varchar', 'constraint' => 150, 'null' => true],
      'Method' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'Additive' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'AddQty' => ['type' => 'float', 'null' => true],
      'AddUnit' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'PrepStart' => ['type' => 'datetime', 'null' => true]


value00: app/Database/Migrations/2026-01-01-000006_CreateSpecimens.php
value01:
<?php

namespace App\Database\Migrations;

use CodeIgniter\Database\Migration;

class CreateSpecimens extends Migration {
  public function up() {
    $this->forge->addField([
      'InternalSID' => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
      'SID' => ['type' => 'VARCHAR', 'constraint' => 30,  'null' => false],
      'SiteID' => ['type' => 'INT', 'null' => true],
      'OrderID' => ['type' => 'INT', 'null' => true],
      'ConDefID' => ['type' => 'INT', 'null' => true],
      'Parent' => ['type' => 'varchar', 'constraint' => 30, 'null' => true]


value00: app/Database/Migrations/2026-01-01-000006_CreateSpecimens.php
value01:
,
      'AddUnit' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'PrepStart' => ['type' => 'datetime', 'null' => true],
      'PrepEnd' => ['type' => 'datetime', 'null' => true],
      'CreateDate' => ['type' => 'Datetime', 'null' => true],
      'EndDate' => ['type' => 'DATETIME', 'null' => true],
      'ArchiveDate' => ['type' => 'DATETIME', 'null' => true]
    ]);
    $this->forge->addKey('SpcPrpID', true);
    $this->forge->createTable('specimenprep');

  }

  public function down() {
    $this->forge->dropTable('specimenprep');
    $this->forge->dropTable('specimencollection');
    $this->forge->dropTable('specimenstatus');
    $this->forge->dropTable('specimen');
  }
}


value00: app/Database/Migrations/2026-01-01-000006_CreateSpecimens.php
value01:
,
      'FastingVolume' => ['type' => 'varchar', 'constraint'=> 2, 'null' => true],
      'ColStart' => ['type' => 'datetime', 'null' => true],
      'ColEnd' => ['type' => 'datetime', 'null' => true],
      'CreateDate' => ['type' => 'Datetime', 'null' => true],
      'EndDate' => ['type' => 'DATETIME', 'null' => true],
      'ArchiveDate' => ['type' => 'DATETIME', 'null' => true]
    ]);
    $this->forge->addKey('SpcColID', true);
    $this->forge->createTable('specimencollection');

    $this->forge->addField([
      'SpcPrpID' => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
      'SpcStaID' => ['type' => 'INT', 'unsigned' => true]


value00: app/Database/Migrations/2026-01-01-000006_CreateSpecimens.php
value01:
,
      'ConDefID' => ['type' => 'INT', 'null' => true],
      'Parent' => ['type' => 'varchar', 'constraint' => 30, 'null' => true],
      'Qty' => ['type' => 'INT', 'null' => true],
      'Unit' => ['type' => 'varchar', 'constraint'=> 30, 'null' => true],
      'GenerateBy' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'SchDateTime' => ['type' => 'datetime', 'null' => true]


value00: app/Database/Seeds/DBSeeder.php
value01:
<?php

namespace App\Database\Seeds;

use CodeIgniter\Database\Seeder;

class DBSeeder extends Seeder
{
  public function run()
  {
    $this->call('OrganizationSeeder');
		$this->call('HostAppCodingSysSeeder');
    $this->call('CounterSeeder');
    $this->call('ContactSeeder');
    $this->call('LocationSeeder');
    $this->call('AreaGeoSeeder');
    $this->call('SpecimenSeeder');
    $this->call('TestSeeder');
    $this->call('PatientSeeder');
    $this->call('DummySeeder');
    $this->call('OrderSeeder');
  }
}


value00: app/Database/Migrations/2026-01-01-000003_CreateUsers.php
value01:
<?php

namespace App\Database\Migrations;

use CodeIgniter\Database\Migration;

class CreateSecurity extends Migration {
  public function up() {
    $this->forge->addField([
      'id'       => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
      'role_id'  => ['type' => 'TINYINT', 'null' => false],
      'username' => ['type' => 'VARCHAR', 'constraint' => 50, 'null' => true],
      'password' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
    ]);
    $this->forge->addKey('id', true);
    $this->forge->createTable('users');

    $this->forge->addField([
      'ContactID'       => [ 'type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true ]


value00: app/Database/Migrations/2026-01-01-000003_CreateUsers.php
value01:
    $this->forge->addKey('LoginAttemptId', true);
    $this->forge->addKey('ContactID');
    $this->forge->addKey(['IpAddress', 'AttemptAt']);
    $this->forge->addKey(['ContactID', 'AttemptAt']);
    $this->forge->createTable('loginattempts', true);
  }


value00: app/Database/Migrations/2026-01-01-000003_CreateUsers.php
value01:
    $this->forge->addKey(['ContactID', 'DeviceFingerprint'], false, true);
    $this->forge->createTable('userdevices', true);

    $this->forge->addField([
      'LoginAttemptId' => ['type' => 'BIGINT', 'unsigned' => true, 'auto_increment' => true],
      'ContactID'      => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true],
      'DeviceId'       => ['type' => 'CHAR', 'constraint' => 36, 'null' => true],
      'IpAddress'      => ['type' => 'VARCHAR', 'constraint' => 45],
      'AttemptAt'      => ['type' => 'DATETIME'],
      'IsSuccess'      => ['type' => 'TINYINT', 'constraint' => 1],
      'FailureReason'  => ['type' => 'VARCHAR', 'constraint' => 50, 'null' => true],
    ]);
    $this->forge->addKey('LoginAttemptId', true);
    $this->forge->addKey('ContactID');


value00: app/Database/Migrations/2026-01-01-000003_CreateUsers.php
value01:
[
      'ContactID'       => [ 'type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true ],
      'NameFirst'       => [ 'type' => 'VARCHAR', 'constraint' => 100, 'null' => true ],
      'NameLast'        => [ 'type' => 'VARCHAR', 'constraint' => 100, 'null' => true ],
      'Title'           => [ 'type' => 'VARCHAR', 'constraint' => 50, 'null' => true ],
      'Initial'         => [ 'type' => 'VARCHAR', 'constraint' => 10, 'null' => true ],
      'Birthdate'       => [ 'type' => 'DATE', 'null' => true ],
      'EmailAddress1'   => [ 'type' => 'VARCHAR', 'constraint' => 150, 'null' => true ],
      'EmailAddress2'   => [ 'type' => 'VARCHAR', 'constraint' => 150, 'null' => true ]


value00: app/Database/Migrations/2026-01-01-000003_CreateUsers.php
value01:
,
      'LockReason'      => [ 'type' => 'VARCHAR', 'constraint' => 150, 'null' => true ],
      'Role'            => [ 'type' => "ENUM('admin','staff','user')", 'default' => 'user', 'null' => false ],
      'CreateDate'      => ['type' => 'DATETIME', 'null' => true],
      'EndDate'         => [ 'type' => 'DATETIME', 'null' => true ],
    ]);
    $this->forge->addKey('ContactID', true);
    $this->forge->addUniqueKey('EmailAddress1');
    $this->forge->createTable('contact');

    $this->forge->addField([
      'ContactDetID' => [ 'type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true ],
      'ContactID' => [ 'type' => 'INT', 'constraint' => 11 ],
      'SiteID' => [ 'type' => 'INT', 'constraint' => 11, 'null' => true ]


value00: app/Database/Migrations/2026-01-01-000003_CreateUsers.php
value01:
  public function down() {
    $this->forge->dropTable('loginattempts');
    $this->forge->dropTable('userdevices');
    $this->forge->dropTable('contactdetail');
    $this->forge->dropTable('contact');
    $this->forge->dropTable('users');
  }
}


value00: app/Database/Migrations/2026-01-01-000003_CreateUsers.php
value01:
,
      'ContactStartDate' => [ 'type' => 'DATETIME', 'null' => true ],
      'ContactEndDate' => [ 'type' => 'DATETIME ', 'null' => true ],
    ]);
    $this->forge->addKey('ContactDetID', true);
    $this->forge->addUniqueKey(['SiteID','ContactID']);
    $this->forge->createTable('contactdetail');

    $this->forge->addField([
      'UserDeviceId'      => ['type' => 'CHAR', 'constraint' => 36],
      'ContactID'         => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true],
      'DeviceFingerprint' => ['type' => 'CHAR', 'constraint' => 64],
      'DeviceLabel'       => ['type' => 'VARCHAR', 'constraint' => 100, 'null' => true]


value00: app/Database/Migrations/2026-01-01-000003_CreateUsers.php
value01:
,
      'EmailAddress2'   => [ 'type' => 'VARCHAR', 'constraint' => 150, 'null' => true ],
      'Phone'           => [ 'type' => 'VARCHAR', 'constraint' => 50, 'null' => true ],
      'MobilePhone1'    => [ 'type' => 'VARCHAR', 'constraint' => 50, 'null' => true ],
      'MobilePhone2'    => [ 'type' => 'VARCHAR', 'constraint' => 50, 'null' => true ],
      'Specialty'       => [ 'type' => 'VARCHAR', 'constraint' => 100, 'null' => true ],
      'SubSpecialty'    => [ 'type' => 'VARCHAR', 'constraint' => 100, 'null' => true ],
      'Password'        => [ 'type' => 'VARCHAR', 'constraint' => 255, 'null' => true ],
      'LockedAt'        => [ 'type' => 'DATETIME', 'null' => true ],
      'LockReason'      => [ 'type' => 'VARCHAR', 'constraint' => 150, 'null' => true ]


value00: app/Database/Migrations/2026-01-01-000003_CreateUsers.php
value01:
,
      'ContactID' => [ 'type' => 'INT', 'constraint' => 11 ],
      'SiteID' => [ 'type' => 'INT', 'constraint' => 11, 'null' => true ],
      'ContactCode' => [ 'type' => 'varchar', 'constraint' => 11, 'null' => false ],
      'ContactEmail' => [ 'type' => 'VARCHAR', 'constraint' => 150, 'null' => true ],
      'OccupationID' => [ 'type' => 'VARCHAR', 'constraint' => 50, 'null' => true ],
      'JobTitle' => [ 'type' => 'VARCHAR', 'constraint' => 100, 'null' => true ],
      'Department' => [ 'type' => 'VARCHAR', 'constraint' => 100, 'null' => true ],
      'ContactStartDate' => [ 'type' => 'DATETIME', 'null' => true ],
      'ContactEndDate' => [ 'type' => 'DATETIME ', 'null' => true ],
    ]


value00: app/Database/Migrations/2026-01-01-000003_CreateUsers.php
value01:
,
      'DeviceLabel'       => ['type' => 'VARCHAR', 'constraint' => 100, 'null' => true],
      'Browser'           => ['type' => 'VARCHAR', 'constraint' => 50, 'null' => true],
      'BrowserVersion'    => ['type' => 'VARCHAR', 'constraint' => 20, 'null' => true],
      'Platform'          => ['type' => 'VARCHAR', 'constraint' => 50, 'null' => true],
      'Timezone'          => ['type' => 'VARCHAR', 'constraint' => 50],
      'Language'          => ['type' => 'VARCHAR', 'constraint' => 10],
      'IpAddress'         => ['type' => 'VARCHAR', 'constraint' => 45],
      'FirstLoginAt'      => ['type' => 'DATETIME'],
      'LastLoginAt'       => ['type' => 'DATETIME'],
      'IsActive'          => ['type' => 'TINYINT', 'constraint' => 1, 'default' => 1],
    ]


value00: app/Database/Migrations/2026-01-01-000003_CreateUsers.php
value01:
      'LastLoginAt'       => ['type' => 'DATETIME'],
      'IsActive'          => ['type' => 'TINYINT', 'constraint' => 1, 'default' => 1],
    ]);
    $this->forge->addKey('UserDeviceId', true);
    $this->forge->addKey(['ContactID', 'DeviceFingerprint'], false, true);
    $this->forge->createTable('userdevices', true);


value00: app/Database/Seeds/CounterSeeder.php
value01:
<?php

namespace App\Database\Seeds;

use CodeIgniter\Database\Seeder;

class CounterSeeder extends Seeder {
  public function run() {
    $now = date('Y-m-d H:i:s');
    // counter
    $data = [
      ['CounterID' => 1, 'CounterValue' => '1', 'CounterStart' => '1', 'CounterEnd' => '99999', 'CounterDesc' => 'Counter for Order#', 'CounterReset' => 'Y', 'CreateDate' => "$now" ],
      ['CounterID' => 2, 'CounterValue' => '1', 'CounterStart' => '1', 'CounterEnd' => '9999', 'CounterDesc' => 'Counter for Visit#', 'CounterReset' => 'M', 'CreateDate' => "$now" ]
    ];
    $this->db->table('counter')->insertBatch($data);
  }
}


value00: app/Controllers/Test/TestMapController.php
value01:
            $this->model->update($id, ['EndDate' => date('Y-m-d H:i:s')]);
            
            // Soft delete all related details
            $this->modelDetail->where('TestMapID', $id)
                ->where('EndDate', null)
                ->set('EndDate', date('Y-m-d H:i:s'))
                ->update();
            
            $this->db->transComplete();
            
            return $this->respond([ 'status'  => 'success', 'message' => "data deleted successfully", 'data' => $id ], 200);
        } catch (\Exception $e) {
            return $this->failServerError('Something went wrong: ' . $e->getMessage());
        }
    }


value00: app/Controllers/ResultController.php
value01:
    /**
     * Get single result
     * GET /api/result/{id}
     */
    public function show($id) {
        try {
            $result = $this->model->getWithRelations((int)$id);

            if (!$result) {
                return $this->respond([
                    'status' => 'failed',
                    'message' => 'Result not found',
                    'data' => []
                ], 404);
            }

            return $this->respond([
                'status' => 'success',
                'message' => 'Result retrieved successfully',
                'data' => $result
            ], 200);

        } catch (\Exception $e) {
            log_message('error', 'ResultController::show error: ' . $e->getMessage());


value00: app/Controllers/ResultController.php
value01:
            $result = $this->model->updateWithValidation((int)$id, $data);

            if (!$result['success']) {
                return $this->respond([
                    'status' => 'failed',
                    'message' => $result['message'],
                    'data' => []
                ], 400);
            }

            // Get updated result with relations
            $updatedResult = $this->model->getWithRelations((int)$id);


value00: app/Controllers/Test/TestMapController.php
value01:
    public function showByTestCode($testCode = null) {
        if (!$testCode) { return $this->failValidationErrors('TestCode is required.'); }
        
        $rows = $this->model->getMappingsByTestCode($testCode);
        if (empty($rows)) { return $this->respond([ 'status'  => 'success', 'message' => "no Data.", 'data' => [] ], 200); }

        $rows = ValueSet::transformLabels($rows, [
            'HostType' => 'entity_type',
            'ClientType' => 'entity_type',
        ]);

        return $this->respond([ 'status' => 'success', 'message'=> "Data fetched successfully", 'data' => $rows ], 200);
    }


value00: app/Controllers/Test/TestMapController.php
value01:
        foreach ($items as $index => $item) {
            $id = $item['TestMapID'] ?? null;
            if (!$id) {
                $results['failed'][] = ['index' => $index, 'error' => 'TestMapID required'];
                continue;
            }
            if (!$this->validateData($item, $this->rules)) {
                $results['failed'][] = ['index' => $index, 'errors' => $this->validator->getErrors()];
                continue;
            }
            try {
                $this->model->update($id, $item);
                $results['success'][] = ['index' => $index, 'TestMapID' => $id];
            } catch (\Exception $e) {
                $results['failed'][] = ['index' => $index, 'error' => $e->getMessage()];
            }
        }


value00: app/Controllers/Test/TestMapController.php
value01:
        $results = ['success' => [], 'failed' => []];
        $this->db->transStart();
        
        foreach ($items as $index => $item) {
            if (!$this->validateData($item, $this->rules)) {
                $results['failed'][] = ['index' => $index, 'errors' => $this->validator->getErrors()];
                continue;
            }
            try {
                $id = $this->model->insert($item);
                $results['success'][] = ['index' => $index, 'TestMapID' => $id];
            } catch (\Exception $e) {
                $results['failed'][] = ['index' => $index, 'error' => $e->getMessage()];
            }
        }
        
        $this->db->transComplete();


value00: app/Controllers/Test/TestMapController.php
value01:
    public function delete() {
        $input = $this->request->getJSON(true);
        $id = $input["TestMapID"] ?? null;
        if (!$id) { return $this->failValidationErrors('TestMapID is required.'); }
        
        try {
                $row = $this->model->where('TestMapID', $id)->where('EndDate', null)->first();
            if (empty($row)) { return $this->respond([ 'status'  => 'failed', 'message' => "Data not found or already deleted.", 'data' => null ], 404); }
            
            $this->db->transStart();
            
            // Soft delete the testmap
            $this->model->update($id, ['EndDate' => date('Y-m-d H:i:s')]);


value00: app/Controllers/Test/TestMapController.php
value01:
    public function index() {
        $rows = $this->model->getUniqueGroupings();
        if (empty($rows)) { return $this->respond([ 'status'  => 'success', 'message' => "no Data.", 'data' => [] ], 200); }

        $rows = ValueSet::transformLabels($rows, [
            'HostType' => 'entity_type',
            'ClientType' => 'entity_type',
        ]);

        return $this->respond([ 'status' => 'success', 'message'=> "Data fetched successfully", 'data' => $rows ], 200);
    }


value00: app/Controllers/Test/TestMapController.php
value01:
        $this->db->transComplete();
        return $this->respond([
            'status' => empty($results['failed']) ? 'success' : 'partial',
            'message' => 'Batch update completed',
            'data' => $results
        ], 200);
    }


value00: app/Controllers/Test/TestMapController.php
value01:
    public function show($id = null) {
        $row = $this->model->where('TestMapID',$id)->where('EndDate', null)->first();
        if (empty($row)) { return $this->respond([ 'status'  => 'success', 'message' => "no Data.", 'data' => null ], 200); }

        $row = ValueSet::transformLabels([$row], [
            'HostType' => 'entity_type',
            'ClientType' => 'entity_type',
        ])[0];

        // Include testmapdetail records
        $row['details'] = $this->modelDetail->getDetailsByTestMap($id);

        return $this->respond([ 'status' => 'success', 'message'=> "Data fetched successfully", 'data' => $row ], 200);
    }


value00: app/Controllers/Test/TestMapController.php
value01:
        $results = ['success' => [], 'failed' => []];
        $this->db->transStart();
        
        foreach ($ids as $id) {
            try {
            $row = $this->model->where('TestMapID', $id)->where('EndDate', null)->first();
                if (empty($row)) {
                    $results['failed'][] = ['TestMapID' => $id, 'error' => 'Not found or already deleted'];
                    continue;
                }
                $this->model->update($id, ['EndDate' => date('Y-m-d H:i:s')]);
                $results['success'][] = $id;
            } catch (\Exception $e) {
                $results['failed'][] = ['TestMapID' => $id, 'error' => $e->getMessage()];
            }
        }
        
        $this->db->transComplete();


value00: app/Controllers/ResultController.php
value01:
    /**
     * Update result with validation
     * PATCH /api/result/{id}
     */
    public function update($id) {
        try {
            $data = $this->request->getJSON(true);
            
            if (empty($data)) {
                return $this->respond([
                    'status' => 'failed',
                    'message' => 'No data provided',
                    'data' => []
                ], 400);
            }

            $result = $this->model->updateWithValidation((int)$id, $data);

            if (!$result['success'])


value00: app/Controllers/Test/TestMapController.php
value01:
    public function batchDelete() {
        $ids = $this->request->getJSON(true);
        if (!is_array($ids)) { return $this->failValidationErrors('Expected array of TestMapIDs'); }
        
        $results = ['success' => [], 'failed' => []];
        $this->db->transStart();


value00: app/Controllers/Test/TestMapController.php
value01:
    public function create() {
        $input = $this->request->getJSON(true);
        if (!$this->validateData($input, $this->rules)) { return $this->failValidationErrors($this->validator->getErrors()); }
        try {
            $id = $this->model->insert($input);
            return $this->respondCreated([ 'status'  => 'success', 'message' => "data created successfully", 'data' => $id ]);
        } catch (\Exception $e) {
            return $this->failServerError('Something went wrong: ' . $e->getMessage());
        }
    }


value00: app/Controllers/ResultController.php
value01:
            // Get updated result with relations
            $updatedResult = $this->model->getWithRelations((int)$id);

            return $this->respond([
                'status' => 'success',
                'message' => $result['message'],
                'data' => [
                    'result' => $updatedResult,
                    'flag' => $result['flag']
                ]
            ], 200);

        } catch (\Exception $e) {
            log_message('error', 'ResultController::update error: ' . $e->getMessage());
            return $this->respond([
                'status' => 'failed',
                'message' => 'Failed to update result',
                'data' => []
            ], 500);
        }
    }


value00: app/Controllers/Test/TestMapController.php
value01:
        if (!$this->validateData($input, $this->rules)) { return $this->failValidationErrors( $this->validator->getErrors() ); }
        try {
            $this->model->update($id,$input);
            return $this->respondCreated([ 'status'  => 'success', 'message' => "data updated successfully", 'data' => $id ]);
        } catch (\Exception $e) {
            return $this->failServerError('Something went wrong: ' . $e->getMessage());
        }
    }


value00: app/Controllers/Test/TestMapController.php
value01:
<?php
namespace App\Controllers\Test;

use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Libraries\ValueSet;
use App\Models\Test\TestMapModel;
use App\Models\Test\TestMapDetailModel;


value00: app/Controllers/ResultController.php
value01:
            $orderID = $this->request->getGet('order_id');
            $patientID = $this->request->getGet('patient_id');

            if ($orderID) {
                $results = $this->model->getByOrder((int)$orderID);
            } elseif ($patientID) {
                $results = $this->model->getByPatient((int)$patientID);
            } else {
                // Get all results with pagination
                $page = (int)($this->request->getGet('page') ?? 1);
                $perPage = (int)($this->request->getGet('per_page') ?? 20);
                
                $results = $this->model
                    ->where('DelDate', null)
                    ->orderBy('ResultID', 'DESC')
                    ->paginate($perPage, 'default', $page);
            }


value00: app/Controllers/Test/TestMapController.php
value01:
        $this->db->transComplete();
        return $this->respond([
            'status' => empty($results['failed']) ? 'success' : 'partial',
            'message' => 'Batch create completed',
            'data' => $results
        ], 200);
    }


value00: app/Controllers/ResultController.php
value01:
            return $this->respond([
                'status' => 'success',
                'message' => 'Results retrieved successfully',
                'data' => $results
            ], 200);

        } catch (\Exception $e) {
            log_message('error', 'ResultController::index error: ' . $e->getMessage());
            return $this->respond([
                'status' => 'failed',
                'message' => 'Failed to retrieve results',
                'data' => []
            ], 500);
        }
    }


value00: app/Controllers/Test/TestMapController.php
value01:
    public function batchCreate() {
        $items = $this->request->getJSON(true);
        if (!is_array($items)) { return $this->failValidationErrors('Expected array of items'); }
        
        $results = ['success' => [], 'failed' => []];
        $this->db->transStart();


value00: app/Controllers/ResultController.php
value01:
<?php

namespace App\Controllers;

use App\Traits\ResponseTrait;
use CodeIgniter\Controller;
use App\Models\PatResultModel;

class ResultController extends Controller {
    use ResponseTrait;

    protected $model;

    public function __construct() {
        $this->model = new PatResultModel();
    }

    /**
     * List results with optional filters
     * GET /api/result
     */
    public function index() {
        try {
            $orderID = $this->request->getGet('order_id');
            $patientID = $this->request->getGet('patient_id');


value00: app/Controllers/Test/TestMapController.php
value01:
    public function batchUpdate() {
        $items = $this->request->getJSON(true);
        if (!is_array($items)) { return $this->failValidationErrors('Expected array of items'); }
        
        $results = ['success' => [], 'failed' => []];
        $this->db->transStart();


value00: app/Controllers/ResultController.php
value01:
            return $this->respond([
                'status' => 'success',
                'message' => 'Result deleted successfully',
                'data' => []
            ], 200);

        } catch (\Exception $e) {
            log_message('error', 'ResultController::delete error: ' . $e->getMessage());
            return $this->respond([
                'status' => 'failed',
                'message' => 'Failed to delete result',
                'data' => []
            ], 500);
        }
    }
}


value00: app/Controllers/Test/TestMapController.php
value01:
        $this->db->transComplete();
        return $this->respond([
            'status' => empty($results['failed']) ? 'success' : 'partial',
            'message' => 'Batch delete completed',
            'data' => $results
        ], 200);
    }

}


value00: app/Controllers/ResultController.php
value01:
    /**
     * Soft delete result
     * DELETE /api/result/{id}
     */
    public function delete($id) {
        try {
            $result = $this->model->find((int)$id);

            if (!$result) {
                return $this->respond([
                    'status' => 'failed',
                    'message' => 'Result not found',
                    'data' => []
                ], 404);
            }

            $deleted = $this->model->softDelete((int)$id);

            if (!$deleted) {
                return $this->respond([
                    'status' => 'failed',
                    'message' => 'Failed to delete result',
                    'data' => []
                ], 500);
            }


value00: app/Controllers/ResultController.php
value01:
catch (\Exception $e) {
            log_message('error', 'ResultController::show error: ' . $e->getMessage());
            return $this->respond([
                'status' => 'failed',
                'message' => 'Failed to retrieve result',
                'data' => []
            ], 500);
        }
    }


value00: app/Controllers/Test/TestMapController.php
value01:
use App\Controllers\BaseController;
use App\Libraries\ValueSet;
use App\Models\Test\TestMapModel;
use App\Models\Test\TestMapDetailModel;

class TestMapController extends BaseController {
    use ResponseTrait;

    protected $db;
    protected $rules;
    protected $model;
    protected $modelDetail;

    public function __construct() {
        $this->db = \Config\Database::connect();
        $this->model = new TestMapModel;
        $this->modelDetail = new TestMapDetailModel;
        $this->rules = [
            'TestCode' => 'required',
            'HostID' => 'required|integer',
            'ClientID' => 'required|integer',
        ];
    }


value00: app/Controllers/Test/TestMapController.php
value01:
    public function update($TestMapID = null) {
        $input = $this->request->getJSON(true);
        if (!$TestMapID || !ctype_digit((string) $TestMapID)) { return $this->failValidationErrors('TestMapID is required.'); }
        $id = (int) $TestMapID;
        if (isset($input['TestMapID']) && (string) $input['TestMapID'] !== (string) $id) {
            return $this->failValidationErrors('TestMapID in URL does not match body.');
        }
        $input['TestMapID'] = $id;
        if (!$this->validateData($input, $this->rules)) { return $this->failValidationErrors( $this->validator->getErrors() ); }


value00: app/Database/Seeds/HostAppCodingSysSeeder.php
value01:
,
        'Description' => 'Vaccine codes for immunization records',
        'CreateDate' => $now,
        'EndDate' => null
      ],
      [
        'CodingSysAbb' => 'ICD9',
        'FullText' => 'International Classification of Diseases 9th Revision',
        'Description' => 'Legacy medical diagnosis coding system',
        'CreateDate' => $now,
        'EndDate' => date('Y-m-d H:i:s', strtotime('-2 years'))
      ],
      [
        'CodingSysAbb' => 'ICD9CM',
        'FullText' => 'ICD-9-CM',
        'Description' => 'Legacy procedure coding system',
        'CreateDate' => $now,
        'EndDate' => date('Y-m-d H:i:s', strtotime('-2 years'))
      ],
    ];
    $this->db->table('codingsys')->insertBatch($codingSysData);
  }
}


value00: app/Database/Seeds/HostAppCodingSysSeeder.php
value01:
<?php

namespace App\Database\Seeds;

use CodeIgniter\Database\Seeder;

class HostAppCodingSysSeeder extends Seeder
{
  public function run()
  {
    $now = date('Y-m-d H:i:s');

    // Clear existing data first (avoid foreign key constraint issues by ordering)
    $this->db->table('hostcompara')->emptyTable();
    $this->db->table('hostapp')->emptyTable();
    $this->db->table('codingsys')->emptyTable();

    // HostApp - Host Applications
    $hostAppData = [
      [
        'HostAppID' => 'LIS01',
        'HostAppName' => 'Laboratory Information System Main',
        'SiteID' => 1,
        'CreateDate' => $now,
        'EndDate' => null
      ],
      [
        'HostAppID' => 'LIS02',
        'HostAppName' => 'Laboratory Information System Backup',


value00: app/Database/Seeds/HostAppCodingSysSeeder.php
value01:
    $this->db->table('hostapp')->insertBatch($hostAppData);

    // HostComPara - Host Communication Parameters
    $hostComParaData = [
      [
        'HostAppID' => 'LIS01',
        'HostIP' => '192.168.1.10',
        'HostPort' => '8080',
        'HostPwd' => 'lis_main_pass_2024',
        'CreateDate' => $now,
        'EndDate' => null
      ],
      [
        'HostAppID' => 'LIS02',
        'HostIP' => '192.168.1.11',
        'HostPort' => '8081',
        'HostPwd' => 'lis_backup_pass_2024',
        'CreateDate' => $now,
        'EndDate' => null
      ],
      [
        'HostAppID' => 'EMR01',
        'HostIP' => '192.168.1.20',
        'HostPort' => '443',
        'HostPwd' => 'emr_secure_2024'


value00: app/Database/Seeds/HostAppCodingSysSeeder.php
value01:
    $this->db->table('hostcompara')->insertBatch($hostComParaData);

    // CodingSys - Coding Systems
    $codingSysData = [
      [
        'CodingSysAbb' => 'ICD10',
        'FullText' => 'International Classification of Diseases 10th Revision',
        'Description' => 'Medical diagnosis coding system for diseases and health conditions',
        'CreateDate' => $now,
        'EndDate' => null
      ],
      [
        'CodingSysAbb' => 'ICD10PCS',
        'FullText' => 'ICD-10 Procedure Coding System',
        'Description' => 'Classification system for inpatient hospital procedures',
        'CreateDate' => $now,
        'EndDate' => null
      ],
      [
        'CodingSysAbb' => 'LOINC',
        'FullText' => 'Logical Observation Identifiers Names and Codes'


value00: app/Database/Seeds/HostAppCodingSysSeeder.php
value01:
,
      [
        'CodingSysAbb' => 'HCPCS',
        'FullText' => 'Healthcare Common Procedure Coding System',
        'Description' => 'Medical code set for equipment, supplies, and services',
        'CreateDate' => $now,
        'EndDate' => null
      ],
      [
        'CodingSysAbb' => 'RXNORM',
        'FullText' => 'RxNorm',
        'Description' => 'Normalized naming system for medications',
        'CreateDate' => $now,
        'EndDate' => null
      ],


value00: app/Database/Seeds/HostAppCodingSysSeeder.php
value01:
,
      [
        'CodingSysAbb' => 'NDC',
        'FullText' => 'National Drug Code',
        'Description' => 'Unique identifier for human drugs in the United States',
        'CreateDate' => $now,
        'EndDate' => null
      ],
      [
        'CodingSysAbb' => 'UCUM',
        'FullText' => 'Unified Code for Units of Measure',
        'Description' => 'Standard for units of measurement in clinical and scientific contexts',
        'CreateDate' => $now,
        'EndDate' => null
      ],
      [
        'CodingSysAbb' => 'CVX',
        'FullText' => 'Vaccines Administered',
        'Description' => 'Vaccine codes for immunization records',
        'CreateDate' => $now,
        'EndDate' => null
      ],


value00: app/Database/Migrations/2026-01-01-000021_CreateOrders.php
value01:
,
      'SiteID'  => ['type' => 'VARCHAR', 'constraint'=> 15, 'null' => false],
      'PVADTID'  => ['type' => 'INT', 'null' => false],
      'ReqApp'  => ['type' => 'VARCHAR', 'constraint'=> 15, 'null' => true],
      'Priority' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'TrnDate' => ['type' => 'Datetime', 'null' => true],
      'EffDate' => ['type' => 'Datetime', 'null' => true],
      'CreateDate' => ['type' => 'Datetime', 'null' => true],
      'EndDate' => ['type' => 'Datetime', 'null' => true],
      'ArchiveDate' => ['type' => 'Datetime', 'null' => true],
      'DelDate' => ['type' => 'Datetime', 'null' => true]
    ]);
    $this->forge->addKey('InternalOID', true);
    $this->forge->addUniqueKey('OrderID');


value00: app/Database/Seeds/HostAppCodingSysSeeder.php
value01:
      [
        'HostAppID' => 'EMR01',
        'HostIP' => '192.168.1.20',
        'HostPort' => '443',
        'HostPwd' => 'emr_secure_2024',
        'CreateDate' => $now,
        'EndDate' => null
      ],
      [
        'HostAppID' => 'PACS01',
        'HostIP' => '192.168.1.30',
        'HostPort' => '8042',
        'HostPwd' => 'pacs_dicom_2024',
        'CreateDate' => $now,
        'EndDate' => null
      ],
      [
        'HostAppID' => 'BILL01',
        'HostIP' => '192.168.1.40',
        'HostPort' => '8443',
        'HostPwd' => 'bill_payment_2024',
        'CreateDate' => $now,
        'EndDate' => null
      ]


value00: app/Database/Seeds/HostAppCodingSysSeeder.php
value01:
,
      [
        'CodingSysAbb' => 'LOINC',
        'FullText' => 'Logical Observation Identifiers Names and Codes',
        'Description' => 'Standard for identifying medical laboratory observations',
        'CreateDate' => $now,
        'EndDate' => null
      ],
      [
        'CodingSysAbb' => 'SNOMED',
        'FullText' => 'SNOMED CT',
        'Description' => 'Systematized Nomenclature of Medicine - Clinical Terms',
        'CreateDate' => $now,
        'EndDate' => null
      ],
      [
        'CodingSysAbb' => 'CPT',
        'FullText' => 'Current Procedural Terminology',
        'Description' => 'Medical code set for medical procedures and services',
        'CreateDate' => $now,
        'EndDate' => null
      ],


value00: app/Database/Migrations/2026-01-01-000021_CreateOrders.php
value01:
    $this->forge->addKey('OrderComID', true);
    $this->forge->createTable('ordercom');

    $this->forge->addField([
      'OrderAttID'  => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
      'InternalOID' => ['type' => 'INT', 'null' => false],
      'Address'  => ['type' => 'varchar', 'constraint'=>255,  'null' => true],
      'CreateDate' => ['type' => 'Datetime', 'null' => true],
      'EndDate' => ['type' => 'Datetime', 'null' => true],
      'ArchiveDate' => ['type' => 'Datetime', 'null' => true],
      'DelDate' => ['type' => 'Datetime', 'null' => true]
    ]);
    $this->forge->addKey('OrderAttID', true);
    $this->forge->createTable('orderatt');


value00: app/Database/Seeds/HostAppCodingSysSeeder.php
value01:
,
      [
        'HostAppID' => 'LIS02',
        'HostAppName' => 'Laboratory Information System Backup',
        'SiteID' => 1,
        'CreateDate' => $now,
        'EndDate' => null
      ],
      [
        'HostAppID' => 'EMR01',
        'HostAppName' => 'Electronic Medical Record System',
        'SiteID' => 1,
        'CreateDate' => $now,
        'EndDate' => null
      ],
      [
        'HostAppID' => 'PACS01',
        'HostAppName' => 'Picture Archiving System',
        'SiteID' => 1,
        'CreateDate' => $now,
        'EndDate' => null
      ],
      [
        'HostAppID' => 'BILL01',
        'HostAppName' => 'Billing System',
        'SiteID' => 1,
        'CreateDate' => $now,
        'EndDate' => null
      ],


value00: app/Database/Migrations/2026-01-01-000021_CreateOrders.php
value01:
    $this->forge->addKey('OrderAttID', true);
    $this->forge->createTable('orderatt');

    $this->forge->addField([
      'OrderStatID'  => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
      'InternalOID' => ['type' => 'INT', 'null' => false],
      'OrderStatus'  => ['type' => 'VARCHAR', 'constraint' => 10,  'null' => false],
      'CreateDate' => ['type' => 'Datetime', 'null' => true],
      'EndDate' => ['type' => 'Datetime', 'null' => true],
      'ArchiveDate' => ['type' => 'Datetime', 'null' => true],
      'DelDate' => ['type' => 'Datetime', 'null' => true]
    ]);
    $this->forge->addKey('OrderStatID', true);
    $this->forge->createTable('orderstatus');
  }


value00: app/Database/Migrations/2026-01-01-000021_CreateOrders.php
value01:
    $this->forge->addKey('InternalOID', true);
    $this->forge->addUniqueKey('OrderID');
    $this->forge->createTable('ordertest');

    $this->forge->addField([
      'OrderComID'  => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
      'InternalOID' => ['type' => 'INT', 'null' => false],
      'Comment'  => ['type' => 'text', 'null' => true],
      'CreateDate' => ['type' => 'Datetime', 'null' => true],
      'EndDate' => ['type' => 'Datetime', 'null' => true],
      'ArchiveDate' => ['type' => 'Datetime', 'null' => true],
      'DelDate' => ['type' => 'Datetime', 'null' => true]
    ]);
    $this->forge->addKey('OrderComID', true);
    $this->forge->createTable('ordercom');


value00: app/Database/Migrations/2026-01-01-000021_CreateOrders.php
value01:
<?php

namespace App\Database\Migrations;

use CodeIgniter\Database\Migration;

class CreateOrders extends Migration {
  public function up() {
    $this->forge->addField([
      'InternalOID' => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
      'OrderID'  => ['type' => 'VARCHAR', 'constraint'=> 22, 'null' => false],
      'PlacerID'  => ['type' => 'VARCHAR', 'constraint'=> 22, 'null' => true],
      'InternalPID'  => ['type' => 'INT', 'null' => false],
      'SiteID'  => ['type' => 'VARCHAR', 'constraint'=> 15, 'null' => false],
      'PVADTID'  => ['type' => 'INT', 'null' => false]


value00: app/Database/Migrations/2026-01-01-000021_CreateOrders.php
value01:
  public function down() {
    $this->forge->dropTable('orderstatus');
    $this->forge->dropTable('orderatt');
    $this->forge->dropTable('ordercom');
    $this->forge->dropTable('ordertest');
  }
}


value00: app/Database/Seeds/HostAppCodingSysSeeder.php
value01:
,
        'HostAppName' => 'Billing System',
        'SiteID' => 1,
        'CreateDate' => $now,
        'EndDate' => null
      ],
      [
        'HostAppID' => 'INS01',
        'HostAppName' => 'Insurance System Integration',
        'SiteID' => 1,
        'CreateDate' => $now,
        'EndDate' => null
      ],
      [
        'HostAppID' => 'OLD01',
        'HostAppName' => 'Legacy Laboratory System',
        'SiteID' => 1,
        'CreateDate' => $now,
        'EndDate' => date('Y-m-d H:i:s', strtotime('-1 year'))
      ],
    ];
    $this->db->table('hostapp')->insertBatch($hostAppData);

    // HostComPara - Host Communication Parameters


value00: app/Database/Seeds/LocationSeeder.php
value01:
<?php

namespace App\Database\Seeds;

use CodeIgniter\Database\Seeder;

class LocationSeeder extends Seeder
{
  public function run()
  {
    $now = date('Y-m-d H:i:s');

    // location
    $data = [
      ['LocationID' => 1, 'LocCode' => 'QLOC', 'LocFull' => 'Dummy Location', 'LocType' => 'ROOM', 'Description' => 'Location made for dummy testing', 'CreateDate' => "$now" ],
      ['LocationID' => 2, 'LocCode' => 'DEFLOC', 'LocFull' => 'Default Location', 'LocType' => 'ROOM', 'Description' => 'Default location', 'CreateDate' => "$now" ]
    ];
    $this->db->table('location')->insertBatch($data);


value00: app/Database/Seeds/HostAppCodingSysSeeder.php
value01:
,
        'HostPort' => '8443',
        'HostPwd' => 'bill_payment_2024',
        'CreateDate' => $now,
        'EndDate' => null
      ],
      [
        'HostAppID' => 'INS01',
        'HostIP' => '192.168.1.50',
        'HostPort' => '443',
        'HostPwd' => 'ins_claim_2024',
        'CreateDate' => $now,
        'EndDate' => null
      ],
      [
        'HostAppID' => 'OLD01',
        'HostIP' => '192.168.1.99',
        'HostPort' => '8080',
        'HostPwd' => 'old_legacy_pass',
        'CreateDate' => $now,
        'EndDate' => date('Y-m-d H:i:s', strtotime('-1 year'))
      ],
    ];
    $this->db->table('hostcompara')->insertBatch($hostComParaData);

    // CodingSys - Coding Systems


value00: app/Database/Seeds/OrderSeeder.php
value01:
    // ========================================
    $orderID1 = '001' . date('ymd', strtotime('-2 days')) . '00001';
    $internalOID1 = $this->createOrder([
      'OrderID' => $orderID1,
      'PlacerID' => 'PLC001',
      'InternalPID' => 1,
      'SiteID' => '1',
      'PVADTID' => 2,
      'ReqApp' => 'HIS',
      'Priority' => 'R',
      'TrnDate' => $twoDaysAgo,
      'EffDate' => $twoDaysAgo,
      'CreateDate' => $twoDaysAgo
    ]);

    echo "Created Order 1: {$orderID1} (InternalOID: {$internalOID1})\n";

    // Create specimen for CBC (EDTA tube - ConDefID = 9)
    $specimenID1 = $orderID1 . '-S01';


value00: app/Database/Migrations/2026-01-01-000012_CreatePatientVisits.php
value01:
      'AdmDoc' => ['type' => 'INT', 'constraint' => 11, 'null' => true],
      'CnsDoc' => ['type' => 'INT', 'constraint' => 11, 'null' => true],
      'CreateDate' => ['type' => 'DATETIME', 'null' => true],
      'EndDate' => ['type' => 'DATETIME', 'null' => true],
      'ArchivedDate'=> ['type' => 'DATETIME', 'null' => true],
      'DelDate' => ['type' => 'DATETIME', 'null' => true],
    ]);
    $this->forge->addKey('PVADTID', true);
    $this->forge->createTable('patvisitadt');

  }

  public function down() {
    $this->forge->dropTable('patvisitadt');
    $this->forge->dropTable('patdiag');
    $this->forge->dropTable('patvisit');
  }
}


value00: app/Database/Migrations/2026-01-01-000022_CreateResults.php
value01:
<?php

namespace App\Database\Migrations;

use CodeIgniter\Database\Migration;

class CreateResults extends Migration {
  public function up() {
    $this->forge->addField([
      'ResultID' => ['type' => 'INT', 'auto_increment' => true],
      'SiteID' => ['type' => 'INT', 'null' => true],
      'OrderID' => ['type' => 'INT', 'null' => true],
      'InternalSID' => ['type' => 'INT', 'null' => true],
      'SID' => ['type' => 'varchar', 'constraint' => 30],
      'SampleID' => ['type' => 'varchar', 'constraint' => 30],
      'TestSiteID' => ['type' => 'INT', 'null' => true],
      'TestSiteCode' => ['type' => 'CHAR', 'constraint' => 6, 'null' => true],
      'AspCnt' => ['type' => 'INT', 'default' => 1, 'null' => true]


value00: app/Database/Migrations/2026-01-01-000012_CreatePatientVisits.php
value01:
{
    $this->forge->addField([
      'SiteID' => ['type' => 'INT', 'constraint' => 11, 'null' => true],
      'InternalPVID'=> ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
      'PVID' => ['type' => 'VARCHAR', 'constraint' => 20, 'null' => true],
      'InternalPID' => ['type' => 'INT', 'constraint' => 11, 'null' => true],
      'EpisodeID' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'LastVisitADT' => ['type' => 'VARCHAR', 'constraint' => 5, 'null' => true],
      'CreateDate' => ['type' => 'DATETIME', 'null' => true],
      'EndDate' => ['type' => 'DATETIME', 'null' => true],
      'ArchivedDate'=> ['type' => 'DATETIME', 'null' => true],
      'DelDate' => ['type' => 'DATETIME', 'null' => true],
    ]);


value00: app/Database/Seeds/LocationSeeder.php
value01:
    $this->db->table('location')->insertBatch($data);
    $data = [
      ['LocationID' => 1, 'Street1' => 'Jalan Nginden', 'Street2' => 'Intan Raya', 'City' => 'Surabaya', 'Province' => 'East Java', 'PostCode' => '60222', 'CreateDate' => "$now"],
      ['LocationID' => 2, 'Street1' => 'Jalan ', 'Street2' => 'Jalan jalan', 'City' => 'Depok', 'Province' => 'DKI Jakarta', 'PostCode' => '10123', 'CreateDate' => "$now"]
    ];
    $this->db->table('locationaddress')->insertBatch($data);
  }
}


value00: app/Database/Migrations/2026-01-01-000012_CreatePatientVisits.php
value01:
    $this->forge->addKey('InternalPVID', true);
    $this->forge->addUniqueKey('PVID');
    $this->forge->createTable('patvisit');

    $this->forge->addField([
      'InternalPVID' => ['type' => 'INT', 'constraint' => 11],
      'InternalPID'  => ['type' => 'INT', 'constraint' => 11, 'null' => true],
      'DiagCode'   => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'Diagnosis'    => ['type' => 'TEXT', 'null' => true],
      'CreateDate' => ['type' => 'DATETIME', 'null' => true],
      'EndDate'    => ['type' => 'DATETIME', 'null' => true],
      'ArchivedDate' => ['type' => 'DATETIME', 'null' => true],
      'DelDate'    => ['type' => 'DATETIME', 'null' => true],
    ]);
    $this->forge->addKey('InternalPVID', true);


value00: app/Database/Seeds/OrderSeeder.php
value01:
    // Create pending results for Chemistry
    $chemTests = ['GLU', 'CREA', 'UREA', 'SGOT', 'SGPT'];
    foreach ($chemTests as $testCode) {
      if (isset($testIDs[$testCode])) {
        $this->createPatRes([
          'OrderID' => $internalOID5,
          'InternalSID' => $internalSID5b,
          'SID' => $specimenID5b,
          'TestSiteID' => $testIDs[$testCode],
          'TestSiteCode' => $testCode,
          'Result' => null,
          'SampleType' => 'Serum',
          'ResultDateTime' => $now,
          'CreateDate' => $now
        ]);
      }
    }

    echo "  Created " . (count($cbcTests) + count($chemTests)) . " pending results\n";

    // Create order status
    $this->createOrderStatus($internalOID5, 'ORDERED', $now);


value00: app/Database/Migrations/2026-01-01-000022_CreateResults.php
value01:
,
      'AspCnt' => ['type' => 'INT', 'default' => 1, 'null' => true],
      'Result' => ['type' => 'varchar', 'constraint' => 255, 'null' => true],
      'SampleType' => ['type' => 'varchar', 'constraint' => 50, 'null' => true],
      'ResultDateTime' => ['type' => 'DATETIME'],
      'WorkstationID' => ['type' => 'INT', 'null' => true],
      'EquipmentID' => ['type' => 'INT', 'null' => true],
      'RefNumID' => ['type' => 'INT', 'null' => true],
      'RefTxtID' => ['type' => 'INT', 'null' => true],
      'CreateDate' => ['type' => 'DATETIME', 'null' => true],
      'EndDate' => ['type' => 'DATETIME', 'null' => true],
      'ArchiveDate' => ['type' => 'DATETIME', 'null' => true],
      'DelDate' => ['type' => 'DATETIME', 'null' => true],
    ]


value00: app/Database/Seeds/OrderSeeder.php
value01:
    foreach ($lipidTests as $test) {
      if (isset($testIDs[$test['code']])) {
        $this->createPatRes([
          'OrderID' => $internalOID2,
          'InternalSID' => $internalSID2,
          'SID' => $specimenID2,
          'TestSiteID' => $testIDs[$test['code']],
          'TestSiteCode' => $test['code'],
          'Result' => $test['result'],
          'SampleType' => 'Serum',
          'ResultDateTime' => date('Y-m-d H:i:s', strtotime('-1 day +6 hours')),
          'CreateDate' => date('Y-m-d H:i:s', strtotime('-1 day +6 hours'))
        ]);
      }
    }

    // Create Liver Function results
    $liverTests = [
      ['code' => 'SGOT', 'result' => '28'],
      ['code' => 'SGPT', 'result' => '32'],
    ];


value00: app/Database/Seeds/OrderSeeder.php
value01:
    // Create specimen for CBC (EDTA tube - ConDefID = 9)
    $specimenID1 = $orderID1 . '-S01';
    $internalSID1 = $this->createSpecimen([
      'SID' => $specimenID1,
      'SiteID' => '1',
      'OrderID' => $internalOID1,
      'ConDefID' => 9,
      'Qty' => 1,
      'Unit' => 'tube',
      'GenerateBy' => 'ORDER',
      'CreateDate' => $twoDaysAgo
    ]);

    // Create specimen status progression
    $this->createSpecimenStatus($specimenID1, $internalOID1, 'PENDING', $twoDaysAgo);
    $this->createSpecimenStatus($specimenID1, $internalOID1, 'COLLECTED', date('Y-m-d H:i:s', strtotime('-2 days +30 minutes')));
    $this->createSpecimenStatus($specimenID1, $internalOID1, 'RECEIVED', date('Y-m-d H:i:s', strtotime('-2 days +1 hour')));


value00: app/Database/Migrations/2026-01-01-000022_CreateResults.php
value01:
      'TestSiteCode' => ['type' => 'CHAR', 'constraint' => 6, 'null' => true],
      'AspCnt' => ['type' => 'INT', 'default' => 1, 'null' => true],
      'Result' => ['type' => 'varchar', 'constraint' => 255, 'null' => true]


value00: app/Database/Seeds/OrderSeeder.php
value01:
    // Create order status
    $this->createOrderStatus($internalOID4, 'ORDERED', $now);

    // ========================================
    // ORDER 5: Patient 3 - Multiple tubes (CBC + Chemistry)
    // ========================================
    $orderID5 = '001' . date('ymd') . '00005';
    $internalOID5 = $this->createOrder([
      'OrderID' => $orderID5,
      'PlacerID' => 'PLC005',
      'InternalPID' => 3,
      'SiteID' => '1',
      'PVADTID' => 4,
      'ReqApp' => 'HIS',
      'Priority' => 'R',
      'TrnDate' => $now,
      'EffDate' => $now,
      'CreateDate' => $now
    ]);

    echo "\nCreated Order 5: {$orderID5} (InternalOID: {$internalOID5}) [Multi-container]\n";


value00: app/Database/Seeds/OrderSeeder.php
value01:
    $this->createSpecimenStatus($specimenID3, $internalOID3, 'PROCESSING', date('Y-m-d H:i:s', strtotime('-1 day +45 minutes')));
    $this->createSpecimenStatus($specimenID3, $internalOID3, 'COMPLETED', date('Y-m-d H:i:s', strtotime('-1 day +2 hours')));

    echo "  Created Specimen: {$specimenID3} (SST)\n";

    // Create Renal Function results
    $renalTests = [
      ['code' => 'CREA', 'result' => '1.1'],
      ['code' => 'UREA', 'result' => '18'],
    ];


value00: app/Database/Seeds/OrderSeeder.php
value01:
    $this->createSpecimenStatus($specimenID1, $internalOID1, 'PROCESSING', date('Y-m-d H:i:s', strtotime('-2 days +2 hours')));
    $this->createSpecimenStatus($specimenID1, $internalOID1, 'COMPLETED', date('Y-m-d H:i:s', strtotime('-1 day +8 hours')));

    echo "  Created Specimen: {$specimenID1} (EDTA)\n";

    // Create CBC test results
    $cbcTests = [
      ['code' => 'HB', 'result' => '14.2'],
      ['code' => 'HCT', 'result' => '42.5'],
      ['code' => 'RBC', 'result' => '4.85'],
      ['code' => 'WBC', 'result' => '7.2'],
      ['code' => 'PLT', 'result' => '285'],
      ['code' => 'MCV', 'result' => '87.6'],
      ['code' => 'MCH', 'result' => '29.3'],
      ['code' => 'MCHC', 'result' => '33.4'],
    ];


value00: app/Database/Migrations/2026-01-01-000022_CreateResults.php
value01:
    $this->forge->addPrimaryKey('ResFlagID');
    $this->forge->createTable('patresflag');

    $this->forge->addField([
      'ResStatusID' => ['type' => 'INT', 'auto_increment' => true],
      'ResultID' => ['type' => 'INT', 'null' => false],
      'SID' => ['type' => 'varchar', 'constraint' => 30],
      'TestAct' => ['type' => 'varchar', 'constraint' => 255],
      'ActRes' => ['type' => 'INT', 'null' => true],
      'TestStatus' => ['type' => 'INT', 'null' => true],
      'CurrSiteID' => ['type' => 'INT', 'null' => true],
      'CurrLocID' => ['type' => 'INT', 'null' => true],
      'Origin' => ['type' => 'varchar', 'constraint' => 255, 'null' => true]


value00: app/Database/Seeds/OrderSeeder.php
value01:
    // Create specimen for Renal/Glucose (SST tube)
    $specimenID3 = $orderID3 . '-S01';
    $internalSID3 = $this->createSpecimen([
      'SID' => $specimenID3,
      'SiteID' => '1',
      'OrderID' => $internalOID3,
      'ConDefID' => 1,
      'Qty' => 1,
      'Unit' => 'tube',
      'GenerateBy' => 'ORDER',
      'CreateDate' => $yesterday
    ]);

    $this->createSpecimenStatus($specimenID3, $internalOID3, 'PENDING', $yesterday);
    $this->createSpecimenStatus($specimenID3, $internalOID3, 'COLLECTED', date('Y-m-d H:i:s', strtotime('-1 day +20 minutes')));
    $this->createSpecimenStatus($specimenID3, $internalOID3, 'RECEIVED', date('Y-m-d H:i:s', strtotime('-1 day +30 minutes')));


value00: app/Database/Seeds/OrderSeeder.php
value01:
    // ORDER 4: Patient 1 - Urinalysis (PENDING)
    // ========================================
    $orderID4 = '001' . date('ymd') . '00004';
    $internalOID4 = $this->createOrder([
      'OrderID' => $orderID4,
      'PlacerID' => 'PLC004',
      'InternalPID' => 1,
      'SiteID' => '1',
      'PVADTID' => 2,
      'ReqApp' => 'HIS',
      'Priority' => 'R',
      'TrnDate' => $now,
      'EffDate' => $now,
      'CreateDate' => $now
    ]);

    echo "\nCreated Order 4: {$orderID4} (InternalOID: {$internalOID4})\n";

    // Create urine specimen (Pot Urin - ConDefID = 12)
    $specimenID4 = $orderID4 . '-S01';


value00: app/Database/Migrations/2026-01-01-000022_CreateResults.php
value01:
      'ArchiveDate' => ['type' => 'DATETIME', 'null' => true],
      'DelDate' => ['type' => 'DATETIME', 'null' => true],
    ]);
    $this->forge->addPrimaryKey('ResultID');
    $this->forge->createTable('patres');

    $this->forge->addField([
      'ResFlagID' => ['type' => 'INT', 'auto_increment' => true],
      'ResultID' => ['type' => 'INT', 'null' => false],
      'Flag' => ['type' => 'varchar', 'constraint' => 50, 'null' => true],
      'CreateDate' => ['type' => 'DATETIME', 'null' => true],
      'EndDate' => ['type' => 'DATETIME', 'null' => true],
      'ArchiveDate' => ['type' => 'DATETIME', 'null' => true],
      'DelDate' => ['type' => 'DATETIME', 'null' => true],
    ]);
    $this->forge->addPrimaryKey('ResFlagID');
    $this->forge->createTable('patresflag');


value00: app/Database/Seeds/OrderSeeder.php
value01:
    // ========================================
    $orderID2 = '001' . date('ymd', strtotime('-1 day')) . '00002';
    $internalOID2 = $this->createOrder([
      'OrderID' => $orderID2,
      'PlacerID' => 'PLC002',
      'InternalPID' => 2,
      'SiteID' => '1',
      'PVADTID' => 3,
      'ReqApp' => 'HIS',
      'Priority' => 'R',
      'TrnDate' => $yesterday,
      'EffDate' => $yesterday,
      'CreateDate' => $yesterday
    ]);

    echo "\nCreated Order 2: {$orderID2} (InternalOID: {$internalOID2})\n";

    // Create specimen for Lipid/Liver (SST tube - ConDefID = 1)
    $specimenID2 = $orderID2 . '-S01';


value00: app/Database/Seeds/OrderSeeder.php
value01:
    echo "  Created Specimen: {$specimenID5b} (SST)\n";

    // Create pending results for CBC
    foreach ($cbcTests as $test) {
      if (isset($testIDs[$test['code']])) {
        $this->createPatRes([
          'OrderID' => $internalOID5,
          'InternalSID' => $internalSID5a,
          'SID' => $specimenID5a,
          'TestSiteID' => $testIDs[$test['code']],
          'TestSiteCode' => $test['code'],
          'Result' => null,
          'SampleType' => 'Whole Blood',
          'ResultDateTime' => $now,
          'CreateDate' => $now
        ]);
      }
    }

    // Create pending results for Chemistry
    $chemTests = ['GLU', 'CREA', 'UREA', 'SGOT', 'SGPT'];


value00: app/Database/Migrations/2026-01-01-000012_CreatePatientVisits.php
value01:
    $this->forge->addKey('InternalPVID', true);
    $this->forge->createTable('patdiag');

    $this->forge->addField([
      'PVADTID'   => ['type' => 'INT', 'unsigned' => true, 'auto_increment' => true],
      'InternalPVID'=> ['type' => 'INT', 'null' => true],
      'ADTCode' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'LocationID' => ['type' => 'INT', 'constraint' => 11, 'null' => true],
      'AttDoc' => ['type' => 'INT', 'constraint' => 11, 'null' => true],
      'RefDoc' => ['type' => 'INT', 'constraint' => 11, 'null' => true],
      'AdmDoc' => ['type' => 'INT', 'constraint' => 11, 'null' => true],
      'CnsDoc' => ['type' => 'INT', 'constraint' => 11, 'null' => true]


value00: app/Database/Migrations/2026-03-12-000040_CreateTestRules.php
value01:
        // testrule - mapping table for many-to-many relationship between ruledef and tests
        $this->forge->addField([
            'TestRuleID' => ['type' => 'INT', 'unsigned' => true, 'auto_increment' => true],
            'RuleID' => ['type' => 'INT', 'unsigned' => true, 'null' => false],
            'TestSiteID' => ['type' => 'INT', 'unsigned' => true, 'null' => false],
            'CreateDate' => ['type' => 'DATETIME', 'null' => true],
            'EndDate' => ['type' => 'DATETIME', 'null' => true],
        ]);
        $this->forge->addKey('TestRuleID', true);
        $this->forge->addKey('RuleID');
        $this->forge->addKey('TestSiteID');
        $this->forge->createTable('testrule');

        // Foreign keys for mapping table


value00: app/Database/Migrations/2026-01-01-000012_CreatePatientVisits.php
value01:
<?php

namespace App\Database\Migrations;

use CodeIgniter\Database\Migration;

class CreatePatientVisits extends Migration {
  public function up() {
    $this->forge->addField([
      'SiteID' => ['type' => 'INT', 'constraint' => 11, 'null' => true]


value00: app/Database/Seeds/OrderSeeder.php
value01:
    $this->createSpecimenStatus($specimenID6, $internalOID6, 'COMPLETED', date('Y-m-d H:i:s', strtotime('+6 hours')));

    echo "  Created Specimen: {$specimenID6} (SST)\n";

    // Create order status
    $this->createOrderStatus($internalOID6, 'ORDERED', $now);
    $this->createOrderStatus($internalOID6, 'SPECIMEN_COLLECTED', date('Y-m-d H:i:s', strtotime('+30 minutes')));
    $this->createOrderStatus($internalOID6, 'IN_LAB', date('Y-m-d H:i:s', strtotime('+2 hours')));
    $this->createOrderStatus($internalOID6, 'COMPLETED', date('Y-m-d H:i:s', strtotime('+6 hours')));
    $this->createOrderStatus($internalOID6, 'REPORTED', date('Y-m-d H:i:s', strtotime('+7 hours')));


value00: app/Database/Migrations/2026-01-01-000022_CreateResults.php
value01:
,
      'CurrLocID' => ['type' => 'INT', 'null' => true],
      'Origin' => ['type' => 'varchar', 'constraint' => 255, 'null' => true],
      'GeoLocationSystem' => ['type' => 'varchar', 'constraint' => 255, 'null' => true],
      'GeoLocationData' => ['type' => 'TEXT', 'null' => true],
      'DIDType' => ['type' => 'varchar', 'constraint' => 255, 'null' => true]


value00: app/Database/Seeds/OrderSeeder.php
value01:
    foreach ($urineTests as $test) {
      if (isset($testIDs[$test['code']])) {
        $this->createPatRes([
          'OrderID' => $internalOID4,
          'InternalSID' => $internalSID4,
          'SID' => $specimenID4,
          'TestSiteID' => $testIDs[$test['code']],
          'TestSiteCode' => $test['code'],
          'Result' => null,
          'SampleType' => 'Urine',
          'ResultDateTime' => $now,
          'CreateDate' => $now
        ]);
      }
    }

    echo "  Created " . count($urineTests) . " pending urinalysis results\n";

    // Create order status
    $this->createOrderStatus($internalOID4, 'ORDERED', $now);


value00: app/Database/Migrations/2026-03-12-000040_CreateTestRules.php
value01:
/**
 * Rule engine tables: ruledef + testrule.
 *
 * ruleaction is deprecated; actions are embedded in ruledef.ConditionExprCompiled.
 */
class CreateTestRules extends Migration
{
    public function up()
    {
        // ruledef - rule definitions (not linked to specific test)
        $this->forge->addField([
            'RuleID' => ['type' => 'INT', 'unsigned' => true, 'auto_increment' => true],
            'RuleCode' => ['type' => 'VARCHAR', 'constraint' => 50, 'null' => false],
            'RuleName' => ['type' => 'VARCHAR', 'constraint' => 100, 'null' => false],
            'Description' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
            'EventCode' => ['type' => 'VARCHAR', 'constraint' => 50, 'null' => false]


value00: app/Database/Seeds/OrderSeeder.php
value01:
    $liverTests = [
      ['code' => 'SGOT', 'result' => '28'],
      ['code' => 'SGPT', 'result' => '32'],
    ];

    foreach ($liverTests as $test) {
      if (isset($testIDs[$test['code']])) {
        $this->createPatRes([
          'OrderID' => $internalOID2,
          'InternalSID' => $internalSID2,
          'SID' => $specimenID2,
          'TestSiteID' => $testIDs[$test['code']],
          'TestSiteCode' => $test['code'],
          'Result' => $test['result'],
          'SampleType' => 'Serum',
          'ResultDateTime' => date('Y-m-d H:i:s', strtotime('-1 day +6 hours')),
          'CreateDate' => date('Y-m-d H:i:s', strtotime('-1 day +6 hours'))
        ]);
      }
    }


value00: app/Database/Seeds/OrderSeeder.php
value01:
    // Create specimen for Lipid/Liver (SST tube - ConDefID = 1)
    $specimenID2 = $orderID2 . '-S01';
    $internalSID2 = $this->createSpecimen([
      'SID' => $specimenID2,
      'SiteID' => '1',
      'OrderID' => $internalOID2,
      'ConDefID' => 1,
      'Qty' => 1,
      'Unit' => 'tube',
      'GenerateBy' => 'ORDER',
      'CreateDate' => $yesterday
    ]);

    $this->createSpecimenStatus($specimenID2, $internalOID2, 'PENDING', $yesterday);
    $this->createSpecimenStatus($specimenID2, $internalOID2, 'COLLECTED', date('Y-m-d H:i:s', strtotime('-1 day +30 minutes')));
    $this->createSpecimenStatus($specimenID2, $internalOID2, 'RECEIVED', date('Y-m-d H:i:s', strtotime('-1 day +2 hours')));


value00: app/Database/Seeds/OrderSeeder.php
value01:
    foreach ($cbcTests as $test) {
      if (isset($testIDs[$test['code']])) {
        $this->createPatRes([
          'OrderID' => $internalOID1,
          'InternalSID' => $internalSID1,
          'SID' => $specimenID1,
          'TestSiteID' => $testIDs[$test['code']],
          'TestSiteCode' => $test['code'],
          'Result' => $test['result'],
          'SampleType' => 'Whole Blood',
          'ResultDateTime' => date('Y-m-d H:i:s', strtotime('-1 day +8 hours')),
          'CreateDate' => date('Y-m-d H:i:s', strtotime('-1 day +8 hours'))
        ]);
      }
    }

    echo "  Created " . count($cbcTests) . " CBC results\n";


value00: app/Database/Seeds/OrderSeeder.php
value01:
    $renalTests = [
      ['code' => 'CREA', 'result' => '1.1'],
      ['code' => 'UREA', 'result' => '18'],
    ];

    foreach ($renalTests as $test) {
      if (isset($testIDs[$test['code']])) {
        $this->createPatRes([
          'OrderID' => $internalOID3,
          'InternalSID' => $internalSID3,
          'SID' => $specimenID3,
          'TestSiteID' => $testIDs[$test['code']],
          'TestSiteCode' => $test['code'],
          'Result' => $test['result'],
          'SampleType' => 'Serum',
          'ResultDateTime' => date('Y-m-d H:i:s', strtotime('-1 day +2 hours')),
          'CreateDate' => date('Y-m-d H:i:s', strtotime('-1 day +2 hours'))
        ]);
      }
    }

    // Create Glucose result


value00: app/Database/Seeds/OrderSeeder.php
value01:
    // Create order status
    $this->createOrderStatus($internalOID5, 'ORDERED', $now);

    // ========================================
    // ORDER 6: Patient 2 - Bilirubin Panel (TBIL, DBIL, IBIL)
    // ========================================
    $orderID6 = '001' . date('ymd') . '00006';
    $internalOID6 = $this->createOrder([
      'OrderID' => $orderID6,
      'PlacerID' => 'PLC006',
      'InternalPID' => 2,
      'SiteID' => '1',
      'PVADTID' => 3,
      'ReqApp' => 'HIS',
      'Priority' => 'R',
      'TrnDate' => $now,
      'EffDate' => $now,
      'CreateDate' => $now
    ]);

    echo "\nCreated Order 6: {$orderID6} (InternalOID: {$internalOID6}) [Bilirubin Panel]\n";

    // Create specimen for Bilirubin tests (SST tube - ConDefID = 1)


value00: app/Database/Migrations/2026-03-12-000040_CreateTestRules.php
value01:
        $this->forge->createTable('testrule');

        // Foreign keys for mapping table
        $this->db->query('ALTER TABLE `testrule` ADD CONSTRAINT `fk_testrule_ruledef` FOREIGN KEY (`RuleID`) REFERENCES `ruledef`(`RuleID`) ON DELETE CASCADE ON UPDATE CASCADE');
        $this->db->query('ALTER TABLE `testrule` ADD CONSTRAINT `fk_testrule_testsite` FOREIGN KEY (`TestSiteID`) REFERENCES `testdefsite`(`TestSiteID`) ON DELETE CASCADE ON UPDATE CASCADE');
    }

    public function down()
    {
        $this->forge->dropTable('testrule', true);
        $this->forge->dropTable('ruledef', true);
    }
}


value00: app/Database/Seeds/OrderSeeder.php
value01:
    // Create specimen for Bilirubin tests (SST tube - ConDefID = 1)
    $specimenID6 = $orderID6 . '-S01';
    $internalSID6 = $this->createSpecimen([
      'SID' => $specimenID6,
      'SiteID' => '1',
      'OrderID' => $internalOID6,
      'ConDefID' => 1,
      'Qty' => 1,
      'Unit' => 'tube',
      'GenerateBy' => 'ORDER',
      'CreateDate' => $now
    ]);

    $this->createSpecimenStatus($specimenID6, $internalOID6, 'PENDING', $now);
    $this->createSpecimenStatus($specimenID6, $internalOID6, 'COLLECTED', date('Y-m-d H:i:s', strtotime('+30 minutes')));
    $this->createSpecimenStatus($specimenID6, $internalOID6, 'RECEIVED', date('Y-m-d H:i:s', strtotime('+2 hours')));


value00: app/Database/Seeds/OrderSeeder.php
value01:
    // Create urine specimen (Pot Urin - ConDefID = 12)
    $specimenID4 = $orderID4 . '-S01';
    $internalSID4 = $this->createSpecimen([
      'SID' => $specimenID4,
      'SiteID' => '1',
      'OrderID' => $internalOID4,
      'ConDefID' => 12,
      'Qty' => 1,
      'Unit' => 'container',
      'GenerateBy' => 'ORDER',
      'CreateDate' => $now
    ]);

    $this->createSpecimenStatus($specimenID4, $internalOID4, 'PENDING', $now);

    echo "  Created Specimen: {$specimenID4} (Urine)\n";

    // Create Urinalysis results (pending - use current time for ResultDateTime)
    $urineTests = [
      ['code' => 'UCOLOR'],
      ['code' => 'UGLUC'],
      ['code' => 'UPROT'],
      ['code' => 'PH'],
    ];


value00: app/Database/Migrations/2026-03-12-000040_CreateTestRules.php
value01:
,
            'EventCode' => ['type' => 'VARCHAR', 'constraint' => 50, 'null' => false],
            'ConditionExpr' => ['type' => 'VARCHAR', 'constraint' => 1000, 'null' => true],
            'ConditionExprCompiled' => ['type' => 'JSON', 'null' => true],
            'CreateDate' => ['type' => 'DATETIME', 'null' => true],
            'StartDate' => ['type' => 'DATETIME', 'null' => true],
            'EndDate' => ['type' => 'DATETIME', 'null' => true],
        ]);
        $this->forge->addKey('RuleID', true);
        $this->forge->addKey('RuleCode');
        $this->forge->addKey('EventCode');
        $this->forge->createTable('ruledef');

        // testrule - mapping table for many-to-many relationship between ruledef and tests


value00: app/Database/Seeds/OrderSeeder.php
value01:
    echo "  Created Specimen: {$specimenID5a} (EDTA)\n";

    // Create SST specimen for Chemistry
    $specimenID5b = $orderID5 . '-S02';
    $internalSID5b = $this->createSpecimen([
      'SID' => $specimenID5b,
      'SiteID' => '1',
      'OrderID' => $internalOID5,
      'ConDefID' => 1,
      'Qty' => 1,
      'Unit' => 'tube',
      'GenerateBy' => 'ORDER',
      'CreateDate' => $now
    ]);

    $this->createSpecimenStatus($specimenID5b, $internalOID5, 'PENDING', $now);

    echo "  Created Specimen: {$specimenID5b} (SST)\n";

    // Create pending results for CBC


value00: app/Database/Seeds/OrderSeeder.php
value01:
    // Create Glucose result
    if (isset($testIDs['GLU'])) {
      $this->createPatRes([
        'OrderID' => $internalOID3,
        'InternalSID' => $internalSID3,
        'SID' => $specimenID3,
        'TestSiteID' => $testIDs['GLU'],
        'TestSiteCode' => 'GLU',
        'Result' => '95',
        'SampleType' => 'Serum',
        'ResultDateTime' => date('Y-m-d H:i:s', strtotime('-1 day +2 hours')),
        'CreateDate' => date('Y-m-d H:i:s', strtotime('-1 day +2 hours'))
      ]);
    }

    echo "  Created " . (count($renalTests) + 1) . " urgent chemistry results\n";


value00: app/Database/Seeds/OrderSeeder.php
value01:
    echo "  Created " . count($cbcTests) . " CBC results\n";

    // Create order status
    $this->createOrderStatus($internalOID1, 'ORDERED', $twoDaysAgo);
    $this->createOrderStatus($internalOID1, 'SPECIMEN_COLLECTED', date('Y-m-d H:i:s', strtotime('-2 days +30 minutes')));
    $this->createOrderStatus($internalOID1, 'IN_LAB', date('Y-m-d H:i:s', strtotime('-2 days +2 hours')));
    $this->createOrderStatus($internalOID1, 'COMPLETED', date('Y-m-d H:i:s', strtotime('-1 day +8 hours')));
    $this->createOrderStatus($internalOID1, 'REPORTED', date('Y-m-d H:i:s', strtotime('-1 day +9 hours')));


value00: app/Database/Migrations/2026-01-01-000022_CreateResults.php
value01:
      'GeoLocationData' => ['type' => 'TEXT', 'null' => true],
      'DIDType' => ['type' => 'varchar', 'constraint' => 255, 'null' => true],
      'DID' => ['type' => 'varchar', 'constraint' => 255, 'null' => true],
      'UserID' => ['type' => 'varchar', 'constraint' => 255, 'null' => true],
      'LogDate' => ['type' => 'DATETIME', 'null' => true],
      'EndDate' => ['type' => 'DATETIME', 'null' => true],
      'ArchiveDate' => ['type' => 'DATETIME', 'null' => true],
    ]);
    $this->forge->addPrimaryKey('ResStatusID');
    $this->forge->createTable('patrestatus');
  }

  public function down() {
    $this->forge->dropTable('patrestatus');
    $this->forge->dropTable('patresflag');
    $this->forge->dropTable('patres');
  }
}


value00: app/Database/Seeds/OrderSeeder.php
value01:
    echo "\nCreated Order 5: {$orderID5} (InternalOID: {$internalOID5}) [Multi-container]\n";

    // Create EDTA specimen for CBC
    $specimenID5a = $orderID5 . '-S01';
    $internalSID5a = $this->createSpecimen([
      'SID' => $specimenID5a,
      'SiteID' => '1',
      'OrderID' => $internalOID5,
      'ConDefID' => 9,
      'Qty' => 1,
      'Unit' => 'tube',
      'GenerateBy' => 'ORDER',
      'CreateDate' => $now
    ]);

    $this->createSpecimenStatus($specimenID5a, $internalOID5, 'PENDING', $now);

    echo "  Created Specimen: {$specimenID5a} (EDTA)\n";

    // Create SST specimen for Chemistry


value00: app/Database/Seeds/OrderSeeder.php
value01:
    echo "  Created " . (count($lipidTests) + count($liverTests)) . " chemistry results\n";

    // Create order status
    $this->createOrderStatus($internalOID2, 'ORDERED', $yesterday);
    $this->createOrderStatus($internalOID2, 'SPECIMEN_COLLECTED', date('Y-m-d H:i:s', strtotime('-1 day +30 minutes')));
    $this->createOrderStatus($internalOID2, 'IN_LAB', date('Y-m-d H:i:s', strtotime('-1 day +2 hours')));
    $this->createOrderStatus($internalOID2, 'COMPLETED', date('Y-m-d H:i:s', strtotime('-1 day +6 hours')));

    // ========================================
    // ORDER 3: Patient 3 - Renal Function + Glucose (URGENT)
    // ========================================
    $orderID3 = '001' . date('ymd', strtotime('-1 day')) . '00003';


value00: app/Database/Seeds/OrderSeeder.php
value01:
    $this->createSpecimenStatus($specimenID2, $internalOID2, 'COMPLETED', date('Y-m-d H:i:s', strtotime('-1 day +6 hours')));

    echo "  Created Specimen: {$specimenID2} (SST)\n";

    // Create Lipid Profile results
    $lipidTests = [
      ['code' => 'CHOL', 'result' => '195'],
      ['code' => 'TG', 'result' => '145'],
      ['code' => 'HDL', 'result' => '55'],
      ['code' => 'LDL', 'result' => '115'],
    ];


value00: app/Database/Seeds/OrderSeeder.php
value01:
    // ========================================
    $orderID3 = '001' . date('ymd', strtotime('-1 day')) . '00003';
    $internalOID3 = $this->createOrder([
      'OrderID' => $orderID3,
      'PlacerID' => 'PLC003',
      'InternalPID' => 3,
      'SiteID' => '1',
      'PVADTID' => 4,
      'ReqApp' => 'EMR',
      'Priority' => 'U',
      'TrnDate' => $yesterday,
      'EffDate' => $yesterday,
      'CreateDate' => $yesterday
    ]);

    echo "\nCreated Order 3: {$orderID3} (InternalOID: {$internalOID3}) [URGENT]\n";

    // Create specimen for Renal/Glucose (SST tube)
    $specimenID3 = $orderID3 . '-S01';


value00: app/Database/Seeds/OrderSeeder.php
value01:
    $this->createOrderStatus($internalOID1, 'REPORTED', date('Y-m-d H:i:s', strtotime('-1 day +9 hours')));

    // Create order comment
    $this->createOrderComment($internalOID1, 'Routine CBC ordered for annual checkup', $twoDaysAgo);

    // ========================================
    // ORDER 2: Patient 2 - Lipid Profile + Liver Function
    // ========================================
    $orderID2 = '001' . date('ymd', strtotime('-1 day')) . '00002';


value00: app/Database/Seeds/OrderSeeder.php
value01:
    $this->createOrderStatus($internalOID3, 'REPORTED', date('Y-m-d H:i:s', strtotime('-1 day +2 hours +15 minutes')));

    // Create order comment
    $this->createOrderComment($internalOID3, 'STAT order for suspected kidney dysfunction. Patient has diabetes history.', $yesterday);

    // ========================================
    // ORDER 4: Patient 1 - Urinalysis (PENDING)
    // ========================================


value00: app/Database/Migrations/2026-02-27-001500_CreateHostAppAndCodingSys.php
value01:
    $this->forge->addKey('HostAppID', true);
    $this->forge->createTable('hostcompara');

    // Table: codingsys
    $this->forge->addField([
      'CodingSysID' => ['type' => 'INT', 'unsigned' => true, 'auto_increment' => true],
      'CodingSysAbb' => ['type' => 'VARCHAR', 'constraint' => 6, 'null' => false, 'unique' => true],
      'FullText' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'Description' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'CreateDate' => ['type' => 'DATETIME', 'null' => true],
      'EndDate' => ['type' => 'DATETIME', 'null' => true]
    ]);
    $this->forge->addKey('CodingSysID', true);
    $this->forge->createTable('codingsys');
  }


value00: app/Database/Seeds/OrderSeeder.php
value01:
    echo "  Created " . (count($renalTests) + 1) . " urgent chemistry results\n";

    // Create order status (fast-tracked for urgent)
    $this->createOrderStatus($internalOID3, 'ORDERED', $yesterday);
    $this->createOrderStatus($internalOID3, 'SPECIMEN_COLLECTED', date('Y-m-d H:i:s', strtotime('-1 day +20 minutes')));
    $this->createOrderStatus($internalOID3, 'IN_LAB', date('Y-m-d H:i:s', strtotime('-1 day +30 minutes')));
    $this->createOrderStatus($internalOID3, 'COMPLETED', date('Y-m-d H:i:s', strtotime('-1 day +2 hours')));
    $this->createOrderStatus($internalOID3, 'REPORTED', date('Y-m-d H:i:s', strtotime('-1 day +2 hours +15 minutes')));


value00: app/Database/Seeds/OrderSeeder.php
value01:
    echo "Creating dummy orders with specimens and results...\n";

    // Get existing test IDs from testdefsite
    $testIDs = $this->getTestIDs();
    if (empty($testIDs)) {
      echo "Error: No test definitions found. Please run TestSeeder first.\n";
      return;
    }

    // Get container definitions
    $containers = $this->db->table('containerdef')->get()->getResultArray();
    if (empty($containers)) {
      echo "Error: No container definitions found. Please run SpecimenSeeder first.\n";
      return;
    }

    // ========================================
    // ORDER 1: Patient 1 - Complete Blood Count (CBC)
    // ========================================
    $orderID1 = '001' . date('ymd', strtotime('-2 days')) . '00001';


value00: app/Database/Migrations/2026-02-27-001500_CreateHostAppAndCodingSys.php
value01:
    $this->forge->addKey('HostAppID', true);
    $this->forge->createTable('hostapp');

    // Table: hostcompara
    $this->forge->addField([
      'HostAppID' => ['type' => 'VARCHAR', 'constraint' => 5, 'null' => false],
      'HostIP' => ['type' => 'VARCHAR', 'constraint' => 15, 'null' => true],
      'HostPort' => ['type' => 'VARCHAR', 'constraint' => 6, 'null' => true],
      'HostPwd' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'CreateDate' => ['type' => 'DATETIME', 'null' => true],
      'EndDate' => ['type' => 'DATETIME', 'null' => true]
    ]);
    $this->forge->addKey('HostAppID', true);
    $this->forge->createTable('hostcompara');

    // Table: codingsys


value00: app/Database/Migrations/2026-02-27-001500_CreateHostAppAndCodingSys.php
value01:
<?php

namespace App\Database\Migrations;

use CodeIgniter\Database\Migration;

class CreateHostAppAndCodingSys extends Migration {
  public function up() {
    // Table: hostapp
    $this->forge->addField([
      'HostAppID' => ['type' => 'VARCHAR', 'constraint' => 5, 'null' => false],
      'HostAppName' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => false],
      'SiteID' => ['type' => 'INT', 'unsigned' => true, 'null' => true],
      'CreateDate' => ['type' => 'DATETIME', 'null' => true],
      'EndDate' => ['type' => 'DATETIME', 'null' => true]
    ]);
    $this->forge->addKey('HostAppID', true);
    $this->forge->createTable('hostapp');

    // Table: hostcompara


value00: app/Database/Seeds/OrderSeeder.php
value01:
<?php

namespace App\Database\Seeds;

use CodeIgniter\Database\Seeder;

class OrderSeeder extends Seeder
{
  public function run()
  {
    $now = date('Y-m-d H:i:s');
    $yesterday = date('Y-m-d H:i:s', strtotime('-1 day'));
    $twoDaysAgo = date('Y-m-d H:i:s', strtotime('-2 days'));

    echo "Creating dummy orders with specimens and results...\n";


value00: app/Database/Seeds/OrderSeeder.php
value01:
    $this->createOrderStatus($internalOID6, 'REPORTED', date('Y-m-d H:i:s', strtotime('+7 hours')));

    // Create order comment
    $this->createOrderComment($internalOID6, 'Bilirubin panel ordered for liver function assessment', $now);

    // ========================================
    // SUMMARY
    // ========================================
    echo "\n";
    echo "========================================\n";
    echo "ORDER SEEDING COMPLETED SUCCESSFULLY!\n";
    echo "========================================\n";
    echo "Orders Created: 6\n";
    echo "  - Order 1: CBC (Complete Blood Count) - COMPLETED\n";
    echo "  - Order 2: Lipid + Liver Profile - COMPLETED\n";
    echo "  - Order 3: Renal + Glucose - COMPLETED (URGENT)\n";


value00: app/Database/Seeds/OrderSeeder.php
value01:
    echo "  - Order 3: Renal + Glucose - COMPLETED (URGENT)\n";
    echo "  - Order 4: Urinalysis - PENDING\n";
    echo "  - Order 5: CBC + Chemistry - PENDING (Multi-container)\n";
    echo "  - Order 6: Bilirubin Panel (TBIL, DBIL, IBIL) - COMPLETED\n";
    echo "----------------------------------------\n";
    echo "Specimens Created: 7\n";
    echo "  - SST tubes: 5\n";
    echo "  - EDTA tubes: 2\n";
    echo "  - Urine containers: 1\n";
    echo "----------------------------------------\n";
    echo "Patient Results: 29 total\n";
    echo "  - With results: 16\n";
    echo "  - Pending: 13\n";
    echo "========================================\n";
  }


value00: app/Database/Seeds/OrderSeeder.php
value01:
  private function createSpecimenStatus(string $specimenID, int $orderID, string $status, string $dateTime): void
  {
    $this->db->table('specimenstatus')->insert([
      'SID' => $specimenID,
      'OrderID' => $orderID,
      'SpcStatus' => $status,
      'CreateDate' => $dateTime
    ]);
  }

  private function createPatRes(array $data): void
  {
    $this->db->table('patres')->insert($data);
  }


value00: app/Database/Seeds/OrderSeeder.php
value01:
  private function getTestIDs(): array
  {
    $tests = $this->db->table('testdefsite')
      ->select('TestSiteID, TestSiteCode')
      ->where('EndDate IS NULL')
      ->get()
      ->getResultArray();

    $testIDs = [];
    foreach ($tests as $test) {
      $testIDs[$test['TestSiteCode']] = $test['TestSiteID'];
    }

    return $testIDs;
  }

  private function createOrder(array $data): int
  {
    $this->db->table('ordertest')->insert($data);
    return $this->db->insertID();
  }

  private function createSpecimen(array $data): int
  {
    $this->db->table('specimen')->insert($data);
    return $this->db->insertID();
  }


value00: app/Database/Migrations/2026-02-27-001500_CreateHostAppAndCodingSys.php
value01:
    $this->forge->addKey('CodingSysID', true);
    $this->forge->createTable('codingsys');
  }

  public function down() {
    $this->forge->dropTable('codingsys');
    $this->forge->dropTable('hostcompara');
    $this->forge->dropTable('hostapp');
  }
}


value00: app/Database/Migrations/2026-03-12-000040_CreateTestRules.php
value01:
<?php

namespace App\Database\Migrations;

use CodeIgniter\Database\Migration;

/**
 * Rule engine tables: ruledef + testrule.
 *
 * ruleaction is deprecated; actions are embedded in ruledef.ConditionExprCompiled.
 */


value00: app/Database/Seeds/OrderSeeder.php
value01:
  private function createPatRes(array $data): void
  {
    $this->db->table('patres')->insert($data);
  }

  private function createOrderStatus(int $orderID, string $status, string $dateTime): void
  {
    $this->db->table('orderstatus')->insert([
      'InternalOID' => $orderID,
      'OrderStatus' => $status,
      'CreateDate' => $dateTime
    ]);
  }

  private function createOrderComment(int $orderID, string $comment, string $dateTime): void
  {
    $this->db->table('ordercom')->insert([
      'InternalOID' => $orderID,
      'Comment' => $comment,
      'CreateDate' => $dateTime
    ]);
  }
}


value00: app/Database/Seeds/TestSeeder.php
value01:
    // ========================================
    // DEPARTMENT 1 - HEMATOLOGY (Discipline 1)
    // Order: GROUP (<100) → PARAM (<100) → TEST (100+) → CALC (100+)
    // ========================================
    
    // 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


value00: app/Database/Seeds/TestSeeder.php
value01:
    $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' => '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();


value00: app/Database/Seeds/TestSeeder.php
value01:
    $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' => '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();


value00: app/Database/Seeds/TestSeeder.php
value01:
    $this->db->table('testdefgrp')->insertBatch([
      ['TestSiteID' => $tIDs['CBC'], 'Member' => $tIDs['HB'], 'CreateDate' => "$now"],
      ['TestSiteID' => $tIDs['CBC'], 'Member' => $tIDs['HCT'], 'CreateDate' => "$now"],
      ['TestSiteID' => $tIDs['CBC'], 'Member' => $tIDs['RBC'], 'CreateDate' => "$now"],
      ['TestSiteID' => $tIDs['CBC'], 'Member' => $tIDs['WBC'], 'CreateDate' => "$now"],
      ['TestSiteID' => $tIDs['CBC'], 'Member' => $tIDs['PLT'], 'CreateDate' => "$now"],
      ['TestSiteID' => $tIDs['CBC'], 'Member' => $tIDs['MCV'], 'CreateDate' => "$now"],
      ['TestSiteID' => $tIDs['CBC'], 'Member' => $tIDs['MCH'], 'CreateDate' => "$now"],
      ['TestSiteID' => $tIDs['CBC'], 'Member' => $tIDs['MCHC'], 'CreateDate' => "$now"]
    ]);


value00: app/Database/Seeds/TestSeeder.php
value01:
    $this->db->table('testdefsite')->insert($data);
    $tIDs['WBC'] = $this->db->insertID();

    $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();


value00: app/Database/Seeds/TestSeeder.php
value01:
    $this->db->table('testdefcal')->insert($data);

    $this->db->table('testdefgrp')->insertBatch([
      ['TestSiteID' => $tIDs['BMI'], 'Member' => $tIDs['WEIGHT'], 'CreateDate' => "$now"],
      ['TestSiteID' => $tIDs['BMI'], 'Member' => $tIDs['HEIGHT'], 'CreateDate' => "$now"],
      ['TestSiteID' => $tIDs['EGFR'], 'Member' => $tIDs['AGE'], 'CreateDate' => "$now"],
    ]);

    // ========================================
    // TEST MAP - Specimen Mapping
    // ========================================
    
    // Get container IDs (inserted by SpecimenSeeder in order)
    $conSST = 1;        // SST (Serum Separator Tube)
    $conEDTA = 9;       // EDTA - Hematology
    $conCitrate = 10;   // Citrate - Koagulasi
    $conPotUrin = 12;   // Pot Urin


value00: app/Database/Seeds/TestSeeder.php
value01:
    $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)


value00: app/Database/Seeds/TestSeeder.php
value01:
    $this->db->table('testdefsite')->insert($data);
    $tIDs['EGFR'] = $this->db->insertID();
    $data = ['TestSiteID' => $tIDs['EGFR'], 'DisciplineID' => '2', 'DepartmentID' => '2', '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);


value00: app/Database/Seeds/TestSeeder.php
value01:
    $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' => '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();


value00: app/Database/Seeds/TestSeeder.php
value01:
    $instUrinalysis = 4;   // Urisys 1100
    
    // Helper to get or create TestMap header ID (relationship only)
    $getTestMapID = function($hostType, $hostID, $clientType, $clientID) use (&$testMapHeaders, &$now) {
      $key = "{$hostType}_{$hostID}_{$clientType}_{$clientID}";
      if (!isset($testMapHeaders[$key])) {
        $data = [
          'HostType' => $hostType,
          'HostID' => $hostID,
          'ClientType' => $clientType,
          'ClientID' => $clientID,
          'CreateDate' => $now
        ];
        $this->db->table('testmap')->insert($data);
        $testMapHeaders[$key] = $this->db->insertID();
      }
      return $testMapHeaders[$key];
    };
    
    // Helper to add detail record


value00: app/Database/Seeds/TestSeeder.php
value01:
    $this->db->table('testdefsite')->insert($data);
    $tIDs['TBIL'] = $this->db->insertID();

    $data = ['SiteID' => '1', 'TestSiteCode' => 'DBIL', 'TestSiteName' => 'Direct Bilirubin', 'TestType' => 'TEST', 'Description' => 'Bilirubin Direk', 'SeqScr' => '186', 'SeqRpt' => '186', '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' => '2', 'Method' => 'Diazo', 'CreateDate' => "$now"];
    $this->db->table('testdefsite')->insert($data);
    $tIDs['DBIL'] = $this->db->insertID();

    // CALC: Chemistry Calculated Tests


value00: app/Database/Seeds/TestSeeder.php
value01:
    $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();


value00: app/Database/Seeds/TestSeeder.php
value01:
    $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();


value00: app/Database/Seeds/TestSeeder.php
value01:
    $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();


value00: app/Database/Seeds/TestSeeder.php
value01:
    $this->db->table('testdefsite')->insert($data);
    $tIDs['HB'] = $this->db->insertID();

    $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();


value00: app/Database/Seeds/TestSeeder.php
value01:
    $this->db->table('testdefgrp')->insertBatch([
      ['TestSiteID' => $tIDs['LFT'], 'Member' => $tIDs['SGOT'], 'CreateDate' => "$now"],
      ['TestSiteID' => $tIDs['LFT'], 'Member' => $tIDs['SGPT'], 'CreateDate' => "$now"],
      ['TestSiteID' => $tIDs['LFT'], 'Member' => $tIDs['TBIL'], 'CreateDate' => "$now"],
      ['TestSiteID' => $tIDs['LFT'], 'Member' => $tIDs['DBIL'], 'CreateDate' => "$now"],
      ['TestSiteID' => $tIDs['LFT'], 'Member' => $tIDs['IBIL'], 'CreateDate' => "$now"]
    ]);


value00: app/Database/Seeds/TestSeeder.php
value01:
    $this->db->table('testdefsite')->insert($data);
    $tIDs['LDL'] = $this->db->insertID();

    $data = ['SiteID' => '1', 'TestSiteCode' => 'TBIL', 'TestSiteName' => 'Total Bilirubin', 'TestType' => 'TEST', 'Description' => 'Bilirubin Total', 'SeqScr' => '185', 'SeqRpt' => '185', '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' => '2', 'Method' => 'Diazo', 'CreateDate' => "$now"];
    $this->db->table('testdefsite')->insert($data);
    $tIDs['TBIL'] = $this->db->insertID();


value00: app/Database/Seeds/TestSeeder.php
value01:
    $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"]
    ]);

    // ========================================
    // 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


value00: app/Database/Seeds/TestSeeder.php
value01:
    $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();


value00: app/Database/Seeds/TestSeeder.php
value01:
    // 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' => '', 'FormulaCode' => 'WEIGHT / ((HEIGHT/100) * (HEIGHT/100))', 'RefType' => 'RANGE', 'Unit1' => 'kg/m2', 'Factor' => '', 'Unit2' => '', 'Decimal' => '1', 'CreateDate' => "$now"];
    $this->db->table('testdefcal')->insert($data);


value00: app/Database/Seeds/TestSeeder.php
value01:
    $testMappings = [
      // Hematology: Site → HAUTO → Sysmex (EDTA)
      [
        'tests' => ['HB', 'HCT', 'RBC', 'WBC', 'PLT', 'MCV', 'MCH', 'MCHC'],
        'panels' => ['CBC'],
        'siteToWs' => ['ws' => $wsHAuto, 'con' => null],
        'wsToInst' => ['ws' => $wsHAuto, 'inst' => $instHematology, 'con' => $conEDTA],
      ],
      // Chemistry: Site → CAUTO → Cobas (SST)
      [
        'tests' => ['GLU', 'CREA', 'UREA', 'SGOT', 'SGPT', 'CHOL', 'TG', 'HDL', 'LDL', 'TBIL', 'DBIL'],
        'panels' => ['LIPID', 'LFT', 'RFT'],
        'siteToWs' => ['ws' => $wsCAuto, 'con' => null],
        'wsToInst' => ['ws' => $wsCAuto, 'inst' => $instChemistry, 'con' => $conSST],
      ],
      // Calculated: Site → CAUTO → Cobas (SST)


value00: app/Database/Seeds/TestSeeder.php
value01:
;

    // Load entity types for mapping
    $entitySite = $this->getEntityType('SITE') ?? 'SITE';
    $entityWst = $this->getEntityType('WST') ?? 'WST';
    $entityInst = $this->getEntityType('INST') ?? 'INST';


value00: app/Database/Seeds/TestSeeder.php
value01:
    $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();


value00: app/Database/Seeds/TestSeeder.php
value01:
    $conEDTA = 9;       // EDTA - Hematology
    $conCitrate = 10;   // Citrate - Koagulasi
    $conPotUrin = 12;   // Pot Urin
    $conUrine = 13;     // Urine Container
    
    // Workstation IDs from OrganizationSeeder
    $wsHAuto = 1;       // Hematologi Auto
    $wsCAuto = 3;       // Kimia Auto
    $wsIAuto = 6;       // Imunologi Auto
    $wsUAuto = 8;       // Urin Auto
    
    // Equipment IDs from OrganizationSeeder
    $instHematology = 1;   // Sysmex XN-1000
    $instChemistry = 2;    // Roche Cobas C501
    $instImmunology = 3;   // Architect i2000
    $instUrinalysis = 4;   // Urisys 1100
    
    // Helper to get or create TestMap header ID (relationship only)


value00: app/Database/Seeds/TestSeeder.php
value01:
      // Workstation → Instrument mapping
      if ($mapping['wsToInst'] !== null) {
        $testMapWsInstID = $getTestMapID($entityWst, (string)$mapping['wsToInst']['ws'], $entityInst, (string)$mapping['wsToInst']['inst']);
        foreach ($mapping['tests'] as $testCode) {
          $addDetail($testMapWsInstID, $testCode, $testCode, $mapping['wsToInst']['con'], $testCode, $testCode);
        }
        foreach ($mapping['panels'] as $panelCode) {
          $addDetail($testMapWsInstID, $panelCode, $panelCode, $mapping['wsToInst']['con'], $panelCode, $panelCode);
        }
      }
    }
    
    // Insert all testmapdetail records
    if (!empty($testMapDetails)) {
      $this->db->table('testmapdetail')->insertBatch($testMapDetails);
    }
  }
}


value00: app/Database/Seeds/TestSeeder.php
value01:
    $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();


value00: app/Database/Seeds/TestSeeder.php
value01:
    // 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();


value00: app/Database/Seeds/TestSeeder.php
value01:
    $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();


value00: app/Database/Seeds/TestSeeder.php
value01:
    $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();


value00: app/Database/Seeds/TestSeeder.php
value01:
    $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' => '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();


value00: app/Database/Seeds/TestSeeder.php
value01:
    // ========================================

    // 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();


value00: app/Database/Seeds/TestSeeder.php
value01:
    // 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();


value00: app/Database/Seeds/TestSeeder.php
value01:
      [
        'tests' => ['EGFR', 'LDLCALC', 'IBIL'],
        'panels' => [],
        'siteToWs' => ['ws' => $wsCAuto, 'con' => null],
        'wsToInst' => ['ws' => $wsCAuto, 'inst' => $instChemistry, 'con' => $conSST],
      ],
      // Urinalysis: Site → UAUTO → Urisys (Pot Urin)
      [
        'tests' => ['UCOLOR', 'UGLUC', 'UPROT', 'PH'],
        'panels' => [],
        'siteToWs' => ['ws' => $wsUAuto, 'con' => null],
        'wsToInst' => ['ws' => $wsUAuto, 'inst' => $instUrinalysis, 'con' => $conPotUrin],
      ],
      // BMI: Site → CAUTO (no instrument mapping - calculated)
      [
        'tests' => ['BMI'],
        'panels' => [],
        'siteToWs' => ['ws' => $wsCAuto, 'con' => null],
        'wsToInst' => null,
      ],
    ]


value00: app/Database/Seeds/TestSeeder.php
value01:
    // 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"],
      ['TestSiteID' => $tIDs['LIPID'], 'Member' => $tIDs['HDL'], 'CreateDate' => "$now"],
      ['TestSiteID' => $tIDs['LIPID'], 'Member' => $tIDs['LDL'], 'CreateDate' => "$now"],
      ['TestSiteID' => $tIDs['LIPID'], 'Member' => $tIDs['LDLCALC'], 'CreateDate' => "$now"]
    ]);


value00: app/Database/Seeds/TestSeeder.php
value01:
    $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();


value00: app/Database/Seeds/TestSeeder.php
value01:
    $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' => '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();


value00: app/Database/Seeds/TestSeeder.php
value01:
  private function getKey(string $lookupName, string $key): ?string
  {
    $data = ValueSet::getRaw($lookupName);
    if ($data === null) return null;
    foreach ($data as $item) {
      if (($item['key'] ?? '') === $key) {
        return $item['key'];
      }
    }
    return null;
  }

  public function run()
  {
    $now = date('Y-m-d H:i:s');

    $tIDs = [];
    $testMapHeaders = []; // Store TestMapID by mapping key
    $testMapDetails = []; // Store detail records

    // Helper to get or create TestMap header ID


value00: app/Database/Seeds/TestSeeder.php
value01:
    $this->db->table('testdefsite')->insert($data);
    $tIDs['IBIL'] = $this->db->insertID();
    $data = ['TestSiteID' => $tIDs['IBIL'], 'DisciplineID' => '2', 'DepartmentID' => '2', 'FormulaCode' => '{TBIL} - {DBIL}', 'RefType' => 'RANGE', 'Unit1' => 'mg/dL', 'Factor' => '', 'Unit2' => '', 'Decimal' => '2', 'CreateDate' => "$now"];
    $this->db->table('testdefcal')->insert($data);

    // CALC dependencies are grouped via testdefgrp


value00: app/Database/Seeds/TestSeeder.php
value01:
    $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();


value00: app/Database/Seeds/TestSeeder.php
value01:
    // Helper to add detail record
    $addDetail = function($testMapID, $hostTestCode, $hostTestName, $conDefID, $clientTestCode, $clientTestName) use (&$testMapDetails, &$now) {
      $testMapDetails[] = [
        'TestMapID' => $testMapID,
        'HostTestCode' => $hostTestCode,
        'HostTestName' => $hostTestName,
        'ConDefID' => $conDefID,
        'ClientTestCode' => $clientTestCode,
        'ClientTestName' => $clientTestName,
        'CreateDate' => $now
      ];
    };
    
    // Test mappings configuration


value00: app/Database/Seeds/TestSeeder.php
value01:
<?php

namespace App\Database\Seeds;

use CodeIgniter\Database\Seeder;
use App\Libraries\ValueSet;

class TestSeeder extends Seeder
{
  private array $entityTypes = [];

  private function loadEntityTypes(): void
  {
    if (empty($this->entityTypes)) {
      $json = file_get_contents(APPPATH . 'Libraries/Data/entity_type.json');
      $data = json_decode($json, true);
      foreach ($data['values'] as $item) {
        $this->entityTypes[$item['key']] = $item['key'];
      }
    }
  }

  private function getEntityType(string $name): ?string
  {
    $this->loadEntityTypes();
    return $this->entityTypes[$name] ?? null;
  }

  private function getKey(string $lookupName, string $key): ?string


value00: app/Database/Seeds/TestSeeder.php
value01:
    $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();


value00: app/Database/Seeds/TestSeeder.php
value01:
    $entityWst = $this->getEntityType('WST') ?? 'WST';
    $entityInst = $this->getEntityType('INST') ?? 'INST';

    foreach ($testMappings as $mapping) {
      // 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);
      }
      foreach ($mapping['panels'] as $panelCode) {
        $addDetail($testMapSiteWsID, $panelCode, $panelCode, $mapping['siteToWs']['con'], $panelCode, $panelCode);
      }

      // Workstation → Instrument mapping


value00: app/Database/Seeds/TestSeeder.php
value01:
    $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' => '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();


value00: app/Database/Seeds/TestSeeder.php
value01:
    $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();


value00: app/Database/Seeds/TestSeeder.php
value01:
    $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)


value00: app/Database/Seeds/TestSeeder.php
value01:
    $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();


value00: app/Database/Seeds/TestSeeder.php
value01:
    $this->db->table('testdefcal')->insert($data);

    // CALC dependencies are grouped via testdefgrp
    $this->db->table('testdefgrp')->insertBatch([
      ['TestSiteID' => $tIDs['EGFR'], 'Member' => $tIDs['CREA'], 'CreateDate' => "$now"],
      ['TestSiteID' => $tIDs['LDLCALC'], 'Member' => $tIDs['CHOL'], 'CreateDate' => "$now"],
      ['TestSiteID' => $tIDs['LDLCALC'], 'Member' => $tIDs['HDL'], 'CreateDate' => "$now"],
      ['TestSiteID' => $tIDs['LDLCALC'], 'Member' => $tIDs['TG'], 'CreateDate' => "$now"],
      ['TestSiteID' => $tIDs['IBIL'], 'Member' => $tIDs['TBIL'], 'CreateDate' => "$now"],
      ['TestSiteID' => $tIDs['IBIL'], 'Member' => $tIDs['DBIL'], 'CreateDate' => "$now"],
    ]);

    // Add Chemistry Group members now that tests are defined


value00: app/Database/Seeds/TestSeeder.php
value01:
    $this->db->table('testdefsite')->insert($data);
    $tIDs['LDLCALC'] = $this->db->insertID();
    $data = ['TestSiteID' => $tIDs['LDLCALC'], 'DisciplineID' => '2', 'DepartmentID' => '2', 'FormulaCode' => 'CHOL - HDL - (TG/5)', 'RefType' => 'RANGE', 'Unit1' => 'mg/dL', 'Factor' => '', 'Unit2' => '', 'Decimal' => '0', 'CreateDate' => "$now"];
    $this->db->table('testdefcal')->insert($data);

    $data = ['SiteID' => '1', 'TestSiteCode' => 'IBIL', 'TestSiteName' => 'Indirect Bilirubin', 'TestType' => 'CALC', 'Description' => 'Bilirubin Indirek: TBIL - DBIL', 'SeqScr' => '210', 'SeqRpt' => '210', 'IndentLeft' => '1', 'VisibleScr' => '1', 'VisibleRpt' => '1', 'CountStat' => '0', 'CreateDate' => "$now"];
    $this->db->table('testdefsite')->insert($data);


value00: app/Database/Seeds/TestSeeder.php
value01:
    $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();


value00: app/Database/Seeds/TestSeeder.php
value01:
    $testMapDetails = []; // Store detail records

    // Helper to get or create TestMap header ID
    $getTestMapID = function($testSiteID, $hostType, $hostID, $clientType, $clientID) use (&$testMapHeaders, &$now) {
      $key = "{$hostType}_{$hostID}_{$clientType}_{$clientID}";
      if (!isset($testMapHeaders[$key])) {
        $data = [
          'TestSiteID' => $testSiteID,
          'HostType' => $hostType,
          'HostID' => $hostID,
          'ClientType' => $clientType,
          'ClientID' => $clientID,
          'CreateDate' => $now
        ];
        $this->db->table('testmap')->insert($data);
        $testMapHeaders[$key] = $this->db->insertID();
      }
      return $testMapHeaders[$key];
    };

    // ========================================


value00: app/Database/Seeds/TestSeeder.php
value01:
    $tIDs['MCH'] = $this->db->insertID();

    $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();

    // Add CBC members now that tests are defined


value00: app/Database/Seeds/TestSeeder.php
value01:
    $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' => '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();


value00: app/Database/Seeds/TestSeeder.php
value01:
    $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


value00: app/Database/Seeds/TestSeeder.php
value01:
    $this->db->table('testdefsite')->insert($data);
    $tIDs['DBIL'] = $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();


value00: app/Database/Migrations/2026-01-01-000011_CreatePatientCore.php
value01:
,
      'NameMiddle'  => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'NameMaiden'  => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'NameLast'    => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'Suffix'    => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'NameAlias'   => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'Sex'    => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'PlaceOfBirth'  => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'Birthdate'   => ['type' => 'DATE', 'null' => true]


value00: app/Database/Migrations/2026-01-01-000011_CreatePatientCore.php
value01:
,
      'DeathIndicator'=> ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'TimeOfDeath' => ['type' => 'DATETIME', 'null' => true],
      'LinkTo' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'CreateDate' => ['type' => 'DATETIME', 'null' => true],
      'DelDate' => ['type' => 'DATETIME', 'null' => true]
    ]);
    $this->forge->addKey('InternalPID', true);
    $this->forge->addUniqueKey('PatientID');
    $this->forge->addUniqueKey('AlternatePID');
    $this->forge->addUniqueKey('EmailAddress1');


value00: app/Database/Seeds/PatientSeeder.php
value01:
      [
        'InternalPID' => 2,
        'PatientID' => 'PAT002',
        'AlternatePID' => 'ALT002',
        'Prefix' => 'Mrs.',
        'NameFirst' => 'Jane',
        'NameMiddle' => null,
        'NameMaiden' => 'Smith',
        'NameLast' => 'Doe',
        'Suffix' => null,
        'NameAlias' => 'JaneDoe',
        'Sex' => '2',
        'PlaceOfBirth' => 'Bandung',
        'Birthdate' => '1985-10-20',
        'Street_1' => 'Jl. Asia Afrika No. 456'


value00: app/Database/Seeds/PatientSeeder.php
value01:
        'Birthdate' => '2000-01-01',
        'Street_1' => 'Jl. Pahlawan No. 789',
        'Street_2' => 'RT 05 RW 06',
        'Street_3' => null,
        'City' => '2',
        'Province' => '1',
        'ZIP' => '10220',
        'EmailAddress1' => 'budi.wijaya@test.com',
        'EmailAddress2' => null,
        'Phone' => null,
        'MobilePhone' => '081111222333',
        'Custodian' => 1,
        'AccountNumber' => null,
        'Country' => 'IDN',
        'Race' => 'JAWA',
        'MaritalStatus' => 'S',
        'Religion' => 'ISLAM',
        'Ethnic' => 'TNGHA'


value00: app/Database/Seeds/AreaGeoSeeder.php
value01:
    //   // Fetch data from external API
    //   $options = [
    //     'baseURI' => $this->apiUrl,
    //     'timeout' => 30,
    //   ];

    //   // $client = new CURLRequest($options);
    //   $client = Services::curlrequest([
    //       'baseURI' => $this->apiUrl,
    //       'timeout' => 30,
    //   ]);

    //   $response = $client->get('');

    //   if ($response->getStatusCode() !== 200) {
    //     echo "Failed to fetch data from API. Status: " . $response->getStatusCode() . "\n";
    //     return;
    //   }

    //   $externalData = $response->getJSON(true);

    //   if (empty($externalData)) {
    //     echo "No data found from API.\n";
    //     return;


value00: app/Filters/Cors.php
value01:
        // Allow same-origin requests (when no Origin header is present)
        if (empty($origin)) {
            return null;
        }

        if (in_array($origin, $this->allowedOrigins)) {
            $response->setHeader('Access-Control-Allow-Origin', $origin);
            $response->setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS');
            $response->setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With, Accept, Origin, Cache-Control, Pragma');
            // $response->setHeader('Access-Control-Allow-Headers', '*');
            $response->setHeader('Access-Control-Allow-Credentials', 'true');
        }

        // Tangani preflight OPTIONS dengan return response


value00: app/Database/Seeds/PatientSeeder.php
value01:
,
        'NameAlias' => 'DummyTest',
        'Sex' => '1',
        'PlaceOfBirth' => 'Jakarta',
        'Birthdate' => '1990-05-15',
        'Street_1' => 'Jl. Sudirman No. 123',
        'Street_2' => 'RT 01 RW 02',
        'Street_3' => 'Kelurahan Menteng',
        'City' => '2',
        'Province' => '1',
        'ZIP' => '10110',
        'EmailAddress1' => 'dummy1@test.com',
        'EmailAddress2' => 'dummy1alt@test.com'


value00: app/Database/Seeds/PatientSeeder.php
value01:
    foreach ($patidts as $patidt) {
      $exists = $this->db->table('patidt')->where('PatIdtID', $patidt['PatIdtID'])->get()->getRow();
      if (!$exists) {
        $this->db->table('patidt')->insert($patidt);
      }
    }
    echo "PatIdt data seeded (2 identifiers).\n";

    // ========================================
    // PATATT - Patient Attachments
    // ========================================


value00: app/Database/Seeds/PatientSeeder.php
value01:
,
        'Sex' => '2',
        'PlaceOfBirth' => 'Bandung',
        'Birthdate' => '1985-10-20',
        'Street_1' => 'Jl. Asia Afrika No. 456',
        'Street_2' => 'RT 03 RW 04',
        'Street_3' => null,
        'City' => '8',
        'Province' => '7',
        'ZIP' => '40112',
        'EmailAddress1' => 'jane.doe@test.com',
        'EmailAddress2' => null,
        'Phone' => '02298765432',
        'MobilePhone' => '089876543210',
        'Custodian' => null,
        'AccountNumber' => null,
        'Country' => 'IDN',
        'Race' => 'JAWA',
        'MaritalStatus' => 'S',
        'Religion' => 'KRSTN',
        'Ethnic' => 'TNGHA'


value00: app/Database/Migrations/2026-01-01-000011_CreatePatientCore.php
value01:
    $this->forge->addKey('PatIdtID', true);
    $this->forge->createTable('patidt');

    $this->forge->addField([
      'PatRelID'   => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
      'InternalPID'=> ['type' => 'INT', 'constraint' => 11, 'null' => true],
      'CreateDate' => ['type' => 'DATETIME', 'null' => true],
      'EndDate'  => ['type' => 'DATETIME', 'null' => true],
    ]);
    $this->forge->addKey('PatRelID', true);
    $this->forge->createTable('patrelation');

  }


value00: app/Database/Migrations/2026-01-01-000011_CreatePatientCore.php
value01:
    $this->forge->addKey('PatAttID', true);
    $this->forge->addUniqueKey('Address');
    $this->forge->createTable('patatt');

    $this->forge->addField([
      'PatComID'   => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
      'InternalPID'=> ['type' => 'INT', 'constraint' => 11, 'null' => true],
      'Comment'  => ['type' => 'TEXT', 'null' => true],
      'CreateDate' => ['type' => 'DATETIME', 'null' => true],
      'EndDate'  => ['type' => 'DATETIME', 'null' => true],
    ]);
    $this->forge->addKey('PatComID', true);
    $this->forge->addUniqueKey('InternalPID');
    $this->forge->createTable('patcom');


value00: app/Database/Seeds/PatientSeeder.php
value01:
    // PATIDT - Patient Identifiers (KTP, etc.)
    // ========================================
    $patidts = [
      [
        'PatIdtID' => 1,
        'InternalPID' => 1,
        'IdentifierType' => 'KTP',
        'Identifier' => '3171234567890001',
        'EffectiveDate' => '2015-01-01',
        'ExpirationDate' => '2025-01-01',
        'CreateDate' => $now,
        'DelDate' => null
      ],
      [
        'PatIdtID' => 2,
        'InternalPID' => 3,
        'IdentifierType' => 'SIM',
        'Identifier' => '1234567890123456789',
        'EffectiveDate' => '2020-06-15',
        'ExpirationDate' => '2025-06-15',
        'CreateDate' => $now,
        'DelDate' => null
      ],
    ];


value00: app/Database/Migrations/2026-01-01-000011_CreatePatientCore.php
value01:
,
      'EmailAddress1' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'EmailAddress2' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'Phone'     => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'MobilePhone'   => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'Custodian'   => ['type' => 'INT', 'constraint' => 11, 'null' => true],
      'AccountNumber' => ['type' => 'INT', 'constraint' => 11, 'null' => true],
      'Country'  => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'Race'    => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'MaritalStatus' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true]


value00: app/Database/Migrations/2026-01-01-000011_CreatePatientCore.php
value01:
,
      'MaritalStatus' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'Religion'  => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'Ethnic'    => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'Citizenship'   => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'DeathIndicator'=> ['type' => 'VARCHAR', 'constraint' => 10, 'null' => true],
      'TimeOfDeath' => ['type' => 'DATETIME', 'null' => true]


value00: app/Database/Seeds/PatientSeeder.php
value01:
    foreach ($patatts as $patatt) {
      $exists = $this->db->table('patatt')->where('PatAttID', $patatt['PatAttID'])->get()->getRow();
      if (!$exists) {
        $this->db->table('patatt')->insert($patatt);
      }
    }
    echo "PatAtt data seeded (3 attachments).\n";

    // ========================================
    // PATCOM - Patient Comments
    // ========================================


value00: app/Filters/Cors.php
value01:
        // Tangani preflight OPTIONS dengan return response
        if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
            log_message('debug', 'Cors Filter Triggered OK');
            return $response->setStatusCode(200)->setBody('OK');
        }
        log_message('debug', 'Cors Filter Triggered Second');
    }


value00: app/Database/Seeds/PatientSeeder.php
value01:
    // ========================================
    // PATATT - Patient Attachments
    // ========================================
    $patatts = [
      [
        'PatAttID' => 1,
        'InternalPID' => 1,
        'Address' => '/api/upload/patient1_photo1.jpg',
        'UserID' => 1,
        'CreateDate' => $now,
        'DelDate' => null
      ],
      [
        'PatAttID' => 2,
        'InternalPID' => 1,
        'Address' => '/api/upload/patient1_ktp.jpg',
        'UserID' => 1,
        'CreateDate' => $now,
        'DelDate' => null
      ],
      [
        'PatAttID' => 3,
        'InternalPID' => 3,
        'Address' => '/api/upload/patient3_photo.jpg',
        'UserID' => 1,
        'CreateDate' => $now,
        'DelDate' => null
      ],
    ];


value00: app/Database/Seeds/PatientSeeder.php
value01:
        'Country' => 'IDN',
        'Race' => 'JAWA',
        'MaritalStatus' => 'S',
        'Religion' => 'KRSTN',
        'Ethnic' => 'TNGHA',
        'Citizenship' => 'WNI',
        'DeathIndicator' => 'N',
        'TimeOfDeath' => null,
        'LinkTo' => null,
        'CreateDate' => $now,
        'DelDate' => null
      ],


value00: app/Database/Migrations/2026-01-01-000011_CreatePatientCore.php
value01:
,
      'PlaceOfBirth'  => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'Birthdate'   => ['type' => 'DATE', 'null' => true],
      'Street_1'    => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'Street_2'    => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'Street_3'    => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'City'      => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'Province'    => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'ZIP'       => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'EmailAddress1' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true]


value00: app/Database/Migrations/2026-01-01-000011_CreatePatientCore.php
value01:
  public function down() {
    $this->forge->dropTable('patrelation');
    $this->forge->dropTable('patidt');
    $this->forge->dropTable('patcom');
    $this->forge->dropTable('patatt');
    $this->forge->dropTable('patient');
  }
}


value00: app/Filters/Cors.php
value01:
    public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
    {
        log_message('debug', 'Cors Filter Triggered Thrid');
        return $response;
    }
}


value00: app/Database/Migrations/2026-01-01-000011_CreatePatientCore.php
value01:
    $this->forge->addUniqueKey('InternalPID');
    $this->forge->createTable('patcom');

    $this->forge->addField([
      'PatIdtID'    => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
      'InternalPID'   => ['type' => 'INT', 'constraint' => 11, 'null' => true],
      'IdentifierType'=> ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'Identifier'  => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'EffectiveDate' => ['type' => 'DATETIME', 'null' => true],
      'ExpirationDate'=> ['type' => 'DATETIME', 'null' => true],
      'CreateDate' => ['type' => 'DATETIME', 'null' => true],
      'DelDate'     => ['type' => 'DATETIME', 'null' => true],
    ]);
    $this->forge->addKey('PatIdtID', true);


value00: app/Filters/Cors.php
value01:
<?php

namespace App\Filters;

use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\Filters\FilterInterface;

class Cors implements FilterInterface
{
    protected $allowedOrigins = [
        'http://localhost:5173',
        'http://localhost',
        'https://clqms01.services-summit.my.id',
        'https://clqms01x.services-summit.my.id',
    ];

    public function before(RequestInterface $request, $arguments = null)
    {
        log_message('debug', 'Cors Filter Triggered First');
        $origin = $_SERVER['HTTP_ORIGIN'] ?? '';
        $response = service('response');

        // Allow same-origin requests (when no Origin header is present)


value00: app/Database/Seeds/PatientSeeder.php
value01:
    // ========================================
    // PATCOM - Patient Comments
    // ========================================
    $patcoms = [
      [
        'PatComID' => 1,
        'InternalPID' => 1,
        'Comment' => 'Test patient for unit testing. Has complete data including ID and attachments.',
        'CreateDate' => $now,
        'EndDate' => null
      ],
      [
        'PatComID' => 2,
        'InternalPID' => 3,
        'Comment' => 'Patient with custodian relationship to patient 1.',
        'CreateDate' => $now,
        'EndDate' => null
      ],
    ];


value00: app/Database/Seeds/PatientSeeder.php
value01:
<?php

namespace App\Database\Seeds;

use CodeIgniter\Database\Seeder;

class PatientSeeder extends Seeder
{
  public function run()
  {
    $now = date('Y-m-d H:i:s');

    // ========================================
    // PATIENT - Main patient data
    // ========================================
    $patients = [
      [
        'InternalPID' => 1,
        'PatientID' => 'SMAJ1',
        'AlternatePID' => 'ALT001',
        'Prefix' => 'Mr.',
        'NameFirst' => 'Dummy',
        'NameMiddle' => 'Test',
        'NameMaiden' => null,
        'NameLast' => 'Patient',
        'Suffix' => 'S.Kom',
        'NameAlias' => 'DummyTest',
        'Sex' => '1',
        'PlaceOfBirth' => 'Jakarta',
        'Birthdate' => '1990-05-15'


value00: app/Database/Seeds/AreaGeoSeeder.php
value01:
    //   if (empty($externalData)) {
    //     echo "No data found from API.\n";
    //     return;
    //   }

    //   // Prepare data for insertion (exclude AreaGeoID to allow auto-increment)
    //   $data = [];
    //   foreach ($externalData as $row) {
    //     $data[] = [
    //       'AreaCode' => $row['zonecode'] ?? null,
    //       'Class' => $row['zoneclass'] ?? null,
    //       'AreaName' => str_replace('_', ' ', $row['zonename'] ?? ''),
    //       'Parent' => $row['parentzoneid'] ?? null,
    //     ];
    //   }

    //   // Insert into local database
    //   $this->db->table('areageo')->insertBatch($data);

    //   echo "Successfully seeded " . count($data) . " area geo records.\n";


value00: app/Database/Seeds/PatientSeeder.php
value01:
        'Province' => '1',
        'ZIP' => '10110',
        'EmailAddress1' => 'dummy1@test.com',
        'EmailAddress2' => 'dummy1alt@test.com',
        'Phone' => '02112345678',
        'MobilePhone' => '081234567890',
        'Custodian' => null,
        'AccountNumber' => null,
        'Country' => 'IDN',
        'Race' => 'JAWA',
        'MaritalStatus' => 'M',
        'Religion' => 'ISLAM',
        'Ethnic' => 'TNGHA',
        'Citizenship' => 'WNI',
        'DeathIndicator' => 'N',
        'TimeOfDeath' => null,
        'LinkTo' => null,
        'CreateDate' => $now,
        'DelDate' => null
      ],


value00: app/Database/Seeds/AreaGeoSeeder.php
value01:
<?php

namespace App\Database\Seeds;

use CodeIgniter\Database\Seeder;
use CodeIgniter\HTTP\CURLRequest;
use Config\Services;

class AreaGeoSeeder extends Seeder
{
    /**
     * API configuration for fetching zones data
     * Configure in .env: AREAGEO_API_URL=https://services-summit.my.id/api/zones
     */
    protected string $apiUrl = 'http://services-summit.my.id/api/zones';

    // public function run()
    // {
    //   // Fetch data from external API
    //   $options = [
    //     'baseURI' => $this->apiUrl,
    //     'timeout' => 30,


value00: app/Database/Seeds/AreaGeoSeeder.php
value01:
        $payload = json_decode($response->getBody(), true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            echo "Invalid JSON response: " . json_last_error_msg() . PHP_EOL;
            return;
        }

        if (
            !isset($payload['status'], $payload['data']) ||
            $payload['status'] !== 'success'
        ) {
            echo "Unexpected API structure." . PHP_EOL;
            return;
        }

        $zones = $payload['data'];

        if (empty($zones)) {
            echo "No zone data found." . PHP_EOL;
            return;
        }


value00: app/Database/Seeds/AreaGeoSeeder.php
value01:
        if (empty($zones)) {
            echo "No zone data found." . PHP_EOL;
            return;
        }

        $data = [];
        foreach ($zones as $row) {
            $data[] = [
                'AreaCode' => $row['zonecode'] ?? null,
                'Class' => $row['zoneclass'] ?? null,
                'AreaName' => str_replace('_', ' ', $row['zonename'] ?? ''),
                'Parent' => $row['parentzoneid'],
            ];
        }

        $this->db->table('areageo')->insertBatch($data);

        echo "Seeded " . count($data) . " records." . PHP_EOL;
    }
}


value00: app/Database/Seeds/PatientSeeder.php
value01:
        'Country' => 'IDN',
        'Race' => 'JAWA',
        'MaritalStatus' => 'S',
        'Religion' => 'ISLAM',
        'Ethnic' => 'TNGHA',
        'Citizenship' => 'WNI',
        'DeathIndicator' => 'N',
        'TimeOfDeath' => null,
        'LinkTo' => '1,2',
        'CreateDate' => $now,
        'DelDate' => null
      ],
    ];

    foreach ($patients as $patient) {
      $exists = $this->db->table('patient')->where('InternalPID', $patient['InternalPID'])->get()->getRow();
      if (!$exists) {
        $this->db->table('patient')->insert($patient);
      }
    }
    echo "Patient data seeded (3 patients).\n";

    // ========================================
    // PATIDT - Patient Identifiers (KTP, etc.)
    // ========================================


value00: app/Database/Seeds/PatientSeeder.php
value01:
      [
        'InternalPID' => 3,
        'PatientID' => 'PAT003',
        'AlternatePID' => 'ALT003',
        'Prefix' => 'Mr.',
        'NameFirst' => 'Budi',
        'NameMiddle' => 'Santoso',
        'NameMaiden' => null,
        'NameLast' => 'Wijaya',
        'Suffix' => null,
        'NameAlias' => 'BudiW',
        'Sex' => '1',
        'PlaceOfBirth' => 'Surabaya',
        'Birthdate' => '2000-01-01',
        'Street_1' => 'Jl. Pahlawan No. 789',
        'Street_2' => 'RT 05 RW 06',
        'Street_3' => null


value00: app/Database/Seeds/PatientSeeder.php
value01:
    foreach ($patcoms as $patcom) {
      $exists = $this->db->table('patcom')->where('PatComID', $patcom['PatComID'])->get()->getRow();
      if (!$exists) {
        $this->db->table('patcom')->insert($patcom);
      }
    }
    echo "PatCom data seeded (2 comments).\n";

    echo "\n✅ All Patient test data seeded successfully!\n";
    echo "-------------------------------------------\n";
    echo "Patients: 3 (IDs: 1, 2, 3)\n";
    echo "  - Patient 1: Full data with PatIdt, PatAtt (2), PatCom\n";
    echo "  - Patient 2: No PatIdt, PatAtt, PatCom (for null testing)\n";
    echo "  - Patient 3: Has PatIdt, PatAtt, PatCom, and Custodian link\n";
    echo "-------------------------------------------\n";
  }
}


value00: app/Database/Seeds/AreaGeoSeeder.php
value01:
    //   $this->db->table('areageo')->insertBatch($data);

    //   echo "Successfully seeded " . count($data) . " area geo records.\n";
    // }
    public function run()
    {
        // Get API URL from environment or use default (HTTP for production)
        $this->apiUrl = env('AREAGEO_API_URL', $this->apiUrl);
        
        $client = Services::curlrequest([
            'baseURI' => $this->apiUrl,
            'timeout' => 30,
            'verify' => str_starts_with($this->apiUrl, 'https://'),
        ]);

        $response = $client->get('');

        if ($response->getStatusCode() !== 200) {
            echo "HTTP Error: {$response->getStatusCode()}" . PHP_EOL;
            return;
        }

        $payload = json_decode($response->getBody(), true);


value00: app/Database/Seeds/OrganizationSeeder.php
value01:
    $this->db->table('workstation')->insertBatch($data);

    // Equipment/Instruments for each workstation
    $data = [
      // Hematology Auto (WorkstationID: 1)
      ['EID' => 1, 'IEID' => 'EQ-HEM-001', 'DepartmentID' => '1', 'InstrumentID' => 'SYSMEX_XN1000', 'InstrumentName' => 'Sysmex XN-1000 Hematology Analyzer', 'WorkstationID' => 1, 'Enable' => 1, 'EquipmentRole' => 'A', 'CreateDate' => "$now"],
      ['EID' => 2, 'IEID' => 'EQ-HEM-002', 'DepartmentID' => '1', 'InstrumentID' => 'SYSMEX_XS1000', 'InstrumentName' => 'Sysmex XS-1000i', 'WorkstationID' => 1, 'Enable' => 1, 'EquipmentRole' => 'A', 'CreateDate' => "$now"],
      // Hematology Backup (WorkstationID: 2)
      ['EID' => 3, 'IEID' => 'EQ-HEM-003', 'DepartmentID' => '1', 'InstrumentID' => 'SYSMEX_XN550'


value00: app/Database/Seeds/OrganizationSeeder.php
value01:
    $data = [
      [ 'AccountID' => 1, 'Parent' => null, 'AccountName' => 'Dummy Account', 'Initial' => 'QAC', 'Street_1' => 'Dummy Address', 'EmailAddress1' => 'dummy@summit.co.id', 'Phone' => '092029', 'Fax' => '092029', 'CreateDate' => "$now" ],
      [ 'AccountID' => 2, 'Parent' => null, 'AccountName' => 'Dummy Account', 'Initial' => 'QAC', 'Street_1' => 'Dummy Address', 'EmailAddress1' => 'dummy@summit.co.id', 'Phone' => '092029', 'Fax' => '092029', 'CreateDate' => "$now" ],
      [ 'AccountID' => 3, 'Parent' => null, 'AccountName' => 'Dummy Account', 'Initial' => 'QAC', 'Street_1' => 'Dummy Address', 'EmailAddress1' => 'dummy@summit.co.id', 'Phone' => '092029', 'Fax' => '092029', 'CreateDate' => "$now" ]
    ];
    $this->db->table('account')->insertBatch($data);


value00: app/Database/Seeds/OrganizationSeeder.php
value01:
      ['EID' => 8, 'IEID' => 'EQ-CHEM-004', 'DepartmentID' => '3', 'InstrumentID' => 'SPEC_MANUAL', 'InstrumentName' => 'Spectrophotometer Manual', 'WorkstationID' => 5, 'Enable' => 1, 'EquipmentRole' => 'M', 'CreateDate' => "$now"],
      // Immunology Auto (WorkstationID: 6)
      ['EID' => 9, 'IEID' => 'EQ-IMM-001', 'DepartmentID' => '4', 'InstrumentID' => 'ARCH_I2000', 'InstrumentName' => 'Architect i2000 Immunoassay', 'WorkstationID' => 6, 'Enable' => 1, 'EquipmentRole' => 'A', 'CreateDate' => "$now"],
      ['EID' => 10, 'IEID' => 'EQ-IMM-002', 'DepartmentID' => '4', 'InstrumentID' => 'COBAS_E601', 'InstrumentName' => 'Roche Cobas E601', 'WorkstationID' => 6, 'Enable' => 1, 'EquipmentRole' => 'A', 'CreateDate' => "$now"],
      // Immunology Manual (WorkstationID: 7)


value00: app/Database/Seeds/OrganizationSeeder.php
value01:
      ['EID' => 5, 'IEID' => 'EQ-CHEM-001', 'DepartmentID' => '3', 'InstrumentID' => 'COBAS_C501', 'InstrumentName' => 'Roche Cobas C501 Chemistry Analyzer', 'WorkstationID' => 3, 'Enable' => 1, 'EquipmentRole' => 'A', 'CreateDate' => "$now"],
      ['EID' => 6, 'IEID' => 'EQ-CHEM-002', 'DepartmentID' => '3', 'InstrumentID' => 'COBAS_C311', 'InstrumentName' => 'Roche Cobas C311', 'WorkstationID' => 3, 'Enable' => 1, 'EquipmentRole' => 'A', 'CreateDate' => "$now"],
      // Chemistry Backup (WorkstationID: 4)
      ['EID' => 7, 'IEID' => 'EQ-CHEM-003', 'DepartmentID' => '3', 'InstrumentID' => 'COBAS_E411', 'InstrumentName' => 'Roche Cobas E411 Immunoassay', 'WorkstationID' => 4, 'Enable' => 1, 'EquipmentRole' => 'B', 'CreateDate' => "$now"],
      // Chemistry Manual (WorkstationID: 5)


value00: app/Database/Seeds/OrganizationSeeder.php
value01:
,
      ['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"],


value00: app/Database/Seeds/OrganizationSeeder.php
value01:
,
      ['WorkstationID' => '4', 'DepartmentID' => '3', 'WorkstationCode' => 'CBACK', 'WorkstationName' => 'Kimia Backup', 'Type' => '1', 'LinkTo' => '3', 'Enable' => '1', 'CreateDate' => "$now"],
      ['WorkstationID' => '5', 'DepartmentID' => '3', 'WorkstationCode' => 'CMAN', 'WorkstationName' => 'Kimia Manual', 'Type' => '0', 'LinkTo' => '', 'Enable' => '1', 'CreateDate' => "$now"],
      ['WorkstationID' => '6', 'DepartmentID' => '4', 'WorkstationCode' => 'IAUTO', 'WorkstationName' => 'Imunologi Auto', 'Type' => '0', 'LinkTo' => '', 'Enable' => '1', 'CreateDate' => "$now"],
      ['WorkstationID' => '7', 'DepartmentID' => '4', 'WorkstationCode' => 'IMAN', 'WorkstationName' => 'Imunologi Manual', 'Type' => '0', 'LinkTo' => '', 'Enable' => '1', 'CreateDate' => "$now"],


value00: app/Database/Seeds/OrganizationSeeder.php
value01:
, 'WorkstationCode' => 'IMAN', 'WorkstationName' => 'Imunologi Manual', 'Type' => '0', 'LinkTo' => '', 'Enable' => '1', 'CreateDate' => "$now"],
			['WorkstationID' => '8', 'DepartmentID' => '6', 'WorkstationCode' => 'UAUTO', 'WorkstationName' => 'Urin Auto', 'Type' => '0', 'LinkTo' => '', 'Enable' => '1', 'CreateDate' => "$now"],
      ['WorkstationID' => '9', 'DepartmentID' => '6', 'WorkstationCode' => 'UMAN', 'WorkstationName' => 'Urin Manual', 'Type' => '0', 'LinkTo' => '', 'Enable' => '1', 'CreateDate' => "$now"],
    ];
    $this->db->table('workstation')->insertBatch($data);

    // Equipment/Instruments for each workstation


value00: app/Database/Migrations/2026-01-01-000011_CreatePatientCore.php
value01:
    $this->forge->addUniqueKey('AlternatePID');
    $this->forge->addUniqueKey('EmailAddress1');
    $this->forge->createTable('patient');

    $this->forge->addField([
      'PatAttID'   => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
      'InternalPID'=> ['type' => 'INT', 'constraint' => 11, 'null' => true],
      'Address'  => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'UserID'   => ['type' => 'INT', 'constraint' => 11, 'null' => true],
      'CreateDate' => ['type' => 'DATETIME', 'null' => true],
      'DelDate'  => ['type' => 'DATETIME', 'null' => true],
    ]);
    $this->forge->addKey('PatAttID', true);
    $this->forge->addUniqueKey('Address');
    $this->forge->createTable('patatt');


value00: app/Database/Migrations/2026-01-01-000011_CreatePatientCore.php
value01:
<?php

namespace App\Database\Migrations;

use CodeIgniter\Database\Migration;

class CreatePatientCore extends Migration {
  public function up() {
    $this->forge->addField([
      'InternalPID'   => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
      'PatientID'   => ['type' => 'VARCHAR', 'constraint' => 255],
      'AlternatePID'  => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'Prefix'    => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'NameFirst'   => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
      'NameMiddle'  => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true]


value00: app/Database/Seeds/OrganizationSeeder.php
value01:
      ['DepartmentID' => '2', 'DisciplineID' => '1', 'SiteID' => '165', 'DepartmentCode' => 'HKHS', 'DepartmentName' => 'Hematologi Khusus', 'CreateDate' => "$now"],
      ['DepartmentID' => '3', 'DisciplineID' => '2', 'SiteID' => '165', 'DepartmentCode' => 'KK', 'DepartmentName' => 'Kimia Klinik', 'CreateDate' => "$now"],
      ['DepartmentID' => '4', 'DisciplineID' => '3', 'SiteID' => '165', 'DepartmentCode' => 'IMS', 'DepartmentName' => 'Imunologi', 'CreateDate' => "$now"],
      ['DepartmentID' => '5', 'DisciplineID' => '3', 'SiteID' => '165', 'DepartmentCode' => 'SRL', 'DepartmentName' => 'Serologi', 'CreateDate' => "$now"],
      ['DepartmentID' => '6', 'DisciplineID' => '4', 'SiteID' => '165', 'DepartmentCode' => 'UF', 'DepartmentName' => 'Urin', 'CreateDate' => "$now"]
    ]


value00: app/Database/Seeds/OrganizationSeeder.php
value01:
<?php

namespace App\Database\Seeds;

use CodeIgniter\Database\Seeder;
use App\Libraries\ValueSet;

class OrganizationSeeder extends Seeder
{
  public function run()
  {
    $now = date('Y-m-d H:i:s');

    // Organization
    $data = [
      [ 'AccountID' => 1, 'Parent' => null, 'AccountName' => 'Dummy Account', 'Initial' => 'QAC', 'Street_1' => 'Dummy Address'


value00: app/Database/Seeds/OrganizationSeeder.php
value01:
    $this->db->table('account')->insertBatch($data);

    $data = [
      [ '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, 'SeqScr' => 10, 'SeqRpt' => 10, 'CreateDate' => "$now"],
      ['DisciplineID' => '2', 'DisciplineCode' => 'CHEM', 'DisciplineName' => 'Clinical Chemistry', 'Parent' => null, 'SeqScr' => 20, 'SeqRpt' => 20


value00: app/Database/Seeds/OrganizationSeeder.php
value01:
;
    $this->db->table('department')->insertBatch($data);

    $data = [
      ['WorkstationID' => '1', 'DepartmentID' => '1', 'WorkstationCode' => 'HAUTO', 'WorkstationName' => 'Hematologi Auto', 'Type' => '0', 'LinkTo' => '', 'Enable' => '1', 'CreateDate' => "$now"],
      ['WorkstationID' => '2', 'DepartmentID' => '1', 'WorkstationCode' => 'HBACK', 'WorkstationName' => 'Hematologi Backup', 'Type' => '1', 'LinkTo' => '1', 'Enable' => '1', 'CreateDate' => "$now"],
      ['WorkstationID' => '3', 'DepartmentID' => '3', 'WorkstationCode' => 'CAUTO', 'WorkstationName' => 'Kimia Auto', 'Type' => '0', 'LinkTo' => '', 'Enable' => '1', 'CreateDate' => "$now"],
      ['WorkstationID' => '4', 'DepartmentID' => '3', 'WorkstationCode' => 'CBACK', 'WorkstationName' => 'Kimia Backup',


value00: app/Database/Seeds/OrganizationSeeder.php
value01:
,
      // Immunology Manual (WorkstationID: 7)
      ['EID' => 11, 'IEID' => 'EQ-IMM-003', 'DepartmentID' => '4', 'InstrumentID' => 'ELISA_READER', 'InstrumentName' => 'ELISA Plate Reader', 'WorkstationID' => 7, 'Enable' => 1, 'EquipmentRole' => 'M', 'CreateDate' => "$now"],
      ['EID' => 12, 'IEID' => 'EQ-IMM-004', 'DepartmentID' => '4', 'InstrumentID' => 'FLUORIMETER', 'InstrumentName' => 'Fluorimeter Manual', 'WorkstationID' => 7, 'Enable' => 1, 'EquipmentRole' => 'M', 'CreateDate' => "$now"],
      // Urinalysis Auto (WorkstationID: 8)
      ['EID' => 13, 'IEID' => 'EQ-URI-001', 'DepartmentID' => '6', 'InstrumentID' => 'URISYS_1100', 'InstrumentName' => 'Urisys 1100 Urine Analyzer', 'WorkstationID' => 8, 'Enable' => 1, 'EquipmentRole' => 'A', 'CreateDate' => "$now"],


value00: app/Database/Seeds/OrganizationSeeder.php
value01:
      ['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"],


value00: app/Database/Seeds/OrganizationSeeder.php
value01:
,
      ['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"],
    ]


value00: app/Database/Seeds/OrganizationSeeder.php
value01:
,
      ['EID' => 14, 'IEID' => 'EQ-URI-002', 'DepartmentID' => '6', 'InstrumentID' => 'CLINITEK', 'InstrumentName' => 'Clinitek Urine Chemistry', 'WorkstationID' => 8, 'Enable' => 1, 'EquipmentRole' => 'A', 'CreateDate' => "$now"],
      // Urinalysis Manual (WorkstationID: 9)
      ['EID' => 15, 'IEID' => 'EQ-URI-003', 'DepartmentID' => '6', 'InstrumentID' => 'MICROSCOPE', 'InstrumentName' => 'Microscope Manual', 'WorkstationID' => 9, 'Enable' => 1, 'EquipmentRole' => 'M', 'CreateDate' => "$now"],
      ['EID' => 16, 'IEID' => 'EQ-URI-004', 'DepartmentID' => '6', 'InstrumentID' => 'CENTRIFUGE', 'InstrumentName' => 'Centrifuge Urine', 'WorkstationID' => 9, 'Enable' => 1, 'EquipmentRole' => 'M', 'CreateDate' => "$now"],
      // Additional equipment (disabled)


value00: app/Database/Seeds/OrganizationSeeder.php
value01:
,
      // Hematology Backup (WorkstationID: 2)
      ['EID' => 3, 'IEID' => 'EQ-HEM-003', 'DepartmentID' => '1', 'InstrumentID' => 'SYSMEX_XN550', 'InstrumentName' => 'Sysmex XN-550 Backup', 'WorkstationID' => 2, 'Enable' => 1, 'EquipmentRole' => 'B', 'CreateDate' => "$now"],
      ['EID' => 4, 'IEID' => 'EQ-HEM-004', 'DepartmentID' => '1', 'InstrumentID' => 'MANUAL_DIFF', 'InstrumentName' => 'Manual Differential Counter', 'WorkstationID' => 2, 'Enable' => 1, 'EquipmentRole' => 'M', 'CreateDate' => "$now"],
      // Chemistry Auto (WorkstationID: 3)


value00: app/Database/Seeds/OrganizationSeeder.php
value01:
;
    $this->db->table('discipline')->insertBatch($data);

    $data = [
      ['DepartmentID' => '1', 'DisciplineID' => '1', 'SiteID' => '165', 'DepartmentCode' => 'HRTN', 'DepartmentName' => 'Hematologi Rutin', 'CreateDate' => "$now"],
      ['DepartmentID' => '2', 'DisciplineID' => '1', 'SiteID' => '165', 'DepartmentCode' => 'HKHS', 'DepartmentName' => 'Hematologi Khusus'


value00: app/Database/Seeds/OrganizationSeeder.php
value01:
, 'WorkstationID' => 9, 'Enable' => 1, 'EquipmentRole' => 'M', 'CreateDate' => "$now"],
      // Additional equipment (disabled)
      ['EID' => 17, 'IEID' => 'EQ-HEM-005', 'DepartmentID' => '1', 'InstrumentID' => 'OLD_ANALYZER', 'InstrumentName' => 'Old Hematology Analyzer', 'WorkstationID' => 1, 'Enable' => 0, 'EquipmentRole' => 'B', 'CreateDate' => "$now"],
      ['EID' => 18, 'IEID' => 'EQ-CHEM-005', 'DepartmentID' => '3', 'InstrumentID' => 'MAINTENANCE', 'InstrumentName' => 'Chemistry Analyzer Under Maintenance', 'WorkstationID' => 3, 'Enable' => 0, 'EquipmentRole' => 'B', 'CreateDate' => "$now"],
    ];
    $this->db->table('equipmentlist')->insertBatch($data);
  }
}


value00: app/Database/Seeds/SpecimenSeeder.php
value01:
      ['ConCode' => '200', 'ConName' => 'Aliquot', 'ConDesc' => 'General specimen container, no additive, non-sterile. Untuk aliquot', 'Additive' => null, 'ConClass' => 'Sec', 'CreateDate' => "$now"],
      ['ConCode' => '290', 'ConName' => 'Pot Urin', 'ConDesc' => 'Non-sterile urine specimen container IVD', 'Additive' => null, 'ConClass' => 'Pri', 'CreateDate' => "$now"],
      ['ConCode' => '295', 'ConName' => 'Urine Container', 'ConDesc' => 'Urine specimen container', 'Additive' => null, 'ConClass' => 'Pri', 'CreateDate' => "$now"],
      ['ConCode' => '900', 'ConName' => 'Packing Pengiriman', 'ConDesc' => 'Specimen Transport Packaging', 'Additive' => null, 'ConClass' => 'Sec', 'CreateDate' => "$now"],
    ];
    $this->db->table('containerdef')->insertBatch($data);
    
  }
}


value00: app/Database/Seeds/SpecimenSeeder.php
value01:
<?php

namespace App\Database\Seeds;

use CodeIgniter\Database\Seeder;

class SpecimenSeeder extends Seeder {
  
  protected array $map = [];

  public function run() {
    $now = date('Y-m-d H:i:s');
    
    // containerdef
    $data = [
      ['ConCode' => '1', 'ConName' => 'SST', 'ConDesc' => 'Evacuated blood collection tube, gel separator', 'Additive' => "Hep", 'ConClass' => 'Pri', 'CreateDate' => "$now"],
      ['ConCode' => '11', 'ConName' => 'Plain', 'ConDesc' => 'Evacuated blood collection tube, no additive/metal-free', 'Additive' => "EDTA", 'ConClass' => 'Pri', 'CreateDate' => "$now"],
      ['ConCode' => '12', 'ConName' => '2Hr PP', 'ConDesc' => 'Evacuated blood collection tube, untuk Glukosa 2 Jam PP', 'Additive' => "NaF"


value00: app/Database/Seeds/SpecimenSeeder.php
value01:
,
      ['ConCode' => '12', 'ConName' => '2Hr PP', 'ConDesc' => 'Evacuated blood collection tube, untuk Glukosa 2 Jam PP', 'Additive' => "NaF", 'ConClass' => 'Pri', 'CreateDate' => "$now"],
      ['ConCode' => '13', 'ConName' => 'Glukosa Sewaktu', 'ConDesc' => 'Evacuated blood collection tube, untuk Glukosa Sewaktu', 'Additive' => "NaF", 'ConClass' => 'Pri', 'CreateDate' => "$now"],
      ['ConCode' => '14', 'ConName' => 'GTT 30 menit', 'ConDesc' => 'Evacuated blood collection tube, untuk GTT 30 menit', 'Additive' => "NaF", 'ConClass' => 'Pri', 'CreateDate' => "$now"],
      ['ConCode' => '15', 'ConName' => 'GTT 60 menit', 'ConDesc' => 'Evacuated blood collection tube, untuk GTT 60 menit', 'Additive' => "NaF", 'ConClass' => 'Pri', 'CreateDate' => "$now"],


value00: app/Database/Seeds/SpecimenSeeder.php
value01:
,
      ['ConCode' => '16', 'ConName' => 'GTT 120 menit', 'ConDesc' => 'Evacuated blood collection tube, untuk GTT 90 menit', 'Additive' => "NaF", 'ConClass' => 'Pri', 'CreateDate' => "$now"],
      ['ConCode' => '20', 'ConName' => 'RST', 'ConDesc' => 'Evacuated blood collection tube, thrombin/clot activator/gel separator', 'Additive' => "NaF", 'ConClass' => 'Pri', 'CreateDate' => "$now"],
      ['ConCode' => '101', 'ConName' => 'EDTA -  Hematologi', 'ConDesc' => 'Evacuated blood collection tube, K2EDTA/aprotinin', 'Additive' => "NaCtrt", 'ConClass' => 'Pri', 'CreateDate' => "$now"],
      ['ConCode' => '150', 'ConName' => 'Citrate - Koagulasi', 'ConDesc' => 'Evacuated blood collection tube, untuk koagulasi', 'Additive' => "NaCtrt", 'ConClass' => 'Pri', 'CreateDate' => "$now"],


value00: app/Filters/AuthFilter.php
value01:
        try {
            // Decode JWT : jika error maka akan mentrigger catch
            $decoded = JWT::decode($token, new Key($key, 'HS256'));

            file_put_contents(WRITEPATH . 'logs/tokens.log', date('Y-m-d H:i:s') . ' - ' . $token . PHP_EOL, FILE_APPEND);
            
            // Kalau mau, bisa inject user info ke request
            // $request->userData = $decoded;

        }


value00: app/Filters/AuthFilter.php
value01:
function addCorsHeaders($response)
    {
        $origin = $_SERVER['HTTP_ORIGIN'] ?? '';
        $allowedOrigins = [
            'http://localhost:5173',
            'http://localhost',
            'https://clqms01.services-summit.my.id',
        ];
        
        if (in_array($origin, $allowedOrigins)) {
            $response->setHeader('Access-Control-Allow-Origin', $origin);
            $response->setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS');
            $response->setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With, Accept, Origin, Cache-Control, Pragma');
            $response->setHeader('Access-Control-Allow-Credentials', 'true');
        }
        
        return $response;
    }


value00: app/Filters/AuthFilter.php
value01:
            // Kalau mau, bisa inject user info ke request
            // $request->userData = $decoded;

        } catch (\Exception $e) {
            if ($isApiRequest) {
                return Services::response()
                    ->setStatusCode(401)
                    ->setJSON([
                        'status'  => 'failed',
                        'message' => 'Unauthorized: ' . $e->getMessage()
                    ]);
            }
            // Redirect to login for page requests
            return redirect()->to('/v2/login');
        }
    }

    public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
    {
        // Tidak perlu apa-apa
    }
}


value00: app/Filters/AuthFilter.php
value01:
    public function before(RequestInterface $request, $arguments = null)
    {
        $key   = getenv('JWT_SECRET');
        $token = $request->getCookie('token'); // ambil dari cookie

        // Check if this is an API request or a page request
        $isApiRequest = strpos($request->getUri()->getPath(), '/api/') !== false 
                     || $request->isAJAX();

        // Kalau tidak ada token


value00: app/Filters/AuthFilter.php
value01:
<?php

namespace App\Filters;

use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\Filters\FilterInterface;
use Config\Services;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;

class AuthFilter implements FilterInterface
{
    protected function addCorsHeaders($response)
    {
        $origin = $_SERVER['HTTP_ORIGIN'] ?? '';


value00: app/Filters/AuthFilter.php
value01:
        // Kalau tidak ada token
        if (!$token) {
            if ($isApiRequest) {
                return Services::response()
                    ->setStatusCode(401)
                    ->setJSON([
                        'status'  => 'failed',
                        'message' => 'Unauthorized: Token not found'
                    ]);
            }
            // Redirect to login for page requests
            return redirect()->to('/v2/login');
        }


value00: app/Helpers/json_helper.php
value01:
if (!function_exists('prepare_json_response')) {
  /**
   * Prepare data for JSON response by converting empty strings to null
   * This is a convenience wrapper around convert_empty_strings_to_null
   * 
   * @param mixed $data The data to prepare for JSON response
   * @return mixed The processed data ready for JSON encoding
   */
  function prepare_json_response($data) {
    return convert_empty_strings_to_null($data);
  }
}


value00: app/Helpers/json_helper.php
value01:
convert_empty_strings_to_null($data) {
    if (is_array($data)) {
      foreach ($data as $key => $value) {
        if (is_array($value)) {
          $data[$key] = convert_empty_strings_to_null($value);
        } elseif (is_object($value)) {
          $data[$key] = convert_empty_strings_to_null($value);
        } elseif ($value === '') {
          $data[$key] = null;
        }
      }
    } elseif (is_object($data)) {
      foreach ($data as $key => $value) {
        if (is_array($value)) {
          $data->$key = convert_empty_strings_to_null($value);
        } elseif (is_object($value)) {
          $data->$key = convert_empty_strings_to_null($value);
        } elseif ($value === '') {
          $data->$key = null;
        }
      }
    }
    return $data;
  }
}


value00: app/Helpers/json_helper.php
value01:
<?php

if (!function_exists('convert_empty_strings_to_null')) {
  /**
   * Recursively convert empty strings to null in arrays or objects
   * 
   * @param mixed $data The data to process (array, object, or scalar)
   * @return mixed The processed data with empty strings converted to null
   */
  function convert_empty_strings_to_null($data)


value00: app/Models/BaseModel.php
value01:
<?php

namespace App\Models;

use CodeIgniter\Model;

class BaseModel extends Model {
  protected $beforeInsert = ['normalizeDatesToUTC'];
  protected $beforeUpdate = ['normalizeDatesToUTC'];
  protected $afterFind = ['convertDatesToUTCISO'];
  protected $afterInsert = ['convertDatesToUTCISO'];
  protected $afterUpdate = ['convertDatesToUTCISO'];

  protected function normalizeDatesToUTC(array $data) {
    helper('utc');
    if (isset($data['data'])) {
      $data['data'] = convert_array_to_utc($data['data']);
    }
    return $data;
  }

  protected function convertDatesToUTCISO(array $data) {
    helper('utc');
    if (isset($data['data'])) {
      $data['data'] = convert_array_to_utc_iso($data['data']);
    }
    return $data;
  }
}


value00: app/Helpers/utc_helper.php
value01:
<?php

use CodeIgniter\I18n\Time;

if (!function_exists('convert_array_to_utc')) {
  function convert_array_to_utc(array $data): array {
    foreach ($data as $key => $value) {
      if (is_array($value)) {
        $data[$key] = convert_array_to_utc($value);
      } elseif (is_string($value) && is_datetime_string($value)) {
        if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) {
          continue;
        }
        try {
          $data[$key] = Time::parse($value)->setTimezone('UTC')->toDateTimeString();
        } catch (\Exception $e) {
          // skip if not valid
        }
      }
    }
    return $data;
  }
}


value00: app/Helpers/utc_helper.php
value01:
if (!function_exists('is_datetime_string')) {
  function is_datetime_string(string $value): bool {
    return preg_match(
      '/^\d{4}-\d{2}-\d{2}(?:[T\s]\d{2}:\d{2}:\d{2}(?:[\+\-]\d{2}:?\d{2}|Z)?)?$/',
      $value
    ) === 1;
  }
}


value00: app/Helpers/utc_helper.php
value01:
if (!function_exists('convert_array_to_utc_iso')) {
  function convert_array_to_utc_iso($data) {
    if (is_array($data)) {
      foreach ($data as $key => $value) {
        if (is_array($value)) {
          $data[$key] = convert_array_to_utc_iso($value);
        } elseif (is_string($value) && is_datetime_string($value)) {
          if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) {
            continue;
          }
          try {
            $data[$key] = Time::parse($value, 'UTC')->format('Y-m-d\TH:i:s.v\Z');
          } catch (\Exception $e) {
            // skip
          }
        }
      }
    } elseif (is_object($data)) {
      foreach ($data as $key => $value) {
        $data->$key = convert_array_to_utc_iso($value);
      }
    }
    return $data;
  }
}


value00: app/Database/Seeds/ClearOrderDataSeeder.php
value01:
    $this->db->table('specimenstatus')->truncate();
    echo "  - specimenstatus truncated\n";

    $this->db->table('specimen')->truncate();
    echo "  - specimen truncated\n";

    $this->db->table('ordertest')->truncate();
    echo "  - ordertest truncated\n";

    // Re-enable foreign key checks
    $this->db->query('SET FOREIGN_KEY_CHECKS = 1');

    echo "\nAll order-related tables cleared successfully!\n";
  }
}


value00: app/Database/Seeds/ClearOrderDataSeeder.php
value01:
<?php

namespace App\Database\Seeds;

use CodeIgniter\Database\Seeder;

class ClearOrderDataSeeder extends Seeder
{
  public function run()
  {
    echo "Clearing order-related tables...\n";

    // Disable foreign key checks temporarily
    $this->db->query('SET FOREIGN_KEY_CHECKS = 0');

    // Clear tables in reverse dependency order
    $this->db->table('ordercom')->truncate();
    echo "  - ordercom truncated\n";

    $this->db->table('orderstatus')->truncate();
    echo "  - orderstatus truncated\n";

    $this->db->table('patres')->truncate();
    echo "  - patres truncated\n";

    $this->db->table('specimenstatus')->truncate();
    echo "  - specimenstatus truncated\n";


value00: app/Database/Seeds/ContactSeeder.php
value01:
    $data = [
      [
        'ContactID' => 1, 'NameFirst' => 'Default', 'NameLast' => 'Doctor', 'Title' => '', 'Initial' => 'DEFDOC', 
        'Birthdate' => '', 'EmailAddress1' => 'd1@as.com', 'EmailAddress2' => '', 'Phone' => '', 'MobilePhone1' => '', 'MobilePhone2' => '', 'Specialty' => '', 'SubSpecialty' => '', 'CreateDate' => "$now" 
      ],
      [
        'ContactID' => 2, 'NameFirst' => 'Dummy', 'NameLast' => 'Doctor', 'Title' => '', 'Initial' => 'QDOC',
        'Birthdate' => '', 'EmailAddress1' => 'd2a@gmail.com', 'EmailAddress2' => '', 'Phone' => '', 'MobilePhone1' => '', 'MobilePhone2' => '', 'Specialty' => '', 'SubSpecialty' => '', 'CreateDate' => "$now" 
      ]
    ];
    $this->db->table('contact')->insertBatch($data);


value00: app/Database/Seeds/ContactSeeder.php
value01:
,
      ['SpecialtyID' => '24', 'SpecialtyText' => 'Mikrobiologi Klinik', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '25', 'SpecialtyText' => 'Neurologi', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '26', 'SpecialtyText' => 'Obstetri dan Ginekologi', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '27', 'SpecialtyText' => 'Oftalmologi', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '28', 'SpecialtyText' => 'Onkologi Radiasi', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '29', 'SpecialtyText' => 'Orthopaedi dan Traumatologi', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"]


value00: app/Database/Seeds/ContactSeeder.php
value01:
,
      ['SpecialtyID' => '34', 'SpecialtyText' => 'Pulmonologi dan Kedokteran Respirasi', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '35', 'SpecialtyText' => 'Radiologi', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '36', 'SpecialtyText' => 'Telinga Hidung Tenggorok Bedah Kepala Leher', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '37', 'SpecialtyText' => 'Urologi', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '38', 'SpecialtyText' => 'Bedah Mulut dan Maksilofasial', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"]


value00: app/Database/Seeds/ContactSeeder.php
value01:
,
      ['SpecialtyID' => '13', 'SpecialtyText' => 'Forensik dan Medikolegal', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '14', 'SpecialtyText' => 'Gizi Klinik', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '15', 'SpecialtyText' => 'Jantung dan Pembuluh Darah', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '16', 'SpecialtyText' => 'Kedokteran Fisik dan Rehabilitasi', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"]


value00: app/Database/Seeds/ContactSeeder.php
value01:
    $data = [
      ['SiteID' => 1, 'ContactID' => 1, 'ContactCode' => 'DEFDOC', 'ContactEmail' => 'defdoc@email.com', 'OccupationID' => '', 'JobTitle' => '', 'Department' => 'Jantung Sehat' ],
      ['SiteID' => 2, 'ContactID' => 1, 'ContactCode' => 'QDOC', 'ContactEmail' => 'qdoc@email.com', 'OccupationID' => '', 'JobTitle' => '', 'Department' => 'Hati Sehat' ],
      ['SiteID' => 1, 'ContactID' => 2, 'ContactCode' => 'S923', 'ContactEmail' => 'defdoc@email.com', 'OccupationID' => '', 'JobTitle' => '', 'Department' => 'Jantung Sehat' ],
      ['SiteID' => 2, 'ContactID' => 2, 'ContactCode' => 'B231', 'ContactEmail' => 'defdoc@email.com', 'OccupationID' => '', 'JobTitle' => ''


value00: app/Database/Seeds/ContactSeeder.php
value01:
      ['SpecialtyID' => '20', 'SpecialtyText' => 'Kedokteran Nuklir dan Teranostik Molekuler', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '21', 'SpecialtyText' => 'Kedokteran Okupasi', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '22', 'SpecialtyText' => 'Kedokteran Olahraga', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '23', 'SpecialtyText' => 'Kedokteran Penerbangan', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '24', 'SpecialtyText' => 'Mikrobiologi Klinik', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"]


value00: app/Database/Seeds/ContactSeeder.php
value01:
,
      ['SpecialtyID' => '48', 'SpecialtyText' => 'Alergi-Immunologi Klinik', 'Parent' => '33', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '49', 'SpecialtyText' => 'Gastroenterologi-Hepatologi', 'Parent' => '33', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '50', 'SpecialtyText' => 'Geriatri', 'Parent' => '33', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '51', 'SpecialtyText' => 'Ginjal-Hipertensi', 'Parent' => '33', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '52', 'SpecialtyText' => 'Hematologi-Onkologi Medik', 'Parent' => '33', 'Title' => '', 'CreateDate' => "$now"]


value00: app/Database/Seeds/ContactSeeder.php
value01:
,
      ['SpecialtyID' => '9', 'SpecialtyText' => 'Bedah Toraks, Kardiak, dan Vaskular', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '10', 'SpecialtyText' => 'Dermatologi Venereologi Estetika', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '11', 'SpecialtyText' => 'Emergency Medicine (Kegawatdaruratan Medik)', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '12', 'SpecialtyText' => 'Farmakologi Klinik', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '13', 'SpecialtyText' => 'Forensik dan Medikolegal', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"]


value00: app/Database/Seeds/ContactSeeder.php
value01:
,
      ['SpecialtyID' => '29', 'SpecialtyText' => 'Orthopaedi dan Traumatologi', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '30', 'SpecialtyText' => 'Parasitologi Klinik', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '31', 'SpecialtyText' => 'Patologi Anatomi', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '32', 'SpecialtyText' => 'Patologi Klinik', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '33', 'SpecialtyText' => 'Penyakit Dalam', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '34', 'SpecialtyText' => 'Pulmonologi dan Kedokteran Respirasi', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"]


value00: app/Database/Seeds/ContactSeeder.php
value01:
,
      ['SpecialtyID' => '16', 'SpecialtyText' => 'Kedokteran Fisik dan Rehabilitasi', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '17', 'SpecialtyText' => 'Kedokteran Jiwa', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '18', 'SpecialtyText' => 'Kedokteran Kelautan', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '19', 'SpecialtyText' => 'Kedokteran Keluarga Layanan Primer', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '20', 'SpecialtyText' => 'Kedokteran Nuklir dan Teranostik Molekuler', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"]


value00: app/Database/Seeds/ContactSeeder.php
value01:
    $data = [
      ['SpecialtyID' => '1', 'SpecialtyText' => 'Anak', 'Parent' => '', 'Title' => 'Sp. A', 'CreateDate' => "$now"],
      ['SpecialtyID' => '2', 'SpecialtyText' => 'Andrologi', 'Parent' => '', 'Title' => 'Sp. And', 'CreateDate' => "$now"],
      ['SpecialtyID' => '3', 'SpecialtyText' => 'Anestesiologi dan Terapi Intensif', 'Parent' => '', 'Title' => 'Sp.An-TI', 'CreateDate' => "$now"]


value00: app/Database/Seeds/ContactSeeder.php
value01:
      ['SpecialtyID' => '67', 'SpecialtyText' => 'Nutrisi dan Penyakit Metabolik', 'Parent' => '1', 'Title' => 'Subsp.NPM', 'CreateDate' => "$now"],
      ['SpecialtyID' => '68', 'SpecialtyText' => 'Emergensi dan Rawat Intensif Anak', 'Parent' => '1', 'Title' => 'Subsp.ERIA', 'CreateDate' => "$now"],
      ['SpecialtyID' => '69', 'SpecialtyText' => 'Pencitraan', 'Parent' => '1', 'Title' => 'Subsp.PCT', 'CreateDate' => "$now"],
      ['SpecialtyID' => '70', 'SpecialtyText' => 'Neonatologi', 'Parent' => '1', 'Title' => 'Subsp.Neo', 'CreateDate' => "$now"],
      ['SpecialtyID' => '71', 'SpecialtyText' => 'Respirologi', 'Parent' => '1', 'Title' => 'Subsp.Resp', 'CreateDate' => "$now"]


value00: app/Database/Seeds/ContactSeeder.php
value01:
,
      ['SpecialtyID' => '38', 'SpecialtyText' => 'Bedah Mulut dan Maksilofasial', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '39', 'SpecialtyText' => 'Kedokteran Gigi Anak', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '40', 'SpecialtyText' => 'Konservasi Gigi', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '41', 'SpecialtyText' => 'Odontologi Forensik', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '42', 'SpecialtyText' => 'Ortodonsia', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '43', 'SpecialtyText' => 'Patologi Mulut dan Maksilofasial', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"]


value00: app/Database/Seeds/ContactSeeder.php
value01:
<?php

namespace App\Database\Seeds;

use CodeIgniter\Database\Seeder;

class ContactSeeder extends Seeder {

  public function run() {
    $now = date('Y-m-d H:i:s');
    
    // contact
    $data = [
      [
        'ContactID' => 1, 'NameFirst' => 'Default', 'NameLast' => 'Doctor', 'Title' => '', 'Initial' => 'DEFDOC'


value00: app/Database/Seeds/ContactSeeder.php
value01:
,
      ['SpecialtyID' => '55', 'SpecialtyText' => 'Psikosomatik dan Paliatif Medik', 'Parent' => '33', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '56', 'SpecialtyText' => 'Pulmonologi dan Medik Kritis', 'Parent' => '33', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '57', 'SpecialtyText' => 'Reumatologi', 'Parent' => '33', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '58', 'SpecialtyText' => 'Penyakit Tropik-Infeksi', 'Parent' => '33', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '59', 'SpecialtyText' => 'Alergi Imunologi', 'Parent' => '1', 'Title' => 'Subsp.AI', 'CreateDate' => "$now"]


value00: app/Database/Seeds/ContactSeeder.php
value01:
,
      ['SpecialtyID' => '71', 'SpecialtyText' => 'Respirologi', 'Parent' => '1', 'Title' => 'Subsp.Resp', 'CreateDate' => "$now"],
      ['SpecialtyID' => '72', 'SpecialtyText' => 'Tumbuh Kembang Pediatri Sosial', 'Parent' => '1', 'Title' => 'Subsp.TKPS', 'CreateDate' => "$now"],
    ];
    $this->db->table('medicalspecialty')->insertBatch($data);

  }
}


value00: app/Database/Seeds/ContactSeeder.php
value01:
,
      ['SiteID' => 2, 'ContactID' => 2, 'ContactCode' => 'B231', 'ContactEmail' => 'defdoc@email.com', 'OccupationID' => '', 'JobTitle' => '', 'Department' => 'Ginjal Sehat' ],
      ['SiteID' => 3, 'ContactID' => 2, 'ContactCode' => 'C342', 'ContactEmail' => 'qdoc@email.com', 'OccupationID' => '', 'JobTitle' => '', 'Department' => 'Hati Sehat' ]
    ]


value00: app/Database/Seeds/ContactSeeder.php
value01:
,
      ['SpecialtyID' => '59', 'SpecialtyText' => 'Alergi Imunologi', 'Parent' => '1', 'Title' => 'Subsp.AI', 'CreateDate' => "$now"],
      ['SpecialtyID' => '60', 'SpecialtyText' => 'Endokrinologi', 'Parent' => '1', 'Title' => 'Subsp.End', 'CreateDate' => "$now"],
      ['SpecialtyID' => '61', 'SpecialtyText' => 'Gastro-Hepatologi', 'Parent' => '1', 'Title' => 'Subsp.GH', 'CreateDate' => "$now"],
      ['SpecialtyID' => '62', 'SpecialtyText' => 'Hematologi Onkologi', 'Parent' => '1', 'Title' => 'Subsp.HO', 'CreateDate' => "$now"],
      ['SpecialtyID' => '63', 'SpecialtyText' => 'Infeksi dan Pediatri Tropis', 'Parent' => '1', 'Title' => 'Subsp.IPT', 'CreateDate' => "$now"]


value00: app/Database/Seeds/ContactSeeder.php
value01:
,
      ['SpecialtyID' => '43', 'SpecialtyText' => 'Patologi Mulut dan Maksilofasial', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '44', 'SpecialtyText' => 'Penyakit Mulut', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '45', 'SpecialtyText' => 'Periodonsia', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '46', 'SpecialtyText' => 'Prostodonsia', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '47', 'SpecialtyText' => 'Radiologi Kedokteran Gigi', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '48', 'SpecialtyText' => 'Alergi-Immunologi Klinik', 'Parent' => '33', 'Title' => '', 'CreateDate' => "$now"]


value00: app/Database/Seeds/ContactSeeder.php
value01:
,
      ['SpecialtyID' => '63', 'SpecialtyText' => 'Infeksi dan Pediatri Tropis', 'Parent' => '1', 'Title' => 'Subsp.IPT', 'CreateDate' => "$now"],
      ['SpecialtyID' => '64', 'SpecialtyText' => 'Kardiologi', 'Parent' => '1', 'Title' => 'Subsp.Kardio', 'CreateDate' => "$now"],
      ['SpecialtyID' => '65', 'SpecialtyText' => 'Nefrologi', 'Parent' => '1', 'Title' => 'Subsp.Nefro', 'CreateDate' => "$now"],
      ['SpecialtyID' => '66', 'SpecialtyText' => 'Neurologi', 'Parent' => '1', 'Title' => 'Subsp.Neuro', 'CreateDate' => "$now"],
      ['SpecialtyID' => '67', 'SpecialtyText' => 'Nutrisi dan Penyakit Metabolik', 'Parent' => '1', 'Title' => 'Subsp.NPM', 'CreateDate' => "$now"]


value00: app/Database/Seeds/ContactSeeder.php
value01:
,
      ['SpecialtyID' => '52', 'SpecialtyText' => 'Hematologi-Onkologi Medik', 'Parent' => '33', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '53', 'SpecialtyText' => 'Kardiovaskular', 'Parent' => '33', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '54', 'SpecialtyText' => 'Endokrin-Metabolik-Diabetes', 'Parent' => '33', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '55', 'SpecialtyText' => 'Psikosomatik dan Paliatif Medik', 'Parent' => '33', 'Title' => '', 'CreateDate' => "$now"]


value00: app/Database/Seeds/ContactSeeder.php
value01:
      ['SpecialtyID' => '3', 'SpecialtyText' => 'Anestesiologi dan Terapi Intensif', 'Parent' => '', 'Title' => 'Sp.An-TI', 'CreateDate' => "$now"],
      ['SpecialtyID' => '4', 'SpecialtyText' => 'Akupunktur Medik', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '5', 'SpecialtyText' => 'Bedah', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '6', 'SpecialtyText' => 'Bedah Anak', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '7', 'SpecialtyText' => 'Bedah Plastik, Rekonstruksi, dan Estetik', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"]


value00: app/Database/Seeds/ContactSeeder.php
value01:
;
    $this->db->table('contactdetail')->insertBatch($data);

    $data = [
      ['OccupationID' => 1, 'OccCode' => 'OC001', 'OccText' => 'Medical Doctor', 'Description' => 'Diagnoses and treats, injuries and illnesses', 'CreateDate' => "$now" ],
      ['OccupationID' => 2, 'OccCode' => 'OC002', 'OccText' => 'Trainee Medical Technician', 'Description' => 'Performing basic laboratory task', 'CreateDate' => "$now" ],
      ['OccupationID' => 3, 'OccCode' => 'OC003', 'OccText' => 'Medical Laboratory Technician', 'Description' => 'Perform routine laboratory tests', 'CreateDate' => "$now" ]
    ];
    $this->db->table('occupation')->insertBatch($data);


value00: app/Database/Seeds/DummySeeder.php
value01:
    // users
    // Password: 'password' for all users (bcrypt hash)
    $passwordHash = password_hash('123', PASSWORD_BCRYPT);
    $data = [
      ['id' => 1, 'role_id' => 1, 'username' => 'lisfse', 'password' => $passwordHash],
      ['id' => 2, 'role_id' => 1, 'username' => 'tes', 'password' => $passwordHash]
    ];
    $this->db->table('users')->insertBatch($data);

    // patvisit - 4 visits for 3 patients
    // Visit 1: Patient 1 - Discharged (complete history)
    // Visit 2: Patient 1 - Discharged (complete history)  
    // Visit 3: Patient 2 - Active (no discharge yet)
    // Visit 4: Patient 3 - Discharged (complete history)


value00: app/Database/Seeds/DummySeeder.php
value01:
    $data = [
      // Visit 1 (InternalPVID=1) - Discharged: ER -> Ward A -> Ward B -> Discharge
      ['InternalPVID' => 1, "ADTCode" => "A04", "LocationID" => 1, "AttDoc" => 1, "RefDoc" => null, "AdmDoc" => 1, "CnsDoc" => null, "CreateDate" => "$threeDaysAgo", "EndDate" => null],
      ['InternalPVID' => 1, "ADTCode" => "A01", "LocationID" => 1, "AttDoc" => 1, "RefDoc" => null, "AdmDoc" => 1, "CnsDoc" => null, "CreateDate" => date('Y-m-d H:i:s', strtotime('-2 days +2 hours')), "EndDate" => null],
      ['InternalPVID' => 1, "ADTCode" => "A02", "LocationID" => 2, "AttDoc" => 1, "RefDoc" => 2, "AdmDoc" => 1, "CnsDoc" => null, "CreateDate" => date('Y-m-d H:i:s', strtotime('-2 days +6 hours')), "EndDate" => null],


value00: app/Database/Seeds/DummySeeder.php
value01:
      // Note: No A03 (discharge) - this visit is still active

      // Visit 4 (InternalPVID=4) - Discharged: Direct admit -> Ward B -> Discharge
      ['InternalPVID' => 4, "ADTCode" => "A04", "LocationID" => 2, "AttDoc" => 2, "RefDoc" => null, "AdmDoc" => 2, "CnsDoc" => null, "CreateDate" => "$threeDaysAgo", "EndDate" => null],
      ['InternalPVID' => 4, "ADTCode" => "A01", "LocationID" => 2, "AttDoc" => 2, "RefDoc" => null, "AdmDoc" => 2, "CnsDoc" => null, "CreateDate" => date('Y-m-d H:i:s', strtotime('-2 days +3 hours')), "EndDate" => null],
      ['InternalPVID' => 4, "ADTCode" => "A02", "LocationID" => 1, "AttDoc" => 1, "RefDoc" => 2, "AdmDoc" => 2, "CnsDoc" => 1, "CreateDate" => date('Y-m-d H:i:s', strtotime('-2 days +12 hours')), "EndDate" => null]


value00: app/Language/en/Validation.php
value01:
<?php

// override core en language system validation or define your own en language validation message
return [];


value00: app/Database/Seeds/DummySeeder.php
value01:
,
      ['InternalPVID' => 2, "ADTCode" => "A01", "LocationID" => 1, "AttDoc" => 1, "RefDoc" => null, "AdmDoc" => 1, "CnsDoc" => null, "CreateDate" => date('Y-m-d H:i:s', strtotime('-1 day +1 hour')), "EndDate" => null],
      ['InternalPVID' => 2, "ADTCode" => "A02", "LocationID" => 2, "AttDoc" => 2, "RefDoc" => 1, "AdmDoc" => 1, "CnsDoc" => null, "CreateDate" => date('Y-m-d H:i:s', strtotime('-1 day +4 hours')), "EndDate" => null],
      ['InternalPVID' => 2, "ADTCode" => "A03", "LocationID" => null, "AttDoc" => 2, "RefDoc" => 1, "AdmDoc" => 1, "CnsDoc" => null, "CreateDate" => "$yesterday", "EndDate" => null],

      // Visit 3 (InternalPVID=3) - ACTIVE: ER -> Ward A (no discharge - still admitted)


value00: app/Database/Seeds/DummySeeder.php
value01:
,

      // Visit 3 (InternalPVID=3) - ACTIVE: ER -> Ward A (no discharge - still admitted)
      ['InternalPVID' => 3, "ADTCode" => "A04", "LocationID" => 1, "AttDoc" => 1, "RefDoc" => null, "AdmDoc" => 1, "CnsDoc" => null, "CreateDate" => "$yesterday", "EndDate" => null],
      ['InternalPVID' => 3, "ADTCode" => "A01", "LocationID" => 1, "AttDoc" => 1, "RefDoc" => null, "AdmDoc" => 1, "CnsDoc" => null, "CreateDate" => date('Y-m-d H:i:s', strtotime('-1 day +2 hours')), "EndDate" => null],
      ['InternalPVID' => 3, "ADTCode" => "A02", "LocationID" => 2, "AttDoc" => 2, "RefDoc" => 2, "AdmDoc" => 1, "CnsDoc" => null, "CreateDate" => "$now", "EndDate" => null],
      // Note: No A03 (discharge) - this visit is still active


value00: app/Database/Seeds/DummySeeder.php
value01:
, "AttDoc" => 1, "RefDoc" => 2, "AdmDoc" => 2, "CnsDoc" => 1, "CreateDate" => date('Y-m-d H:i:s', strtotime('-2 days +12 hours')), "EndDate" => null],
      ['InternalPVID' => 4, "ADTCode" => "A03", "LocationID" => null, "AttDoc" => 1, "RefDoc" => 2, "AdmDoc" => 2, "CnsDoc" => 1, "CreateDate" => date('Y-m-d H:i:s', strtotime('-1 day +6 hours')), "EndDate" => null],
    ];
    $this->db->table('patvisitadt')->insertBatch($data);

    echo "✅ Dummy ADT data seeded successfully!\n";
    echo "-------------------------------------------\n";


value00: app/Database/Seeds/DummySeeder.php
value01:
<?php

namespace App\Database\Seeds;

use CodeIgniter\Database\Seeder;

class DummySeeder extends Seeder
{
  public function run()
  {
    $now = date('Y-m-d H:i:s');
    $yesterday = date('Y-m-d H:i:s', strtotime('-1 day'));
    $twoDaysAgo = date('Y-m-d H:i:s', strtotime('-2 days'));
    $threeDaysAgo = date('Y-m-d H:i:s', strtotime('-3 days'));

    // users
    // Password: 'password' for all users (bcrypt hash)
    $passwordHash = password_hash('123', PASSWORD_BCRYPT);


value00: app/Database/Seeds/DummySeeder.php
value01:
,
      ['InternalPVID' => 1, "ADTCode" => "A02", "LocationID" => 2, "AttDoc" => 2, "RefDoc" => 2, "AdmDoc" => 1, "CnsDoc" => 2, "CreateDate" => date('Y-m-d H:i:s', strtotime('-1 day +8 hours')), "EndDate" => null],
      ['InternalPVID' => 1, "ADTCode" => "A03", "LocationID" => null, "AttDoc" => 2, "RefDoc" => 2, "AdmDoc" => 1, "CnsDoc" => 2, "CreateDate" => date('Y-m-d H:i:s', strtotime('-1 day +12 hours')), "EndDate" => null],

      // Visit 2 (InternalPVID=2) - Discharged: ER -> ICU -> Discharge
      ['InternalPVID' => 2, "ADTCode" => "A04", "LocationID" => 1, "AttDoc" => 1, "RefDoc" => null, "AdmDoc" => 1, "CnsDoc" => null, "CreateDate" => "$twoDaysAgo", "EndDate" => null],


value00: app/Database/Seeds/DummySeeder.php
value01:
    // Visit 3: Patient 2 - Active (no discharge yet)
    // Visit 4: Patient 3 - Discharged (complete history)
    $data = [
      ['InternalPVID' => 1, "PVID" => "DV00001", "InternalPID" => 1, "EpisodeID" => 1, "CreateDate" => "$threeDaysAgo"],
      ['InternalPVID' => 2, "PVID" => "DV00002", "InternalPID" => 1, "EpisodeID" => 1, "CreateDate" => "$twoDaysAgo"],
      ['InternalPVID' => 3, "PVID" => "DV00003", "InternalPID" => 2, "EpisodeID" => 2, "CreateDate" => "$yesterday"],
      ['InternalPVID' => 4, "PVID" => "DV00004", "InternalPID" => 3, "EpisodeID" => 3, "CreateDate" => "$threeDaysAgo"],
    ];
    $this->db->table('patvisit')->insertBatch($data);

    // patvisitadt - ADT history for each visit
    // A04: Register, A01: Admit, A02: Transfer, A03: Discharge


value00: app/Database/Seeds/ContactSeeder.php
value01:
,
      ['SpecialtyID' => '7', 'SpecialtyText' => 'Bedah Plastik, Rekonstruksi, dan Estetik', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '8', 'SpecialtyText' => 'Bedah Saraf', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"],
      ['SpecialtyID' => '9', 'SpecialtyText' => 'Bedah Toraks, Kardiak, dan Vaskular', 'Parent' => '', 'Title' => '', 'CreateDate' => "$now"]


value00: app/Database/Seeds/DummySeeder.php
value01:
    echo "✅ Dummy ADT data seeded successfully!\n";
    echo "-------------------------------------------\n";
    echo "Visits created: 4\n";
    echo "  - Visit 1 (DV00001): Complete ADT history with discharge\n";
    echo "  - Visit 2 (DV00002): Complete ADT history with discharge\n";
    echo "  - Visit 3 (DV00003): ACTIVE (no discharge yet)\n";
    echo "  - Visit 4 (DV00004): Complete ADT history with discharge\n";
    echo "-------------------------------------------\n";
    echo "ADT Records: 18 total\n";
    echo "  - A04 (Register): 4 records\n";
    echo "  - A01 (Admit): 4 records\n";
    echo "  - A02 (Transfer): 6 records\n";
    echo "  - A03 (Discharge): 3 records\n";
    echo "-------------------------------------------\n";
  }
}


value00: app/Libraries/TestValidationService.php
value01:
: array
    {
        $testType = strtoupper($testType);
        $resultType = strtoupper($resultType);

        if (!isset(self::TEST_TYPE_RESULT_TYPES[$testType])) {
            return [
                'valid' => false,
                'error' => "Invalid TestType '{$testType}'. Allowed: TEST, PARAM, CALC, GROUP, TITLE"
            ];
        }

        $validResultTypes = self::TEST_TYPE_RESULT_TYPES[$testType];
        
        if (!in_array($resultType, $validResultTypes, true)) {
            return [
                'valid' => false,
                'error' => "Invalid ResultType '{$resultType}' for TestType '{$testType}'. Allowed: " . implode(', ', $validResultTypes)
            ];
        }

        return ['valid' => true, 'error' => null];
    }


value00: app/Libraries/TestValidationService.php
value01:
    /**
     * Get reference table name based on ResultType and RefType
     *
     * @param string $resultType
     * @param string $refType
     * @return string|null Returns table name or null if no reference table needed
     */
    public static function getReferenceTable(?string $resultType, ?string $refType): ?string
    {
        if ($resultType === null || $refType === null) {
            return null;
        }

        $resultType = strtoupper($resultType);
        $refType = strtoupper($refType);

        return self::REFERENCE_TABLES[$resultType][$refType] ?? null;
    }

    /**
     * Check if a test needs reference ranges
     *
     * @param string $resultType
     * @return bool
     */


value00: app/Libraries/TestValidationService.php
value01:
    public static function isGroup(string $testType): bool
    {
        return strtoupper($testType) === 'GROUP';
    }

    /**
     * Check if TestType is TITLE
     *
     * @param string $testType
     * @return bool
     */
    public static function isTitle(string $testType): bool
    {
        return strtoupper($testType) === 'TITLE';
    }

    /**
     * Check if TestType is TEST or PARAM (technical tests)
     *
     * @param string $testType
     * @return bool
     */
    public static function isTechnicalTest(string $testType): bool
    {
        $testType = strtoupper($testType);
        return in_array($testType, ['TEST', 'PARAM'], true);
    }
}


value00: app/Libraries/TestValidationService.php
value01:
<?php

namespace App\Libraries;

/**
 * Test Validation Service
 * 
 * Validates TestType, ResultType, and RefType combinations
 * according to business rules for CLQMS test definitions.
 */
class TestValidationService


value00: app/Libraries/TestValidationService.php
value01:
    private const REFERENCE_TABLES = [
        'NMRIC' => [
            'RANGE' => 'refnum',
            'THOLD' => 'refnum',
        ],
        'RANGE' => [
            'RANGE' => 'refnum',
            'THOLD' => 'refnum',
        ],
        'VSET' => [
            'VSET' => 'reftxt',
        ],
        'TEXT' => [
            'TEXT' => 'reftxt',
        ],
        'NORES' => [
            'NOREF' => null,
        ],
    ];

    /**
     * Validate TestType and ResultType combination
     *
     * @param string $testType
     * @param string $resultType
     * @return array ['valid' => bool, 'error' => string|null]
     */
    public static function validateTestTypeResultType(string $testType, string $resultType): array


value00: app/Libraries/TestValidationService.php
value01:
    /**
     * Validate complete test type combination
     *
     * @param string $testType
     * @param string $resultType
     * @param string $refType
     * @return array ['valid' => bool, 'error' => string|null]
     */
    public static function validate(string $testType, string $resultType, string $refType): array
    {
        // First validate TestType and ResultType
        $testResultValidation = self::validateTestTypeResultType($testType, $resultType);
        if (!$testResultValidation['valid']) {
            return $testResultValidation;
        }

        // Then validate ResultType and RefType
        return self::validateResultTypeRefType($resultType, $refType);
    }


value00: app/Libraries/TestValidationService.php
value01:
: array
    {
        $resultType = strtoupper($resultType);
        $refType = strtoupper($refType);

        if (!isset(self::RESULT_TYPE_REF_TYPES[$resultType])) {
            return [
                'valid' => false,
                'error' => "Invalid ResultType '{$resultType}'. Allowed: NMRIC, RANGE, TEXT, VSET, NORES"
            ];
        }

        $validRefTypes = self::RESULT_TYPE_REF_TYPES[$resultType];
        
        if (!in_array($refType, $validRefTypes, true)) {
            return [
                'valid' => false,
                'error' => "Invalid RefType '{$refType}' for ResultType '{$resultType}'. Allowed: " . implode(', ', $validRefTypes)
            ];
        }

        return ['valid' => true, 'error' => null];
    }


value00: app/Libraries/TestValidationService.php
value01:
    /**
     * Validate ResultType and RefType combination
     *
     * @param string $resultType
     * @param string $refType
     * @return array ['valid' => bool, 'error' => string|null]
     */
    public static function validateResultTypeRefType(string $resultType, string $refType): array
    {
        $resultType = strtoupper($resultType);
        $refType = strtoupper($refType);


value00: app/Libraries/TestValidationService.php
value01:
    /**
     * Check if a test uses reftxt table
     *
     * @param string $resultType
     * @param string $refType
     * @return bool
     */
    public static function usesRefTxt(?string $resultType, ?string $refType): bool
    {
        return self::getReferenceTable($resultType, $refType) === 'reftxt';
    }

    /**
     * Check if TestType is CALC
     *
     * @param string $testType
     * @return bool
     */
    public static function isCalc(string $testType): bool
    {
        return strtoupper($testType) === 'CALC';
    }

    /**
     * Check if TestType is GROUP
     *
     * @param string $testType
     * @return bool
     */


value00: app/Libraries/TestValidationService.php
value01:
    /**
     * Get valid ResultTypes for a TestType
     *
     * @param string $testType
     * @return array
     */
    public static function getValidResultTypes(string $testType): array
    {
        $testType = strtoupper($testType);
        return self::TEST_TYPE_RESULT_TYPES[$testType] ?? [];
    }

    /**
     * Get valid RefTypes for a ResultType
     *
     * @param string $resultType
     * @return array
     */
    public static function getValidRefTypes(string $resultType): array
    {
        $resultType = strtoupper($resultType);
        return self::RESULT_TYPE_REF_TYPES[$resultType] ?? [];
    }


value00: app/Libraries/TestValidationService.php
value01:
{
    /**
     * Valid TestType to ResultType mappings
     */
    private const TEST_TYPE_RESULT_TYPES = [
        'TEST'  => ['NMRIC', 'RANGE', 'TEXT', 'VSET'],
        'PARAM' => ['NMRIC', 'RANGE', 'TEXT', 'VSET'],
        'CALC'  => ['NMRIC'],
        'GROUP' => ['NORES'],
        'TITLE' => ['NORES'],
    ];

    /**
     * Valid ResultType to RefType mappings
     */
    private const RESULT_TYPE_REF_TYPES = [
        'NMRIC' => ['RANGE', 'THOLD'],
        'RANGE' => ['RANGE', 'THOLD'],
        'VSET'  => ['VSET'],
        'TEXT'  => ['TEXT'],
        'NORES' => ['NOREF'],
    ];

    /**
     * Reference table mapping based on ResultType and RefType
     */


value00: app/Libraries/TestValidationService.php
value01:
    /**
     * Check if a test needs reference ranges
     *
     * @param string $resultType
     * @return bool
     */
    public static function needsReferenceRanges(?string $resultType): bool
    {
        if ($resultType === null) {
            return false;
        }

        $resultType = strtoupper($resultType);
        return $resultType !== 'NORES';
    }

    /**
     * Check if a test uses refnum table
     *
     * @param string $resultType
     * @param string $refType
     * @return bool
     */
    public static function usesRefNum(?string $resultType, ?string $refType): bool
    {
        return self::getReferenceTable($resultType, $refType) === 'refnum';
    }


value00: app/Libraries/ValueSet.php
value01:
<?php
namespace App\Libraries;

use CodeIgniter\Cache\CacheFactory;
use CodeIgniter\Config\BaseConfig;

class ValueSet {
    private static $cache = null;
    private static string $dataPath = APPPATH . 'Libraries/Data/';
    private static string $cacheKey = 'valueset_all';
    
    private static function getCacheHandler() {
        if (self::$cache === null) {
            $config = config('Cache');
            self::$cache = CacheFactory::getHandler($config);
        }
        return self::$cache;
    }
    
    public static function get(string $name): ?array {
        $all = self::getAll();
        $values = $all[$name]['values'] ?? null;
        if ($values === null) return null;
        return self::format($values);
    }


value00: app/Libraries/ValueSet.php
value01:
    private static function bundleAll(): array {
        $result = [];
        foreach (glob(self::$dataPath . '*.json') as $file) {
            $name = pathinfo($file, PATHINFO_FILENAME);
            if ($name[0] === '_') continue;
            $data = self::loadFile($file);
            if ($data) {
                $result[$name] = $data;
            }
        }
        return $result;
    }
    
    private static function loadFile(string $path): ?array {
        if (!is_file($path)) return null;
        $content = file_get_contents($path);
        return json_decode($content, true);
    }


value00: app/Libraries/ValueSet.php
value01:
    private static function format(array $values): array {
        return array_map(fn($v) => [
            'value' => (string) ($v['key'] ?? ''),
            'label' => (string) ($v['value'] ?? $v['label'] ?? '')
        ], $values);
    }
}


value00: app/Libraries/ValueSet.php
value01:
    public static function transformLabels(array $data, array $fieldMappings): array {
        foreach ($data as &$row) {
            foreach ($fieldMappings as $field => $lookupName) {
                if (isset($row[$field]) && $row[$field] !== null) {
                    $keyValue = $row[$field];
                    $row[$field] = $keyValue;
                    $row[$field . 'Label'] = self::getLabel($lookupName, $keyValue) ?? '';
                }
            }
        }
        return $data;
    }
    
    public static function clearCache(): bool {
        $handler = self::getCacheHandler();
        return $handler->delete(self::$cacheKey);
    }


value00: app/Libraries/ValueSet.php
value01:
    public static function getRaw(string $name): ?array {
        $all = self::getAll();
        return $all[$name]['values'] ?? null;
    }
    
    public static function getAll(): array {
        $handler = self::getCacheHandler();
        $data = $handler->get(self::$cacheKey);
        
        if ($data !== null) {
            return $data;
        }
        
        $data = self::bundleAll();
        $handler->save(self::$cacheKey, $data, 0);
        return $data;
    }


value00: app/Libraries/ValueSet.php
value01:
    public static function getLabel(string $lookupName, string $key): ?string {
        $raw = self::getRaw($lookupName);
        if ($raw === null) return null;
        foreach ($raw as $item) {
            if (($item['key'] ?? $item['value'] ?? null) === $key) {
                return $item['value'] ?? $item['label'] ?? null;
            }
        }
        return null;
    }
    
    public static function getOptions(string $lookupName): array {
        $raw = self::getRaw($lookupName);
        if ($raw === null) return [];
        return array_map(function ($item) {
            return [
                'key' => $item['key'] ?? '',
                'value' => $item['value'] ?? $item['label'] ?? '',
            ];
        }, $raw);
    }


value00: app/Models/AreaGeoModel.php
value01:
    public function getAreaGeos() {
			if (!empty($filters['AreaGeoID'])) {
				$this->where('AreaGeoID', $filters['AreaGeoID']);
			}
			if (!empty($filters['AreaName'])) {
				$this->like('AreaName', $filters['AreaName'], 'both');
			}
      
			return $this->findAll();
    }

    public function getProvinces() {
			$rows = $this->select('AreaGeoID, AreaName')
				->orGroupStart()
					->where('Parent', null)
					->orWhere('Parent', 0)
				->groupEnd()->findAll();
			
		return $rows;
	}

	public function getCities($filter) {
		$this->select('AreaGeoID, AreaName')->where('Parent > 0');
		if(!empty($filter['Parent'])){
			$this->where('Parent', $filter['Parent']);
		}
		return $this->findAll();
	}

}


value00: app/Models/CounterModel.php
value01:
	protected $createdField = "CreateDate";
	protected $updatedField = "";
	protected $useSoftDeletes = true;
	protected $deletedField = "EndDate";

  public function use($CounterID) {
		$row = $this->where('CounterID',$CounterID)->get()->getResultArray();	
		$cValue = $row[0]['CounterValue'];
		$cStart = $row[0]['CounterStart'];
		$cEnd = $row[0]['CounterEnd'];
		//$cReset = $row[0]['CounterReset'];
		$cPad = strlen((string)$cEnd);
    // next value > end, back to start
		if($cValue > $cEnd) { $cValue = $cStart; }
		$cnum = str_pad($cValue, $cPad, "0", STR_PAD_LEFT); 
		$cValue_next = $cValue+1;
		$this->set('CounterValue', $cValue_next)->where('CounterID',$CounterID)->update();
		return $cnum;
	}

}


value00: app/Models/Contact/ContactDetailModel.php
value01:
<?php

namespace App\Models\Contact;

use App\Models\BaseModel;

class ContactDetailModel extends BaseModel {
  protected $table      = 'contactdetail';
  protected $primaryKey = 'ContactDetID';
  protected $allowedFields = ['ContactID', 'SiteID', 'ContactCode', 'ContactEmail', 'OccupationID', 'JobTitle', 'Department', 'ContactStartDate', 'ContactEndDate']; 
  
  protected $useTimestamps = true;
  protected $createdField = 'ContactStartDate';
  protected $updatedField = '';

  public function syncDetails(int $ContactID, array $contactDetails) {
    try {
      $keptSiteIDs = [];

      foreach ($contactDetails as $detail)


value00: app/Models/Contact/ContactDetailModel.php
value01:
        $keptSiteIDs[] = $detail['SiteID'];
      }

      // Delete missing rows
      if (!empty($keptSiteIDs)) {
        $this->where('ContactID', $ContactID)
          ->whereNotIn('SiteID', $keptSiteIDs)
          ->delete();
      } else {
        $this->where('ContactID', $ContactID)->delete();
      }

      return [
        'status'   => 'success',
        'inserted' => count($contactDetails) - count($keptSiteIDs),
        'kept'     => count($keptSiteIDs),
      ];
    } catch (\Throwable $e) {
        log_message('error', 'syncDetails error: ' . $e->getMessage());

        return [
          'status'  => 'error',
          'message' => $e->getMessage(),
        ];
    }
}
}


value00: app/Models/CounterModel.php
value01:
<?php

namespace App\Models;
use App\Models\BaseModel;
class CounterModel extends BaseModel {
  protected $table      = 'counter';
  protected $primaryKey = 'CounterID';
  protected $allowedFields = ['CounterValue', 'CounterStart', 'CounterEnd', 'CounterReset', 'CounterName', 'CreateDate', 'EndDate']; 
	
	protected $useTimestamps = true;
	protected $createdField = "CreateDate";
	protected $updatedField = "";
	protected $useSoftDeletes = true;


value00: app/Models/Contact/ContactDetailModel.php
value01:
      $keptSiteIDs = [];

      foreach ($contactDetails as $detail) {
        if (empty($detail['SiteID'])) {
          continue;
        }

        $detail['ContactID'] = $ContactID;

        $existing = $this->where('ContactID', $ContactID)
          ->where('SiteID', $detail['SiteID'])
          ->first();

        if ($existing) {
          $this->update($existing[$this->primaryKey], $detail);
        } else {
          $this->insert($detail);
        }

        $keptSiteIDs[] = $detail['SiteID'];
      }

      // Delete missing rows


value00: app/Models/AreaGeoModel.php
value01:
<?php
namespace App\Models;

use App\Models\BaseModel;

class AreaGeoModel extends BaseModel {
    protected $table      = 'areageo';
    protected $primaryKey = 'AreaGeoID';
    protected $allowedFields = ['Parent', 'AreaCode', 'Class', 'AreaName'];


value00: app/Models/Contact/ContactModel.php
value01:
<?php

namespace App\Models\Contact;

use App\Models\BaseModel;

class ContactModel extends BaseModel {
  protected $table      = 'contact';
  protected $primaryKey = 'ContactID';
  protected $allowedFields = ['NameFirst', 'NameLast', 'Title', 'Initial', 'Birthdate', 'EmailAddress1', 'EmailAddress2', 'Phone', 
  	'MobilePhone1', 'MobilePhone2', 'Specialty', 'SubSpecialty', 'CreateDate', 'EndDate']; 
	
  protected $useTimestamps = true;
  protected $createdField = 'CreateDate';
  protected $updatedField = '';


value00: app/Models/Contact/ContactModel.php
value01:
			if (!empty($row['ContactDetID'])) {
				$contact['Details'][] = [
					'SiteID' => $row['SiteID'] ?? null, 
					'ContactDetID' => $row['ContactDetID'],
					'ContactCode' => $row['ContactCode'] ?? null,
					'ContactEmail' => $row['ContactEmail'] ?? null,
					'OccupationID' => $row['OccupationID'] ?? null,
					'JobTitle'  => $row['JobTitle'] ?? null,
					'Department'  => $row['Department'] ?? null,
					'ContactStartDate' => $row['ContactStartDate'] ?? null,
					'ContactEndDate' => $row['ContactEndDate'] ?? null
				];
			}
		}
	
		return $contact;
	}


value00: app/Models/Contact/ContactModel.php
value01:
			if (!$contactId) {
				throw new \RuntimeException('Failed to save contact');
			}

			if (!empty($data['Details'])) {
				$modelDetail = new \App\Models\Contact\ContactDetailModel();
				$result = $modelDetail->syncDetails($contactId, $data['Details']);

				if ($result['status'] !== 'success') {
					throw new \RuntimeException('SyncDetails failed: ' . $result['message']);
				}
			}

			$db->transComplete();

			return [
				'status'    => 'success',
				'ContactID' => $contactId,
			];
    } catch (\Throwable $e) {
			$db->transRollback();

			return [
				'status'  => 'error',
				'message' => $e->getMessage(),
			];
    }
	}

}


value00: app/Models/Contact/ContactModel.php
value01:
  protected $useTimestamps = true;
  protected $createdField = 'CreateDate';
  protected $updatedField = '';
  protected $useSoftDeletes = true;
  protected $deletedField = "EndDate";

	public function getContactsWithDetail() {
 		$rows = $this->select("contact.ContactID, cd.SiteID, cd.ContactCode, NameFirst, NameLast, Specialty")
			->join("contactdetail cd", "contact.ContactID=cd.ContactID", "left") 
			->join("occupation o","cd.OccupationID=o.OccupationID", "left")
			->get()->getResultArray();

		return $rows;
	}


value00: app/Models/Contact/ContactModel.php
value01:
	public function getContacts($ContactName, $Specialty) {
 		$sql = $this->select("ContactID, NameFirst, NameLast, Title, Initial, Specialty");
		if($ContactName !='') { $sql->like('NameFirst',$ContactName,'both')->orLike('NameLast',$ContactName,'both'); }
		if($Specialty != '') { $sql->like('Specialty',$Specialty); }
		$rows = $sql->get()->getResultArray();
		return $rows;
	}

  public function getContactWithDetail($ContactID) {
		$rows = $this->join('contactdetail cd', 'contact.ContactID=cd.ContactID','left')->where('contact.ContactID', $ContactID)->findAll();
		$contact = [];
		
		foreach ($rows as $row)


value00: app/Models/Contact/ContactModel.php
value01:
{
			if(empty($contact['NameFirst'])) {
				$contact = [
					'ContactID' => $ContactID,
					'NameFirst' => $row['NameFirst'] ?? null,
					'NameLast' => $row['NameLast'] ?? null,
					'Title' => $row['Title'] ?? null,
					'Initial' => $row['Initial'] ?? null,
					'Birthdate' => $row['Birthdate'] ?? null,
					'EmailAddress1' => $row['EmailAddress1'] ?? null,
					'EmailAddress2' => $row['EmailAddress2'] ?? null,
					'Phone' => $row['Phone'] ?? null,
					'MobilePhone1' => $row['MobilePhone1'] ?? null,
					'MobilePhone2' => $row['MobilePhone2'] ?? null,
					'Specialty' => $row['Specialty'] ?? null,
					'SubSpecialty' => $row['SubSpecialty'] ?? null,
					'Details' => []
				];
			}


value00: app/Models/EdgeResModel.php
value01:
    protected $createdField = 'CreateDate';
    protected $updatedField = 'EndDate';

    /**
     * Get pending results for processing
     */
    public function getPending($limit = 100) {
        return $this->where('Status', 'pending')
                    ->whereNull('DelDate')
                    ->orderBy('CreateDate', 'ASC')
                    ->findAll($limit);
    }

    /**
     * Mark as processed
     */


value00: app/Models/Contact/ContactModel.php
value01:
	public function saveContact(array $data): array {
    $db = \Config\Database::connect();
    $db->transStart();

    try {
			if (!empty($data['ContactID'])) {
				$contactId = $data['ContactID'];
				$this->update($contactId, $data);
			} else {
				$contactId = $this->insert($data, true);
			}

			if (!$contactId) {
				throw new \RuntimeException('Failed to save contact');
			}


value00: app/Models/Contact/MedicalSpecialtyModel.php
value01:
<?php
namespace App\Models\Contact;

use App\Models\BaseModel;

class MedicalSpecialtyModel extends BaseModel {
  protected $table      = 'medicalspecialty';
  protected $primaryKey = 'SpecialtyID';
  protected $allowedFields = ['SpecialtyID', 'SpecialtyText', 'Parent', 'Title', 'CreateDate', 'EndDate']; 

  public function getOccupations($Parent, $SpecialtyText) {
    if($Parent != '') {
      $this->like('Parent', $Parent, 'both');
    }
    if($SpecialtyText != '') {
      $this->like('SpecialtyText', $SpecialtyText, 'both');
    }
    
    $rows = $this->findAll();
    return $rows;
  }
}


value00: app/Models/EdgeResModel.php
value01:
    /**
     * Mark as processed
     */
    public function markProcessed($id) {
        return $this->update($id, [
            'Status' => 'processed',
            'ProcessedAt' => date('Y-m-d H:i:s')
        ]);
    }

    /**
     * Mark as error
     */
    public function markError($id, $errorMessage) {
        return $this->update($id, [
            'Status' => 'error',
            'ErrorMessage' => $errorMessage,
            'ProcessedAt' => date('Y-m-d H:i:s')
        ]);
    }
}


value00: app/Models/Contact/OccupationModel.php
value01:
<?php
namespace App\Models\Contact;

use App\Models\BaseModel;

class OccupationModel extends BaseModel {
  protected $table      = 'occupation';
  protected $primaryKey = 'OccupationID';
  protected $allowedFields = ['OccCode', 'OccText', 'Description', 'CreateDate']; 

  public function getOccupations($OccCode, $OccText) {
    if($OccCode != '') {
      $this->like('OccCode', $OccCode, 'both');
    }
    if($OccText != '') {
      $this->like('OccText', $OccText, 'both');
    }
    
    $rows = $this->findAll();
    return $rows;
  }
}


value00: app/Models/EdgeResModel.php
value01:
<?php

namespace App\Models;

use CodeIgniter\Model;

class EdgeResModel extends Model {
    protected $table = 'edgeres';
    protected $primaryKey = 'EdgeResID';
    protected $useAutoIncrement = true;
    protected $returnType = 'array';
    protected $useSoftDeletes = false;
    protected $allowedFields = [
        'SiteID',
        'InstrumentID',
        'SampleID',
        'PatientID',
        'Payload',
        'Status',
        'AutoProcess',
        'ProcessedAt',
        'ErrorMessage',
        'CreateDate',
        'EndDate',
        'ArchiveDate',
        'DelDate'
    ];

    protected $useTimestamps = false;
    protected $createdField = 'CreateDate';
    protected $updatedField = 'EndDate';


value00: app/Models/Organization/DisciplineModel.php
value01:
<?php
namespace App\Models\Organization;
use App\Models\BaseModel;

class DisciplineModel extends BaseModel {
  protected $table = 'discipline';
  protected $primaryKey = 'DisciplineID';
  protected $allowedFields = ['DisciplineCode', 'DisciplineName', 'SiteID', 'Parent', 'SeqScr', 'SeqRpt', 'CreateDate', 'EndDate']; 
		
  protected $useTimestamps = true;
  protected $createdField = 'CreateDate';
  protected $updatedField = '';
  protected $useSoftDeletes = true;
  protected $deletedField = 'EndDate';
  
	public function getDisciplines($filter)


value00: app/Models/Organization/DisciplineModel.php
value01:
{
		$builder = $this->select('discipline.DisciplineID, discipline.DisciplineCode, discipline.DisciplineName, discipline.SiteID, 
        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'])) {
			$builder->like('DisciplineCode', $filter['DisciplineCode'], 'both');
		}
		if (!empty($filter['DisciplineName'])) {
			$builder->like('DisciplineName', $filter['DisciplineName'], 'both');
		}

    $rows = $builder->findAll();
    // Build nested structure: parent with children
    $parents = [];
    $children = [];


value00: app/Models/Organization/DisciplineModel.php
value01:
    $rows = $builder->findAll();
    // Build nested structure: parent with children
    $parents = [];
    $children = [];
    foreach ($rows as $row) {
      if (empty($row['Parent'])) {
        $parents[$row['DisciplineID']] = $row;
        $parents[$row['DisciplineID']]['children'] = [];
      } else {
        $children[$row['Parent']][] = $row;
      }
    }
    // Attach children to parents
    foreach ($children as $parentId => $childList) {
      if (isset($parents[$parentId])) {
        $parents[$parentId]['children'] = $childList;
      }
    }
    // Return as indexed array
    return array_values($parents);
  }
}


value00: app/Models/ZonesModel.php
value01:
                    $this->checkDbError($db, 'Delete Zones');
                    $record['deleted']++;
                }
            }

            $db->transCommit();

        } catch (\Exception $e) {
            $db->transRollback();
            throw $e;
        }

        return $record;
    }


    // Check Error and Send Spesific Messages
    private function checkDbError($db, string $context) {
        $error = $db->error();
        if (!empty($error['code'])) {
            throw new \Exception(
                "{$context} failed: {$error['code']} - {$error['message']}"
            );
        }
    }

}
*/


value00: app/Models/ZonesModel.php
value01:
    // synchronize with crm.zones
    public function synchronize($crmData) {
        $db = \Config\Database::connect();
        $db->transBegin();

        try {
            $localData = $this->findAll();
            $localMap  = array_column($localData, null, 'zoneid');
            $crmIds    = array_column($crmData, 'zoneid');

            $record = ['inserted' => 0, 'updated' => 0, 'deleted' => 0, 'skipped' => 0];

            foreach ($crmData as $zone) {
                if (!isset($zone['zoneid'])) continue;

                if (isset($localMap[$zone['zoneid']])) {
                    // Ambil data lokal untuk perbandingan
                    $localRow = $localMap[$zone['zoneid']];


value00: app/Models/ZonesModel.php
value01:
                    } else {
                        $record['skipped']++; // tandai bahwa data identik, tidak diupdate
                    }

                } else {
                    // Insert jika belum ada
                    $this->insert($zone);
                    $this->checkDbError($db, 'Insert Zones');
                    $record['inserted']++;
                }
            }

            // Hapus data yang tidak ada di CRM
            foreach ($localMap as $localId => $row) {
                if (!in_array($localId, $crmIds)) {
                    $this->delete($localId);
                    $this->checkDbError($db, 'Delete Zones');
                    $record['deleted']++;
                }


value00: app/Models/ZonesModel.php
value01:
		}
		if (!empty($filters['zonename'])) {
			$this->like('zonename', $filters['zonename'], 'both');
		}

		return $this->findAll();
	}

	public function getCities($filter = []) {
		$rows = $this->select('zoneid, zonename')->where('parentzoneid IS NOT NULL', null, false);

		if (!empty($filter['zoneid'])) {
			$this->where('parentzoneid', $filter['zoneid']);
		}

		return $this->findAll();
	}

    // synchronize with crm.zones
    public function synchronize($crmData) {
        $db = \Config\Database::connect();


value00: app/Models/Infrastructure/EquipmentListModel.php
value01:
<?php

namespace App\Models\Infrastructure;

use App\Models\BaseModel;

class EquipmentListModel extends BaseModel {
    protected $table = 'equipmentlist';
    protected $primaryKey = 'EID';
    protected $allowedFields = [
        'IEID',
        'DepartmentID',
        'InstrumentID',
        'InstrumentName',
        'WorkstationID',
        'Enable',
        'EquipmentRole',
        'CreateDate',
        'EndDate'
    ];

    protected $useTimestamps = true;
    protected $createdField = 'CreateDate';
    protected $updatedField = '';
    protected $useSoftDeletes = true;
    protected $deletedField = 'EndDate';

    public function getEquipmentLists($filter = []) {
        $builder = $this->builder();


value00: app/Models/Infrastructure/EquipmentListModel.php
value01:
{
        $builder = $this->builder();
        $builder->select('equipmentlist.*, department.DepartmentName, workstation.WorkstationName')
            ->join('department', 'department.DepartmentID = equipmentlist.DepartmentID', 'left')
            ->join('workstation', 'workstation.WorkstationID = equipmentlist.WorkstationID', 'left');


value00: app/Models/ZonesModel.php
value01:
<?php
/*
namespace App\Models;

use App\Models\BaseModel;

class ZonesModel extends BaseModel {
    protected $table      = 'zones';
    protected $primaryKey = 'zoneid';
    protected $allowedFields = ['parentzoneid', 'zonecode', 'zoneclass', 'zonename']; 
 
    public function getZones() {
        return $this->findAll();
    }

    public function getProvinces() {
		$this->select('zoneid, zonename')->where('parentzoneid IS NULL', null, false);

		if (!empty($filters['zoneid'])) {
			$this->where('zoneid', $filters['zoneid']);
		}
		if (!empty($filters['zonename'])) {
			$this->like('zonename', $filters['zonename'], 'both');
		}

		return $this->findAll();


value00: app/Models/Location/LocationModel.php
value01:
    $db = \Config\Database::connect();
    $db->transBegin();
    try {
			if (!empty($data['LocationID'])) {
				$LocationID = $data['LocationID'];
				$this->update($LocationID, $data);
        $modelAddress->update($LocationID, $data);
			} else {
				$LocationID = $this->insert($data, true);
        $data['LocationID'] = $LocationID;
        $modelAddress->insert($data);
			}
      if ($db->transStatus() === false) {
        $error = $db->error();
        $db->transRollback();
        throw new \Exception($error['message'] ?? 'Transaction failed');
      }
      $db->transCommit();
			return [ 'status' => 'success', 'LocationID' => $LocationID ];
    } catch (\Throwable $e) {
      $db->transRollback();
      throw $e;
    }
	}


value00: app/Models/Location/LocationModel.php
value01:
  public function deleteLocation(array $data): array {
    $modelAddress = new \App\Models\Location\LocationAddressModel();
    $db = \Config\Database::connect();
    $db->transBegin();
    try {
      $LocationID = $data['LocationID'];
			$this->delete($LocationID);
      $modelAddress->delete($LocationID);
      if ($db->transStatus() === false) {
        $error = $db->error();
        $db->transRollback();
        throw new \Exception($error['message'] ?? 'Transaction failed');
      }
      $db->transCommit();
			return [ 'status' => 'success', 'LocationID' => $LocationID ];
    } catch (\Throwable $e) {
      $db->transRollback();
      throw $e;
    }
	}
}


value00: app/Models/ZonesModel.php
value01:
                    $localRow = $localMap[$zone['zoneid']];

                    // Buat array subset dari kolom yang relevan untuk dibandingkan
                    $fieldsToCompare = ['parentzoneid', 'zonecode', 'zoneclass', 'zonename'];

                    $isDifferent = false;
                    foreach ($fieldsToCompare as $field) {
                        $crmVal   = $zone[$field] ?? null;
                        $localVal = $localRow[$field] ?? null;

                        if ($crmVal != $localVal) { // gunakan != agar beda tipe tetap terdeteksi
                            $isDifferent = true;
                            break;
                        }


value00: app/Models/Infrastructure/EquipmentListModel.php
value01:
    public function getEquipmentList($EID) {
        $builder = $this->builder();
        $row = $builder->select('equipmentlist.*, department.DepartmentName, workstation.WorkstationName')
            ->join('department', 'department.DepartmentID = equipmentlist.DepartmentID', 'left')
            ->join('workstation', 'workstation.WorkstationID = equipmentlist.WorkstationID', 'left')
            ->where('equipmentlist.EID', $EID)
            ->get()
            ->getRowArray();

        return $row;
    }
}


value00: app/Models/Organization/CodingSysModel.php
value01:
<?php

namespace App\Models\Organization;

use App\Models\BaseModel;

class CodingSysModel extends BaseModel {
  protected $table = 'codingsys';
  protected $primaryKey = 'CodingSysID';
  protected $allowedFields = ['CodingSysAbb', 'FullText', 'Description', 'CreateDate', 'EndDate'];

  protected $useTimestamps = true;
  protected $createdField = 'CreateDate';
  protected $updatedField = '';
  protected $useSoftDeletes = true;
  protected $deletedField = 'EndDate';
}


value00: app/Models/Infrastructure/EquipmentListModel.php
value01:
        if (!empty($filter['IEID'])) {
            $builder->like('equipmentlist.IEID', $filter['IEID'], 'both');
        }
        if (!empty($filter['InstrumentName'])) {
            $builder->like('equipmentlist.InstrumentName', $filter['InstrumentName'], 'both');
        }
        if (!empty($filter['DepartmentID'])) {
            $builder->where('equipmentlist.DepartmentID', $filter['DepartmentID']);
        }
        if (!empty($filter['WorkstationID'])) {
            $builder->where('equipmentlist.WorkstationID', $filter['WorkstationID']);
        }
        if (isset($filter['Enable'])) {
            $builder->where('equipmentlist.Enable', $filter['Enable']);
        }

        $rows = $builder->get()->getResultArray();
        return $rows;
    }


value00: app/Models/Location/LocationAddressModel.php
value01:
<?php
namespace App\Models\Location;
use App\Models\BaseModel;

class LocationAddressModel extends BaseModel {
  protected $table = 'locationaddress';
  protected $primaryKey = 'LocationID';
  protected $allowedFields = ['LocationID', 'Street1', 'Street2', 'City', 'Province', 'PostCode', 
		'GeoLocationSystem', 'GeoLocationData', 'Phone', 'Email', 'CreateDate', 'EndDate']; 
		
  protected $useTimestamps = true;
  protected $createdField = 'CreateDate';
  protected $updatedField = '';


value00: app/Models/Location/LocationModel.php
value01:
<?php
namespace App\Models\Location;
use App\Models\BaseModel;
use App\Libraries\ValueSet;

class LocationModel extends BaseModel {
  protected $table      = 'location';
  protected $primaryKey = 'LocationID';
  protected $allowedFields = ['SiteID', 'LocCode', 'Parent', 'LocFull', 'Description', 'LocType', 'CreateDate', 'EndDate']; 
		
  protected $useTimestamps = true;
  protected $createdField = 'CreateDate';
  protected $updatedField = '';


value00: app/Models/Location/LocationAddressModel.php
value01:
  protected $useTimestamps = true;
  protected $createdField = 'CreateDate';
  protected $updatedField = '';
  protected $useSoftDeletes = true;
  protected $deletedField = 'EndDate';
	
  protected $beforeInsert = ['normalizeDatesToUTC'];
  protected $beforeUpdate = ['normalizeDatesToUTC'];
  protected $afterFind = ['convertDatesToUTCISO'];
  protected $afterInsert = ['convertDatesToUTCISO'];
  protected $afterUpdate = ['convertDatesToUTCISO'];

}


value00: app/Models/Location/LocationModel.php
value01:
  public function saveLocation(array $data): array {
    $modelAddress = new \App\Models\Location\LocationAddressModel();
    $db = \Config\Database::connect();
    $db->transBegin();
    try {
			if


value00: app/Models/Location/LocationModel.php
value01:
  protected $useTimestamps = true;
  protected $createdField = 'CreateDate';
  protected $updatedField = '';
  protected $useSoftDeletes = true;
  protected $deletedField = 'EndDate';

  public function getLocations($LocCode, $LocName) {
    $sql = $this->select("LocationID, LocCode, Parent, LocFull, LocType");
    if($LocName != '') { $sql->like('LocFull', $LocName, 'both'); }
    if($LocCode != '') { $sql->like('LocCode', $LocCode, 'both'); }
    $rows = $sql->findAll();
    $rows = ValueSet::transformLabels($rows, [
      'LocType' => 'location_type',
    ]);
    return $rows;
 	}


value00: app/Models/Location/LocationModel.php
value01:
  public function getLocation($LocationID) {
    $row = $this->select("location.*, la.Street1, la.Street2, la.PostCode, la.GeoLocationSystem, la.GeoLocationData, 
      la.Province as Province, prop.AreaName as ProvinceLabel, la.City as City, city.AreaName as CityLabel, site.SiteID, site.SiteName")
    ->join("locationaddress la", "location.LocationID=la.LocationID", "left")
    ->join("areageo prop", "la.Province=prop.AreaGeoID", "left")
    ->join("areageo city", "la.City=city.AreaGeoID", "left")
    ->join("site", "site.SiteID=location.SiteID", "left")
    ->where('location.LocationID', (int) $LocationID)->first();
    
    if (!$row) return null;
    
    $row = ValueSet::transformLabels([$row], [
      'LocType' => 'location_type',
    ])[0];
    
    return $row;
  }


value00: app/Models/ZonesModel.php
value01:
                            $isDifferent = true;
                            break;
                        }
                    }

                    if ($isDifferent) {
                        $this->update($zone['zoneid'], [
                            'parentzoneid' => $zone['parentzoneid'],
                            'zonecode'     => $zone['zonecode'],
                            'zoneclass'    => $zone['zoneclass'],
                            'zonename'     => $zone['zonename']
                        ]);
                        $this->checkDbError($db, 'Update Zones');
                        $record['updated']++;
                    } else {
                        $record['skipped']++; // tandai bahwa data identik, tidak diupdate


value00: app/Models/OrderTest/OrderTestModel.php
value01:
        $year = $date->format('y');
        $month = $date->format('m');
        $day = $date->format('d');

        $counter = $this->db->table('counter')
            ->where('CounterName', 'ORDER')
            ->get()
            ->getRow();

        if (!$counter) {
            $this->db->table('counter')->insert([
                'CounterName' => 'ORDER',
                'CounterValue' => 1
            ]);
            $seq = 1;
        } else {
            $seq = $counter->CounterValue + 1;
            $this->db->table('counter')
                ->where('CounterName', 'ORDER')
                ->update(['CounterValue' => $seq]);
        }

        $seqStr = str_pad($seq, 5, '0', STR_PAD_LEFT);
        return $siteCode . $year . $month . $day . $seqStr;
    }


value00: app/Models/Organization/AccountModel.php
value01:
<?php
namespace App\Models\Organization;
use App\Models\BaseModel;
use App\Libraries\ValueSet;

class AccountModel extends BaseModel {
  protected $table = 'account';
  protected $primaryKey = 'AccountID';
  protected $allowedFields = ['Parent', 'AccountName', 'Initial', 'Street_1', 'Street_2', 'Street_3', 
    'City', 'Province', 'ZIP', 'Country', 'AreaCode', 'EmailAddress1', 'EmailAddress2', 
    'Phone', 'Fax', 'CreateDate', 'EndDate']; 
		
  protected $useTimestamps = true;
  protected $createdField = 'CreateDate';
  protected $updatedField = '';


value00: app/Models/Organization/AccountModel.php
value01:
  protected $useTimestamps = true;
  protected $createdField = 'CreateDate';
  protected $updatedField = '';
  protected $useSoftDeletes = true;
  protected $deletedField = 'EndDate';
   
  public function getAccounts($filter=[]) {
		$builder = $this->select('account.AccountID, account.AccountName, account.Parent, pa.AccountName as ParentName, account.Initial')
      ->join('account pa', 'pa.AccountID=account.Parent', 'left');

		if (!empty($filter['Parent'])) {
			$builder->where('account.Parent', $filter['Parent']);
		}
		if (!empty($filter['AccountName'])) {
			$builder->like('account.AccountName', $filter['AccountName'], 'both');
		}

		$rows = $builder->findAll();
    return $rows;
  }


value00: app/Models/OrderTest/OrderTestModel.php
value01:
                    // Create specimen status
                    $this->db->table('specimenstatus')->insert([
                        'SID' => $specimenID,
                        'OrderID' => $internalOID,
                        'SpcStatus' => 'PENDING',
                        'CreateDate' => date('Y-m-d H:i:s')
                    ]);
                    
                    // Store mapping for patres creation
                    foreach ($containerData['tests'] as $tid => $tinfo) {
                        $specimenConDefMap[$tid] = [
                            'InternalSID' => $internalSID,
                            'SID' => $specimenID,
                            'ConDefID' => $conDefID
                        ];
                    }
                }


value00: app/Models/Organization/AccountModel.php
value01:
  public function getAccount($AccountID) {
    $row = $this->select('account.*, pa.AccountName as ParentName, areageo.AreaName, areageo.AreaGeoID, 
			account.City as City, city.AreaName as CityLabel, account.Province as Province, prov.AreaName as ProvinceLabel')
      ->join('account pa', 'pa.AccountID=account.Parent', 'left')
      ->join('areageo', 'areageo.AreaCode=account.AreaCode', 'left')
      ->join('areageo city', 'city.AreaGeoID=account.City', 'left')
      ->join('areageo prov', 'prov.AreaGeoID=account.Province', 'left')
      ->where('account.AccountID', $AccountID)
      ->first();
    
    if (!$row) return null;
    
    $row = ValueSet::transformLabels([$row], [
      'Country' => 'account_Country',
    ])[0];
    
    return $row;
  }
}


value00: app/Models/OrderTest/OrderTestModel.php
value01:
    private function getContainerRequirement($testSiteID, $testMapDetailModel, $containerDefModel): array {
        // Try to find container requirement from test mapping
        $containerDef = $this->db->table('testmapdetail tmd')
            ->select('tmd.ConDefID, cd.ConCode, cd.ConName')
            ->join('containerdef cd', 'cd.ConDefID = tmd.ConDefID', 'left')
            ->where('tmd.ClientTestCode', function($builder) use ($testSiteID) {
                return $builder->select('TestSiteCode')
                    ->from('testdefsite')
                    ->where('TestSiteID', $testSiteID);
            })
            ->where('tmd.EndDate IS NULL')
            ->get()
            ->getRowArray();


value00: app/Models/OrderTest/OrderTestModel.php
value01:
    public function generateSpecimenID(string $orderID, int $seq): string {
        return $orderID . '-S' . str_pad($seq, 2, '0', STR_PAD_LEFT);
    }

    public function createOrder(array $data): string {
        $this->db->transStart();

        try {
            $orderID = !empty($data['OrderID']) ? $data['OrderID'] : $this->generateOrderID($data['SiteCode'] ?? '00');


value00: app/Models/OrderTest/OrderTestModel.php
value01:
    private function expandTest($testSiteID, &$testToOrder, $testModel, $grpModel, $calModel) {
        if (isset($testToOrder[$testSiteID])) return;

        $testInfo = $testModel->find($testSiteID);
        if (!$testInfo) return;

        $testToOrder[$testSiteID] = [
            'TestSiteCode' => $testInfo['TestSiteCode'],
            'TestType' => $testInfo['TestType']
        ];

        // Handle Group Expansion
        if ($testInfo['TestType'] === 'GROUP') {
            $members = $grpModel->where('TestSiteID', $testSiteID)->findAll();
            foreach ($members as $m) {
                $this->expandTest($m['Member'], $testToOrder, $testModel, $grpModel, $calModel);
            }
        }

        // Handle Calculated Test Dependencies


value00: app/Models/OrderTest/OrderTestModel.php
value01:
                        if ($specimenInfo) {
                            $patResData['InternalSID'] = $specimenInfo['InternalSID'];
                        }
                        
                        $resModel->insert($patResData);

                        // Fire test_created rules after the test row is persisted


value00: app/Models/OrderTest/OrderTestModel.php
value01:
                                'testSiteID' => $tid,
                            ]);
                        } catch (\Throwable $e) {
                            log_message('error', 'OrderTestModel::createOrder rule engine error: ' . $e->getMessage());
                        }
                    }
                }
            }
            
            $this->db->transComplete();
            
            if ($this->db->transStatus() === false) {
                throw new \Exception('Transaction failed');
            }
            
            return $orderID;
        } catch (\Exception $e) {
            $this->db->transRollback();
            throw $e;
        }
    }


value00: app/Models/OrderTest/OrderTestModel.php
value01:
                // Create specimens for each unique container requirement
                $specimenSeq = 1;
                foreach ($testsByContainer as $conDefID => $containerData) {
                    $specimenID = $this->generateSpecimenID($orderID, $specimenSeq++);


value00: app/Models/OrderTest/OrderTestModel.php
value01:
{
                    $specimenID = $this->generateSpecimenID($orderID, $specimenSeq++);
                    
                    $specimenData = [
                        'SID' => $specimenID,
                        'SiteID' => $data['SiteID'] ?? '1',
                        'OrderID' => $internalOID,
                        'ConDefID' => $conDefID,
                        'Qty' => 1,
                        'Unit' => 'tube',
                        'GenerateBy' => 'ORDER',
                        'CreateDate' => date('Y-m-d H:i:s')
                    ];
                    
                    $this->db->table('specimen')->insert($specimenData);
                    $internalSID = $this->db->insertID();
                    
                    // Create specimen status


value00: app/Models/OrderTest/OrderTestModel.php
value01:
<?php
namespace App\Models\OrderTest;

use App\Models\BaseModel;

class OrderTestModel extends BaseModel {
    protected $table = 'ordertest';
    protected $primaryKey = 'InternalOID';
    protected $useAutoIncrement = true;
    protected $allowedFields = [
        'InternalOID',
        'OrderID',
        'PlacerID',
        'InternalPID',
        'SiteID',
        'PVADTID',
        'ReqApp',
        'Priority',
        'TrnDate',
        'EffDate',
        'CreateDate',
        'EndDate',
        'ArchiveDate',
        'DelDate'
    ];

    public function generateOrderID(string $siteCode = '00'): string {
        $date = new \DateTime();
        $year = $date->format('y');
        $month = $date->format('m');


value00: app/Models/OrderTest/OrderTestModel.php
value01:
    public function updateStatus(string $orderID, string $status): bool {
        $order = $this->getOrder($orderID);
        if (!$order) return false;

        return (bool)$this->db->table('orderstatus')->insert([
            'InternalOID' => $order['InternalOID'],
            'OrderStatus' => $status,
            'CreateDate' => date('Y-m-d H:i:s')
        ]);
    }

    public function softDelete(string $orderID): bool {
        return $this->where('OrderID', $orderID)->update(null, ['DelDate' => date('Y-m-d H:i:s')]);
    }
}


value00: app/Models/OrderTest/OrderTestModel.php
value01:
                // Insert unique tests into patres with specimen links
                if (!empty($testToOrder)) {
                    $resModel = new \App\Models\PatResultModel();
                    $patientModel = new \App\Models\Patient\PatientModel();
                    $patient = $patientModel->find((int) $data['InternalPID']);
                    $age = null;


value00: app/Models/OrderTest/OrderTestModel.php
value01:
{
                        $specimenInfo = $specimenConDefMap[$tid] ?? null;
                        
                        $patResData = [
                            'OrderID' => $internalOID,
                            'TestSiteID' => $tid,
                            'TestSiteCode' => $tinfo['TestSiteCode'],
                            'SID' => $orderID, 
                            'SampleID' => $orderID,
                            'ResultDateTime' => $orderData['TrnDate'],
                            'CreateDate' => date('Y-m-d H:i:s')
                        ];


value00: app/Models/OrderTest/OrderTestModel.php
value01:
{
            $orderID = !empty($data['OrderID']) ? $data['OrderID'] : $this->generateOrderID($data['SiteCode'] ?? '00');

            $orderData = [
                'OrderID' => $orderID,
                'PlacerID' => $data['PlacerID'] ?? null,
                'InternalPID' => $data['InternalPID'],
                'SiteID' => $data['SiteID'] ?? '1',
                'PVADTID' => $data['PatVisitID'] ?? $data['PVADTID'] ?? 0,
                'ReqApp' => $data['ReqApp'] ?? null,
                'Priority' => $data['Priority'] ?? 'R',
                'TrnDate' => $data['OrderDateTime'] ?? $data['TrnDate'] ?? date('Y-m-d H:i:s'),
                'EffDate' => $data['EffDate'] ?? date('Y-m-d H:i:s'),
                'CreateDate' => date('Y-m-d H:i:s')
            ];


value00: app/Models/OrderTest/OrderTestModel.php
value01:
                $containerDefModel = new \App\Models\Specimen\ContainerDefModel();
                
                foreach ($data['Tests'] as $test) {
                    $testSiteID = $test['TestSiteID'] ?? $test['TestID'] ?? null;
                    if ($testSiteID) {
                        $this->expandTest($testSiteID, $testToOrder, $testModel, $grpModel, $calModel);
                    }
                }
                
                // Group tests by container requirement
                $testsByContainer = [];


value00: app/Models/OrderTest/OrderTestModel.php
value01:
                    $patient = $patientModel->find((int) $data['InternalPID']);
                    $age = null;
                    if (is_array($patient) && !empty($patient['Birthdate'])) {
                        try {
                            $birthdate = new \DateTime((string) $patient['Birthdate']);
                            $age = (new \DateTime())->diff($birthdate)->y;
                        } catch (\Throwable $e) {
                            $age = null;
                        }
                    }

                    $ruleEngine = new \App\Services\RuleEngineService();

                    foreach ($testToOrder as $tid => $tinfo) {
                        $specimenInfo = $specimenConDefMap[$tid] ?? null;


value00: app/Models/OrderTest/OrderTestModel.php
value01:
        if ($containerDef) {
            return [
                'ConDefID' => $containerDef['ConDefID'],
                'ConCode' => $containerDef['ConCode'],
                'ConName' => $containerDef['ConName']
            ];
        }
        
        return [
            'ConDefID' => null,
            'ConCode' => 'DEFAULT',
            'ConName' => 'Default Container'
        ];
    }


value00: app/Models/OrderTest/OrderTestModel.php
value01:
        // Handle Calculated Test Dependencies
        if ($testInfo['TestType'] === 'CALC') {
            $members = $grpModel->getGroupMembers($testSiteID);
            foreach ($members as $member) {
                $memberID = $member['TestSiteID'] ?? null;
                if ($memberID) {
                    $this->expandTest($memberID, $testToOrder, $testModel, $grpModel, $calModel);
                }
            }
        }
    }


value00: app/Models/OrderTest/OrderTestModel.php
value01:
    public function getOrder(string $orderID): ?array {
        return $this->select('*')
            ->where('OrderID', $orderID)
            ->where('DelDate', null)
            ->get()
            ->getRowArray();
    }

    public function getOrdersByPatient(int $internalPID): array {
        return $this->select('*')
            ->where('InternalPID', $internalPID)
            ->where('DelDate', null)
            ->orderBy('TrnDate', 'DESC')
            ->get()
            ->getResultArray();
    }


value00: app/Models/OrderTest/OrderTestModel.php
value01:
                        try {
                            $ruleEngine->run('test_created', [
                                'order' => [
                                    'InternalOID' => $internalOID,
                                    'Priority' => $orderData['Priority'] ?? 'R',
                                    'TestSiteID' => $tid,
                                ],
                                'patient' => [
                                    'Sex' => is_array($patient) ? ($patient['Sex'] ?? null) : null,
                                ],
                                'age' => $age,
                                'testSiteID' => $tid,
                            ]);
                        }


value00: app/Models/OrderTest/OrderTestModel.php
value01:
            $testToOrder = [];
            $specimenConDefMap = []; // Map ConDefID to specimen info
            
            if (isset($data['Tests']) && is_array($data['Tests'])) {
                $testModel = new \App\Models\Test\TestDefSiteModel();
                $grpModel = new \App\Models\Test\TestDefGrpModel();
                $calModel = new \App\Models\Test\TestDefCalModel();
                $testMapDetailModel = new \App\Models\Test\TestMapDetailModel();
                $containerDefModel = new \App\Models\Specimen\ContainerDefModel();
                
                foreach ($data['Tests'] as


value00: app/Models/OrderTest/OrderTestModel.php
value01:
            $internalOID = $this->insert($orderData);

            if (!$internalOID) {
                throw new \Exception('Failed to create order');
            }

            // Handle Order Comments
            if (!empty($data['Comment'])) {
                $this->db->table('ordercom')->insert([
                    'InternalOID' => $internalOID,
                    'Comment' => $data['Comment'],
                    'CreateDate' => date('Y-m-d H:i:s')
                ]);
            }
            
            // Process Tests Expansion
            $testToOrder = [];
            $specimenConDefMap = []; // Map ConDefID to specimen info


value00: app/Models/OrderTest/OrderTestModel.php
value01:
                foreach ($testToOrder as $tid => $tinfo) {
                    // Find container requirement for this test
                    $containerReq = $this->getContainerRequirement($tid, $testMapDetailModel, $containerDefModel);
                    $conDefID = $containerReq['ConDefID'] ?? null;
                    
                    if (!isset($testsByContainer[$conDefID])) {
                        $testsByContainer[$conDefID] = [
                            'tests' => [],
                            'containerInfo' => $containerReq
                        ];
                    }
                    $testsByContainer[$conDefID]['tests'][$tid] = $tinfo;
                }
                
                // Create specimens for each unique container requirement


value00: app/Models/Organization/DepartmentModel.php
value01:
  protected $updatedField = '';
  protected $useSoftDeletes = true;
  protected $deletedField = 'EndDate';
  
  public function getDepartments($filter = []) {
    $this->select('department.*, discipline.DisciplineCode, discipline.DisciplineName, site.SiteCode, site.SiteName')
      ->join('discipline', 'discipline.DisciplineID=department.DisciplineID', 'left')
      ->join('site', 'department.SiteID=site.SiteID', 'left');
      
    if (!empty($filter['DepartmentCode'])) {
      $this->like('department.DepartmentCode', $filter['DepartmentCode'], 'both');
    }
    if (!empty($filter['DepartmentName'])) {
      $this->like('department.DepartmentName', $filter['DepartmentName'], 'both');
    }
    $rows = $this->findAll();

    return $rows;
  }


value00: app/Models/Organization/DepartmentModel.php
value01:
  public function getDepartment($DepartmentID) {
    $row = $this->select('department.*, discipline.DisciplineCode, discipline.DisciplineName, site.SiteCode, site.SiteName')
      ->join('discipline', 'discipline.DisciplineID=department.DisciplineID', 'left')
      ->join('site', 'site.SiteID=department.SiteID', 'left')
      ->where('department.DepartmentID', $DepartmentID)
      ->first();
    return $row;
  }
}


value00: app/Models/Organization/DepartmentModel.php
value01:
<?php
namespace App\Models\Organization;
use App\Models\BaseModel;

class DepartmentModel extends BaseModel {
  protected $table = 'department';
  protected $primaryKey = 'DepartmentID';
  protected $allowedFields = ['DisciplineID', 'SiteID', 'DepartmentCode', 'DepartmentName', 'CreateDate', 'EndDate']; 
		
  protected $useTimestamps = true;
  protected $createdField = 'CreateDate';
  protected $updatedField = '';
  protected $useSoftDeletes = true;
  protected $deletedField = 'EndDate';


value00: app/Models/PatResultModel.php
value01:
<?php
namespace App\Models;

use App\Models\RefRange\RefNumModel;
use App\Models\OrderTest\OrderTestModel;
use App\Models\Patient\PatientModel;

class PatResultModel extends BaseModel {
    protected $table = 'patres';
    protected $primaryKey = 'ResultID';
    protected $allowedFields = [
        'SiteID',
        'OrderID',
        'InternalSID',
        'SID',
        'SampleID',
        'TestSiteID',
        'TestSiteCode',
        'AspCnt',
        'Result',
        'SampleType',
        'ResultDateTime',
        'WorkstationID',
        'EquipmentID',
        'RefNumID',
        'RefTxtID',
        'CreateDate',
        'EndDate',
        'ArchiveDate',
        'DelDate'
    ];


value00: app/Models/PatResultModel.php
value01:
        $value = floatval($resultValue);
        $low = floatval($ref['Low']);
        $high = floatval($ref['High']);

        // Check low
        if ($ref['LowSign'] === '<=' && $value <= $low) {
            return 'L';
        }
        if ($ref['LowSign'] === '<' && $value < $low) {
            return 'L';
        }

        // Check high
        if ($ref['HighSign'] === '>=' && $value >= $high) {
            return 'H';
        }
        if ($ref['HighSign'] === '>' && $value > $high) {
            return 'H';
        }

        return null; // Normal
    }


value00: app/Models/PatResultModel.php
value01:
    /**
     * Get cumulative patient results across all orders
     * 
     * @param int $internalPID
     * @return array
     */
    public function getByPatient(int $internalPID): array {
        $builder = $this->db->table('patres pr');
        $builder->select('
            pr.ResultID,
            pr.OrderID,
            pr.TestSiteID,
            pr.TestSiteCode,
            pr.Result,
            pr.ResultDateTime,
            pr.RefNumID,
            tds.TestSiteName,
            tds.Unit1,
            tds.Unit2,
            ot.OrderID as OrderNumber,
            ot.TrnDate as OrderDate
        ');
        $builder->join('testdefsite tds', 'tds.TestSiteID = pr.TestSiteID', 'left');


value00: app/Models/Organization/HostAppModel.php
value01:
<?php

namespace App\Models\Organization;

use App\Models\BaseModel;

class HostAppModel extends BaseModel {
  protected $table = 'hostapp';
  protected $primaryKey = 'HostAppID';
  protected $allowedFields = ['HostAppID', 'HostAppName', 'SiteID', 'CreateDate', 'EndDate'];

  protected $useTimestamps = true;
  protected $createdField = 'CreateDate';
  protected $updatedField = '';
  protected $useSoftDeletes = true;
  protected $deletedField = 'EndDate';
}


value00: app/Models/PatResultModel.php
value01:
        if (!$ref) {
            return null;
        }

        // Get patient info from order
        $orderModel = new OrderTestModel();
        $order = $orderModel->find($orderID);
        
        if (!$order) {
            return null;
        }

        $patientModel = new PatientModel();
        $patient = $patientModel->find($order['InternalPID']);
        
        if (!$patient) {
            return null;
        }

        // Check if patient matches criteria (sex)
        if (!empty($ref['Sex']) && $ref['Sex'] !== 'ALL') {
            if ($patient['Sex'] !== $ref['Sex']) {
                return null;
            }
        }

        // Check age criteria


value00: app/Models/PatResultModel.php
value01:
                    if (is_array($order) && !empty($order['InternalPID'])) {
                        $patientModel = new \App\Models\Patient\PatientModel();
                        $patient = $patientModel->find((int) $order['InternalPID']);
                        if (is_array($patient) && !empty($patient['Birthdate'])) {
                            try {
                                $birthdate = new \DateTime((string) $patient['Birthdate']);
                                $age = (new \DateTime())->diff($birthdate)->y;
                            } catch (\Throwable $e) {
                                $age = null;
                            }
                        }
                    }

                    $engine = new \App\Services\RuleEngineService();


value00: app/Models/PatResultModel.php
value01:
        $builder->join('ordertest ot', 'ot.InternalOID = pr.OrderID', 'left');
        $builder->where('ot.InternalPID', $internalPID);
        $builder->where('pr.DelDate', null);
        $builder->orderBy('pr.ResultDateTime', 'DESC');
        
        return $builder->get()->getResultArray();
    }


value00: app/Models/PatResultModel.php
value01:
        $builder->join('testdefsite tds', 'tds.TestSiteID = pr.TestSiteID', 'left');
        $builder->join('refnum rn', 'rn.RefNumID = pr.RefNumID', 'left');
        $builder->where('pr.OrderID', $orderID);
        $builder->where('pr.DelDate', null);
        $builder->orderBy('tds.SeqScr', 'ASC');
        
        return $builder->get()->getResultArray();
    }


value00: app/Models/PatResultModel.php
value01:
    /**
     * Update result with validation and return flag (not stored)
     * 
     * @param int $resultID
     * @param array $data
     * @return array ['success' => bool, 'flag' => string|null, 'message' => string]
     */
    public function updateWithValidation(int $resultID, array $data): array {
        $this->db->transStart();

        try {
            $result = $this->find($resultID);
            if (!$result) {
                throw new \Exception('Result not found');
            }

            $flag = null;

            // If result value is being updated, validate it


value00: app/Models/PatResultModel.php
value01:
            $this->db->transComplete();

            // Fire result_updated rules (non-blocking)
            try {
                $fresh = $this->find($resultID);
                if (is_array($fresh) && !empty($fresh['OrderID']) && !empty($fresh['TestSiteID'])) {
                    $orderModel = new \App\Models\OrderTest\OrderTestModel();
                    $order = $orderModel->find((int) $fresh['OrderID']);

                    $patient = null;
                    $age = null;
                    if (is_array($order) && !empty($order['InternalPID']))


value00: app/Models/PatResultModel.php
value01:
    /**
     * Validate result value against reference range and return flag
     * 
     * @param float|string $resultValue The result value to validate
     * @param int $refNumID The reference range ID
     * @param int $orderID The order ID to get patient info
     * @return string|null 'L' for low, 'H' for high, or null for normal/no match
     */
    public function validateAndFlag($resultValue, int $refNumID, int $orderID): ?string {
        if (!is_numeric($resultValue)) {
            return null;
        }

        $refNumModel = new RefNumModel();
        $ref = $refNumModel->find($refNumID);
        
        if (!$ref) {
            return null;
        }

        // Get patient info from order


value00: app/Models/PatResultModel.php
value01:
                    $engine = new \App\Services\RuleEngineService();
                    $engine->run('result_updated', [
                        'order' => [
                            'InternalOID' => (int) ($fresh['OrderID'] ?? 0),
                            'Priority' => is_array($order) ? ($order['Priority'] ?? null) : null,
                            'TestSiteID' => (int) ($fresh['TestSiteID'] ?? 0),
                        ],
                        'patient' => [
                            'Sex' => is_array($patient) ? ($patient['Sex'] ?? null) : null,
                        ],
                        'age' => $age,
                        'testSiteID' => (int) ($fresh['TestSiteID'] ?? 0),
                        'result' => $fresh,
                    ]);
                }


value00: app/Models/PatResultModel.php
value01:
    public function getWithRelations(int $resultID): ?array {
        $builder = $this->db->table('patres pr');
        $builder->select('
            pr.*,
            tds.TestSiteName,
            tds.TestSiteCode,
            tds.Unit1,
            tds.Unit2,
            rn.Low,
            rn.High,
            rn.LowSign,
            rn.HighSign,
            rn.Display as RefDisplay,
            ot.OrderID as OrderNumber,
            ot.InternalPID
        ');
        $builder->join('testdefsite tds', 'tds.TestSiteID = pr.TestSiteID', 'left');
        $builder->join('refnum rn', 'rn.RefNumID = pr.RefNumID', 'left');
        $builder->join('ordertest ot', 'ot.InternalOID = pr.OrderID', 'left');


value00: app/Models/PatResultModel.php
value01:
    /**
     * Get all results for an order with test names and reference ranges
     * 
     * @param int $orderID
     * @return array
     */
    public function getByOrder(int $orderID): array {
        $builder = $this->db->table('patres pr');
        $builder->select('
            pr.ResultID,
            pr.OrderID,
            pr.TestSiteID,
            pr.TestSiteCode,
            pr.Result,
            pr.ResultDateTime,
            pr.RefNumID,
            pr.RefTxtID,
            pr.CreateDate,
            tds.TestSiteName,
            tds.Unit1,
            tds.Unit2,
            rn.Low,
            rn.High,
            rn.LowSign,
            rn.HighSign,
            rn.Display as RefDisplay
        ');


value00: app/Models/PatResultModel.php
value01:
            $flag = null;

            // If result value is being updated, validate it
            if (isset($data['Result']) && !empty($data['Result'])) {
                $refNumID = $data['RefNumID'] ?? $result['RefNumID'];
                
                if ($refNumID) {
                    $flag = $this->validateAndFlag($data['Result'], $refNumID, $result['OrderID']);
                }
            }

            // Set update timestamp
            $data['StartDate'] = date('Y-m-d H:i:s');

            $updated = $this->update($resultID, $data);

            if (!$updated) {
                throw new \Exception('Failed to update result');
            }

            $this->db->transComplete();

            // Fire result_updated rules (non-blocking)


value00: app/Models/PatResultModel.php
value01:
            } catch (\Throwable $e) {
                log_message('error', 'PatResultModel::updateWithValidation rule engine error: ' . $e->getMessage());
            }

            return [
                'success' => true,
                'flag' => $flag,
                'message' => 'Result updated successfully'
            ];
        } catch (\Exception $e) {
            $this->db->transRollback();
            return [
                'success' => false,
                'flag' => null,
                'message' => $e->getMessage()
            ];
        }
    }

    /**
     * Get single result with related data
     * 
     * @param int $resultID
     * @return array|null
     */


value00: app/Models/PatResultModel.php
value01:
        $builder->join('ordertest ot', 'ot.InternalOID = pr.OrderID', 'left');
        $builder->where('pr.ResultID', $resultID);
        $builder->where('pr.DelDate', null);
        
        $result = $builder->get()->getRowArray();
        return $result ?: null;
    }


value00: app/Models/PatResultModel.php
value01:
    /**
     * Soft delete result
     * 
     * @param int $resultID
     * @return bool
     */
    public function softDelete(int $resultID): bool {
        return $this->update($resultID, ['DelDate' => date('Y-m-d H:i:s')]);
    }
}


value00: app/Models/PatResultModel.php
value01:
        // Check age criteria
        if ($ref['AgeStart'] !== null || $ref['AgeEnd'] !== null) {
            $birthdate = new \DateTime($patient['Birthdate']);
            $today = new \DateTime();
            $age = $birthdate->diff($today)->y;

            if ($ref['AgeStart'] !== null && $age < $ref['AgeStart']) {
                return null;
            }
            if ($ref['AgeEnd'] !== null && $age > $ref['AgeEnd']) {
                return null;
            }
        }

        $value = floatval($resultValue);
        $low = floatval($ref['Low']);
        $high = floatval($ref['High']);


value00: app/Models/Organization/HostComParaModel.php
value01:
<?php

namespace App\Models\Organization;

use App\Models\BaseModel;

class HostComParaModel extends BaseModel {
  protected $table = 'hostcompara';
  protected $primaryKey = 'HostAppID';
  protected $allowedFields = ['HostAppID', 'HostIP', 'HostPort', 'HostPwd', 'CreateDate', 'EndDate'];

  protected $useTimestamps = true;
  protected $createdField = 'CreateDate';
  protected $updatedField = '';
  protected $useSoftDeletes = true;
  protected $deletedField = 'EndDate';
}


value00: app/Models/Organization/SiteModel.php
value01:
<?php
namespace App\Models\Organization;
use App\Models\BaseModel;
use App\Libraries\ValueSet;

class SiteModel extends BaseModel {
  protected $table = 'site';
  protected $primaryKey = 'SiteID';
  protected $allowedFields = ['SiteCode', 'ExtSiteID', 'SiteName', 'AccountID', 'SiteType', 'Parent', 'SiteClass', 'ME',
    'CreateDate', 'EndDate']; 
		
  protected $useTimestamps = true;
  protected $createdField = 'CreateDate';
  protected $updatedField = '';


value00: app/Models/Organization/WorkstationModel.php
value01:
<?php
namespace App\Models\Organization;
use App\Models\BaseModel;
use App\Libraries\ValueSet;

class WorkstationModel extends BaseModel {
  protected $table = 'workstation';
  protected $primaryKey = 'WorkstationID';
  protected $allowedFields = ['DepartmentID', 'WorkstationCode', 'WorkstationName', 'Type', 'LinkTo', 'Enable',
    'EquipmentID', 'CreateDate', 'EndDate']; 
		
  protected $useTimestamps = true;
  protected $createdField = 'CreateDate';
  protected $updatedField = '';
  protected $useSoftDeletes = true;
  protected $deletedField = 'EndDate';


value00: app/Models/Patient/PatientModel.php
value01:
      ->join('areageo areageo1', 'areageo1.AreaGeoID = patient.Province', 'left')
      ->join('areageo areageo2', 'areageo2.AreaGeoID = patient.City', 'left')
      ->where('patient.InternalPID', (int) $InternalPID)
      ->findAll();

    if (empty($rows)) { return null; }

    $patient = $rows[0];
    
    if (method_exists($this, 'transformPatientData')) { $patient = $this->transformPatientData($patient); }
    unset($patient['Address']);
    unset($patient['IdentifierType']);
    unset($patient['Identifier']);
    unset($patient['Comment']);


value00: app/Models/Patient/PatAttModel.php
value01:
  // Reactivate (hapus DelDate) 1 address jika pernah dihapus
  // Return true jika berhasil re-activate
  public function reactivateAddress(string $InternalPID, string $Address): bool {
    return $this->where('InternalPID', $InternalPID)->where('Address', $Address)->where('DelDate IS NOT NULL', null, false)->set('DelDate', null)->orderBy('PatAttID', 'DESC')->limit(1)->update();
  }
}


value00: app/Models/PatVisit/PatVisitADTModel.php
value01:
<?php

namespace App\Models\PatVisit;
use App\Models\BaseModel;

class PatVisitADTModel extends BaseModel {
	protected $table      = 'patvisitadt';
	protected $primaryKey = 'PVADTID';
	protected $allowedFields = ['InternalPVID', 'ADTCode', 'LocationID', 'AttDoc', 'RefDoc', 'AdmDoc', 'CnsDoc',
		'CreateDate', 'EndDate', 'ArchivedDate', 'DelDate'];
	protected $visnum_prefix;
  
  protected $useTimestamps = true;
  protected $createdField = 'CreateDate';
  protected $updatedField = '';
	protected $useSoftDeletes = true;
	protected $deletedField = 'DelDate';

}


value00: app/Models/Patient/PatAttModel.php
value01:
  protected $afterInsert  = ['convertDatesToUTCISO'];
  protected $afterUpdate  = ['convertDatesToUTCISO'];

  public function createPatAtt($patatt, string $newInternalPID) {
    foreach ($patatt as &$row) {
      $row['InternalPID'] = (string)$newInternalPID;
    }
    $this->insertBatch($patatt);
  }

  public function updatePatAtt(array $patatt, string $InternalPID) {
    // Ambil daftar address aktif saat ini dari DB
    $oldActive = $this->getActiveAddresses($InternalPID);

    // Normalisasi input baru (hapus duplikat & pastikan punya key 'Address')


value00: app/Models/Patient/PatAttModel.php
value01:
    $added   = array_diff($newData, $oldActive); // address baru
    $removed = array_diff($oldActive, $newData); // address lama dihapus

    // Soft delete yang dihapus
    if (!empty($removed)) {
      $this->softDeleteByAddresses($InternalPID, $removed);
    }

    // Tambahkan / re-activate yang baru
    foreach ($added as $addr) {
      $data = $mapNew[$addr];
      $data['InternalPID'] = $InternalPID;

      // Coba re-activate dulu
      $this->reactivateAddress($InternalPID, $addr);

      // Cek apakah ada baris yang di-update (re-activate)
      if ($this->db->affectedRows() === 0) {
          // Tidak ada baris lama yang dihapus → insert baru
          $this->insert($data);
      }
    }
  }


value00: app/Models/Patient/PatAttModel.php
value01:
  public function deletePatAtt(string $InternalPID) {
    $this->where('InternalPID', $InternalPID)->delete();
  }

  // Dapatkan daftar alamat aktif berdasarkan InternalPID
  public function getActiveAddresses(string $InternalPID): array {
    $rows = $this->select('Address')->where('InternalPID', $InternalPID)->where('DelDate', null)->findAll();
    return array_column($rows, 'Address');
  }
  // Soft delete beberapa address sekaligus
  public function softDeleteByAddresses(string $InternalPID, array $addresses) {
    if (empty($addresses)) return;
    $this->where('InternalPID', $InternalPID)->whereIn('Address', $addresses)->delete(); 
  }
  // Reactivate (hapus DelDate) 1 address jika pernah dihapus
  // Return true jika berhasil re-activate


value00: app/Models/Organization/SiteModel.php
value01:
  protected $useTimestamps = true;
  protected $createdField = 'CreateDate';
  protected $updatedField = '';
  protected $useSoftDeletes = true;
  protected $deletedField = 'EndDate';
   
  public function getSites($filter) {
		$builder = $this->select('site.SiteID, site.SiteCode, site.SiteName, s1.SiteName as ParentName, account.AccountName')
      ->join('account', 'account.AccountID=site.AccountID', 'left')
      ->join('site s1', 's1.SiteID=site.Parent', 'left');

		if (!empty($filter['SiteCode'])) {
			$builder->like('site.SiteCode', $filter['SiteCode'], 'both');
		}
		if (!empty($filter['SiteName'])) {
			$builder->like('site.SiteName', $filter['SiteName'], 'both');
		}

		$rows = $builder->findAll();
    
    return $rows;
  }


value00: app/Models/Organization/WorkstationModel.php
value01:
  protected $updatedField = '';
  protected $useSoftDeletes = true;
  protected $deletedField = 'EndDate';
   
  public function getWorkstations($filter = []) {
    $this->select('workstation.*, department.DepartmentName, wst1.WorkstationName as LinkToName')
      ->join('workstation wst1', 'workstation.LinkTo=wst1.WorkstationID', 'left')
      ->join('department', 'department.DepartmentID=workstation.DepartmentID', 'left');
       
    if (!empty($filter['WorkstationCode'])) {
      $this->like('workstation.WorkstationCode', $filter['WorkstationCode'], 'both');
    }
    if (!empty($filter['WorkstationName'])) {
      $this->like('workstation.WorkstationName', $filter['WorkstationName'], 'both');
    }
    $rows = $this->findAll();
    return $rows;
  }


value00: app/Models/Organization/SiteModel.php
value01:
  public function getSite($SiteID) {
    $row = $this->select('site.*, account.AccountName, s1.SiteName as ParentName')
      ->join('account', 'account.AccountID=site.AccountID', 'left')
      ->join('site s1', 's1.SiteID=site.Parent', 'left')
      ->where('site.SiteID', $SiteID)
      ->first();
    
    if (!$row) return null;
    
    $row = ValueSet::transformLabels([$row], [
      'SiteTypeID' => 'site_type',
      'SiteClassID' => 'site_class',
    ])[0];
    
    return $row;
  }
}


value00: app/Models/Patient/PatientModel.php
value01:
{
  protected $table      = 'patient';
  protected $primaryKey = 'InternalPID';
  protected $allowedFields = ['PatientID', 'AlternatePID', 'Prefix', 'NameFirst', 'NameMiddle', 'NameMaiden', 'NameLast', 'Suffix', 'NameAlias', 'Sex', 'Birthdate', 'PlaceOfBirth', 'Street_1', 'Street_2', 'Street_3', 
    'City', 'Province', 'ZIP', 'EmailAddress1', 'EmailAddress2', 'Phone', 'MobilePhone', 'Custodian', 'AccountNumber', 'Country', 'Race', 'MaritalStatus', 'Religion', 'Ethnic', 'Citizenship', 
    'DeathIndicator', 'TimeOfDeath', 'LinkTo', 'CreateDate', 'DelDate' ];
    
  protected $useTimestamps = true;
  protected $createdField = 'CreateDate';
  protected $updatedField = '';
  protected $useSoftDeletes = true;
  protected $deletedField  = 'DelDate';


value00: app/Models/Patient/PatientModel.php
value01:
<?php
namespace App\Models\Patient;

use App\Models\BaseModel;
use App\Libraries\ValueSet;

use App\Models\Patient\PatAttModel;
use App\Models\Patient\PatComModel;
use App\Models\Patient\PatIdtModel;

use App\Services\AuditService;

class PatientModel extends BaseModel {
  protected $table      = 'patient';
  protected $primaryKey = 'InternalPID';


value00: app/Models/Patient/PatAttModel.php
value01:
<?php
namespace App\Models\Patient;

use App\Models\BaseModel;
use App\Models\BaseUtcModel;

class PatAttModel extends BaseModel {
  protected $table      = 'patatt';
  protected $primaryKey = 'PatAttID';
  protected $allowedFields = ['InternalPID', 'Address', 'UserID', 'CreateDate', 'DelDate'];
    
  protected $useTimestamps  = true;
  protected $createdField   = 'CreateDate';
  protected $updatedField   = '';
  protected $useSoftDeletes = true;
  protected $deletedField   = 'DelDate';

  protected $beforeInsert = ['normalizeDatesToUTC'];
  protected $beforeUpdate = ['normalizeDatesToUTC'];
  protected $afterFind    = ['convertDatesToUTCISO'];
  protected $afterInsert  = ['convertDatesToUTCISO'];
  protected $afterUpdate  = ['convertDatesToUTCISO'];


value00: app/Models/Patient/PatientModel.php
value01:
  private function calculateAgeFromBirthdate($birthdate, $deathdate) {
    $dob = new \DateTime($birthdate);

    if ($deathdate == null) {
      $today = new \DateTime();
    } else {
      $deathdate = new \DateTime($deathdate);
      $today = $deathdate;
    }

    $diff = $today->diff($dob);
    $formattedAge = "";
    if ($diff->y > 0){
      $formattedAge .= "{$diff->y} Years ";
    }
    if ($diff->m > 0){
      $formattedAge .= "{$diff->m} Months ";
    }
    if ($diff->d > 0){
      $formattedAge .= "{$diff->d} Days";
    }

    return $formattedAge;
  }


value00: app/Models/Patient/PatientModel.php
value01:
  public function createPatient($input) {
    $db = \Config\Database::connect();

    $modelPatAtt = new PatAttModel();
    $modelPatCom = new PatComModel();
    $modelPatIdt = new PatIdtModel();

    // Extract nested data before filtering
    $patIdt = $input['PatIdt'] ?? null;
    $patCom = $input['PatCom'] ?? null;
    $patAtt = $input['PatAtt'] ?? null;

    // Remove nested arrays that don't belong to patient table
    unset($input['PatIdt'], $input['PatCom'], $input['PatAtt']);


value00: app/Models/Patient/PatientModel.php
value01:
  private function isValidDateTime($datetime) {
    if (empty($datetime) || $datetime=="") {return null; }
    try {
      if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $datetime)) {
          $dt = \DateTime::createFromFormat('Y-m-d', $datetime);
          return $dt ? $dt->format('Y-m-d') : null;
      }

      $dt = new \DateTime($datetime);
      return $dt->format('Y-m-d H:i:s');

    } catch (\Exception $e) {
      return null;
    }
  }

}


value00: app/Models/Patient/PatientModel.php
value01:
  public function deletePatient($InternalPID) {
    $db = \Config\Database::connect();
    $db->transBegin();
    
    try {
      $previousData = $this->find($InternalPID);
      if (!$previousData) {
        throw new \Exception('Patient not found');
      }
      
      $this->delete($InternalPID);
      $this->checkDbError($db, 'Delete patient');
      
      AuditService::logData(
        'DELETE',
        'patient',
        (string) $InternalPID,
        'patient',
        null,
        $previousData,
        [],
        'Patient deleted',
        ['PatientID' => $previousData['PatientID'] ?? null]
      );
      
      $db->transCommit();
      return $InternalPID;
    } catch (\Exception $e) {
      $db->transRollback();
      throw $e;
    }
  }


value00: app/Models/Organization/WorkstationModel.php
value01:
  public function getWorkstation($WorkstationID) {
    $row = $this->select("workstation.*, department.DepartmentName, wst1.WorkstationName as LinkToName")
      ->join('workstation wst1', 'workstation.LinkTo=wst1.WorkstationID', 'left')
      ->join('department', 'department.DepartmentID=workstation.DepartmentID', 'left')
      ->where('workstation.WorkstationID', $WorkstationID)
      ->first();
    
    if (!$row) return null;
    
    $row = ValueSet::transformLabels([$row], [
      'Type' => 'ws_type',
      'Enable' => 'enable_disable',
    ])[0];
    
    return $row;
  }
}


value00: app/Models/Patient/PatientModel.php
value01:
    // Remove nested arrays that don't belong to patient table
    unset($input['PatIdt'], $input['PatCom'], $input['PatAtt']);

    if (!empty($input['Custodian'])) {
        if (is_array($input['Custodian'])) {
            $input['Custodian'] = $input['Custodian']['InternalPID'] ?? null;
            if ($input['Custodian'] !== null) {
                $input['Custodian'] = (int) $input['Custodian'];
            }
        }
    }

    if (!empty($input['LinkTo']) && is_array($input['LinkTo'])) {
        $internalPids = array_column($input['LinkTo'], 'InternalPID');
        $input['LinkTo'] = implode(',', $internalPids);
    }
    $input['LinkTo'] = empty($input['LinkTo']) ? null : $input['LinkTo'];

    $db->transBegin();


value00: app/Models/Patient/PatComModel.php
value01:
  protected $afterUpdate  = ['convertDatesToUTCISO'];

  public function createPatCom(string $patcom, string $newInternalPID) {
    $this->insert(["InternalPID" => $newInternalPID, "Comment" => $patcom]);
  }

  public function updatePatCom(string $patcom, string $InternalPID) {
    $exists = $this->withDeleted()->where('InternalPID', $InternalPID)->first();

    if ($exists) {
      //Update
      $this->where('InternalPID', $InternalPID)->set(['EndDate' => null,'Comment' => $patcom])->update();
    } else {
      //Insert
      $this->insert(['InternalPID' => $InternalPID, 'Comment' => $patcom]);
    }
  }

  public function deletePatCom(string $InternalPID) {
    $this->where('InternalPID', $InternalPID)->delete();
  }

}


value00: app/Models/Patient/PatientModel.php
value01:
    $input['LinkTo'] = empty($input['LinkTo']) ? null : $input['LinkTo'];

    $db->transBegin();

    try {
      $InternalPID = $input['InternalPID'];
      $previousData = $this->find($InternalPID) ?? [];
      $this->where('InternalPID',$InternalPID)->set($input)->update();
      $this->checkDbError($db, 'Update patient');
      
      $changedFields = array_keys(array_diff_assoc((array) $previousData, (array) $input));


value00: app/Models/Patient/PatientModel.php
value01:
      if (!empty($patCom)) {
        $modelPatCom->createPatCom($patCom, $newInternalPID);
        $this->checkDbError($db, 'Insert PatCom');
      }

      if (!empty($patAtt)) {
        $modelPatAtt->createPatAtt($patAtt, $newInternalPID);
        $this->checkDbError($db, 'Insert PatAtt');
      }

      $db->transCommit();

      return $newInternalPID;

    } catch (\Exception $e) {
        $db->transRollback();
        throw $e; 
    }
  }


value00: app/Models/Patient/PatientModel.php
value01:
  private function transformPatientData(array $patient): array {
    $patient["Age"] = $this->calculateAgeFromBirthdate($patient["Birthdate"], $patient["TimeOfDeath"]);
    $patient["TimeOfDeath"] = $this->formattedDate($patient["TimeOfDeath"]);
    $patient["CreateDate"] = $this->formattedDate($patient["CreateDate"]);
    $patient["BirthdateConversion"] = $this->formatedDateForDisplay($patient["Birthdate"]);
    $patient["LinkTo"]    = $this->getLinkedPatients($patient['LinkTo']);
    $patient["Custodian"] = $this->getCustodian($patient['Custodian']);
    $patient['PatCom'] = $patient['Comment'];

    return $patient;
  }


value00: app/Models/Patient/PatientModel.php
value01:
  public function getPatient($InternalPID) {
    $rows = $this->select("
          patient.*,
          patcom.Comment as Comment,
          patidt.IdentifierType,
          patidt.Identifier,
          patatt.Address,
          patient.Province as Province,
          areageo1.AreaName as ProvinceLabel,
          patient.City as City,
          areageo2.AreaName as CityLabel

      ")
      ->join('patcom', 'patcom.InternalPID = patient.InternalPID', 'left')
      ->join('patidt', 'patidt.InternalPID = patient.InternalPID', 'left')
      ->join('patatt', 'patatt.InternalPID = patient.InternalPID and patatt.DelDate is null', 'left')
      ->join('areageo areageo1', 'areageo1.AreaGeoID = patient.Province', 'left')
      ->


value00: app/Models/Patient/PatientModel.php
value01:
    unset($patient['IdentifierType']);
    unset($patient['Identifier']);
    unset($patient['Comment']);

    $patient = ValueSet::transformLabels([$patient], [
      'Sex' => 'sex',
      'Country' => 'country',
      'Race' => 'race',
      'Religion' => 'religion',
      'Ethnic' => 'ethnic',
      'DeathIndicator' => 'death_indicator',
      'MaritalStatus' => 'marital_status',
    ])[0];

    $patient['PatIdt'] = null;
    $patient['PatAtt'] = [];


value00: app/Models/Patient/PatientModel.php
value01:
  private function checkDbError($db, string $context) {
      $error = $db->error();
      if (!empty($error['code'])) {
          throw new \Exception(
              "{$context} failed: {$error['code']} - {$error['message']}"
          );
      }
  }


value00: app/Models/Patient/PatAttModel.php
value01:
    // Normalisasi input baru (hapus duplikat & pastikan punya key 'Address')
    $mapNew = [];
    foreach ($patatt as $row) {
      if (empty($row['Address'])) continue;
      $mapNew[$row['Address']] = $row; // overwrite duplikat
    }

    $newData = array_keys($mapNew);

    // Hitung data yang ditambah & dihapus
    $added   = array_diff($newData, $oldActive); // address baru
    $removed = array_diff($oldActive, $newData);


value00: app/Models/Patient/PatComModel.php
value01:
<?php
namespace App\Models\Patient;

use App\Models\BaseModel;
use App\Models\BaseUtcModel;

class PatComModel extends BaseModel {
  protected $table      = 'patcom';
  protected $primaryKey = 'PatComID ';
  protected $allowedFields = ['InternalPID', 'Comment', 'CreateDate', 'EndDate'];
    
  protected $useTimestamps  = true;
  protected $createdField   = 'CreateDate';
  protected $updatedField   = '';
  protected $useSoftDeletes = true;
  protected $deletedField   = 'EndDate';

  protected $beforeInsert = ['normalizeDatesToUTC'];
  protected $beforeUpdate = ['normalizeDatesToUTC'];
  protected $afterFind    = ['convertDatesToUTCISO'];
  protected $afterInsert  = ['convertDatesToUTCISO'];
  protected $afterUpdate  = ['convertDatesToUTCISO'];


value00: app/Models/Patient/PatientModel.php
value01:
  public function updatePatient($input) {
    $db = \Config\Database::connect();
    $modelPatIdt = new PatIdtModel();
    $modelPatCom = new PatComModel();
    $modelPatAtt = new PatAttModel();

    if (!empty($input['Custodian'])) {
        if (is_array($input['Custodian'])) {
            $input['Custodian'] = $input['Custodian']['InternalPID'] ?? null;
            if ($input['Custodian'] !== null) {
                $input['Custodian'] = (int) $input['Custodian'];
            }
        }
    }

    if (!empty($input['LinkTo']) && is_array($input['LinkTo'])) {
        $internalPids = array_column($input['LinkTo'], 'InternalPID');
        $input['LinkTo'] = implode(',', $internalPids);
    }
    $input['LinkTo'] = empty($input['LinkTo']) ? null : $input['LinkTo'];


value00: app/Models/Patient/PatientModel.php
value01:
      if (!empty($input['PatCom'])) {
        $modelPatCom->updatePatCom($input['PatCom'], $InternalPID);
        $this->checkDbError($db, 'Update PatCom');
      } else {
        $modelPatCom->deletePatCom($InternalPID);
        $this->checkDbError($db, 'Update patcom');
      }

      if (!empty($input['PatAtt'])) {
        $modelPatAtt->updatePatAtt($input['PatAtt'], $InternalPID);
        $this->checkDbError($db, 'Update PatAtt');
      } else {
        $modelPatAtt->deletePatAtt($InternalPID);
        $this->checkDbError($db, 'Update/Delete patatt');
      }

      $db->transCommit();

      return $InternalPID;

    } catch (\Exception $e) {
        $db->transRollback();
        throw $e; 
    }
  }


value00: app/Models/Patient/PatientModel.php
value01:
      $changedFields = array_keys(array_diff_assoc((array) $previousData, (array) $input));
      AuditService::logData(
        'UPDATE',
        'patient',
        (string) $InternalPID,
        'patient',
        null,
        (array) $previousData,
        $input,
        'Patient data updated',
        ['changed_fields' => $changedFields]
      );

      if (!empty($input['PatIdt'])) {
        $modelPatIdt->updatePatIdt($input['PatIdt'], $InternalPID);
        $this->checkDbError($db, 'Update patIdt');
      } else {
        $modelPatIdt->deletePatIdt($InternalPID);
        $this->checkDbError($db, 'Update patidt');
      }


value00: app/Models/Patient/PatientModel.php
value01:
  private function formattedDate(?string $dateString): ?string {
    try {
        if (empty($dateString)) {return null;}

        $dt = new \DateTime($dateString, new \DateTimeZone("UTC"));
        return $dt->format('Y-m-d\TH:i:s\Z');
    } catch (\Exception $e) {
        return null; 
    }
  }

  private function formatedDateForDisplay($dateString) {    
    $date = \DateTime::createFromFormat('Y-m-d H:i', $dateString);

    if (!$date) {
      $timestamp = strtotime($dateString);
      if ($timestamp) {
        return date('j M Y', $timestamp);
      }
      return null;
    }

    return $date->format('j M Y');
  }


value00: app/Models/Patient/PatientModel.php
value01:
    $input['LinkTo'] = empty($input['LinkTo']) ? null : $input['LinkTo'];

    $db->transBegin();

    try {
      $previousData = [];
      $this->insert($input);
      $newInternalPID = $this->getInsertID();
      $this->checkDbError($db, 'Insert patient');
      
      AuditService::logData(
        'CREATE',
        'patient',
        (string) $newInternalPID,
        'patient',
        null,
        $previousData,
        $input,
        'Patient registration',
        ['PatientID' => $input['PatientID'] ?? null]
      );

      if (!empty($patIdt)) {
        $modelPatIdt->createPatIdt($patIdt, $newInternalPID);
        $this->checkDbError($db, 'Insert PatIdt');
      }


value00: app/Models/Patient/PatientModel.php
value01:
    $patient['PatIdt'] = null;
    $patient['PatAtt'] = [];
    
    foreach ($rows as $row) {
      if ($row['IdentifierType'] && $row['Identifier'] && !$patient['PatIdt']) {
        $patient['PatIdt'] = [
          'IdentifierType' => $row['IdentifierType'],
          'Identifier'     => $row['Identifier'],
        ];
      }

      if ($row['Address']) {
        $patient['PatAtt'][] = ['Address' => $row['Address']];
      }
    }

    if (empty($patient['PatIdt'])) { $patient['PatIdt'] = null; }
    if (empty($patient['PatAtt'])) { $patient['PatAtt'] = null; }

    return $patient;
  }


value00: app/Models/Patient/PatientModel.php
value01:
    if (!empty($filters['Birthdate'])) {
      $this->where('Birthdate', $filters['Birthdate']);
    }

    $rows = $this->findAll();
    $rows = ValueSet::transformLabels($rows, [
      'Sex' => 'sex',
    ]);
    return $rows;
  }


value00: app/Models/Patient/PatientModel.php
value01:
  protected $useSoftDeletes = true;
  protected $deletedField  = 'DelDate';

  public function getPatients($filters = []) {
    $qname = "CONCAT_WS(' ', IFNULL(NameFirst,''), IFNULL(NameMiddle,''), IFNULL(NameLast,''))";

    $this->select("InternalPID, PatientID, $qname as FullName, Sex, Birthdate, EmailAddress1 as Email, MobilePhone");

    if (!empty($filters['Name'])) {
      $this->like($qname, $filters['Name'], 'both');
    }

    if (!empty($filters['InternalPID'])) {
      $this->where('InternalPID', $filters['InternalPID']);
    }

    if (!empty($filters['PatientID'])) {
      $this->like('PatientID', $filters['PatientID'], 'both');
    }

    if (!empty($filters['Birthdate'])) {
      $this->where('Birthdate', $filters['Birthdate']);
    }


value00: app/Models/Patient/PatIdtModel.php
value01:
<?php
namespace App\Models\Patient;

use App\Models\BaseModel;
use App\Models\BaseUtcModel;

class PatIdtModel extends BaseModel {
  protected $table      = 'patidt';
  protected $primaryKey = 'PatIdtID';
  protected $allowedFields = ['InternalPID', 'IdentifierType', 'Identifier', 'EffectiveDate', 'ExpirationDate', 'CreateDate', 'DelDate'];
    
  protected $useTimestamps  = true;
  protected $createdField   = 'CreateDate';
  protected $updatedField   = '';
  protected $useSoftDeletes = true;
  protected $deletedField   = 'DelDate';

  protected $beforeInsert = ['normalizeDatesToUTC'];


value00: app/Models/Patient/PatIdtModel.php
value01:
  public function updatePatIdt($patidt, string $InternalPID) {
    $exists = $this->where('InternalPID', $InternalPID)->first();
    // $exists = $db->table('patidt')->where('InternalPID', $InternalPID)->get()->getRowArray();

    if ($exists) {
      // Update
      $this->where('InternalPID', $InternalPID)->set($patidt)->update();
      // $db->table('patidt')->where('InternalPID', $InternalPID)->update($patidt);

    } else {
      // Insert
      $patidt['InternalPID'] = $InternalPID;
      $this->insert($patidt);
      
      // $patidt['InternalPID'] = $InternalPID;
      // $db->table('patidt')->insert($patidt);
    }
  }

  public function deletePatIdt(string $InternalPID) {
    $this->where('InternalPID', $InternalPID)->delete();
  }

}


value00: app/Models/Patient/PatientModel.php
value01:
  private function getLinkedPatients(?string $linkTo): ?array {
    if (empty($linkTo)) { return null; }

    $ids = array_filter(explode(',', $linkTo));

    return $this->db->table('patient')
      ->select('InternalPID, PatientID')
      ->whereIn('InternalPID', $ids)
      ->get()
      ->getResultArray() ?: null;
  }

  private function getCustodian($custodianId): ?array {
    if (empty($custodianId)) {
      return null;
    }

    return $this->select('InternalPID, PatientID')
      ->where('InternalPID', (int) $custodianId)
      ->first() ?: null;
  }


value00: app/Models/PatVisit/PatDiagModel.php
value01:
<?php

namespace App\Models\PatVisit;
use App\Models\BaseModel;

class PatDiagModel extends BaseModel {
	protected $table      = 'patdiag';
	protected $primaryKey = 'InternalPVID';
	protected $allowedFields = ['InternalPVID','InternalPID', 'DiagCode', 'Diagnosis', 'CreateDate', 'EndDate', 'ArchivedDate', 'DelDate'];
	protected $visnum_prefix;
  
  protected $useTimestamps = true;
  protected $createdField = 'CreateDate';
  protected $updatedField = '';
	protected $useSoftDeletes = true;
	protected $deletedField = 'DelDate';

}


value00: app/Models/Patient/PatIdtModel.php
value01:
  protected $deletedField   = 'DelDate';

  protected $beforeInsert = ['normalizeDatesToUTC'];
  protected $beforeUpdate = ['normalizeDatesToUTC'];
  protected $afterFind    = ['convertDatesToUTCISO'];
  protected $afterInsert  = ['convertDatesToUTCISO'];
  protected $afterUpdate  = ['convertDatesToUTCISO'];

  public function createPatIdt($patidt, string $newInternalPID) {
    $this->insert(["InternalPID" => $newInternalPID, "IdentifierType" => $patidt['IdentifierType'], 'Identifier' => $patidt['Identifier']]);
  }


value00: app/Models/RefRange/RefNumModel.php
value01:
[
              '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


value00: app/Models/RefRange/RefNumModel.php
value01:
  /**
   * 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']


value00: app/Models/RefRange/RefNumModel.php
value01:
              '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
   */


value00: app/Models/RefRange/RefNumModel.php
value01:
,
              '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']) : ''


value00: app/Models/RefRange/RefNumModel.php
value01:
  protected $useTimestamps = true;
  protected $createdField = 'CreateDate';
  protected $updatedField = '';
  protected $useSoftDeletes = true;
  protected $deletedField = "EndDate";

  /**
   * Get active numeric reference ranges for a test
   */
  public function getActiveByTestSiteID($testSiteID) {
    return $this->where('TestSiteID', $testSiteID)
      ->where('EndDate', null)
      ->orderBy('Display', 'ASC')
      ->findAll();
  }


  /**
   * Get formatted numeric reference ranges with labels
   *
   * @param int $testSiteID
   * @return array
   */


value00: app/Models/RefRange/RefNumModel.php
value01:
<?php

namespace App\Models\RefRange;

use App\Models\BaseModel;

class RefNumModel extends BaseModel
{
  protected $table = 'refnum';
  protected $primaryKey = 'RefNumID';
  protected $allowedFields = [
    'SiteID',
    'TestSiteID',
    'SpcType',
    'Sex',
    'Criteria',
    'AgeStart',
    'AgeEnd',
    'NumRefType',
    'RangeType',
    'LowSign',
    'Low',
    'HighSign',
    'High',
    'Display',
    'Flag',
    'Interpretation',
    'Notes',
    'CreateDate',
    'StartDate',
    'EndDate'
  ];

  protected $useTimestamps = true;
  protected $createdField = 'CreateDate';
  protected $updatedField = '';


value00: app/Models/RefRange/RefNumModel.php
value01:
  /**
   * 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']) : ''


value00: app/Models/RefRange/RefNumModel.php
value01:
,
              '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'),
          ]);
      }
  }
}


value00: app/Models/PatVisit/PatVisitModel.php
value01:
<?php

namespace App\Models\PatVisit;
use App\Models\BaseModel;
use App\Models\CounterModel;
use App\Models\PatVisit\PatDiagModel;
use App\Models\PatVisit\PatVisitADTModel;

class PatVisitModel extends BaseModel {
	protected $table      = 'patvisit';
	protected $primaryKey = 'InternalPVID';
	protected $allowedFields = ['PVID', 'InternalPID', 'EpisodeID', 'LastVisitADT', 'CreateDate', 'EndDate', 'ArchivedDate', 'DelDate'];
  
  protected $useTimestamps = true;
  protected $createdField = 'CreateDate';
  protected $updatedField = '';
	protected $useSoftDeletes = true;
	protected $deletedField = 'EndDate';

	protected $visnum_prefix = "DV";


value00: app/Models/PatVisit/PatVisitModel.php
value01:
	protected $useSoftDeletes = true;
	protected $deletedField = 'EndDate';

	protected $visnum_prefix = "DV";

	public function show($PVID) {
		$row = $this->select("*, patvisit.InternalPID, patvisit.CreateDate as PVCreateDate, patdiag.CreateDate as PDCreateDate, patvisitadt.CreateDate as PVACreateDate")
			->join('patdiag', 'patdiag.InternalPVID=patvisit.InternalPVID and patdiag.DelDate is null', 'left')
			->join('patvisitadt', 'patvisitadt.InternalPVID=patvisit.InternalPVID', 'left')
			->where('patvisit.PVID',$PVID)
			->where('patvisit.EndDate IS NULL') // Exclude soft deleted
			->first();
		return $row;
	}

	public function showByPatient($InternalPID) {
		$rows =


value00: app/Models/PatVisit/PatVisitModel.php
value01:
			if($InternalPVID === false) { throw new \Exception("Failed to insert main PatVisit record."); }
			if( !empty($input['PatDiag']) && ( !empty($input['PatDiag']['DiagCode']) || !empty($input['PatDiag']['Diagnosis']) ) ) { 
				$input['PatDiag']['InternalPVID'] = $InternalPVID;
				$tmp = $modelPD->insert($input['PatDiag']);
				if ($tmp === false) { throw new \Exception("Failed to insert PatDiag record."); }
			}


value00: app/Models/PatVisit/PatVisitModel.php
value01:
			->join('location', 'location.LocationID=patvisitadt.LocationID', 'left')
			->where('patvisit.InternalPID',$InternalPID)
			->where('patvisit.EndDate IS NULL') // Exclude soft deleted
			->findAll();
		return $rows;
	}

	public function createPatVisit($input) {
		$db = $this->db;
    $modelPD = new PatDiagModel();
    $modelPVA = new PatVisitADTModel();
		$db->transBegin();
		try{
			
			if (!isset($input['PVID']) || $input['PVID']=='') {
				$modelCounter = new CounterModel();
        $input['PVID'] = $this->visnum_prefix .$modelCounter->use(2);
			}
			
			$InternalPVID = $this->insert($input, true);
			if($InternalPVID === false) { throw new \Exception("Failed to insert main PatVisit record."); }


value00: app/Models/PatVisit/PatVisitModel.php
value01:
{ 
			$error = $db->error();
			throw new \Exception("Failed to update PatDiag record. ". $error['message']); 
		}

		if(!empty($input['PatVisitADT'])) { 
			$adt = $input['PatVisitADT'];
			$adt['InternalPVID'] = $InternalPVID;
			$tmp = $modelPVA->insert($adt);	
			if ($tmp === false) { 
				$error = $db->error();
				throw new \Exception("Failed to update PatVisitADT record. ". $error['message']); 
			}
		}

		if ($db->transStatus() === FALSE) {
				$db->transRollback();
				return false;
			} else {
				$db->transCommit();
				$data = [ "PVID" => $input['PVID'], "InternalPVID" => $InternalPVID ];
				return $data;
			}

		} catch (\Exception $e) {
			$this->db->transRollback();
      throw $e; 
		}
	}
}


value00: app/Models/PatVisit/PatVisitModel.php
value01:
	public function updatePatVisit($input) {
		$InternalPVID = $input['InternalPVID'];
		$modelPD = new PatDiagModel();
    $modelPVA = new PatVisitADTModel();

		$db = $this->db;
		$db->transBegin();
		try{
			// Check if visit is not soft deleted before updating
			$visit = $this->where('InternalPVID', $InternalPVID)
				->where('EndDate IS NULL')
				->first();
			
			if (!$visit) {
				throw new \Exception("Visit not found or has been deleted.");
			}
			
			$this->where('InternalPVID',$InternalPVID)->set($input)->update();
			
			// patdiag
			$exist = $modelPD->where('InternalPVID',$InternalPVID)->find();


