feat: implement comprehensive order management with specimens and tests
Major updates to order system: - Add specimen and test data to order responses (index, show, create, update, status update) - Implement getOrderSpecimens() and getOrderTests() private methods in OrderTestController - Support 'include=details' query parameter for expanded order data - Update OrderTestModel with enhanced query capabilities and transaction handling API documentation: - Update OpenAPI specs for orders, patient-visits, and tests - Add new schemas for order specimens and tests - Regenerate bundled API documentation Database: - Add Requestable field to TestDefSite migration - Create OrderSeeder and ClearOrderDataSeeder for test data - Update DBSeeder to include order seeding Patient visits: - Add filtering by PatientID, PatientName, and date ranges - Include LastLocation in visit queries Testing: - Add OrderCreateTest with comprehensive test coverage Documentation: - Update AGENTS.md with improved controller structure examples
This commit is contained in:
parent
e9c7beeb2b
commit
42006e1af9
18
AGENTS.md
18
AGENTS.md
@ -70,6 +70,8 @@ use Firebase\JWT\JWT;
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Controller Structure
|
### Controller Structure
|
||||||
|
Controllers handle HTTP requests and delegate business logic to Models. They should NOT contain database queries.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
@ -82,16 +84,24 @@ class ExampleController extends Controller
|
|||||||
use ResponseTrait;
|
use ResponseTrait;
|
||||||
|
|
||||||
protected $model;
|
protected $model;
|
||||||
protected $db;
|
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->db = \Config\Database::connect();
|
|
||||||
$this->model = new \App\Models\ExampleModel();
|
$this->model = new \App\Models\ExampleModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function index() { /* ... */ }
|
public function index()
|
||||||
public function create() { /* ... */ }
|
{
|
||||||
|
$data = $this->model->findAll();
|
||||||
|
return $this->respond(['status' => 'success', 'data' => $data], 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create()
|
||||||
|
{
|
||||||
|
$data = $this->request->getJSON(true);
|
||||||
|
$result = $this->model->createWithRelations($data);
|
||||||
|
return $this->respond(['status' => 'success', 'data' => $result], 201);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -4,9 +4,9 @@ namespace App\Controllers;
|
|||||||
use App\Traits\ResponseTrait;
|
use App\Traits\ResponseTrait;
|
||||||
use CodeIgniter\Controller;
|
use CodeIgniter\Controller;
|
||||||
use App\Libraries\ValueSet;
|
use App\Libraries\ValueSet;
|
||||||
use App\Models\OrderTestModel;
|
use App\Models\OrderTest\OrderTestModel;
|
||||||
use App\Models\Patient\PatientModel;
|
use App\Models\Patient\PatientModel;
|
||||||
use App\Models\Patient\PatVisitModel;
|
use App\Models\PatVisit\PatVisitModel;
|
||||||
|
|
||||||
class OrderTestController extends Controller {
|
class OrderTestController extends Controller {
|
||||||
use ResponseTrait;
|
use ResponseTrait;
|
||||||
@ -29,6 +29,7 @@ class OrderTestController extends Controller {
|
|||||||
|
|
||||||
public function index() {
|
public function index() {
|
||||||
$internalPID = $this->request->getVar('InternalPID');
|
$internalPID = $this->request->getVar('InternalPID');
|
||||||
|
$includeDetails = $this->request->getVar('include') === 'details';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if ($internalPID) {
|
if ($internalPID) {
|
||||||
@ -46,6 +47,13 @@ class OrderTestController extends Controller {
|
|||||||
'OrderStatus' => 'order_status',
|
'OrderStatus' => 'order_status',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if ($includeDetails && !empty($rows)) {
|
||||||
|
foreach ($rows as &$row) {
|
||||||
|
$row['Specimens'] = $this->getOrderSpecimens($row['InternalOID']);
|
||||||
|
$row['Tests'] = $this->getOrderTests($row['InternalOID']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $this->respond([
|
return $this->respond([
|
||||||
'status' => 'success',
|
'status' => 'success',
|
||||||
'message' => 'Data fetched successfully',
|
'message' => 'Data fetched successfully',
|
||||||
@ -72,6 +80,10 @@ class OrderTestController extends Controller {
|
|||||||
'OrderStatus' => 'order_status',
|
'OrderStatus' => 'order_status',
|
||||||
])[0];
|
])[0];
|
||||||
|
|
||||||
|
// Include specimens and tests
|
||||||
|
$row['Specimens'] = $this->getOrderSpecimens($row['InternalOID']);
|
||||||
|
$row['Tests'] = $this->getOrderTests($row['InternalOID']);
|
||||||
|
|
||||||
return $this->respond([
|
return $this->respond([
|
||||||
'status' => 'success',
|
'status' => 'success',
|
||||||
'message' => 'Data fetched successfully',
|
'message' => 'Data fetched successfully',
|
||||||
@ -82,6 +94,39 @@ class OrderTestController extends Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getOrderSpecimens($internalOID) {
|
||||||
|
$specimens = $this->db->table('specimen s')
|
||||||
|
->select('s.*, cd.ConCode, cd.ConName')
|
||||||
|
->join('containerdef cd', 'cd.ConDefID = s.ConDefID', 'left')
|
||||||
|
->where('s.OrderID', $internalOID)
|
||||||
|
->where('s.EndDate IS NULL')
|
||||||
|
->get()
|
||||||
|
->getResultArray();
|
||||||
|
|
||||||
|
// Get status for each specimen
|
||||||
|
foreach ($specimens as &$specimen) {
|
||||||
|
$status = $this->db->table('specimenstatus')
|
||||||
|
->where('SID', $specimen['SID'])
|
||||||
|
->where('EndDate IS NULL')
|
||||||
|
->orderBy('CreateDate', 'DESC')
|
||||||
|
->get()
|
||||||
|
->getRowArray();
|
||||||
|
$specimen['Status'] = $status['SpcStatus'] ?? 'PENDING';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $specimens;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getOrderTests($internalOID) {
|
||||||
|
return $this->db->table('patres pr')
|
||||||
|
->select('pr.*, tds.TestSiteCode, tds.TestSiteName')
|
||||||
|
->join('testdefsite tds', 'tds.TestSiteID = pr.TestSiteID', 'left')
|
||||||
|
->where('pr.OrderID', $internalOID)
|
||||||
|
->where('pr.DelDate IS NULL')
|
||||||
|
->get()
|
||||||
|
->getResultArray();
|
||||||
|
}
|
||||||
|
|
||||||
public function create() {
|
public function create() {
|
||||||
$input = $this->request->getJSON(true);
|
$input = $this->request->getJSON(true);
|
||||||
|
|
||||||
@ -103,10 +148,15 @@ class OrderTestController extends Controller {
|
|||||||
|
|
||||||
$orderID = $this->model->createOrder($input);
|
$orderID = $this->model->createOrder($input);
|
||||||
|
|
||||||
|
// Fetch complete order details
|
||||||
|
$order = $this->model->getOrder($orderID);
|
||||||
|
$order['Specimens'] = $this->getOrderSpecimens($order['InternalOID']);
|
||||||
|
$order['Tests'] = $this->getOrderTests($order['InternalOID']);
|
||||||
|
|
||||||
return $this->respondCreated([
|
return $this->respondCreated([
|
||||||
'status' => 'success',
|
'status' => 'success',
|
||||||
'message' => 'Order created successfully',
|
'message' => 'Order created successfully',
|
||||||
'data' => ['OrderID' => $orderID]
|
'data' => $order
|
||||||
], 201);
|
], 201);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return $this->failServerError('Something went wrong: ' . $e->getMessage());
|
return $this->failServerError('Something went wrong: ' . $e->getMessage());
|
||||||
@ -137,10 +187,14 @@ class OrderTestController extends Controller {
|
|||||||
$this->model->update($order['InternalOID'], $updateData);
|
$this->model->update($order['InternalOID'], $updateData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$updatedOrder = $this->model->getOrder($input['OrderID']);
|
||||||
|
$updatedOrder['Specimens'] = $this->getOrderSpecimens($updatedOrder['InternalOID']);
|
||||||
|
$updatedOrder['Tests'] = $this->getOrderTests($updatedOrder['InternalOID']);
|
||||||
|
|
||||||
return $this->respond([
|
return $this->respond([
|
||||||
'status' => 'success',
|
'status' => 'success',
|
||||||
'message' => 'Order updated successfully',
|
'message' => 'Order updated successfully',
|
||||||
'data' => $this->model->getOrder($input['OrderID'])
|
'data' => $updatedOrder
|
||||||
], 200);
|
], 200);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return $this->failServerError('Something went wrong: ' . $e->getMessage());
|
return $this->failServerError('Something went wrong: ' . $e->getMessage());
|
||||||
@ -192,10 +246,14 @@ class OrderTestController extends Controller {
|
|||||||
|
|
||||||
$this->model->updateStatus($input['OrderID'], $input['OrderStatus']);
|
$this->model->updateStatus($input['OrderID'], $input['OrderStatus']);
|
||||||
|
|
||||||
|
$updatedOrder = $this->model->getOrder($input['OrderID']);
|
||||||
|
$updatedOrder['Specimens'] = $this->getOrderSpecimens($updatedOrder['InternalOID']);
|
||||||
|
$updatedOrder['Tests'] = $this->getOrderTests($updatedOrder['InternalOID']);
|
||||||
|
|
||||||
return $this->respond([
|
return $this->respond([
|
||||||
'status' => 'success',
|
'status' => 'success',
|
||||||
'message' => 'Order status updated successfully',
|
'message' => 'Order status updated successfully',
|
||||||
'data' => $this->model->getOrder($input['OrderID'])
|
'data' => $updatedOrder
|
||||||
], 200);
|
], 200);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return $this->failServerError('Something went wrong: ' . $e->getMessage());
|
return $this->failServerError('Something went wrong: ' . $e->getMessage());
|
||||||
|
|||||||
@ -20,15 +20,43 @@ class PatVisitController extends BaseController {
|
|||||||
try {
|
try {
|
||||||
$InternalPID = $this->request->getVar('InternalPID');
|
$InternalPID = $this->request->getVar('InternalPID');
|
||||||
$PVID = $this->request->getVar('PVID');
|
$PVID = $this->request->getVar('PVID');
|
||||||
|
$PatientID = $this->request->getVar('PatientID');
|
||||||
|
$PatientName = $this->request->getVar('PatientName');
|
||||||
|
$CreateDateFrom = $this->request->getVar('CreateDateFrom');
|
||||||
|
$CreateDateTo = $this->request->getVar('CreateDateTo');
|
||||||
|
|
||||||
$builder = $this->model->select('patvisit.*, patient.NameFirst, patient.NameLast, patient.PatientID')
|
$builder = $this->model->select('patvisit.*, patient.NameFirst, patient.NameLast, patient.PatientID, location.LocFull as LastLocation')
|
||||||
->join('patient', 'patient.InternalPID=patvisit.InternalPID', 'left');
|
->join('patient', 'patient.InternalPID=patvisit.InternalPID', 'left')
|
||||||
|
->join('(SELECT a1.*
|
||||||
|
FROM patvisitadt a1
|
||||||
|
INNER JOIN (
|
||||||
|
SELECT InternalPVID, MAX(PVADTID) AS MaxID
|
||||||
|
FROM patvisitadt
|
||||||
|
GROUP BY InternalPVID
|
||||||
|
) a2 ON a1.InternalPVID = a2.InternalPVID AND a1.PVADTID = a2.MaxID
|
||||||
|
) AS latest_patvisitadt', 'latest_patvisitadt.InternalPVID = patvisit.InternalPVID', 'left')
|
||||||
|
->join('location', 'location.LocationID = latest_patvisitadt.LocationID', 'left');
|
||||||
|
|
||||||
if ($InternalPID) {
|
if ($InternalPID) {
|
||||||
$builder->where('patvisit.InternalPID', $InternalPID);
|
$builder->where('patvisit.InternalPID', $InternalPID);
|
||||||
}
|
}
|
||||||
if ($PVID) {
|
if ($PVID) {
|
||||||
$builder->where('patvisit.PVID', $PVID);
|
$builder->like('patvisit.PVID', $PVID, 'both');
|
||||||
|
}
|
||||||
|
if ($PatientID) {
|
||||||
|
$builder->like('patient.PatientID', $PatientID, 'both');
|
||||||
|
}
|
||||||
|
if ($PatientName) {
|
||||||
|
$builder->groupStart()
|
||||||
|
->like('patient.NameFirst', $PatientName, 'both')
|
||||||
|
->orLike('patient.NameLast', $PatientName, 'both')
|
||||||
|
->groupEnd();
|
||||||
|
}
|
||||||
|
if ($CreateDateFrom) {
|
||||||
|
$builder->where('patvisit.CreateDate >=', $CreateDateFrom);
|
||||||
|
}
|
||||||
|
if ($CreateDateTo) {
|
||||||
|
$builder->where('patvisit.CreateDate <=', $CreateDateTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
$rows = $builder->orderBy('patvisit.CreateDate', 'DESC')->findAll();
|
$rows = $builder->orderBy('patvisit.CreateDate', 'DESC')->findAll();
|
||||||
|
|||||||
@ -364,7 +364,18 @@ class TestsController extends BaseController
|
|||||||
$this->modelRefTxt->where('TestSiteID', $id)->set('EndDate', $now)->update();
|
$this->modelRefTxt->where('TestSiteID', $id)->set('EndDate', $now)->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->modelMap->disableByTestSiteID($id);
|
// Disable testmap by test code
|
||||||
|
$testSiteCode = $existing['TestSiteCode'] ?? null;
|
||||||
|
if ($testSiteCode) {
|
||||||
|
$existingMaps = $this->modelMap->getMappingsByTestCode($testSiteCode);
|
||||||
|
foreach ($existingMaps as $existingMap) {
|
||||||
|
$this->modelMapDetail->where('TestMapID', $existingMap['TestMapID'])
|
||||||
|
->where('EndDate', null)
|
||||||
|
->set('EndDate', $now)
|
||||||
|
->update();
|
||||||
|
$this->modelMap->update($existingMap['TestMapID'], ['EndDate' => $now]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->db->transComplete();
|
$this->db->transComplete();
|
||||||
|
|
||||||
@ -387,10 +398,12 @@ class TestsController extends BaseController
|
|||||||
private function handleDetails($testSiteID, $input, $action)
|
private function handleDetails($testSiteID, $input, $action)
|
||||||
{
|
{
|
||||||
$testTypeID = $input['TestType'] ?? null;
|
$testTypeID = $input['TestType'] ?? null;
|
||||||
|
$testSiteCode = null;
|
||||||
|
|
||||||
if (!$testTypeID && $action === 'update') {
|
if (!$testTypeID && $action === 'update') {
|
||||||
$existing = $this->model->find($testSiteID);
|
$existing = $this->model->find($testSiteID);
|
||||||
$testTypeID = $existing['TestType'] ?? null;
|
$testTypeID = $existing['TestType'] ?? null;
|
||||||
|
$testSiteCode = $existing['TestSiteCode'] ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$testTypeID) {
|
if (!$testTypeID) {
|
||||||
@ -416,7 +429,7 @@ class TestsController extends BaseController
|
|||||||
|
|
||||||
case 'TITLE':
|
case 'TITLE':
|
||||||
if (isset($input['testmap']) && is_array($input['testmap'])) {
|
if (isset($input['testmap']) && is_array($input['testmap'])) {
|
||||||
$this->saveTestMap($testSiteID, $input['testmap'], $action);
|
$this->saveTestMap($testSiteID, $testSiteCode, $input['testmap'], $action);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -443,7 +456,7 @@ class TestsController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((TestValidationService::isTechnicalTest($typeCode) || TestValidationService::isCalc($typeCode)) && isset($input['testmap']) && is_array($input['testmap'])) {
|
if ((TestValidationService::isTechnicalTest($typeCode) || TestValidationService::isCalc($typeCode)) && isset($input['testmap']) && is_array($input['testmap'])) {
|
||||||
$this->saveTestMap($testSiteID, $input['testmap'], $action);
|
$this->saveTestMap($testSiteID, $testSiteCode, $input['testmap'], $action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -572,12 +585,11 @@ class TestsController extends BaseController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function saveTestMap($testSiteID, $mappings, $action)
|
private function saveTestMap($testSiteID, $testSiteCode, $mappings, $action)
|
||||||
{
|
{
|
||||||
if ($action === 'update') {
|
if ($action === 'update' && $testSiteCode) {
|
||||||
$existingMaps = $this->modelMap->where('TestSiteID', $testSiteID)
|
// Find existing mappings by test code through testmapdetail
|
||||||
->where('EndDate', null)
|
$existingMaps = $this->modelMap->getMappingsByTestCode($testSiteCode);
|
||||||
->findAll();
|
|
||||||
|
|
||||||
foreach ($existingMaps as $existingMap) {
|
foreach ($existingMaps as $existingMap) {
|
||||||
$this->modelMapDetail->where('TestMapID', $existingMap['TestMapID'])
|
$this->modelMapDetail->where('TestMapID', $existingMap['TestMapID'])
|
||||||
@ -586,13 +598,15 @@ class TestsController extends BaseController
|
|||||||
->update();
|
->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->modelMap->disableByTestSiteID($testSiteID);
|
// Soft delete the testmap headers
|
||||||
|
foreach ($existingMaps as $existingMap) {
|
||||||
|
$this->modelMap->update($existingMap['TestMapID'], ['EndDate' => date('Y-m-d H:i:s')]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_array($mappings)) {
|
if (is_array($mappings)) {
|
||||||
foreach ($mappings as $map) {
|
foreach ($mappings as $map) {
|
||||||
$mapData = [
|
$mapData = [
|
||||||
'TestSiteID' => $testSiteID,
|
|
||||||
'HostType' => $map['HostType'] ?? null,
|
'HostType' => $map['HostType'] ?? null,
|
||||||
'HostID' => $map['HostID'] ?? null,
|
'HostID' => $map['HostID'] ?? null,
|
||||||
'ClientType' => $map['ClientType'] ?? null,
|
'ClientType' => $map['ClientType'] ?? null,
|
||||||
|
|||||||
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Database\Migrations;
|
||||||
|
|
||||||
|
use CodeIgniter\Database\Migration;
|
||||||
|
|
||||||
|
class AddRequestableToTestDefSite extends Migration
|
||||||
|
{
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
$fields = [
|
||||||
|
'Requestable' => [
|
||||||
|
'type' => 'TINYINT',
|
||||||
|
'constraint' => 1,
|
||||||
|
'null' => true,
|
||||||
|
'default' => 1,
|
||||||
|
'comment' => 'Flag indicating if test can be requested (1=yes, 0=no)'
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->forge->addColumn('testdefsite', $fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
$this->forge->dropColumn('testdefsite', 'Requestable');
|
||||||
|
}
|
||||||
|
}
|
||||||
40
app/Database/Seeds/ClearOrderDataSeeder.php
Normal file
40
app/Database/Seeds/ClearOrderDataSeeder.php
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?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";
|
||||||
|
|
||||||
|
$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";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -17,5 +17,6 @@ class DBSeeder extends Seeder
|
|||||||
$this->call('TestSeeder');
|
$this->call('TestSeeder');
|
||||||
$this->call('PatientSeeder');
|
$this->call('PatientSeeder');
|
||||||
$this->call('DummySeeder');
|
$this->call('DummySeeder');
|
||||||
|
$this->call('OrderSeeder');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
537
app/Database/Seeds/OrderSeeder.php
Normal file
537
app/Database/Seeds/OrderSeeder.php
Normal file
@ -0,0 +1,537 @@
|
|||||||
|
<?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";
|
||||||
|
|
||||||
|
// 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';
|
||||||
|
$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';
|
||||||
|
$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')));
|
||||||
|
$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'],
|
||||||
|
];
|
||||||
|
|
||||||
|
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";
|
||||||
|
|
||||||
|
// 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')));
|
||||||
|
|
||||||
|
// 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';
|
||||||
|
$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';
|
||||||
|
$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')));
|
||||||
|
$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'],
|
||||||
|
];
|
||||||
|
|
||||||
|
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'],
|
||||||
|
];
|
||||||
|
|
||||||
|
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'))
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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';
|
||||||
|
$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';
|
||||||
|
$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')));
|
||||||
|
$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'],
|
||||||
|
];
|
||||||
|
|
||||||
|
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
|
||||||
|
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";
|
||||||
|
|
||||||
|
// 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')));
|
||||||
|
|
||||||
|
// Create order comment
|
||||||
|
$this->createOrderComment($internalOID3, 'STAT order for suspected kidney dysfunction. Patient has diabetes history.', $yesterday);
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// 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';
|
||||||
|
$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'],
|
||||||
|
];
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// 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";
|
||||||
|
|
||||||
|
// 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
|
||||||
|
$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
|
||||||
|
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'];
|
||||||
|
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);
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// SUMMARY
|
||||||
|
// ========================================
|
||||||
|
echo "\n";
|
||||||
|
echo "========================================\n";
|
||||||
|
echo "ORDER SEEDING COMPLETED SUCCESSFULLY!\n";
|
||||||
|
echo "========================================\n";
|
||||||
|
echo "Orders Created: 5\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";
|
||||||
|
echo " - Order 4: Urinalysis - PENDING\n";
|
||||||
|
echo " - Order 5: CBC + Chemistry - PENDING (Multi-container)\n";
|
||||||
|
echo "----------------------------------------\n";
|
||||||
|
echo "Specimens Created: 6\n";
|
||||||
|
echo " - SST tubes: 4\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";
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -52,7 +52,14 @@ class OrderTestModel extends BaseModel {
|
|||||||
return $siteCode . $year . $month . $day . $seqStr;
|
return $siteCode . $year . $month . $day . $seqStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
public function createOrder(array $data): string {
|
||||||
|
$this->db->transStart();
|
||||||
|
|
||||||
|
try {
|
||||||
$orderID = $data['OrderID'] ?? $this->generateOrderID($data['SiteCode'] ?? '00');
|
$orderID = $data['OrderID'] ?? $this->generateOrderID($data['SiteCode'] ?? '00');
|
||||||
|
|
||||||
$orderData = [
|
$orderData = [
|
||||||
@ -70,6 +77,10 @@ class OrderTestModel extends BaseModel {
|
|||||||
|
|
||||||
$internalOID = $this->insert($orderData);
|
$internalOID = $this->insert($orderData);
|
||||||
|
|
||||||
|
if (!$internalOID) {
|
||||||
|
throw new \Exception('Failed to create order');
|
||||||
|
}
|
||||||
|
|
||||||
// Handle Order Comments
|
// Handle Order Comments
|
||||||
if (!empty($data['Comment'])) {
|
if (!empty($data['Comment'])) {
|
||||||
$this->db->table('ordercom')->insert([
|
$this->db->table('ordercom')->insert([
|
||||||
@ -80,11 +91,15 @@ class OrderTestModel extends BaseModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process Tests Expansion
|
// Process Tests Expansion
|
||||||
if (isset($data['Tests']) && is_array($data['Tests'])) {
|
|
||||||
$testToOrder = [];
|
$testToOrder = [];
|
||||||
|
$specimenConDefMap = []; // Map ConDefID to specimen info
|
||||||
|
|
||||||
|
if (isset($data['Tests']) && is_array($data['Tests'])) {
|
||||||
$testModel = new \App\Models\Test\TestDefSiteModel();
|
$testModel = new \App\Models\Test\TestDefSiteModel();
|
||||||
$grpModel = new \App\Models\Test\TestDefGrpModel();
|
$grpModel = new \App\Models\Test\TestDefGrpModel();
|
||||||
$calModel = new \App\Models\Test\TestDefCalModel();
|
$calModel = new \App\Models\Test\TestDefCalModel();
|
||||||
|
$testMapDetailModel = new \App\Models\Test\TestMapDetailModel();
|
||||||
|
$containerDefModel = new \App\Models\Specimen\ContainerDefModel();
|
||||||
|
|
||||||
foreach ($data['Tests'] as $test) {
|
foreach ($data['Tests'] as $test) {
|
||||||
$testSiteID = $test['TestSiteID'] ?? $test['TestID'] ?? null;
|
$testSiteID = $test['TestSiteID'] ?? $test['TestID'] ?? null;
|
||||||
@ -93,11 +108,66 @@ class OrderTestModel extends BaseModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert unique tests into patres
|
// Group tests by container requirement
|
||||||
|
$testsByContainer = [];
|
||||||
|
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
|
||||||
|
$specimenSeq = 1;
|
||||||
|
foreach ($testsByContainer as $conDefID => $containerData) {
|
||||||
|
$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
|
||||||
|
$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
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert unique tests into patres with specimen links
|
||||||
if (!empty($testToOrder)) {
|
if (!empty($testToOrder)) {
|
||||||
$resModel = new \App\Models\PatResultModel();
|
$resModel = new \App\Models\PatResultModel();
|
||||||
foreach ($testToOrder as $tid => $tinfo) {
|
foreach ($testToOrder as $tid => $tinfo) {
|
||||||
$resModel->insert([
|
$specimenInfo = $specimenConDefMap[$tid] ?? null;
|
||||||
|
|
||||||
|
$patResData = [
|
||||||
'OrderID' => $internalOID,
|
'OrderID' => $internalOID,
|
||||||
'TestSiteID' => $tid,
|
'TestSiteID' => $tid,
|
||||||
'TestSiteCode' => $tinfo['TestSiteCode'],
|
'TestSiteCode' => $tinfo['TestSiteCode'],
|
||||||
@ -105,12 +175,57 @@ class OrderTestModel extends BaseModel {
|
|||||||
'SampleID' => $orderID,
|
'SampleID' => $orderID,
|
||||||
'ResultDateTime' => $orderData['TrnDate'],
|
'ResultDateTime' => $orderData['TrnDate'],
|
||||||
'CreateDate' => date('Y-m-d H:i:s')
|
'CreateDate' => date('Y-m-d H:i:s')
|
||||||
]);
|
];
|
||||||
|
|
||||||
|
if ($specimenInfo) {
|
||||||
|
$patResData['InternalSID'] = $specimenInfo['InternalSID'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$resModel->insert($patResData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->db->transComplete();
|
||||||
|
|
||||||
|
if ($this->db->transStatus() === false) {
|
||||||
|
throw new \Exception('Transaction failed');
|
||||||
|
}
|
||||||
|
|
||||||
return $orderID;
|
return $orderID;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->db->transRollback();
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
if ($containerDef) {
|
||||||
|
return [
|
||||||
|
'ConDefID' => $containerDef['ConDefID'],
|
||||||
|
'ConCode' => $containerDef['ConCode'],
|
||||||
|
'ConName' => $containerDef['ConName']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'ConDefID' => null,
|
||||||
|
'ConCode' => 'DEFAULT',
|
||||||
|
'ConName' => 'Default Container'
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function expandTest($testSiteID, &$testToOrder, $testModel, $grpModel, $calModel) {
|
private function expandTest($testSiteID, &$testToOrder, $testModel, $grpModel, $calModel) {
|
||||||
|
|||||||
@ -17,7 +17,7 @@ class TestDefSiteModel extends BaseModel {
|
|||||||
'Unit1', 'Factor', 'Unit2', 'Decimal',
|
'Unit1', 'Factor', 'Unit2', 'Decimal',
|
||||||
'ReqQty', 'ReqQtyUnit', 'CollReq', 'Method', 'ExpectedTAT',
|
'ReqQty', 'ReqQtyUnit', 'CollReq', 'Method', 'ExpectedTAT',
|
||||||
'SeqScr', 'SeqRpt', 'IndentLeft', 'FontStyle', 'VisibleScr', 'VisibleRpt',
|
'SeqScr', 'SeqRpt', 'IndentLeft', 'FontStyle', 'VisibleScr', 'VisibleRpt',
|
||||||
'CountStat', 'Level',
|
'CountStat', 'Level', 'Requestable',
|
||||||
'CreateDate', 'StartDate','EndDate'
|
'CreateDate', 'StartDate','EndDate'
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -148,7 +148,7 @@ class TestDefSiteModel extends BaseModel {
|
|||||||
->get()->getResultArray();
|
->get()->getResultArray();
|
||||||
|
|
||||||
$testMapModel = new \App\Models\Test\TestMapModel();
|
$testMapModel = new \App\Models\Test\TestMapModel();
|
||||||
$row['testmap'] = $testMapModel->where('TestSiteID', $TestSiteID)->where('EndDate IS NULL')->findAll();
|
$row['testmap'] = $testMapModel->getMappingsByTestCode($row['TestSiteCode']);
|
||||||
|
|
||||||
} elseif (TestValidationService::isGroup($typeCode)) {
|
} elseif (TestValidationService::isGroup($typeCode)) {
|
||||||
$row['testdefgrp'] = $db->table('testdefgrp')
|
$row['testdefgrp'] = $db->table('testdefgrp')
|
||||||
@ -164,11 +164,11 @@ class TestDefSiteModel extends BaseModel {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$testMapModel = new \App\Models\Test\TestMapModel();
|
$testMapModel = new \App\Models\Test\TestMapModel();
|
||||||
$row['testmap'] = $testMapModel->where('TestSiteID', $TestSiteID)->where('EndDate IS NULL')->findAll();
|
$row['testmap'] = $testMapModel->getMappingsByTestCode($row['TestSiteCode']);
|
||||||
|
|
||||||
} elseif (TestValidationService::isTitle($typeCode)) {
|
} elseif (TestValidationService::isTitle($typeCode)) {
|
||||||
$testMapModel = new \App\Models\Test\TestMapModel();
|
$testMapModel = new \App\Models\Test\TestMapModel();
|
||||||
$row['testmap'] = $testMapModel->where('TestSiteID', $TestSiteID)->where('EndDate IS NULL')->findAll();
|
$row['testmap'] = $testMapModel->getMappingsByTestCode($row['TestSiteCode']);
|
||||||
|
|
||||||
} elseif (TestValidationService::isTechnicalTest($typeCode)) {
|
} elseif (TestValidationService::isTechnicalTest($typeCode)) {
|
||||||
// Technical details are now flattened into the main row
|
// Technical details are now flattened into the main row
|
||||||
@ -182,7 +182,7 @@ class TestDefSiteModel extends BaseModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$testMapModel = new \App\Models\Test\TestMapModel();
|
$testMapModel = new \App\Models\Test\TestMapModel();
|
||||||
$row['testmap'] = $testMapModel->where('TestSiteID', $TestSiteID)->where('EndDate IS NULL')->findAll();
|
$row['testmap'] = $testMapModel->getMappingsByTestCode($row['TestSiteCode']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $row;
|
return $row;
|
||||||
|
|||||||
@ -104,13 +104,4 @@ class TestMapModel extends BaseModel {
|
|||||||
->where('testmapdetail.EndDate IS NULL')
|
->where('testmapdetail.EndDate IS NULL')
|
||||||
->findAll();
|
->findAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Disable test mappings by TestSiteID
|
|
||||||
*/
|
|
||||||
public function disableByTestSiteID($testSiteID) {
|
|
||||||
$this->db->table('testmap')
|
|
||||||
->where('TestSiteID', $testSiteID)
|
|
||||||
->update(['EndDate' => date('Y-m-d H:i:s')]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1045,13 +1045,34 @@ paths:
|
|||||||
VER: Verified
|
VER: Verified
|
||||||
REV: Reviewed
|
REV: Reviewed
|
||||||
REP: Reported
|
REP: Reported
|
||||||
|
- name: include
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- details
|
||||||
|
description: Include specimens and tests in response
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: List of orders
|
description: List of orders
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
data:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/OrderTest'
|
||||||
post:
|
post:
|
||||||
tags:
|
tags:
|
||||||
- Orders
|
- Orders
|
||||||
summary: Create order
|
summary: Create order with specimens and tests
|
||||||
|
description: Creates an order with associated specimens and patres records. Tests are grouped by container type to minimize specimen creation.
|
||||||
security:
|
security:
|
||||||
- bearerAuth: []
|
- bearerAuth: []
|
||||||
requestBody:
|
requestBody:
|
||||||
@ -1061,12 +1082,22 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
- PatientID
|
- InternalPID
|
||||||
- Tests
|
- Tests
|
||||||
properties:
|
properties:
|
||||||
PatientID:
|
OrderID:
|
||||||
type: string
|
type: string
|
||||||
VisitID:
|
description: Optional custom order ID (auto-generated if not provided)
|
||||||
|
InternalPID:
|
||||||
|
type: integer
|
||||||
|
description: Patient internal ID
|
||||||
|
PatVisitID:
|
||||||
|
type: integer
|
||||||
|
description: Visit ID
|
||||||
|
SiteID:
|
||||||
|
type: integer
|
||||||
|
default: 1
|
||||||
|
PlacerID:
|
||||||
type: string
|
type: string
|
||||||
Priority:
|
Priority:
|
||||||
type: string
|
type: string
|
||||||
@ -1074,26 +1105,48 @@ paths:
|
|||||||
- R
|
- R
|
||||||
- S
|
- S
|
||||||
- U
|
- U
|
||||||
|
default: R
|
||||||
description: |
|
description: |
|
||||||
R: Routine
|
R: Routine
|
||||||
S: Stat
|
S: Stat
|
||||||
U: Urgent
|
U: Urgent
|
||||||
SiteID:
|
ReqApp:
|
||||||
type: integer
|
type: string
|
||||||
RequestingPhysician:
|
description: Requesting application
|
||||||
|
Comment:
|
||||||
type: string
|
type: string
|
||||||
Tests:
|
Tests:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
type: object
|
type: object
|
||||||
|
required:
|
||||||
|
- TestSiteID
|
||||||
properties:
|
properties:
|
||||||
|
TestSiteID:
|
||||||
|
type: integer
|
||||||
|
description: Test definition site ID
|
||||||
TestID:
|
TestID:
|
||||||
type: integer
|
type: integer
|
||||||
SpecimenType:
|
description: Alias for TestSiteID
|
||||||
type: string
|
|
||||||
responses:
|
responses:
|
||||||
'201':
|
'201':
|
||||||
description: Order created successfully
|
description: Order created successfully with specimens and tests
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
example: success
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
data:
|
||||||
|
$ref: '#/components/schemas/OrderTest'
|
||||||
|
'400':
|
||||||
|
description: Validation error
|
||||||
|
'500':
|
||||||
|
description: Server error
|
||||||
patch:
|
patch:
|
||||||
tags:
|
tags:
|
||||||
- Orders
|
- Orders
|
||||||
@ -1105,16 +1158,64 @@ paths:
|
|||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/OrderTest'
|
type: object
|
||||||
|
required:
|
||||||
|
- OrderID
|
||||||
|
properties:
|
||||||
|
OrderID:
|
||||||
|
type: string
|
||||||
|
Priority:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- R
|
||||||
|
- S
|
||||||
|
- U
|
||||||
|
OrderStatus:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- ORD
|
||||||
|
- SCH
|
||||||
|
- ANA
|
||||||
|
- VER
|
||||||
|
- REV
|
||||||
|
- REP
|
||||||
|
OrderingProvider:
|
||||||
|
type: string
|
||||||
|
DepartmentID:
|
||||||
|
type: integer
|
||||||
|
WorkstationID:
|
||||||
|
type: integer
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Order updated
|
description: Order updated
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
data:
|
||||||
|
$ref: '#/components/schemas/OrderTest'
|
||||||
delete:
|
delete:
|
||||||
tags:
|
tags:
|
||||||
- Orders
|
- Orders
|
||||||
summary: Delete order
|
summary: Delete order
|
||||||
security:
|
security:
|
||||||
- bearerAuth: []
|
- bearerAuth: []
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- OrderID
|
||||||
|
properties:
|
||||||
|
OrderID:
|
||||||
|
type: string
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Order deleted
|
description: Order deleted
|
||||||
@ -1156,11 +1257,23 @@ paths:
|
|||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Order status updated
|
description: Order status updated
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
data:
|
||||||
|
$ref: '#/components/schemas/OrderTest'
|
||||||
/api/ordertest/{id}:
|
/api/ordertest/{id}:
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
- Orders
|
- Orders
|
||||||
summary: Get order by ID
|
summary: Get order by ID
|
||||||
|
description: Returns order details with associated specimens and tests
|
||||||
security:
|
security:
|
||||||
- bearerAuth: []
|
- bearerAuth: []
|
||||||
parameters:
|
parameters:
|
||||||
@ -1169,9 +1282,21 @@ paths:
|
|||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
|
description: Order ID (e.g., 0025030300001)
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Order details
|
description: Order details with specimens and tests
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
data:
|
||||||
|
$ref: '#/components/schemas/OrderTest'
|
||||||
/api/organization/account/{id}:
|
/api/organization/account/{id}:
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
@ -1885,6 +2010,38 @@ paths:
|
|||||||
security:
|
security:
|
||||||
- bearerAuth: []
|
- bearerAuth: []
|
||||||
parameters:
|
parameters:
|
||||||
|
- name: InternalPID
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
description: Filter by internal patient ID (exact match)
|
||||||
|
- name: PVID
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: Filter by visit ID (partial match)
|
||||||
|
- name: PatientID
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: Filter by patient ID (partial match)
|
||||||
|
- name: PatientName
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: Search by patient name (searches in both first and last name)
|
||||||
|
- name: CreateDateFrom
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
description: Filter visits created on or after this date
|
||||||
|
- name: CreateDateTo
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
description: Filter visits created on or before this date
|
||||||
- name: page
|
- name: page
|
||||||
in: query
|
in: query
|
||||||
schema:
|
schema:
|
||||||
@ -4899,6 +5056,9 @@ components:
|
|||||||
SiteID:
|
SiteID:
|
||||||
type: integer
|
type: integer
|
||||||
description: Site reference
|
description: Site reference
|
||||||
|
LastLocation:
|
||||||
|
type: string
|
||||||
|
description: Full name of the last/current location from patvisitadt
|
||||||
CreateDate:
|
CreateDate:
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
@ -5371,6 +5531,10 @@ components:
|
|||||||
default: 1
|
default: 1
|
||||||
Level:
|
Level:
|
||||||
type: integer
|
type: integer
|
||||||
|
Requestable:
|
||||||
|
type: integer
|
||||||
|
default: 1
|
||||||
|
description: Flag indicating if test can be requested (1=yes, 0=no)
|
||||||
CreateDate:
|
CreateDate:
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
@ -5715,13 +5879,48 @@ components:
|
|||||||
OrderTest:
|
OrderTest:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
InternalOID:
|
||||||
|
type: integer
|
||||||
|
description: Internal order ID
|
||||||
OrderID:
|
OrderID:
|
||||||
type: string
|
type: string
|
||||||
PatientID:
|
description: Order ID (e.g., 0025030300001)
|
||||||
|
PlacerID:
|
||||||
type: string
|
type: string
|
||||||
VisitID:
|
nullable: true
|
||||||
|
InternalPID:
|
||||||
|
type: integer
|
||||||
|
description: Patient internal ID
|
||||||
|
SiteID:
|
||||||
|
type: integer
|
||||||
|
PVADTID:
|
||||||
|
type: integer
|
||||||
|
description: Visit ADT ID
|
||||||
|
ReqApp:
|
||||||
type: string
|
type: string
|
||||||
OrderDate:
|
nullable: true
|
||||||
|
Priority:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- R
|
||||||
|
- S
|
||||||
|
- U
|
||||||
|
description: |
|
||||||
|
R: Routine
|
||||||
|
S: Stat
|
||||||
|
U: Urgent
|
||||||
|
PriorityLabel:
|
||||||
|
type: string
|
||||||
|
description: Priority display text
|
||||||
|
TrnDate:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
description: Transaction/Order date
|
||||||
|
EffDate:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
description: Effective date
|
||||||
|
CreateDate:
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
OrderStatus:
|
OrderStatus:
|
||||||
@ -5743,23 +5942,16 @@ components:
|
|||||||
OrderStatusLabel:
|
OrderStatusLabel:
|
||||||
type: string
|
type: string
|
||||||
description: Order status display text
|
description: Order status display text
|
||||||
Priority:
|
Specimens:
|
||||||
type: string
|
type: array
|
||||||
enum:
|
items:
|
||||||
- R
|
$ref: '#/components/schemas/OrderSpecimen'
|
||||||
- S
|
description: Associated specimens for this order
|
||||||
- U
|
Tests:
|
||||||
description: |
|
type: array
|
||||||
R: Routine
|
items:
|
||||||
S: Stat
|
$ref: '#/components/schemas/OrderTestItem'
|
||||||
U: Urgent
|
description: Test results (patres) for this order
|
||||||
PriorityLabel:
|
|
||||||
type: string
|
|
||||||
description: Priority display text
|
|
||||||
SiteID:
|
|
||||||
type: integer
|
|
||||||
RequestingPhysician:
|
|
||||||
type: string
|
|
||||||
OrderItem:
|
OrderItem:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@ -6061,6 +6253,93 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
description: Occupation display text
|
description: Occupation display text
|
||||||
|
OrderSpecimen:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
InternalSID:
|
||||||
|
type: integer
|
||||||
|
description: Internal specimen ID
|
||||||
|
SID:
|
||||||
|
type: string
|
||||||
|
description: Specimen ID (e.g., 0025030300001-S01)
|
||||||
|
SiteID:
|
||||||
|
type: integer
|
||||||
|
OrderID:
|
||||||
|
type: integer
|
||||||
|
description: Reference to internal order ID
|
||||||
|
ConDefID:
|
||||||
|
type: integer
|
||||||
|
description: Container Definition ID
|
||||||
|
nullable: true
|
||||||
|
ConCode:
|
||||||
|
type: string
|
||||||
|
description: Container code
|
||||||
|
nullable: true
|
||||||
|
ConName:
|
||||||
|
type: string
|
||||||
|
description: Container name
|
||||||
|
nullable: true
|
||||||
|
Qty:
|
||||||
|
type: integer
|
||||||
|
description: Quantity
|
||||||
|
Unit:
|
||||||
|
type: string
|
||||||
|
description: Unit of measurement
|
||||||
|
Status:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- PENDING
|
||||||
|
- COLLECTED
|
||||||
|
- RECEIVED
|
||||||
|
- PREPARED
|
||||||
|
- REJECTED
|
||||||
|
description: Current specimen status
|
||||||
|
GenerateBy:
|
||||||
|
type: string
|
||||||
|
description: Source that generated this specimen
|
||||||
|
CreateDate:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
OrderTestItem:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
ResultID:
|
||||||
|
type: integer
|
||||||
|
description: Unique result ID
|
||||||
|
OrderID:
|
||||||
|
type: integer
|
||||||
|
description: Reference to internal order ID
|
||||||
|
InternalSID:
|
||||||
|
type: integer
|
||||||
|
description: Reference to specimen
|
||||||
|
nullable: true
|
||||||
|
TestSiteID:
|
||||||
|
type: integer
|
||||||
|
description: Test definition site ID
|
||||||
|
TestSiteCode:
|
||||||
|
type: string
|
||||||
|
description: Test code
|
||||||
|
TestSiteName:
|
||||||
|
type: string
|
||||||
|
description: Test name
|
||||||
|
nullable: true
|
||||||
|
SID:
|
||||||
|
type: string
|
||||||
|
description: Order ID reference
|
||||||
|
SampleID:
|
||||||
|
type: string
|
||||||
|
description: Sample ID (same as OrderID)
|
||||||
|
Result:
|
||||||
|
type: string
|
||||||
|
description: Test result value
|
||||||
|
nullable: true
|
||||||
|
ResultDateTime:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
description: Result timestamp
|
||||||
|
CreateDate:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
TestMapDetail:
|
TestMapDetail:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
|||||||
@ -1,13 +1,45 @@
|
|||||||
OrderTest:
|
OrderTest:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
InternalOID:
|
||||||
|
type: integer
|
||||||
|
description: Internal order ID
|
||||||
OrderID:
|
OrderID:
|
||||||
type: string
|
type: string
|
||||||
PatientID:
|
description: Order ID (e.g., 0025030300001)
|
||||||
|
PlacerID:
|
||||||
type: string
|
type: string
|
||||||
VisitID:
|
nullable: true
|
||||||
|
InternalPID:
|
||||||
|
type: integer
|
||||||
|
description: Patient internal ID
|
||||||
|
SiteID:
|
||||||
|
type: integer
|
||||||
|
PVADTID:
|
||||||
|
type: integer
|
||||||
|
description: Visit ADT ID
|
||||||
|
ReqApp:
|
||||||
type: string
|
type: string
|
||||||
OrderDate:
|
nullable: true
|
||||||
|
Priority:
|
||||||
|
type: string
|
||||||
|
enum: [R, S, U]
|
||||||
|
description: |
|
||||||
|
R: Routine
|
||||||
|
S: Stat
|
||||||
|
U: Urgent
|
||||||
|
PriorityLabel:
|
||||||
|
type: string
|
||||||
|
description: Priority display text
|
||||||
|
TrnDate:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
description: Transaction/Order date
|
||||||
|
EffDate:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
description: Effective date
|
||||||
|
CreateDate:
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
OrderStatus:
|
OrderStatus:
|
||||||
@ -23,20 +55,100 @@ OrderTest:
|
|||||||
OrderStatusLabel:
|
OrderStatusLabel:
|
||||||
type: string
|
type: string
|
||||||
description: Order status display text
|
description: Order status display text
|
||||||
Priority:
|
Specimens:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/OrderSpecimen'
|
||||||
|
description: Associated specimens for this order
|
||||||
|
Tests:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/OrderTestItem'
|
||||||
|
description: Test results (patres) for this order
|
||||||
|
|
||||||
|
OrderSpecimen:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
InternalSID:
|
||||||
|
type: integer
|
||||||
|
description: Internal specimen ID
|
||||||
|
SID:
|
||||||
type: string
|
type: string
|
||||||
enum: [R, S, U]
|
description: Specimen ID (e.g., 0025030300001-S01)
|
||||||
description: |
|
|
||||||
R: Routine
|
|
||||||
S: Stat
|
|
||||||
U: Urgent
|
|
||||||
PriorityLabel:
|
|
||||||
type: string
|
|
||||||
description: Priority display text
|
|
||||||
SiteID:
|
SiteID:
|
||||||
type: integer
|
type: integer
|
||||||
RequestingPhysician:
|
OrderID:
|
||||||
|
type: integer
|
||||||
|
description: Reference to internal order ID
|
||||||
|
ConDefID:
|
||||||
|
type: integer
|
||||||
|
description: Container Definition ID
|
||||||
|
nullable: true
|
||||||
|
ConCode:
|
||||||
type: string
|
type: string
|
||||||
|
description: Container code
|
||||||
|
nullable: true
|
||||||
|
ConName:
|
||||||
|
type: string
|
||||||
|
description: Container name
|
||||||
|
nullable: true
|
||||||
|
Qty:
|
||||||
|
type: integer
|
||||||
|
description: Quantity
|
||||||
|
Unit:
|
||||||
|
type: string
|
||||||
|
description: Unit of measurement
|
||||||
|
Status:
|
||||||
|
type: string
|
||||||
|
enum: [PENDING, COLLECTED, RECEIVED, PREPARED, REJECTED]
|
||||||
|
description: Current specimen status
|
||||||
|
GenerateBy:
|
||||||
|
type: string
|
||||||
|
description: Source that generated this specimen
|
||||||
|
CreateDate:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
|
||||||
|
OrderTestItem:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
ResultID:
|
||||||
|
type: integer
|
||||||
|
description: Unique result ID
|
||||||
|
OrderID:
|
||||||
|
type: integer
|
||||||
|
description: Reference to internal order ID
|
||||||
|
InternalSID:
|
||||||
|
type: integer
|
||||||
|
description: Reference to specimen
|
||||||
|
nullable: true
|
||||||
|
TestSiteID:
|
||||||
|
type: integer
|
||||||
|
description: Test definition site ID
|
||||||
|
TestSiteCode:
|
||||||
|
type: string
|
||||||
|
description: Test code
|
||||||
|
TestSiteName:
|
||||||
|
type: string
|
||||||
|
description: Test name
|
||||||
|
nullable: true
|
||||||
|
SID:
|
||||||
|
type: string
|
||||||
|
description: Order ID reference
|
||||||
|
SampleID:
|
||||||
|
type: string
|
||||||
|
description: Sample ID (same as OrderID)
|
||||||
|
Result:
|
||||||
|
type: string
|
||||||
|
description: Test result value
|
||||||
|
nullable: true
|
||||||
|
ResultDateTime:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
description: Result timestamp
|
||||||
|
CreateDate:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
|
||||||
OrderItem:
|
OrderItem:
|
||||||
type: object
|
type: object
|
||||||
|
|||||||
@ -16,6 +16,9 @@ PatientVisit:
|
|||||||
SiteID:
|
SiteID:
|
||||||
type: integer
|
type: integer
|
||||||
description: Site reference
|
description: Site reference
|
||||||
|
LastLocation:
|
||||||
|
type: string
|
||||||
|
description: Full name of the last/current location from patvisitadt
|
||||||
CreateDate:
|
CreateDate:
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
|
|||||||
@ -118,6 +118,10 @@ TestDefinition:
|
|||||||
default: 1
|
default: 1
|
||||||
Level:
|
Level:
|
||||||
type: integer
|
type: integer
|
||||||
|
Requestable:
|
||||||
|
type: integer
|
||||||
|
default: 1
|
||||||
|
description: Flag indicating if test can be requested (1=yes, 0=no)
|
||||||
CreateDate:
|
CreateDate:
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
|
|||||||
@ -30,13 +30,33 @@
|
|||||||
VER: Verified
|
VER: Verified
|
||||||
REV: Reviewed
|
REV: Reviewed
|
||||||
REP: Reported
|
REP: Reported
|
||||||
|
- name: include
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
enum: [details]
|
||||||
|
description: Include specimens and tests in response
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: List of orders
|
description: List of orders
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
data:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '../components/schemas/orders.yaml#/OrderTest'
|
||||||
|
|
||||||
post:
|
post:
|
||||||
tags: [Orders]
|
tags: [Orders]
|
||||||
summary: Create order
|
summary: Create order with specimens and tests
|
||||||
|
description: Creates an order with associated specimens and patres records. Tests are grouped by container type to minimize specimen creation.
|
||||||
security:
|
security:
|
||||||
- bearerAuth: []
|
- bearerAuth: []
|
||||||
requestBody:
|
requestBody:
|
||||||
@ -46,36 +66,68 @@
|
|||||||
schema:
|
schema:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
- PatientID
|
- InternalPID
|
||||||
- Tests
|
- Tests
|
||||||
properties:
|
properties:
|
||||||
PatientID:
|
OrderID:
|
||||||
type: string
|
type: string
|
||||||
VisitID:
|
description: Optional custom order ID (auto-generated if not provided)
|
||||||
|
InternalPID:
|
||||||
|
type: integer
|
||||||
|
description: Patient internal ID
|
||||||
|
PatVisitID:
|
||||||
|
type: integer
|
||||||
|
description: Visit ID
|
||||||
|
SiteID:
|
||||||
|
type: integer
|
||||||
|
default: 1
|
||||||
|
PlacerID:
|
||||||
type: string
|
type: string
|
||||||
Priority:
|
Priority:
|
||||||
type: string
|
type: string
|
||||||
enum: [R, S, U]
|
enum: [R, S, U]
|
||||||
|
default: R
|
||||||
description: |
|
description: |
|
||||||
R: Routine
|
R: Routine
|
||||||
S: Stat
|
S: Stat
|
||||||
U: Urgent
|
U: Urgent
|
||||||
SiteID:
|
ReqApp:
|
||||||
type: integer
|
type: string
|
||||||
RequestingPhysician:
|
description: Requesting application
|
||||||
|
Comment:
|
||||||
type: string
|
type: string
|
||||||
Tests:
|
Tests:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
type: object
|
type: object
|
||||||
|
required:
|
||||||
|
- TestSiteID
|
||||||
properties:
|
properties:
|
||||||
|
TestSiteID:
|
||||||
|
type: integer
|
||||||
|
description: Test definition site ID
|
||||||
TestID:
|
TestID:
|
||||||
type: integer
|
type: integer
|
||||||
SpecimenType:
|
description: Alias for TestSiteID
|
||||||
type: string
|
|
||||||
responses:
|
responses:
|
||||||
'201':
|
'201':
|
||||||
description: Order created successfully
|
description: Order created successfully with specimens and tests
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
example: success
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
data:
|
||||||
|
$ref: '../components/schemas/orders.yaml#/OrderTest'
|
||||||
|
'400':
|
||||||
|
description: Validation error
|
||||||
|
'500':
|
||||||
|
description: Server error
|
||||||
|
|
||||||
patch:
|
patch:
|
||||||
tags: [Orders]
|
tags: [Orders]
|
||||||
@ -87,16 +139,55 @@
|
|||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '../components/schemas/orders.yaml#/OrderTest'
|
type: object
|
||||||
|
required:
|
||||||
|
- OrderID
|
||||||
|
properties:
|
||||||
|
OrderID:
|
||||||
|
type: string
|
||||||
|
Priority:
|
||||||
|
type: string
|
||||||
|
enum: [R, S, U]
|
||||||
|
OrderStatus:
|
||||||
|
type: string
|
||||||
|
enum: [ORD, SCH, ANA, VER, REV, REP]
|
||||||
|
OrderingProvider:
|
||||||
|
type: string
|
||||||
|
DepartmentID:
|
||||||
|
type: integer
|
||||||
|
WorkstationID:
|
||||||
|
type: integer
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Order updated
|
description: Order updated
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
data:
|
||||||
|
$ref: '../components/schemas/orders.yaml#/OrderTest'
|
||||||
|
|
||||||
delete:
|
delete:
|
||||||
tags: [Orders]
|
tags: [Orders]
|
||||||
summary: Delete order
|
summary: Delete order
|
||||||
security:
|
security:
|
||||||
- bearerAuth: []
|
- bearerAuth: []
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- OrderID
|
||||||
|
properties:
|
||||||
|
OrderID:
|
||||||
|
type: string
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Order deleted
|
description: Order deleted
|
||||||
@ -132,11 +223,23 @@
|
|||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Order status updated
|
description: Order status updated
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
data:
|
||||||
|
$ref: '../components/schemas/orders.yaml#/OrderTest'
|
||||||
|
|
||||||
/api/ordertest/{id}:
|
/api/ordertest/{id}:
|
||||||
get:
|
get:
|
||||||
tags: [Orders]
|
tags: [Orders]
|
||||||
summary: Get order by ID
|
summary: Get order by ID
|
||||||
|
description: Returns order details with associated specimens and tests
|
||||||
security:
|
security:
|
||||||
- bearerAuth: []
|
- bearerAuth: []
|
||||||
parameters:
|
parameters:
|
||||||
@ -145,6 +248,18 @@
|
|||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
|
description: Order ID (e.g., 0025030300001)
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Order details
|
description: Order details with specimens and tests
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
data:
|
||||||
|
$ref: '../components/schemas/orders.yaml#/OrderTest'
|
||||||
|
|||||||
@ -5,6 +5,38 @@
|
|||||||
security:
|
security:
|
||||||
- bearerAuth: []
|
- bearerAuth: []
|
||||||
parameters:
|
parameters:
|
||||||
|
- name: InternalPID
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
description: Filter by internal patient ID (exact match)
|
||||||
|
- name: PVID
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: Filter by visit ID (partial match)
|
||||||
|
- name: PatientID
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: Filter by patient ID (partial match)
|
||||||
|
- name: PatientName
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: Search by patient name (searches in both first and last name)
|
||||||
|
- name: CreateDateFrom
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
description: Filter visits created on or after this date
|
||||||
|
- name: CreateDateTo
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
description: Filter visits created on or before this date
|
||||||
- name: page
|
- name: page
|
||||||
in: query
|
in: query
|
||||||
schema:
|
schema:
|
||||||
|
|||||||
210
tests/feature/Orders/OrderCreateTest.php
Normal file
210
tests/feature/Orders/OrderCreateTest.php
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature\Orders;
|
||||||
|
|
||||||
|
use CodeIgniter\Test\FeatureTestTrait;
|
||||||
|
use CodeIgniter\Test\CIUnitTestCase;
|
||||||
|
use Faker\Factory;
|
||||||
|
|
||||||
|
class OrderCreateTest extends CIUnitTestCase
|
||||||
|
{
|
||||||
|
use FeatureTestTrait;
|
||||||
|
|
||||||
|
protected $endpoint = 'api/ordertest';
|
||||||
|
|
||||||
|
public function testCreateOrderSuccess()
|
||||||
|
{
|
||||||
|
$faker = Factory::create('id_ID');
|
||||||
|
|
||||||
|
// First create a patient using the same approach as PatientCreateTest
|
||||||
|
$patientPayload = [
|
||||||
|
"PatientID" => "ORD" . $faker->numberBetween(1, 1000). $faker->numberBetween(1, 1000).$faker->numberBetween(1, 1000),
|
||||||
|
"AlternatePID" => "DMY" . $faker->numberBetween(1, 1000). $faker->numberBetween(1, 1000).$faker->numberBetween(1, 1000),
|
||||||
|
"Prefix" => $faker->title,
|
||||||
|
"NameFirst" => "Order",
|
||||||
|
"NameMiddle" => $faker->firstName,
|
||||||
|
"NameMaiden" => $faker->firstName,
|
||||||
|
"NameLast" => "Test",
|
||||||
|
"Suffix" => "S.Kom",
|
||||||
|
"NameAlias" => $faker->userName,
|
||||||
|
"Sex" => $faker->numberBetween(5, 6),
|
||||||
|
"PlaceOfBirth" => $faker->city,
|
||||||
|
"Birthdate" => "1990-01-01",
|
||||||
|
"ZIP" => $faker->postcode,
|
||||||
|
"Street_1" => $faker->streetAddress,
|
||||||
|
"Street_2" => "RT " . $faker->numberBetween(1, 10) . " RW " . $faker->numberBetween(1, 10),
|
||||||
|
"Street_3" => "Blok " . $faker->numberBetween(1, 20),
|
||||||
|
"City" => $faker->city,
|
||||||
|
"Province" => $faker->state,
|
||||||
|
"EmailAddress1" => "A" . $faker->numberBetween(1, 1000). $faker->numberBetween(1, 1000).$faker->numberBetween(1, 1000).'@gmail.com',
|
||||||
|
"EmailAddress2" => "B" . $faker->numberBetween(1, 1000). $faker->numberBetween(1, 1000).$faker->numberBetween(1, 1000).'@gmail.com',
|
||||||
|
"Phone" => $faker->numerify('08##########'),
|
||||||
|
"MobilePhone" => $faker->numerify('08##########'),
|
||||||
|
"Race" => (string) $faker->numberBetween(175, 205),
|
||||||
|
"Country" => (string) $faker->numberBetween(221, 469),
|
||||||
|
"MaritalStatus" => (string) $faker->numberBetween(8, 15),
|
||||||
|
"Religion" => (string) $faker->numberBetween(206, 212),
|
||||||
|
"Ethnic" => (string) $faker->numberBetween(213, 220),
|
||||||
|
"Citizenship" => "WNI",
|
||||||
|
"DeathIndicator" => (string) $faker->numberBetween(16, 17),
|
||||||
|
"LinkTo" => (string) $faker->numberBetween(2, 3),
|
||||||
|
"Custodian" => 1,
|
||||||
|
"PatIdt" => [
|
||||||
|
"IdentifierType" => "KTP",
|
||||||
|
"Identifier" => $faker->nik() ?? $faker->numerify('################')
|
||||||
|
],
|
||||||
|
"PatAtt" => [
|
||||||
|
[ "Address" => "/api/upload/" . $faker->uuid . ".jpg" ]
|
||||||
|
],
|
||||||
|
"PatCom" => $faker->sentence,
|
||||||
|
];
|
||||||
|
|
||||||
|
if($patientPayload['DeathIndicator'] == '16') {
|
||||||
|
$patientPayload['DeathDateTime'] = $faker->date('Y-m-d H:i:s');
|
||||||
|
} else {
|
||||||
|
$patientPayload['DeathDateTime'] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$patientResult = $this->withBodyFormat('json')->call('post', 'api/patient', $patientPayload);
|
||||||
|
|
||||||
|
// Check patient creation succeeded
|
||||||
|
$patientResult->assertStatus(201);
|
||||||
|
|
||||||
|
$patientBody = json_decode($patientResult->getBody(), true);
|
||||||
|
$internalPID = $patientBody['data']['InternalPID'] ?? null;
|
||||||
|
|
||||||
|
$this->assertNotNull($internalPID, 'Failed to create test patient. Response: ' . print_r($patientBody, true));
|
||||||
|
|
||||||
|
// Get available tests from testdefsite
|
||||||
|
$testsResult = $this->call('get', 'api/test');
|
||||||
|
$testsBody = json_decode($testsResult->getBody(), true);
|
||||||
|
$availableTests = $testsBody['data'] ?? [];
|
||||||
|
|
||||||
|
// Skip if no tests available
|
||||||
|
if (empty($availableTests)) {
|
||||||
|
$this->markTestSkipped('No tests available in testdefsite table');
|
||||||
|
}
|
||||||
|
|
||||||
|
$testSiteID = $availableTests[0]['TestSiteID'];
|
||||||
|
|
||||||
|
// Create order with tests
|
||||||
|
$payload = [
|
||||||
|
'InternalPID' => $internalPID,
|
||||||
|
'Priority' => 'R',
|
||||||
|
'Tests' => [
|
||||||
|
['TestSiteID' => $testSiteID]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = $this->withBodyFormat('json')->call('post', $this->endpoint, $payload);
|
||||||
|
|
||||||
|
// Assertions
|
||||||
|
$result->assertStatus(201);
|
||||||
|
|
||||||
|
$body = json_decode($result->getBody(), true);
|
||||||
|
|
||||||
|
$this->assertEquals('success', $body['status']);
|
||||||
|
$this->assertArrayHasKey('data', $body);
|
||||||
|
$this->assertArrayHasKey('OrderID', $body['data']);
|
||||||
|
$this->assertArrayHasKey('Specimens', $body['data']);
|
||||||
|
$this->assertArrayHasKey('Tests', $body['data']);
|
||||||
|
$this->assertIsArray($body['data']['Specimens']);
|
||||||
|
$this->assertIsArray($body['data']['Tests']);
|
||||||
|
$this->assertNotEmpty($body['data']['Tests'], 'Tests array should not be empty');
|
||||||
|
|
||||||
|
return $body['data']['OrderID'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCreateOrderValidationFailsWithoutInternalPID()
|
||||||
|
{
|
||||||
|
$payload = [
|
||||||
|
'Tests' => [
|
||||||
|
['TestSiteID' => 1]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = $this->withBodyFormat('json')->call('post', $this->endpoint, $payload);
|
||||||
|
|
||||||
|
$result->assertStatus(400);
|
||||||
|
|
||||||
|
$body = json_decode($result->getBody(), true);
|
||||||
|
$this->assertIsArray($body);
|
||||||
|
$this->assertArrayHasKey('errors', $body);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCreateOrderFailsWithInvalidPatient()
|
||||||
|
{
|
||||||
|
$payload = [
|
||||||
|
'InternalPID' => 999999,
|
||||||
|
'Tests' => [
|
||||||
|
['TestSiteID' => 1]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = $this->withBodyFormat('json')->call('post', $this->endpoint, $payload);
|
||||||
|
|
||||||
|
$result->assertStatus(400);
|
||||||
|
|
||||||
|
$body = json_decode($result->getBody(), true);
|
||||||
|
$this->assertIsArray($body);
|
||||||
|
$this->assertArrayHasKey('errors', $body);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCreateOrderWithMultipleTests()
|
||||||
|
{
|
||||||
|
$faker = Factory::create('id_ID');
|
||||||
|
|
||||||
|
// First create a patient
|
||||||
|
$patientPayload = [
|
||||||
|
"PatientID" => "ORDM" . $faker->numberBetween(1, 1000). $faker->numberBetween(1, 1000),
|
||||||
|
"NameFirst" => "Multi",
|
||||||
|
"NameLast" => "Test",
|
||||||
|
"Sex" => "2",
|
||||||
|
"Birthdate" => "1985-05-15",
|
||||||
|
"PatIdt" => [
|
||||||
|
"IdentifierType" => "KTP",
|
||||||
|
"Identifier" => $faker->numerify('################')
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$patientResult = $this->withBodyFormat('json')->call('post', 'api/patient', $patientPayload);
|
||||||
|
|
||||||
|
$patientBody = json_decode($patientResult->getBody(), true);
|
||||||
|
$internalPID = $patientBody['data']['InternalPID'] ?? null;
|
||||||
|
|
||||||
|
$this->assertNotNull($internalPID, 'Failed to create test patient');
|
||||||
|
|
||||||
|
// Get available tests
|
||||||
|
$testsResult = $this->call('get', 'api/test');
|
||||||
|
$testsBody = json_decode($testsResult->getBody(), true);
|
||||||
|
$availableTests = $testsBody['data'] ?? [];
|
||||||
|
|
||||||
|
if (count($availableTests) < 2) {
|
||||||
|
$this->markTestSkipped('Need at least 2 tests for this test');
|
||||||
|
}
|
||||||
|
|
||||||
|
$testSiteID1 = $availableTests[0]['TestSiteID'];
|
||||||
|
$testSiteID2 = $availableTests[1]['TestSiteID'];
|
||||||
|
|
||||||
|
// Create order with multiple tests
|
||||||
|
$payload = [
|
||||||
|
'InternalPID' => $internalPID,
|
||||||
|
'Priority' => 'S',
|
||||||
|
'Comment' => 'Urgent order for multiple tests',
|
||||||
|
'Tests' => [
|
||||||
|
['TestSiteID' => $testSiteID1],
|
||||||
|
['TestSiteID' => $testSiteID2]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = $this->withBodyFormat('json')->call('post', $this->endpoint, $payload);
|
||||||
|
|
||||||
|
$result->assertStatus(201);
|
||||||
|
|
||||||
|
$body = json_decode($result->getBody(), true);
|
||||||
|
|
||||||
|
$this->assertEquals('success', $body['status']);
|
||||||
|
$this->assertGreaterThanOrEqual(1, count($body['data']['Specimens']), 'Should have at least one specimen');
|
||||||
|
$this->assertGreaterThanOrEqual(2, count($body['data']['Tests']), 'Should have at least two tests');
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user