clqms-be/app/Models/Patient/PatientModel.php

350 lines
11 KiB
PHP
Raw Normal View History

<?php
namespace App\Models\Patient;
use App\Models\BaseModel;
feat(valueset): refactor from ID-based to name-based lookups Complete overhaul of the valueset system to use human-readable names instead of numeric IDs for improved maintainability and API consistency. - PatientController: Renamed 'Gender' field to 'Sex' in validation rules - ValuesetController: Changed API endpoints from ID-based (/:num) to name-based (/:any) - TestsController: Refactored to use ValueSet library instead of direct valueset queries - Added ValueSet library (app/Libraries/ValueSet.php) with static lookup methods: - getOptions() - returns dropdown format [{value, label}] - getLabel(, ) - returns label for a value - transformLabels(, ) - batch transform records - get() and getRaw() for Lookups compatibility - Added ValueSetApiController for public valueset API endpoints - Added ValueSet refresh endpoint (POST /api/valueset/refresh) - Added DemoOrderController for testing order creation without auth - 2026-01-12-000001: Convert valueset references from VID to VValue - 2026-01-12-000002: Rename patient.Gender column to Sex - OrderTestController: Now uses OrderTestModel with proper model pattern - TestsController: Uses ValueSet library for all lookup operations - ValueSetController: Simplified to use name-based lookups - Updated all organization (account/site/workstation) dialogs and index views - Updated specimen container dialogs and index views - Updated tests_index.php with ValueSet integration - Updated patient dialog form and index views - Removed .factory/config.json and CLAUDE.md (replaced by AGENTS.md) - Consolidated lookups in Lookups.php (removed inline valueset constants) - Updated all test files to match new field names - 32 modified files, 17 new files, 2 deleted files - Net: +661 insertions, -1443 deletions (significant cleanup)
2026-01-12 16:53:41 +07:00
use App\Libraries\ValueSet;
2025-10-16 10:50:09 +07:00
use App\Models\Patient\PatAttModel;
use App\Models\Patient\PatComModel;
use App\Models\Patient\PatIdtModel;
2025-10-14 18:53:06 +07:00
class PatientModel extends BaseModel {
protected $table = 'patient';
protected $primaryKey = 'InternalPID';
feat(valueset): refactor from ID-based to name-based lookups Complete overhaul of the valueset system to use human-readable names instead of numeric IDs for improved maintainability and API consistency. - PatientController: Renamed 'Gender' field to 'Sex' in validation rules - ValuesetController: Changed API endpoints from ID-based (/:num) to name-based (/:any) - TestsController: Refactored to use ValueSet library instead of direct valueset queries - Added ValueSet library (app/Libraries/ValueSet.php) with static lookup methods: - getOptions() - returns dropdown format [{value, label}] - getLabel(, ) - returns label for a value - transformLabels(, ) - batch transform records - get() and getRaw() for Lookups compatibility - Added ValueSetApiController for public valueset API endpoints - Added ValueSet refresh endpoint (POST /api/valueset/refresh) - Added DemoOrderController for testing order creation without auth - 2026-01-12-000001: Convert valueset references from VID to VValue - 2026-01-12-000002: Rename patient.Gender column to Sex - OrderTestController: Now uses OrderTestModel with proper model pattern - TestsController: Uses ValueSet library for all lookup operations - ValueSetController: Simplified to use name-based lookups - Updated all organization (account/site/workstation) dialogs and index views - Updated specimen container dialogs and index views - Updated tests_index.php with ValueSet integration - Updated patient dialog form and index views - Removed .factory/config.json and CLAUDE.md (replaced by AGENTS.md) - Consolidated lookups in Lookups.php (removed inline valueset constants) - Updated all test files to match new field names - 32 modified files, 17 new files, 2 deleted files - Net: +661 insertions, -1443 deletions (significant cleanup)
2026-01-12 16:53:41 +07:00
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',
feat(valueset): refactor from ID-based to name-based lookups Complete overhaul of the valueset system to use human-readable names instead of numeric IDs for improved maintainability and API consistency. - PatientController: Renamed 'Gender' field to 'Sex' in validation rules - ValuesetController: Changed API endpoints from ID-based (/:num) to name-based (/:any) - TestsController: Refactored to use ValueSet library instead of direct valueset queries - Added ValueSet library (app/Libraries/ValueSet.php) with static lookup methods: - getOptions() - returns dropdown format [{value, label}] - getLabel(, ) - returns label for a value - transformLabels(, ) - batch transform records - get() and getRaw() for Lookups compatibility - Added ValueSetApiController for public valueset API endpoints - Added ValueSet refresh endpoint (POST /api/valueset/refresh) - Added DemoOrderController for testing order creation without auth - 2026-01-12-000001: Convert valueset references from VID to VValue - 2026-01-12-000002: Rename patient.Gender column to Sex - OrderTestController: Now uses OrderTestModel with proper model pattern - TestsController: Uses ValueSet library for all lookup operations - ValueSetController: Simplified to use name-based lookups - Updated all organization (account/site/workstation) dialogs and index views - Updated specimen container dialogs and index views - Updated tests_index.php with ValueSet integration - Updated patient dialog form and index views - Removed .factory/config.json and CLAUDE.md (replaced by AGENTS.md) - Consolidated lookups in Lookups.php (removed inline valueset constants) - Updated all test files to match new field names - 32 modified files, 17 new files, 2 deleted files - Net: +661 insertions, -1443 deletions (significant cleanup)
2026-01-12 16:53:41 +07:00
'DeathIndicator', 'TimeOfDeath', 'LinkTo', 'CreateDate', 'DelDate' ];
2025-10-13 15:09:55 +07:00
protected $useTimestamps = true;
protected $createdField = 'CreateDate';
protected $updatedField = '';
2025-10-14 10:48:20 +07:00
protected $useSoftDeletes = true;
protected $deletedField = 'DelDate';
public function getPatients($filters = []) {
$qname = "CONCAT_WS(' ', IFNULL(Prefix,''), IFNULL(NameFirst,''), IFNULL(NameMiddle,''), IFNULL(NameLast,''), IFNULL(NameMaiden,''), IFNULL(Suffix,''))";
feat(valueset): refactor from ID-based to name-based lookups Complete overhaul of the valueset system to use human-readable names instead of numeric IDs for improved maintainability and API consistency. - PatientController: Renamed 'Gender' field to 'Sex' in validation rules - ValuesetController: Changed API endpoints from ID-based (/:num) to name-based (/:any) - TestsController: Refactored to use ValueSet library instead of direct valueset queries - Added ValueSet library (app/Libraries/ValueSet.php) with static lookup methods: - getOptions() - returns dropdown format [{value, label}] - getLabel(, ) - returns label for a value - transformLabels(, ) - batch transform records - get() and getRaw() for Lookups compatibility - Added ValueSetApiController for public valueset API endpoints - Added ValueSet refresh endpoint (POST /api/valueset/refresh) - Added DemoOrderController for testing order creation without auth - 2026-01-12-000001: Convert valueset references from VID to VValue - 2026-01-12-000002: Rename patient.Gender column to Sex - OrderTestController: Now uses OrderTestModel with proper model pattern - TestsController: Uses ValueSet library for all lookup operations - ValueSetController: Simplified to use name-based lookups - Updated all organization (account/site/workstation) dialogs and index views - Updated specimen container dialogs and index views - Updated tests_index.php with ValueSet integration - Updated patient dialog form and index views - Removed .factory/config.json and CLAUDE.md (replaced by AGENTS.md) - Consolidated lookups in Lookups.php (removed inline valueset constants) - Updated all test files to match new field names - 32 modified files, 17 new files, 2 deleted files - Net: +661 insertions, -1443 deletions (significant cleanup)
2026-01-12 16:53:41 +07:00
$this->select("InternalPID, PatientID, $qname as FullName, Sex, Birthdate, EmailAddress1 as Email, MobilePhone");
if (!empty($filters['Name'])) {
2025-10-16 13:22:28 +07:00
$this->like($qname, $filters['Name'], 'both');
}
if (!empty($filters['InternalPID'])) {
2025-10-16 13:22:28 +07:00
$this->where('InternalPID', $filters['InternalPID']);
}
if (!empty($filters['PatientID'])) {
2025-10-16 13:22:28 +07:00
$this->like('PatientID', $filters['PatientID'], 'both');
}
if (!empty($filters['Birthdate'])) {
2025-10-16 13:22:28 +07:00
$this->where('Birthdate', $filters['Birthdate']);
}
feat(valueset): refactor from ID-based to name-based lookups Complete overhaul of the valueset system to use human-readable names instead of numeric IDs for improved maintainability and API consistency. - PatientController: Renamed 'Gender' field to 'Sex' in validation rules - ValuesetController: Changed API endpoints from ID-based (/:num) to name-based (/:any) - TestsController: Refactored to use ValueSet library instead of direct valueset queries - Added ValueSet library (app/Libraries/ValueSet.php) with static lookup methods: - getOptions() - returns dropdown format [{value, label}] - getLabel(, ) - returns label for a value - transformLabels(, ) - batch transform records - get() and getRaw() for Lookups compatibility - Added ValueSetApiController for public valueset API endpoints - Added ValueSet refresh endpoint (POST /api/valueset/refresh) - Added DemoOrderController for testing order creation without auth - 2026-01-12-000001: Convert valueset references from VID to VValue - 2026-01-12-000002: Rename patient.Gender column to Sex - OrderTestController: Now uses OrderTestModel with proper model pattern - TestsController: Uses ValueSet library for all lookup operations - ValueSetController: Simplified to use name-based lookups - Updated all organization (account/site/workstation) dialogs and index views - Updated specimen container dialogs and index views - Updated tests_index.php with ValueSet integration - Updated patient dialog form and index views - Removed .factory/config.json and CLAUDE.md (replaced by AGENTS.md) - Consolidated lookups in Lookups.php (removed inline valueset constants) - Updated all test files to match new field names - 32 modified files, 17 new files, 2 deleted files - Net: +661 insertions, -1443 deletions (significant cleanup)
2026-01-12 16:53:41 +07:00
$rows = $this->findAll();
$rows = ValueSet::transformLabels($rows, [
'Sex' => 'sex',
feat(valueset): refactor from ID-based to name-based lookups Complete overhaul of the valueset system to use human-readable names instead of numeric IDs for improved maintainability and API consistency. - PatientController: Renamed 'Gender' field to 'Sex' in validation rules - ValuesetController: Changed API endpoints from ID-based (/:num) to name-based (/:any) - TestsController: Refactored to use ValueSet library instead of direct valueset queries - Added ValueSet library (app/Libraries/ValueSet.php) with static lookup methods: - getOptions() - returns dropdown format [{value, label}] - getLabel(, ) - returns label for a value - transformLabels(, ) - batch transform records - get() and getRaw() for Lookups compatibility - Added ValueSetApiController for public valueset API endpoints - Added ValueSet refresh endpoint (POST /api/valueset/refresh) - Added DemoOrderController for testing order creation without auth - 2026-01-12-000001: Convert valueset references from VID to VValue - 2026-01-12-000002: Rename patient.Gender column to Sex - OrderTestController: Now uses OrderTestModel with proper model pattern - TestsController: Uses ValueSet library for all lookup operations - ValueSetController: Simplified to use name-based lookups - Updated all organization (account/site/workstation) dialogs and index views - Updated specimen container dialogs and index views - Updated tests_index.php with ValueSet integration - Updated patient dialog form and index views - Removed .factory/config.json and CLAUDE.md (replaced by AGENTS.md) - Consolidated lookups in Lookups.php (removed inline valueset constants) - Updated all test files to match new field names - 32 modified files, 17 new files, 2 deleted files - Net: +661 insertions, -1443 deletions (significant cleanup)
2026-01-12 16:53:41 +07:00
]);
return $rows;
}
public function getPatient($InternalPID) {
2025-10-16 13:22:28 +07:00
$rows = $this->select("
patient.*,
patcom.Comment as Comment,
patidt.IdentifierType,
patidt.Identifier,
patatt.Address,
2025-12-01 16:47:52 +07:00
areageo1.AreaGeoID as ProvinceID,
areageo1.AreaName as Province,
areageo2.AreaGeoID as CityID,
areageo2.AreaName as City
2025-10-23 10:20:55 +07:00
")
2025-10-16 13:22:28 +07:00
->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')
2025-12-01 16:47:52 +07:00
->join('areageo areageo1', 'areageo1.AreaGeoID = patient.Province', 'left')
->join('areageo areageo2', 'areageo2.AreaGeoID = patient.City', 'left')
2025-10-16 13:22:28 +07:00
->where('patient.InternalPID', (int) $InternalPID)
->findAll();
if (empty($rows)) { return null; }
$patient = $rows[0];
if (method_exists($this, 'transformPatientData')) { $patient = $this->transformPatientData($patient); }
2025-10-01 15:36:55 +07:00
unset($patient['Address']);
unset($patient['IdentifierType']);
unset($patient['Identifier']);
unset($patient['Comment']);
feat(valueset): refactor from ID-based to name-based lookups Complete overhaul of the valueset system to use human-readable names instead of numeric IDs for improved maintainability and API consistency. - PatientController: Renamed 'Gender' field to 'Sex' in validation rules - ValuesetController: Changed API endpoints from ID-based (/:num) to name-based (/:any) - TestsController: Refactored to use ValueSet library instead of direct valueset queries - Added ValueSet library (app/Libraries/ValueSet.php) with static lookup methods: - getOptions() - returns dropdown format [{value, label}] - getLabel(, ) - returns label for a value - transformLabels(, ) - batch transform records - get() and getRaw() for Lookups compatibility - Added ValueSetApiController for public valueset API endpoints - Added ValueSet refresh endpoint (POST /api/valueset/refresh) - Added DemoOrderController for testing order creation without auth - 2026-01-12-000001: Convert valueset references from VID to VValue - 2026-01-12-000002: Rename patient.Gender column to Sex - OrderTestController: Now uses OrderTestModel with proper model pattern - TestsController: Uses ValueSet library for all lookup operations - ValueSetController: Simplified to use name-based lookups - Updated all organization (account/site/workstation) dialogs and index views - Updated specimen container dialogs and index views - Updated tests_index.php with ValueSet integration - Updated patient dialog form and index views - Removed .factory/config.json and CLAUDE.md (replaced by AGENTS.md) - Consolidated lookups in Lookups.php (removed inline valueset constants) - Updated all test files to match new field names - 32 modified files, 17 new files, 2 deleted files - Net: +661 insertions, -1443 deletions (significant cleanup)
2026-01-12 16:53:41 +07:00
$patient = ValueSet::transformLabels([$patient], [
'Sex' => 'sex',
feat(valueset): refactor from ID-based to name-based lookups Complete overhaul of the valueset system to use human-readable names instead of numeric IDs for improved maintainability and API consistency. - PatientController: Renamed 'Gender' field to 'Sex' in validation rules - ValuesetController: Changed API endpoints from ID-based (/:num) to name-based (/:any) - TestsController: Refactored to use ValueSet library instead of direct valueset queries - Added ValueSet library (app/Libraries/ValueSet.php) with static lookup methods: - getOptions() - returns dropdown format [{value, label}] - getLabel(, ) - returns label for a value - transformLabels(, ) - batch transform records - get() and getRaw() for Lookups compatibility - Added ValueSetApiController for public valueset API endpoints - Added ValueSet refresh endpoint (POST /api/valueset/refresh) - Added DemoOrderController for testing order creation without auth - 2026-01-12-000001: Convert valueset references from VID to VValue - 2026-01-12-000002: Rename patient.Gender column to Sex - OrderTestController: Now uses OrderTestModel with proper model pattern - TestsController: Uses ValueSet library for all lookup operations - ValueSetController: Simplified to use name-based lookups - Updated all organization (account/site/workstation) dialogs and index views - Updated specimen container dialogs and index views - Updated tests_index.php with ValueSet integration - Updated patient dialog form and index views - Removed .factory/config.json and CLAUDE.md (replaced by AGENTS.md) - Consolidated lookups in Lookups.php (removed inline valueset constants) - Updated all test files to match new field names - 32 modified files, 17 new files, 2 deleted files - Net: +661 insertions, -1443 deletions (significant cleanup)
2026-01-12 16:53:41 +07:00
'Country' => 'country',
'Race' => 'race',
'Religion' => 'religion',
'Ethnic' => 'ethnic',
'DeathIndicator' => 'death_indicator',
'MaritalStatus' => 'marital_status',
])[0];
2025-10-01 15:36:55 +07:00
$patient['PatIdt'] = null;
$patient['PatAtt'] = [];
foreach ($rows as $row) {
2025-10-01 15:36:55 +07:00
if ($row['IdentifierType'] && $row['Identifier'] && !$patient['PatIdt']) {
$patient['PatIdt'] = [
'IdentifierType' => $row['IdentifierType'],
'Identifier' => $row['Identifier'],
];
}
if ($row['Address']) {
2025-10-01 15:36:55 +07:00
$patient['PatAtt'][] = ['Address' => $row['Address']];
}
}
2025-10-01 15:36:55 +07:00
if (empty($patient['PatIdt'])) { $patient['PatIdt'] = null; }
if (empty($patient['PatAtt'])) { $patient['PatAtt'] = null; }
return $patient;
}
public function createPatient($input) {
$db = \Config\Database::connect();
2025-10-16 10:50:09 +07:00
2025-10-16 13:22:28 +07:00
$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']);
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'];
2025-10-16 10:50:09 +07:00
2025-10-14 15:50:22 +07:00
$db->transBegin();
try {
$this->insert($input);
$newInternalPID = $this->getInsertID();
$this->checkDbError($db, 'Insert patient');
if (!empty($patIdt)) {
$modelPatIdt->createPatIdt($patIdt, $newInternalPID);
2025-10-16 10:50:09 +07:00
$this->checkDbError($db, 'Insert PatIdt');
}
if (!empty($patCom)) {
$modelPatCom->createPatCom($patCom, $newInternalPID);
2025-10-16 10:50:09 +07:00
$this->checkDbError($db, 'Insert PatCom');
2025-10-01 15:36:55 +07:00
}
2025-10-16 10:50:09 +07:00
if (!empty($patAtt)) {
$modelPatAtt->createPatAtt($patAtt, $newInternalPID);
2025-10-16 10:50:09 +07:00
$this->checkDbError($db, 'Insert PatAtt');
}
2025-10-14 15:50:22 +07:00
$db->transCommit();
2025-10-16 10:50:09 +07:00
return $newInternalPID;
2025-10-01 15:36:55 +07:00
} catch (\Exception $e) {
2025-10-14 15:50:22 +07:00
$db->transRollback();
2025-10-01 15:36:55 +07:00
throw $e;
}
}
public function updatePatient($input) {
$db = \Config\Database::connect();
2025-10-16 13:22:28 +07:00
$modelPatIdt = new PatIdtModel();
$modelPatCom = new PatComModel();
$modelPatAtt = new PatAttModel();
2025-10-16 10:50:09 +07:00
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'];
2025-10-14 15:50:22 +07:00
$db->transBegin();
2025-10-01 15:36:55 +07:00
try {
2025-10-16 10:50:09 +07:00
2025-10-01 15:36:55 +07:00
$InternalPID = $input['InternalPID'];
$this->where('InternalPID',$InternalPID)->set($input)->update();
$this->checkDbError($db, 'Update patient');
2025-10-16 10:50:09 +07:00
if (!empty($input['PatIdt'])) {
2025-10-16 13:22:28 +07:00
$modelPatIdt->updatePatIdt($input['PatIdt'], $InternalPID);
2025-10-16 10:50:09 +07:00
$this->checkDbError($db, 'Update patIdt');
2025-10-01 15:36:55 +07:00
} else {
2025-10-16 13:22:28 +07:00
$modelPatIdt->deletePatIdt($InternalPID);
$this->checkDbError($db, 'Update patidt');
2025-10-01 15:36:55 +07:00
}
2025-10-16 10:50:09 +07:00
if (!empty($input['PatCom'])) {
2025-10-16 13:22:28 +07:00
$modelPatCom->updatePatCom($input['PatCom'], $InternalPID);
2025-10-16 10:50:09 +07:00
$this->checkDbError($db, 'Update PatCom');
2025-10-01 15:36:55 +07:00
} else {
2025-10-16 13:22:28 +07:00
$modelPatCom->deletePatCom($InternalPID);
$this->checkDbError($db, 'Update patcom');
2025-10-01 15:36:55 +07:00
}
2025-10-16 10:50:09 +07:00
if (!empty($input['PatAtt'])) {
2025-10-16 13:22:28 +07:00
$modelPatAtt->updatePatAtt($input['PatAtt'], $InternalPID);
2025-10-16 10:50:09 +07:00
$this->checkDbError($db, 'Update PatAtt');
2025-10-01 15:36:55 +07:00
} else {
2025-10-16 13:22:28 +07:00
$modelPatAtt->deletePatAtt($InternalPID);
2025-10-16 10:50:09 +07:00
$this->checkDbError($db, 'Update/Delete patatt');
2025-10-01 15:36:55 +07:00
}
2025-10-14 15:50:22 +07:00
$db->transCommit();
2025-10-16 10:50:09 +07:00
2025-10-01 15:36:55 +07:00
return $InternalPID;
2025-10-14 15:50:22 +07:00
} catch (\Exception $e) {
2025-10-14 15:50:22 +07:00
$db->transRollback();
2025-10-01 15:36:55 +07:00
throw $e;
}
}
private function transformPatientData(array $patient): array {
2025-10-21 10:27:31 +07:00
$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']);
2025-10-21 09:33:38 +07:00
$patient["Custodian"] = $this->getCustodian($patient['Custodian']);
$patient['PatCom'] = $patient['Comment'];
return $patient;
}
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;
}
2025-10-16 13:22:28 +07:00
return $this->select('InternalPID, PatientID')
->where('InternalPID', (int) $custodianId)
->first() ?: null;
}
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;
}
private function formattedDate(?string $dateString): ?string {
try {
2025-10-14 15:50:22 +07:00
if (empty($dateString)) {return null;}
$dt = new \DateTime($dateString, new \DateTimeZone("UTC"));
feat(valueset): refactor from ID-based to name-based lookups Complete overhaul of the valueset system to use human-readable names instead of numeric IDs for improved maintainability and API consistency. - PatientController: Renamed 'Gender' field to 'Sex' in validation rules - ValuesetController: Changed API endpoints from ID-based (/:num) to name-based (/:any) - TestsController: Refactored to use ValueSet library instead of direct valueset queries - Added ValueSet library (app/Libraries/ValueSet.php) with static lookup methods: - getOptions() - returns dropdown format [{value, label}] - getLabel(, ) - returns label for a value - transformLabels(, ) - batch transform records - get() and getRaw() for Lookups compatibility - Added ValueSetApiController for public valueset API endpoints - Added ValueSet refresh endpoint (POST /api/valueset/refresh) - Added DemoOrderController for testing order creation without auth - 2026-01-12-000001: Convert valueset references from VID to VValue - 2026-01-12-000002: Rename patient.Gender column to Sex - OrderTestController: Now uses OrderTestModel with proper model pattern - TestsController: Uses ValueSet library for all lookup operations - ValueSetController: Simplified to use name-based lookups - Updated all organization (account/site/workstation) dialogs and index views - Updated specimen container dialogs and index views - Updated tests_index.php with ValueSet integration - Updated patient dialog form and index views - Removed .factory/config.json and CLAUDE.md (replaced by AGENTS.md) - Consolidated lookups in Lookups.php (removed inline valueset constants) - Updated all test files to match new field names - 32 modified files, 17 new files, 2 deleted files - Net: +661 insertions, -1443 deletions (significant cleanup)
2026-01-12 16:53:41 +07:00
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');
}
private function checkDbError($db, string $context) {
$error = $db->error();
if (!empty($error['code'])) {
throw new \Exception(
"{$context} failed: {$error['code']} - {$error['message']}"
);
}
}
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);
feat(valueset): refactor from ID-based to name-based lookups Complete overhaul of the valueset system to use human-readable names instead of numeric IDs for improved maintainability and API consistency. - PatientController: Renamed 'Gender' field to 'Sex' in validation rules - ValuesetController: Changed API endpoints from ID-based (/:num) to name-based (/:any) - TestsController: Refactored to use ValueSet library instead of direct valueset queries - Added ValueSet library (app/Libraries/ValueSet.php) with static lookup methods: - getOptions() - returns dropdown format [{value, label}] - getLabel(, ) - returns label for a value - transformLabels(, ) - batch transform records - get() and getRaw() for Lookups compatibility - Added ValueSetApiController for public valueset API endpoints - Added ValueSet refresh endpoint (POST /api/valueset/refresh) - Added DemoOrderController for testing order creation without auth - 2026-01-12-000001: Convert valueset references from VID to VValue - 2026-01-12-000002: Rename patient.Gender column to Sex - OrderTestController: Now uses OrderTestModel with proper model pattern - TestsController: Uses ValueSet library for all lookup operations - ValueSetController: Simplified to use name-based lookups - Updated all organization (account/site/workstation) dialogs and index views - Updated specimen container dialogs and index views - Updated tests_index.php with ValueSet integration - Updated patient dialog form and index views - Removed .factory/config.json and CLAUDE.md (replaced by AGENTS.md) - Consolidated lookups in Lookups.php (removed inline valueset constants) - Updated all test files to match new field names - 32 modified files, 17 new files, 2 deleted files - Net: +661 insertions, -1443 deletions (significant cleanup)
2026-01-12 16:53:41 +07:00
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;
}
}
}