Re-synced controllers, configs, libraries, seeds, and docs with the latest API expectations and response helpers.
317 lines
10 KiB
PHP
Executable File
317 lines
10 KiB
PHP
Executable File
<?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',
|
|
'ResultCode',
|
|
'Result',
|
|
'ResultValue',
|
|
'SampleType',
|
|
'ResultDateTime',
|
|
'WorkstationID',
|
|
'EquipmentID',
|
|
'RefNumID',
|
|
'RefTxtID',
|
|
'CreateDate',
|
|
'EndDate',
|
|
'ArchiveDate',
|
|
'DelDate'
|
|
];
|
|
|
|
/**
|
|
* 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
|
|
$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 (AgeStart/AgeEnd stored in days)
|
|
if ($ref['AgeStart'] !== null || $ref['AgeEnd'] !== null) {
|
|
$birthdate = new \DateTime($patient['Birthdate']);
|
|
$today = new \DateTime();
|
|
$ageInDays = $birthdate->diff($today, true)->days;
|
|
|
|
if ($ref['AgeStart'] !== null && $ageInDays < (int) $ref['AgeStart']) {
|
|
return null;
|
|
}
|
|
if ($ref['AgeEnd'] !== null && $ageInDays > (int) $ref['AgeEnd']) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
$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
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
');
|
|
$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();
|
|
}
|
|
|
|
/**
|
|
* 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');
|
|
$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();
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
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)
|
|
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'])) {
|
|
$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();
|
|
$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,
|
|
]);
|
|
}
|
|
} 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
|
|
*/
|
|
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');
|
|
$builder->where('pr.ResultID', $resultID);
|
|
$builder->where('pr.DelDate', null);
|
|
|
|
$result = $builder->get()->getRowArray();
|
|
return $result ?: null;
|
|
}
|
|
|
|
/**
|
|
* 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')]);
|
|
}
|
|
}
|