feat: add TestMapDetail controller, model and migration for test site mapping

- Add TestMapDetailController for managing test mapping details
- Create TestMapDetailModel with CRUD operations
- Add migration for TestSiteID column in testmap table
- Update TestMapController and TestMapModel
- Update routes, seeder and PatVisitModel
- Regenerate API documentation bundle
This commit is contained in:
mahdahar 2026-02-26 16:48:10 +07:00
parent d3668fe2b3
commit 5e0a7f21f5
14 changed files with 1281 additions and 556 deletions

View File

@ -279,8 +279,19 @@ $routes->group('api', function ($routes) {
// Filter routes
$routes->get('by-testsite/(:num)', 'Test\TestMapController::showByTestSite/$1');
$routes->get('by-host/(:any)/(:any)', 'Test\TestMapController::showByHost/$1/$2');
$routes->get('by-client/(:any)/(:any)', 'Test\TestMapController::showByClient/$1/$2');
// TestMapDetail nested routes
$routes->group('detail', function ($routes) {
$routes->get('/', 'Test\TestMapDetailController::index');
$routes->get('(:num)', 'Test\TestMapDetailController::show/$1');
$routes->post('/', 'Test\TestMapDetailController::create');
$routes->patch('/', 'Test\TestMapDetailController::update');
$routes->delete('/', 'Test\TestMapDetailController::delete');
$routes->get('by-testmap/(:num)', 'Test\TestMapDetailController::showByTestMap/$1');
$routes->post('batch', 'Test\TestMapDetailController::batchCreate');
$routes->patch('batch', 'Test\TestMapDetailController::batchUpdate');
$routes->delete('batch', 'Test\TestMapDetailController::batchDelete');
});
});
});

View File

@ -5,6 +5,7 @@ use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Libraries\ValueSet;
use App\Models\Test\TestMapModel;
use App\Models\Test\TestMapDetailModel;
class TestMapController extends BaseController {
use ResponseTrait;
@ -12,14 +13,21 @@ class TestMapController extends BaseController {
protected $db;
protected $rules;
protected $model;
protected $modelDetail;
public function __construct() {
$this->db = \Config\Database::connect();
$this->model = new TestMapModel;
$this->modelDetail = new TestMapDetailModel;
$this->rules = [
'TestSiteID' => 'required|integer',
'HostID' => 'required|integer',
'ClientID' => 'required|integer',
];
}
public function index() {
$rows = $this->model->findAll();
$rows = $this->model->getUniqueGroupings();
if (empty($rows)) { return $this->respond([ 'status' => 'success', 'message' => "no Data.", 'data' => [] ], 200); }
$rows = ValueSet::transformLabels($rows, [
@ -31,7 +39,7 @@ class TestMapController extends BaseController {
}
public function show($id = null) {
$row = $this->model->where('TestMapID',$id)->first();
$row = $this->model->where('TestMapID',$id)->where('EndDate IS NULL')->first();
if (empty($row)) { return $this->respond([ 'status' => 'success', 'message' => "no Data.", 'data' => null ], 200); }
$row = ValueSet::transformLabels([$row], [
@ -39,6 +47,9 @@ class TestMapController extends BaseController {
'ClientType' => 'entity_type',
])[0];
// Include testmapdetail records
$row['details'] = $this->modelDetail->getDetailsByTestMap($id);
return $this->respond([ 'status' => 'success', 'message'=> "Data fetched successfully", 'data' => $row ], 200);
}
@ -75,7 +86,19 @@ class TestMapController extends BaseController {
$row = $this->model->where('TestMapID', $id)->where('EndDate IS NULL')->first();
if (empty($row)) { return $this->respond([ 'status' => 'failed', 'message' => "Data not found or already deleted.", 'data' => null ], 404); }
$this->db->transStart();
// Soft delete the testmap
$this->model->update($id, ['EndDate' => date('Y-m-d H:i:s')]);
// Soft delete all related details
$this->modelDetail->where('TestMapID', $id)
->where('EndDate IS NULL')
->set('EndDate', date('Y-m-d H:i:s'))
->update();
$this->db->transComplete();
return $this->respond([ 'status' => 'success', 'message' => "data deleted successfully", 'data' => $id ], 200);
} catch (\Exception $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
@ -93,35 +116,102 @@ class TestMapController extends BaseController {
'ClientType' => 'entity_type',
]);
return $this->respond([ 'status' => 'success', 'message'=> "Data fetched successfully", 'data' => $rows ], 200);
// Include testmapdetail records for each mapping
foreach ($rows as &$row) {
$row['details'] = $this->modelDetail->getDetailsByTestMap($row['TestMapID']);
}
public function showByHost($hostType = null, $hostID = null) {
if (!$hostType || !$hostID) { return $this->failValidationErrors('HostType and HostID are required.'); }
$rows = $this->model->getMappingsByHost($hostType, $hostID);
if (empty($rows)) { return $this->respond([ 'status' => 'success', 'message' => "no Data.", 'data' => [] ], 200); }
$rows = ValueSet::transformLabels($rows, [
'HostType' => 'entity_type',
'ClientType' => 'entity_type',
]);
return $this->respond([ 'status' => 'success', 'message'=> "Data fetched successfully", 'data' => $rows ], 200);
}
public function showByClient($clientType = null, $clientID = null) {
if (!$clientType || !$clientID) { return $this->failValidationErrors('ClientType and ClientID are required.'); }
public function batchCreate() {
$items = $this->request->getJSON(true);
if (!is_array($items)) { return $this->failValidationErrors('Expected array of items'); }
$rows = $this->model->getMappingsByClient($clientType, $clientID);
if (empty($rows)) { return $this->respond([ 'status' => 'success', 'message' => "no Data.", 'data' => [] ], 200); }
$results = ['success' => [], 'failed' => []];
$this->db->transStart();
$rows = ValueSet::transformLabels($rows, [
'HostType' => 'entity_type',
'ClientType' => 'entity_type',
]);
foreach ($items as $index => $item) {
if (!$this->validateData($item, $this->rules)) {
$results['failed'][] = ['index' => $index, 'errors' => $this->validator->getErrors()];
continue;
}
try {
$id = $this->model->insert($item);
$results['success'][] = ['index' => $index, 'TestMapID' => $id];
} catch (\Exception $e) {
$results['failed'][] = ['index' => $index, 'error' => $e->getMessage()];
}
}
return $this->respond([ 'status' => 'success', 'message'=> "Data fetched successfully", 'data' => $rows ], 200);
$this->db->transComplete();
return $this->respond([
'status' => empty($results['failed']) ? 'success' : 'partial',
'message' => 'Batch create completed',
'data' => $results
], 200);
}
public function batchUpdate() {
$items = $this->request->getJSON(true);
if (!is_array($items)) { return $this->failValidationErrors('Expected array of items'); }
$results = ['success' => [], 'failed' => []];
$this->db->transStart();
foreach ($items as $index => $item) {
$id = $item['TestMapID'] ?? null;
if (!$id) {
$results['failed'][] = ['index' => $index, 'error' => 'TestMapID required'];
continue;
}
if (!$this->validateData($item, $this->rules)) {
$results['failed'][] = ['index' => $index, 'errors' => $this->validator->getErrors()];
continue;
}
try {
$this->model->update($id, $item);
$results['success'][] = ['index' => $index, 'TestMapID' => $id];
} catch (\Exception $e) {
$results['failed'][] = ['index' => $index, 'error' => $e->getMessage()];
}
}
$this->db->transComplete();
return $this->respond([
'status' => empty($results['failed']) ? 'success' : 'partial',
'message' => 'Batch update completed',
'data' => $results
], 200);
}
public function batchDelete() {
$ids = $this->request->getJSON(true);
if (!is_array($ids)) { return $this->failValidationErrors('Expected array of TestMapIDs'); }
$results = ['success' => [], 'failed' => []];
$this->db->transStart();
foreach ($ids as $id) {
try {
$row = $this->model->where('TestMapID', $id)->where('EndDate IS NULL')->first();
if (empty($row)) {
$results['failed'][] = ['TestMapID' => $id, 'error' => 'Not found or already deleted'];
continue;
}
$this->model->update($id, ['EndDate' => date('Y-m-d H:i:s')]);
$results['success'][] = $id;
} catch (\Exception $e) {
$results['failed'][] = ['TestMapID' => $id, 'error' => $e->getMessage()];
}
}
$this->db->transComplete();
return $this->respond([
'status' => empty($results['failed']) ? 'success' : 'partial',
'message' => 'Batch delete completed',
'data' => $results
], 200);
}
}

View File

@ -0,0 +1,253 @@
<?php
namespace App\Controllers\Test;
use App\Controllers\BaseController;
use App\Traits\ResponseTrait;
use App\Models\Test\TestMapDetailModel;
class TestMapDetailController extends BaseController {
use ResponseTrait;
protected $db;
protected $rules;
protected $model;
public function __construct() {
$this->db = \Config\Database::connect();
$this->model = new TestMapDetailModel;
$this->rules = [
'TestMapID' => 'required|integer',
'HostTestCode' => 'permit_empty|max_length[10]',
'HostTestName' => 'permit_empty|max_length[100]',
'ConDefID' => 'permit_empty|integer',
'ClientTestCode' => 'permit_empty|max_length[10]',
'ClientTestName' => 'permit_empty|max_length[100]',
];
}
public function index() {
$testMapID = $this->request->getGet('TestMapID');
if ($testMapID) {
$rows = $this->model->getDetailsByTestMap($testMapID);
} else {
$rows = $this->model->where('EndDate IS NULL')->findAll();
}
if (empty($rows)) {
return $this->respond([ 'status' => 'success', 'message' => "no Data.", 'data' => [] ], 200);
}
return $this->respond([ 'status' => 'success', 'message'=> "Data fetched successfully", 'data' => $rows ], 200);
}
public function show($id = null) {
if (!$id) {
return $this->failValidationErrors('TestMapDetailID is required.');
}
$row = $this->model->where('TestMapDetailID', $id)->where('EndDate IS NULL')->first();
if (empty($row)) {
return $this->respond([ 'status' => 'success', 'message' => "no Data.", 'data' => null ], 200);
}
return $this->respond([ 'status' => 'success', 'message'=> "Data fetched successfully", 'data' => $row ], 200);
}
public function showByTestMap($testMapID = null) {
if (!$testMapID) {
return $this->failValidationErrors('TestMapID is required.');
}
$rows = $this->model->getDetailsByTestMap($testMapID);
if (empty($rows)) {
return $this->respond([ 'status' => 'success', 'message' => "no Data.", 'data' => [] ], 200);
}
return $this->respond([ 'status' => 'success', 'message'=> "Data fetched successfully", 'data' => $rows ], 200);
}
public function create() {
$input = $this->request->getJSON(true);
if (!$this->validateData($input, $this->rules)) {
return $this->failValidationErrors($this->validator->getErrors());
}
try {
$id = $this->model->insert($input);
return $this->respondCreated([
'status' => 'success',
'message' => "data created successfully",
'data' => $id
]);
} catch (\Exception $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
public function update() {
$input = $this->request->getJSON(true);
$id = $input["TestMapDetailID"] ?? null;
if (!$id) {
return $this->failValidationErrors('TestMapDetailID is required.');
}
if (!$this->validateData($input, $this->rules)) {
return $this->failValidationErrors($this->validator->getErrors());
}
try {
$this->model->update($id, $input);
return $this->respond([
'status' => 'success',
'message' => "data updated successfully",
'data' => $id
], 200);
} catch (\Exception $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
public function delete() {
$input = $this->request->getJSON(true);
$id = $input["TestMapDetailID"] ?? null;
if (!$id) {
return $this->failValidationErrors('TestMapDetailID is required.');
}
try {
$row = $this->model->where('TestMapDetailID', $id)->where('EndDate IS NULL')->first();
if (empty($row)) {
return $this->respond([
'status' => 'failed',
'message' => "Data not found or already deleted.",
'data' => null
], 404);
}
$this->model->update($id, ['EndDate' => date('Y-m-d H:i:s')]);
return $this->respond([
'status' => 'success',
'message' => "data deleted successfully",
'data' => $id
], 200);
} catch (\Exception $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
public function batchCreate() {
$items = $this->request->getJSON(true);
if (!is_array($items)) {
return $this->failValidationErrors('Expected array of items');
}
$results = ['success' => [], 'failed' => []];
$this->db->transStart();
foreach ($items as $index => $item) {
if (!$this->validateData($item, $this->rules)) {
$results['failed'][] = ['index' => $index, 'errors' => $this->validator->getErrors()];
continue;
}
try {
$id = $this->model->insert($item);
$results['success'][] = ['index' => $index, 'TestMapDetailID' => $id];
} catch (\Exception $e) {
$results['failed'][] = ['index' => $index, 'error' => $e->getMessage()];
}
}
$this->db->transComplete();
return $this->respond([
'status' => empty($results['failed']) ? 'success' : 'partial',
'message' => 'Batch create completed',
'data' => $results
], 200);
}
public function batchUpdate() {
$items = $this->request->getJSON(true);
if (!is_array($items)) {
return $this->failValidationErrors('Expected array of items');
}
$results = ['success' => [], 'failed' => []];
$this->db->transStart();
foreach ($items as $index => $item) {
$id = $item['TestMapDetailID'] ?? null;
if (!$id) {
$results['failed'][] = ['index' => $index, 'error' => 'TestMapDetailID required'];
continue;
}
if (!$this->validateData($item, $this->rules)) {
$results['failed'][] = ['index' => $index, 'errors' => $this->validator->getErrors()];
continue;
}
try {
$this->model->update($id, $item);
$results['success'][] = ['index' => $index, 'TestMapDetailID' => $id];
} catch (\Exception $e) {
$results['failed'][] = ['index' => $index, 'error' => $e->getMessage()];
}
}
$this->db->transComplete();
return $this->respond([
'status' => empty($results['failed']) ? 'success' : 'partial',
'message' => 'Batch update completed',
'data' => $results
], 200);
}
public function batchDelete() {
$ids = $this->request->getJSON(true);
if (!is_array($ids)) {
return $this->failValidationErrors('Expected array of TestMapDetailIDs');
}
$results = ['success' => [], 'failed' => []];
$this->db->transStart();
foreach ($ids as $id) {
try {
$row = $this->model->where('TestMapDetailID', $id)->where('EndDate IS NULL')->first();
if (empty($row)) {
$results['failed'][] = ['TestMapDetailID' => $id, 'error' => 'Not found or already deleted'];
continue;
}
$this->model->update($id, ['EndDate' => date('Y-m-d H:i:s')]);
$results['success'][] = $id;
} catch (\Exception $e) {
$results['failed'][] = ['TestMapDetailID' => $id, 'error' => $e->getMessage()];
}
}
$this->db->transComplete();
return $this->respond([
'status' => empty($results['failed']) ? 'success' : 'partial',
'message' => 'Batch delete completed',
'data' => $results
], 200);
}
}

View File

@ -17,6 +17,7 @@ class TestsController extends BaseController
protected $modelGrp;
protected $modelMap;
protected $modelMapDetail;
protected $modelRefNum;
protected $modelRefTxt;
@ -28,6 +29,7 @@ class TestsController extends BaseController
$this->modelGrp = new \App\Models\Test\TestDefGrpModel;
$this->modelMap = new \App\Models\Test\TestMapModel;
$this->modelMapDetail = new \App\Models\Test\TestMapDetailModel;
$this->modelRefNum = new \App\Models\RefRange\RefNumModel;
$this->modelRefTxt = new \App\Models\RefRange\RefTxtModel;
@ -638,26 +640,52 @@ class TestsController extends BaseController
private function saveTestMap($testSiteID, $mappings, $action)
{
if ($action === 'update') {
// Soft delete existing testmaps and their details for this test site
$existingMaps = $this->modelMap->where('TestSiteID', $testSiteID)
->where('EndDate IS NULL')
->findAll();
foreach ($existingMaps as $existingMap) {
// Soft delete details first
$this->modelMapDetail->where('TestMapID', $existingMap['TestMapID'])
->where('EndDate IS NULL')
->set('EndDate', date('Y-m-d H:i:s'))
->update();
}
// Soft delete the testmap headers
$this->db->table('testmap')
->where('TestSiteID', $testSiteID)
->where('EndDate IS NULL')
->update(['EndDate' => date('Y-m-d H:i:s')]);
}
if (is_array($mappings)) {
foreach ($mappings as $map) {
// Create testmap header
$mapData = [
'TestSiteID' => $testSiteID,
'HostType' => $map['HostType'] ?? null,
'HostID' => $map['HostID'] ?? null,
'HostTestCode' => $map['HostTestCode'] ?? null,
'HostTestName' => $map['HostTestName'] ?? null,
'ClientType' => $map['ClientType'] ?? null,
'ClientID' => $map['ClientID'] ?? null,
'ConDefID' => $map['ConDefID'] ?? null,
'ClientTestCode' => $map['ClientTestCode'] ?? null,
'ClientTestName' => $map['ClientTestName'] ?? null
];
$this->modelMap->insert($mapData);
$testMapID = $this->modelMap->insert($mapData);
// Create testmapdetail records if details are provided
if ($testMapID && isset($map['details']) && is_array($map['details'])) {
foreach ($map['details'] as $detail) {
$detailData = [
'TestMapID' => $testMapID,
'HostTestCode' => $detail['HostTestCode'] ?? null,
'HostTestName' => $detail['HostTestName'] ?? null,
'ConDefID' => $detail['ConDefID'] ?? null,
'ClientTestCode' => $detail['ClientTestCode'] ?? null,
'ClientTestName' => $detail['ClientTestName'] ?? null,
];
$this->modelMapDetail->insert($detailData);
}
}
}
}
}

View File

@ -77,25 +77,6 @@ class CreateTestDefinitions extends Migration {
$this->forge->addForeignKey('Member', 'testdefsite', 'TestSiteID', 'CASCADE', 'CASCADE');
$this->forge->createTable('testdefgrp');
$this->forge->addField([
'TestMapID' => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
'TestSiteID' => ['type' => 'INT', 'unsigned' => true, 'null' => false],
'HostType' => ['type' => 'varchar', 'constraint'=> 20, 'null' => true],
'HostID' => ['type' => 'varchar', 'constraint'=> 50, 'null' => true],
'HostTestCode' => ['type' => 'varchar', 'constraint'=> 10, 'null' => true],
'HostTestName' => ['type' => 'varchar', 'constraint'=> 100, 'null' => true],
'ClientType' => ['type' => 'varchar', 'constraint'=> 20, 'null' => true],
'ClientID' => ['type' => 'varchar', 'constraint'=> 50, 'null' => true],
'ConDefID' => ['type' => 'INT', 'null' => true],
'ClientTestCode' => ['type' => 'varchar', 'constraint'=> 10, 'null' => true],
'ClientTestName' => ['type' => 'varchar', 'constraint'=> 100, 'null' => true],
'CreateDate' => ['type' => 'Datetime', 'null' => true],
'EndDate' => ['type' => 'Datetime', 'null' => true]
]);
$this->forge->addKey('TestMapID', true);
$this->forge->addForeignKey('TestSiteID', 'testdefsite', 'TestSiteID', 'CASCADE', 'CASCADE');
$this->forge->createTable('testmap');
$this->forge->addField([
'RefNumID' => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
'SiteID' => ['type' => 'INT', 'null' => true],
@ -149,16 +130,42 @@ class CreateTestDefinitions extends Migration {
]);
$this->forge->addKey('FlagDefID', true);
$this->forge->createTable('flagdef');
$this->forge->addField([
'TestMapID' => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
'HostType' => ['type' => 'varchar', 'constraint'=> 20, 'null' => true],
'HostID' => ['type' => 'varchar', 'constraint'=> 50, 'null' => true],
'ClientType' => ['type' => 'varchar', 'constraint'=> 20, 'null' => true],
'ClientID' => ['type' => 'varchar', 'constraint'=> 50, 'null' => true],
'CreateDate' => ['type' => 'Datetime', 'null' => true],
'EndDate' => ['type' => 'Datetime', 'null' => true]
]);
$this->forge->addKey('TestMapID', true);
$this->forge->createTable('testmap');
$this->forge->addField([
'TestMapDetailID' => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
'TestMapID' => ['type' => 'INT'],
'HostTestCode' => ['type' => 'varchar', 'constraint'=> 10, 'null' => true],
'HostTestName' => ['type' => 'varchar', 'constraint'=> 100, 'null' => true],
'ConDefID' => ['type' => 'INT', 'null' => true],
'ClientTestCode' => ['type' => 'varchar', 'constraint'=> 10, 'null' => true],
'ClientTestName' => ['type' => 'varchar', 'constraint'=> 100, 'null' => true],
'CreateDate' => ['type' => 'Datetime', 'null' => true],
'EndDate' => ['type' => 'Datetime', 'null' => true]
]);
$this->forge->addKey('TestMapDetailID', true);
$this->forge->createTable('testmapdetail');
}
public function down() {
$this->forge->dropTable('testmapdetail');
$this->forge->dropTable('testmap');
$this->forge->dropTable('flagdef');
$this->forge->dropTable('reftxt');
$this->forge->dropTable('refnum');
$this->forge->dropTable('testmap');
$this->forge->dropTable('testdefgrp');
$this->forge->dropTable('testdefcal');
// testdeftech table removed - merged into testdefsite
$this->forge->dropTable('testdefsite');
}
}

View File

@ -12,6 +12,7 @@ class CreatePatientVisits extends Migration {
'PVID' => ['type' => 'VARCHAR', 'constraint' => 20, 'null' => true],
'InternalPID' => ['type' => 'INT', 'constraint' => 11, 'null' => true],
'EpisodeID' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
'LastVisitADT' => ['type' => 'VARCHAR', 'constraint' => 5, 'null' => true],
'CreateDate' => ['type' => 'DATETIME', 'null' => true],
'EndDate' => ['type' => 'DATETIME', 'null' => true],
'ArchivedDate'=> ['type' => 'DATETIME', 'null' => true],

View File

@ -0,0 +1,29 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddTestSiteIDToTestmap extends Migration
{
public function up()
{
$this->forge->addColumn('testmap', [
'TestSiteID' => [
'type' => 'INT',
'unsigned' => true,
'null' => true,
'after' => 'TestMapID'
]
]);
// Add foreign key if it doesn't exist
$this->forge->addForeignKey('TestSiteID', 'testdefsite', 'TestSiteID', 'CASCADE', 'CASCADE');
}
public function down()
{
$this->forge->dropForeignKey('testmap', 'testmap_TestSiteID_foreign');
$this->forge->dropColumn('testmap', 'TestSiteID');
}
}

View File

@ -24,6 +24,26 @@ class TestSeeder extends Seeder
$now = date('Y-m-d H:i:s');
$tIDs = [];
$testMapHeaders = []; // Store TestMapID by mapping key
$testMapDetails = []; // Store detail records
// Helper to get or create TestMap header ID
$getTestMapID = function($testSiteID, $hostType, $hostID, $clientType, $clientID) use (&$testMapHeaders, &$now) {
$key = "{$hostType}_{$hostID}_{$clientType}_{$clientID}";
if (!isset($testMapHeaders[$key])) {
$data = [
'TestSiteID' => $testSiteID,
'HostType' => $hostType,
'HostID' => $hostID,
'ClientType' => $clientType,
'ClientID' => $clientID,
'CreateDate' => $now
];
$this->db->table('testmap')->insert($data);
$testMapHeaders[$key] = $this->db->insertID();
}
return $testMapHeaders[$key];
};
// ========================================
// TEST TYPE - Actual Laboratory Tests
@ -207,6 +227,7 @@ class TestSeeder extends Seeder
// ========================================
// TEST MAP - Specimen Mapping
// Maps tests to workstations and containers
// New structure: testmap (header) + testmapdetail (details)
// ========================================
// Get container IDs (inserted by SpecimenSeeder in order)
@ -223,291 +244,109 @@ class TestSeeder extends Seeder
$wsIAuto = 6; // Imunologi Auto
$wsUAuto = 8; // Urin Auto
$testMaps = [];
// ============================================
// SITE → WORKSTATION mappings (no ConDefID)
// ============================================
// Hematology tests → HAUTO workstation
$hematologyTests = ['HB', 'HCT', 'RBC', 'WBC', 'PLT', 'MCV', 'MCH', 'MCHC'];
foreach ($hematologyTests as $testCode) {
$testMaps[] = [
'TestSiteID' => $tIDs[$testCode],
'HostType' => 'SITE',
'HostID' => '1',
'HostTestCode' => $testCode,
'HostTestName' => $testCode,
'ClientType' => 'WORKSTATION',
'ClientID' => (string)$wsHAuto,
'ConDefID' => null,
'ClientTestCode' => $testCode,
'ClientTestName' => $testCode,
'CreateDate' => $now
];
}
// Chemistry tests → CAUTO workstation
$chemistryTests = ['GLU', 'CREA', 'UREA', 'SGOT', 'SGPT', 'CHOL', 'TG', 'HDL', 'LDL'];
foreach ($chemistryTests as $testCode) {
$testMaps[] = [
'TestSiteID' => $tIDs[$testCode],
'HostType' => 'SITE',
'HostID' => '1',
'HostTestCode' => $testCode,
'HostTestName' => $testCode,
'ClientType' => 'WORKSTATION',
'ClientID' => (string)$wsCAuto,
'ConDefID' => null,
'ClientTestCode' => $testCode,
'ClientTestName' => $testCode,
'CreateDate' => $now
];
}
// Calculated chemistry tests → CAUTO workstation
$calcChemistryTests = ['EGFR', 'LDLCALC'];
foreach ($calcChemistryTests as $testCode) {
$testMaps[] = [
'TestSiteID' => $tIDs[$testCode],
'HostType' => 'SITE',
'HostID' => '1',
'HostTestCode' => $testCode,
'HostTestName' => $testCode,
'ClientType' => 'WORKSTATION',
'ClientID' => (string)$wsCAuto,
'ConDefID' => null,
'ClientTestCode' => $testCode,
'ClientTestName' => $testCode,
'CreateDate' => $now
];
}
// Urinalysis tests → UAUTO workstation
$urineTests = ['UCOLOR', 'UGLUC', 'UPROT', 'PH'];
foreach ($urineTests as $testCode) {
$testMaps[] = [
'TestSiteID' => $tIDs[$testCode],
'HostType' => 'SITE',
'HostID' => '1',
'HostTestCode' => $testCode,
'HostTestName' => $testCode,
'ClientType' => 'WORKSTATION',
'ClientID' => (string)$wsUAuto,
'ConDefID' => null,
'ClientTestCode' => $testCode,
'ClientTestName' => $testCode,
'CreateDate' => $now
];
}
// Panel/Group tests
$testMaps[] = [
'TestSiteID' => $tIDs['CBC'],
'HostType' => 'SITE',
'HostID' => '1',
'HostTestCode' => 'CBC',
'HostTestName' => 'CBC',
'ClientType' => 'WORKSTATION',
'ClientID' => (string)$wsHAuto,
'ConDefID' => null,
'ClientTestCode' => 'CBC',
'ClientTestName' => 'CBC',
'CreateDate' => $now
];
$testMaps[] = [
'TestSiteID' => $tIDs['LIPID'],
'HostType' => 'SITE',
'HostID' => '1',
'HostTestCode' => 'LIPID',
'HostTestName' => 'LIPID',
'ClientType' => 'WORKSTATION',
'ClientID' => (string)$wsCAuto,
'ConDefID' => null,
'ClientTestCode' => 'LIPID',
'ClientTestName' => 'LIPID',
'CreateDate' => $now
];
$testMaps[] = [
'TestSiteID' => $tIDs['LFT'],
'HostType' => 'SITE',
'HostID' => '1',
'HostTestCode' => 'LFT',
'HostTestName' => 'LFT',
'ClientType' => 'WORKSTATION',
'ClientID' => (string)$wsCAuto,
'ConDefID' => null,
'ClientTestCode' => 'LFT',
'ClientTestName' => 'LFT',
'CreateDate' => $now
];
$testMaps[] = [
'TestSiteID' => $tIDs['RFT'],
'HostType' => 'SITE',
'HostID' => '1',
'HostTestCode' => 'RFT',
'HostTestName' => 'RFT',
'ClientType' => 'WORKSTATION',
'ClientID' => (string)$wsCAuto,
'ConDefID' => null,
'ClientTestCode' => 'RFT',
'ClientTestName' => 'RFT',
'CreateDate' => $now
];
// BMI parameter - no container needed (calculated from parameters)
$testMaps[] = [
'TestSiteID' => $tIDs['BMI'],
'HostType' => 'SITE',
'HostID' => '1',
'HostTestCode' => 'BMI',
'HostTestName' => 'BMI',
'ClientType' => 'WORKSTATION',
'ClientID' => (string)$wsCAuto,
'ConDefID' => null,
'ClientTestCode' => 'BMI',
'ClientTestName' => 'BMI',
'CreateDate' => $now
];
// ============================================
// WORKSTATION → INSTRUMENT mappings (with ConDefID)
// ============================================
// Equipment IDs from OrganizationSeeder
$instHematology = 1; // Sysmex XN-1000
$instChemistry = 2; // Roche Cobas C501
$instImmunology = 3; // Architect i2000
$instUrinalysis = 4; // Urisys 1100
// Hematology tests → HAUTO → Sysmex (EDTA container)
foreach ($hematologyTests as $testCode) {
$testMaps[] = [
'TestSiteID' => $tIDs[$testCode],
'HostType' => 'WORKSTATION',
'HostID' => (string)$wsHAuto,
'HostTestCode' => $testCode,
'HostTestName' => $testCode,
'ClientType' => 'INSTRUMENT',
'ClientID' => (string)$instHematology,
'ConDefID' => $conEDTA,
'ClientTestCode' => $testCode,
'ClientTestName' => $testCode,
// Helper to get or create TestMap header ID (relationship only)
$getTestMapID = function($hostType, $hostID, $clientType, $clientID) use (&$testMapHeaders, &$now) {
$key = "{$hostType}_{$hostID}_{$clientType}_{$clientID}";
if (!isset($testMapHeaders[$key])) {
$data = [
'HostType' => $hostType,
'HostID' => $hostID,
'ClientType' => $clientType,
'ClientID' => $clientID,
'CreateDate' => $now
];
$this->db->table('testmap')->insert($data);
$testMapHeaders[$key] = $this->db->insertID();
}
return $testMapHeaders[$key];
};
// Helper to add detail record
$addDetail = function($testMapID, $hostTestCode, $hostTestName, $conDefID, $clientTestCode, $clientTestName) use (&$testMapDetails, &$now) {
$testMapDetails[] = [
'TestMapID' => $testMapID,
'HostTestCode' => $hostTestCode,
'HostTestName' => $hostTestName,
'ConDefID' => $conDefID,
'ClientTestCode' => $clientTestCode,
'ClientTestName' => $clientTestName,
'CreateDate' => $now
];
};
// ============================================
// TEST MAP CONFIGURATION
// Grouped by discipline for maintainability
// ============================================
$testMappings = [
// Hematology: Site → HAUTO → Sysmex (EDTA)
[
'tests' => ['HB', 'HCT', 'RBC', 'WBC', 'PLT', 'MCV', 'MCH', 'MCHC'],
'panels' => ['CBC'],
'siteToWs' => ['ws' => $wsHAuto, 'con' => null],
'wsToInst' => ['ws' => $wsHAuto, 'inst' => $instHematology, 'con' => $conEDTA],
],
// Chemistry: Site → CAUTO → Cobas (SST)
[
'tests' => ['GLU', 'CREA', 'UREA', 'SGOT', 'SGPT', 'CHOL', 'TG', 'HDL', 'LDL'],
'panels' => ['LIPID', 'LFT', 'RFT'],
'siteToWs' => ['ws' => $wsCAuto, 'con' => null],
'wsToInst' => ['ws' => $wsCAuto, 'inst' => $instChemistry, 'con' => $conSST],
],
// Calculated: Site → CAUTO → Cobas (SST)
[
'tests' => ['EGFR', 'LDLCALC'],
'panels' => [],
'siteToWs' => ['ws' => $wsCAuto, 'con' => null],
'wsToInst' => ['ws' => $wsCAuto, 'inst' => $instChemistry, 'con' => $conSST],
],
// Urinalysis: Site → UAUTO → Urisys (Pot Urin)
[
'tests' => ['UCOLOR', 'UGLUC', 'UPROT', 'PH'],
'panels' => [],
'siteToWs' => ['ws' => $wsUAuto, 'con' => null],
'wsToInst' => ['ws' => $wsUAuto, 'inst' => $instUrinalysis, 'con' => $conPotUrin],
],
// BMI: Site → CAUTO (no instrument mapping - calculated)
[
'tests' => ['BMI'],
'panels' => [],
'siteToWs' => ['ws' => $wsCAuto, 'con' => null],
'wsToInst' => null,
],
];
foreach ($testMappings as $mapping) {
// Site → Workstation mapping (one header per workstation)
$testMapSiteWsID = $getTestMapID('SITE', '1', 'WORKSTATION', (string)$mapping['siteToWs']['ws']);
foreach ($mapping['tests'] as $testCode) {
$addDetail($testMapSiteWsID, $testCode, $testCode, $mapping['siteToWs']['con'], $testCode, $testCode);
}
foreach ($mapping['panels'] as $panelCode) {
$addDetail($testMapSiteWsID, $panelCode, $panelCode, $mapping['siteToWs']['con'], $panelCode, $panelCode);
}
// Chemistry tests → CAUTO → Cobas (SST container)
foreach ($chemistryTests as $testCode) {
$testMaps[] = [
'TestSiteID' => $tIDs[$testCode],
'HostType' => 'WORKSTATION',
'HostID' => (string)$wsCAuto,
'HostTestCode' => $testCode,
'HostTestName' => $testCode,
'ClientType' => 'INSTRUMENT',
'ClientID' => (string)$instChemistry,
'ConDefID' => $conSST,
'ClientTestCode' => $testCode,
'ClientTestName' => $testCode,
'CreateDate' => $now
];
// Workstation → Instrument mapping (one header per instrument)
if ($mapping['wsToInst'] !== null) {
$testMapWsInstID = $getTestMapID('WORKSTATION', (string)$mapping['wsToInst']['ws'], 'INSTRUMENT', (string)$mapping['wsToInst']['inst']);
foreach ($mapping['tests'] as $testCode) {
$addDetail($testMapWsInstID, $testCode, $testCode, $mapping['wsToInst']['con'], $testCode, $testCode);
}
foreach ($mapping['panels'] as $panelCode) {
$addDetail($testMapWsInstID, $panelCode, $panelCode, $mapping['wsToInst']['con'], $panelCode, $panelCode);
}
}
}
// Insert all testmapdetail records
if (!empty($testMapDetails)) {
$this->db->table('testmapdetail')->insertBatch($testMapDetails);
}
// Calculated chemistry tests → CAUTO → Cobas (SST container)
foreach ($calcChemistryTests as $testCode) {
$testMaps[] = [
'TestSiteID' => $tIDs[$testCode],
'HostType' => 'WORKSTATION',
'HostID' => (string)$wsCAuto,
'HostTestCode' => $testCode,
'HostTestName' => $testCode,
'ClientType' => 'INSTRUMENT',
'ClientID' => (string)$instChemistry,
'ConDefID' => $conSST,
'ClientTestCode' => $testCode,
'ClientTestName' => $testCode,
'CreateDate' => $now
];
}
// Urinalysis tests → UAUTO → Urisys (Pot Urin container)
foreach ($urineTests as $testCode) {
$testMaps[] = [
'TestSiteID' => $tIDs[$testCode],
'HostType' => 'WORKSTATION',
'HostID' => (string)$wsUAuto,
'HostTestCode' => $testCode,
'HostTestName' => $testCode,
'ClientType' => 'INSTRUMENT',
'ClientID' => (string)$instUrinalysis,
'ConDefID' => $conPotUrin,
'ClientTestCode' => $testCode,
'ClientTestName' => $testCode,
'CreateDate' => $now
];
}
// Panel/Group tests → Instruments
$testMaps[] = [
'TestSiteID' => $tIDs['CBC'],
'HostType' => 'WORKSTATION',
'HostID' => (string)$wsHAuto,
'HostTestCode' => 'CBC',
'HostTestName' => 'CBC',
'ClientType' => 'INSTRUMENT',
'ClientID' => (string)$instHematology,
'ConDefID' => $conEDTA,
'ClientTestCode' => 'CBC',
'ClientTestName' => 'CBC',
'CreateDate' => $now
];
$testMaps[] = [
'TestSiteID' => $tIDs['LIPID'],
'HostType' => 'WORKSTATION',
'HostID' => (string)$wsCAuto,
'HostTestCode' => 'LIPID',
'HostTestName' => 'LIPID',
'ClientType' => 'INSTRUMENT',
'ClientID' => (string)$instChemistry,
'ConDefID' => $conSST,
'ClientTestCode' => 'LIPID',
'ClientTestName' => 'LIPID',
'CreateDate' => $now
];
$testMaps[] = [
'TestSiteID' => $tIDs['LFT'],
'HostType' => 'WORKSTATION',
'HostID' => (string)$wsCAuto,
'HostTestCode' => 'LFT',
'HostTestName' => 'LFT',
'ClientType' => 'INSTRUMENT',
'ClientID' => (string)$instChemistry,
'ConDefID' => $conSST,
'ClientTestCode' => 'LFT',
'ClientTestName' => 'LFT',
'CreateDate' => $now
];
$testMaps[] = [
'TestSiteID' => $tIDs['RFT'],
'HostType' => 'WORKSTATION',
'HostID' => (string)$wsCAuto,
'HostTestCode' => 'RFT',
'HostTestName' => 'RFT',
'ClientType' => 'INSTRUMENT',
'ClientID' => (string)$instChemistry,
'ConDefID' => $conSST,
'ClientTestCode' => 'RFT',
'ClientTestName' => 'RFT',
'CreateDate' => $now
];
$this->db->table('testmap')->insertBatch($testMaps);
}
}

View File

@ -9,7 +9,7 @@ use App\Models\PatVisit\PatVisitADTModel;
class PatVisitModel extends BaseModel {
protected $table = 'patvisit';
protected $primaryKey = 'InternalPVID';
protected $allowedFields = ['PVID', 'InternalPID', 'EpisodeID', 'CreateDate', 'EndDate', 'ArchivedDate', 'DelDate'];
protected $allowedFields = ['PVID', 'InternalPID', 'EpisodeID', 'LastVisitADT', 'CreateDate', 'EndDate', 'ArchivedDate', 'DelDate'];
protected $useTimestamps = true;
protected $createdField = 'CreateDate';

View File

@ -0,0 +1,53 @@
<?php
namespace App\Models\Test;
use App\Models\BaseModel;
class TestMapDetailModel extends BaseModel {
protected $table = 'testmapdetail';
protected $primaryKey = 'TestMapDetailID';
protected $allowedFields = [
'TestMapID',
'HostTestCode',
'HostTestName',
'ConDefID',
'ClientTestCode',
'ClientTestName',
'CreateDate',
'EndDate'
];
protected $useTimestamps = true;
protected $createdField = 'CreateDate';
protected $updatedField = '';
protected $useSoftDeletes = true;
protected $deletedField = "EndDate";
/**
* Get all details for a test map
*/
public function getDetailsByTestMap($testMapID) {
return $this->where('TestMapID', $testMapID)
->where('EndDate IS NULL')
->findAll();
}
/**
* Get test map detail by host test code
*/
public function getDetailsByHostCode($hostTestCode) {
return $this->where('HostTestCode', $hostTestCode)
->where('EndDate IS NULL')
->findAll();
}
/**
* Get test map detail by client test code
*/
public function getDetailsByClientCode($clientTestCode) {
return $this->where('ClientTestCode', $clientTestCode)
->where('EndDate IS NULL')
->findAll();
}
}

View File

@ -11,13 +11,8 @@ class TestMapModel extends BaseModel {
'TestSiteID',
'HostType',
'HostID',
'HostTestCode',
'HostTestName',
'ClientType',
'ClientID',
'ConDefID',
'ClientTestCode',
'ClientTestName',
'CreateDate',
'EndDate'
];
@ -28,6 +23,45 @@ class TestMapModel extends BaseModel {
protected $useSoftDeletes = true;
protected $deletedField = "EndDate";
/**
* Get all test mappings with names
*/
public function getUniqueGroupings() {
$db = $this->db;
$sql = "
SELECT
tm.TestMapID,
tm.TestSiteID,
tm.HostType,
tm.HostID,
CASE
WHEN tm.HostType = 'SITE' THEN s.SiteName
WHEN tm.HostType = 'WORKSTATION' THEN ws.WorkstationName
WHEN tm.HostType = 'INSTRUMENT' THEN el.InstrumentName
ELSE tm.HostID
END as HostName,
tm.ClientType,
tm.ClientID,
CASE
WHEN tm.ClientType = 'SITE' THEN cs.SiteName
WHEN tm.ClientType = 'WORKSTATION' THEN cws.WorkstationName
WHEN tm.ClientType = 'INSTRUMENT' THEN cel.InstrumentName
ELSE tm.ClientID
END as ClientName
FROM testmap tm
LEFT JOIN site s ON tm.HostType = 'SITE' AND s.SiteID = tm.HostID
LEFT JOIN workstation ws ON tm.HostType = 'WORKSTATION' AND ws.WorkstationID = tm.HostID
LEFT JOIN equipmentlist el ON tm.HostType = 'INSTRUMENT' AND el.EID = tm.HostID
LEFT JOIN site cs ON tm.ClientType = 'SITE' AND cs.SiteID = tm.ClientID
LEFT JOIN workstation cws ON tm.ClientType = 'WORKSTATION' AND cws.WorkstationID = tm.ClientID
LEFT JOIN equipmentlist cel ON tm.ClientType = 'INSTRUMENT' AND cel.EID = tm.ClientID
WHERE tm.EndDate IS NULL
";
return $db->query($sql)->getResultArray();
}
/**
* Get test mappings by test site
*/
@ -38,57 +72,14 @@ class TestMapModel extends BaseModel {
}
/**
* Get test mappings by client (equipment/workstation)
* Get test mappings with details by test site
*/
public function getMappingsByClient($clientType, $clientID) {
return $this->where('ClientType', $clientType)
->where('ClientID', $clientID)
->where('EndDate IS NULL')
public function getMappingsWithDetailsByTestSite($testSiteID) {
return $this->select('testmap.*, testmapdetail.*')
->join('testmapdetail', 'testmapdetail.TestMapID = testmap.TestMapID', 'left')
->where('testmap.TestSiteID', $testSiteID)
->where('testmap.EndDate IS NULL')
->where('testmapdetail.EndDate IS NULL')
->findAll();
}
/**
* Get test mappings by host (site/HIS)
*/
public function getMappingsByHost($hostType, $hostID) {
return $this->where('HostType', $hostType)
->where('HostID', $hostID)
->where('EndDate IS NULL')
->findAll();
}
/**
* Get test mapping by client test code and container
*/
public function getMappingByClientCode($clientTestCode, $conDefID = null) {
$builder = $this->where('ClientTestCode', $clientTestCode)
->where('EndDate IS NULL');
if ($conDefID) {
$builder->where('ConDefID', $conDefID);
}
return $builder->findAll();
}
/**
* Get test mapping by host test code
*/
public function getMappingByHostCode($hostTestCode) {
return $this->where('HostTestCode', $hostTestCode)
->where('EndDate IS NULL')
->findAll();
}
/**
* Check if mapping exists for client and host
*/
public function mappingExists($testSiteID, $clientType, $clientID, $clientTestCode) {
return $this->where('TestSiteID', $testSiteID)
->where('ClientType', $clientType)
->where('ClientID', $clientID)
->where('ClientTestCode', $clientTestCode)
->where('EndDate IS NULL')
->countAllResults() > 0;
}
}

View File

@ -2781,25 +2781,12 @@ paths:
get:
tags:
- Tests
summary: List all test mappings
summary: List all test mappings (unique groupings)
security:
- bearerAuth: []
parameters:
- name: page
in: query
schema:
type: integer
default: 1
description: Page number for pagination
- name: perPage
in: query
schema:
type: integer
default: 20
description: Number of items per page
responses:
'200':
description: List of test mappings
description: List of unique test mapping groupings
content:
application/json:
schema:
@ -2813,11 +2800,24 @@ paths:
data:
type: array
items:
$ref: '#/components/schemas/TestMap'
type: object
properties:
HostType:
type: string
HostID:
type: string
HostName:
type: string
ClientType:
type: string
ClientID:
type: string
ClientName:
type: string
post:
tags:
- Tests
summary: Create test mapping
summary: Create test mapping (header only)
security:
- bearerAuth: []
requestBody:
@ -2836,27 +2836,28 @@ paths:
HostID:
type: string
description: Host identifier
HostTestCode:
type: string
description: Test code in host system
HostTestName:
type: string
description: Test name in host system
ClientType:
type: string
description: Client type code
ClientID:
type: string
description: Client identifier
details:
type: array
description: Optional detail records to create
items:
type: object
properties:
HostTestCode:
type: string
HostTestName:
type: string
ConDefID:
type: integer
description: Connection definition ID
ClientTestCode:
type: string
description: Test code in client system
ClientTestName:
type: string
description: Test name in client system
required:
- TestSiteID
responses:
@ -2897,20 +2898,10 @@ paths:
type: string
HostID:
type: string
HostTestCode:
type: string
HostTestName:
type: string
ClientType:
type: string
ClientID:
type: string
ConDefID:
type: integer
ClientTestCode:
type: string
ClientTestName:
type: string
required:
- TestMapID
responses:
@ -2932,7 +2923,7 @@ paths:
delete:
tags:
- Tests
summary: Soft delete test mapping
summary: Soft delete test mapping (cascades to details)
security:
- bearerAuth: []
requestBody:
@ -2969,7 +2960,7 @@ paths:
get:
tags:
- Tests
summary: Get test mapping by ID
summary: Get test mapping by ID with details
security:
- bearerAuth: []
parameters:
@ -2981,7 +2972,7 @@ paths:
description: Test Map ID
responses:
'200':
description: Test mapping details
description: Test mapping details with nested detail records
content:
application/json:
schema:
@ -2999,7 +2990,7 @@ paths:
get:
tags:
- Tests
summary: Get test mappings by test site
summary: Get test mappings by test site with details
security:
- bearerAuth: []
parameters:
@ -3011,7 +3002,7 @@ paths:
description: Test Site ID
responses:
'200':
description: List of test mappings for the test site
description: List of test mappings with details for the test site
content:
application/json:
schema:
@ -3025,29 +3016,22 @@ paths:
type: array
items:
$ref: '#/components/schemas/TestMap'
/api/test/testmap/by-host/{hostType}/{hostID}:
/api/test/testmap/detail:
get:
tags:
- Tests
summary: Get test mappings by host
summary: List test mapping details
security:
- bearerAuth: []
parameters:
- name: hostType
in: path
required: true
- name: TestMapID
in: query
schema:
type: string
description: Host type code
- name: hostID
in: path
required: true
schema:
type: string
description: Host identifier
type: integer
description: Filter by TestMapID
responses:
'200':
description: List of test mappings for the host
description: List of test mapping details
content:
application/json:
schema:
@ -3060,30 +3044,149 @@ paths:
data:
type: array
items:
$ref: '#/components/schemas/TestMap'
/api/test/testmap/by-client/{clientType}/{clientID}:
$ref: '#/components/schemas/TestMapDetail'
post:
tags:
- Tests
summary: Create test mapping detail
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
TestMapID:
type: integer
description: Test Map ID (required)
HostTestCode:
type: string
HostTestName:
type: string
ConDefID:
type: integer
ClientTestCode:
type: string
ClientTestName:
type: string
required:
- TestMapID
responses:
'201':
description: Test mapping detail created
content:
application/json:
schema:
type: object
properties:
status:
type: string
message:
type: string
data:
type: integer
description: Created TestMapDetailID
patch:
tags:
- Tests
summary: Update test mapping detail
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
TestMapDetailID:
type: integer
description: Test Map Detail ID (required)
TestMapID:
type: integer
HostTestCode:
type: string
HostTestName:
type: string
ConDefID:
type: integer
ClientTestCode:
type: string
ClientTestName:
type: string
required:
- TestMapDetailID
responses:
'200':
description: Test mapping detail updated
delete:
tags:
- Tests
summary: Soft delete test mapping detail
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
TestMapDetailID:
type: integer
description: Test Map Detail ID to delete (required)
required:
- TestMapDetailID
responses:
'200':
description: Test mapping detail deleted
/api/test/testmap/detail/{id}:
get:
tags:
- Tests
summary: Get test mappings by client
summary: Get test mapping detail by ID
security:
- bearerAuth: []
parameters:
- name: clientType
- name: id
in: path
required: true
schema:
type: string
description: Client type code
- name: clientID
in: path
required: true
schema:
type: string
description: Client identifier
type: integer
description: Test Map Detail ID
responses:
'200':
description: List of test mappings for the client
description: Test mapping detail
content:
application/json:
schema:
type: object
properties:
status:
type: string
message:
type: string
data:
$ref: '#/components/schemas/TestMapDetail'
/api/test/testmap/detail/by-testmap/{testMapID}:
get:
tags:
- Tests
summary: Get test mapping details by test map ID
security:
- bearerAuth: []
parameters:
- name: testMapID
in: path
required: true
schema:
type: integer
description: Test Map ID
responses:
'200':
description: List of test mapping details
content:
application/json:
schema:
@ -3096,7 +3199,88 @@ paths:
data:
type: array
items:
$ref: '#/components/schemas/TestMap'
$ref: '#/components/schemas/TestMapDetail'
/api/test/testmap/detail/batch:
post:
tags:
- Tests
summary: Batch create test mapping details
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: array
items:
type: object
properties:
TestMapID:
type: integer
HostTestCode:
type: string
HostTestName:
type: string
ConDefID:
type: integer
ClientTestCode:
type: string
ClientTestName:
type: string
responses:
'200':
description: Batch create results
patch:
tags:
- Tests
summary: Batch update test mapping details
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: array
items:
type: object
properties:
TestMapDetailID:
type: integer
TestMapID:
type: integer
HostTestCode:
type: string
HostTestName:
type: string
ConDefID:
type: integer
ClientTestCode:
type: string
ClientTestName:
type: string
responses:
'200':
description: Batch update results
delete:
tags:
- Tests
summary: Batch delete test mapping details
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: array
items:
type: integer
description: TestMapDetailIDs to delete
responses:
'200':
description: Batch delete results
/api/tests:
get:
tags:
@ -5104,31 +5288,27 @@ components:
type: integer
HostType:
type: string
description: Host type code
description: Host type code (e.g., SITE, WORKSTATION, INSTRUMENT)
HostID:
type: string
description: Host identifier
HostTestCode:
type: string
description: Test code in host system
HostTestName:
type: string
description: Test name in host system
ClientType:
type: string
description: Client type code
description: Client type code (e.g., SITE, WORKSTATION, INSTRUMENT)
ClientID:
type: string
description: Client identifier
ConDefID:
type: integer
description: Connection definition ID
ClientTestCode:
HostName:
type: string
description: Test code in client system
ClientTestName:
description: Resolved host name (from view)
ClientName:
type: string
description: Test name in client system
description: Resolved client name (from view)
details:
type: array
description: Test mapping detail records
items:
$ref: '#/components/schemas/TestMapDetail'
CreateDate:
type: string
format: date-time
@ -5485,6 +5665,35 @@ components:
type: string
format: date-time
description: Occupation display text
TestMapDetail:
type: object
properties:
TestMapDetailID:
type: integer
TestMapID:
type: integer
HostTestCode:
type: string
description: Test code in host system
HostTestName:
type: string
description: Test name in host system
ConDefID:
type: integer
description: Container definition ID
ClientTestCode:
type: string
description: Test code in client system
ClientTestName:
type: string
description: Test name in client system
CreateDate:
type: string
format: date-time
EndDate:
type: string
format: date-time
description: Soft delete timestamp
ValueSetListItem:
type: object
description: Library/system value set summary (from JSON files)

View File

@ -430,25 +430,51 @@ TestMap:
type: integer
HostType:
type: string
description: Host type code
description: Host type code (e.g., SITE, WORKSTATION, INSTRUMENT)
HostID:
type: string
description: Host identifier
ClientType:
type: string
description: Client type code (e.g., SITE, WORKSTATION, INSTRUMENT)
ClientID:
type: string
description: Client identifier
HostName:
type: string
description: Resolved host name (from view)
ClientName:
type: string
description: Resolved client name (from view)
details:
type: array
description: Test mapping detail records
items:
$ref: '#/TestMapDetail'
CreateDate:
type: string
format: date-time
EndDate:
type: string
format: date-time
description: Soft delete timestamp
TestMapDetail:
type: object
properties:
TestMapDetailID:
type: integer
TestMapID:
type: integer
HostTestCode:
type: string
description: Test code in host system
HostTestName:
type: string
description: Test name in host system
ClientType:
type: string
description: Client type code
ClientID:
type: string
description: Client identifier
ConDefID:
type: integer
description: Connection definition ID
description: Container definition ID
ClientTestCode:
type: string
description: Test code in client system

View File

@ -4,19 +4,6 @@
summary: List all test mappings
security:
- bearerAuth: []
parameters:
- name: page
in: query
schema:
type: integer
default: 1
description: Page number for pagination
- name: perPage
in: query
schema:
type: integer
default: 20
description: Number of items per page
responses:
'200':
description: List of test mappings
@ -33,11 +20,28 @@
data:
type: array
items:
$ref: '../components/schemas/tests.yaml#/TestMap'
type: object
properties:
TestMapID:
type: integer
TestSiteID:
type: integer
HostType:
type: string
HostID:
type: string
HostName:
type: string
ClientType:
type: string
ClientID:
type: string
ClientName:
type: string
post:
tags: [Tests]
summary: Create test mapping
summary: Create test mapping (header only)
security:
- bearerAuth: []
requestBody:
@ -56,27 +60,28 @@
HostID:
type: string
description: Host identifier
HostTestCode:
type: string
description: Test code in host system
HostTestName:
type: string
description: Test name in host system
ClientType:
type: string
description: Client type code
ClientID:
type: string
description: Client identifier
details:
type: array
description: Optional detail records to create
items:
type: object
properties:
HostTestCode:
type: string
HostTestName:
type: string
ConDefID:
type: integer
description: Connection definition ID
ClientTestCode:
type: string
description: Test code in client system
ClientTestName:
type: string
description: Test name in client system
required:
- TestSiteID
responses:
@ -117,20 +122,10 @@
type: string
HostID:
type: string
HostTestCode:
type: string
HostTestName:
type: string
ClientType:
type: string
ClientID:
type: string
ConDefID:
type: integer
ClientTestCode:
type: string
ClientTestName:
type: string
required:
- TestMapID
responses:
@ -152,7 +147,7 @@
delete:
tags: [Tests]
summary: Soft delete test mapping
summary: Soft delete test mapping (cascades to details)
security:
- bearerAuth: []
requestBody:
@ -189,7 +184,7 @@
/api/test/testmap/{id}:
get:
tags: [Tests]
summary: Get test mapping by ID
summary: Get test mapping by ID with details
security:
- bearerAuth: []
parameters:
@ -201,7 +196,7 @@
description: Test Map ID
responses:
'200':
description: Test mapping details
description: Test mapping details with nested detail records
content:
application/json:
schema:
@ -219,7 +214,7 @@
/api/test/testmap/by-testsite/{testSiteID}:
get:
tags: [Tests]
summary: Get test mappings by test site
summary: Get test mappings by test site with details
security:
- bearerAuth: []
parameters:
@ -231,7 +226,7 @@
description: Test Site ID
responses:
'200':
description: List of test mappings for the test site
description: List of test mappings with details for the test site
content:
application/json:
schema:
@ -246,28 +241,21 @@
items:
$ref: '../components/schemas/tests.yaml#/TestMap'
/api/test/testmap/by-host/{hostType}/{hostID}:
/api/test/testmap/detail:
get:
tags: [Tests]
summary: Get test mappings by host
summary: List test mapping details
security:
- bearerAuth: []
parameters:
- name: hostType
in: path
required: true
- name: TestMapID
in: query
schema:
type: string
description: Host type code
- name: hostID
in: path
required: true
schema:
type: string
description: Host identifier
type: integer
description: Filter by TestMapID
responses:
'200':
description: List of test mappings for the host
description: List of test mapping details
content:
application/json:
schema:
@ -280,30 +268,149 @@
data:
type: array
items:
$ref: '../components/schemas/tests.yaml#/TestMap'
$ref: '../components/schemas/tests.yaml#/TestMapDetail'
/api/test/testmap/by-client/{clientType}/{clientID}:
post:
tags: [Tests]
summary: Create test mapping detail
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
TestMapID:
type: integer
description: Test Map ID (required)
HostTestCode:
type: string
HostTestName:
type: string
ConDefID:
type: integer
ClientTestCode:
type: string
ClientTestName:
type: string
required:
- TestMapID
responses:
'201':
description: Test mapping detail created
content:
application/json:
schema:
type: object
properties:
status:
type: string
message:
type: string
data:
type: integer
description: Created TestMapDetailID
patch:
tags: [Tests]
summary: Update test mapping detail
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
TestMapDetailID:
type: integer
description: Test Map Detail ID (required)
TestMapID:
type: integer
HostTestCode:
type: string
HostTestName:
type: string
ConDefID:
type: integer
ClientTestCode:
type: string
ClientTestName:
type: string
required:
- TestMapDetailID
responses:
'200':
description: Test mapping detail updated
delete:
tags: [Tests]
summary: Soft delete test mapping detail
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
TestMapDetailID:
type: integer
description: Test Map Detail ID to delete (required)
required:
- TestMapDetailID
responses:
'200':
description: Test mapping detail deleted
/api/test/testmap/detail/{id}:
get:
tags: [Tests]
summary: Get test mappings by client
summary: Get test mapping detail by ID
security:
- bearerAuth: []
parameters:
- name: clientType
- name: id
in: path
required: true
schema:
type: string
description: Client type code
- name: clientID
in: path
required: true
schema:
type: string
description: Client identifier
type: integer
description: Test Map Detail ID
responses:
'200':
description: List of test mappings for the client
description: Test mapping detail
content:
application/json:
schema:
type: object
properties:
status:
type: string
message:
type: string
data:
$ref: '../components/schemas/tests.yaml#/TestMapDetail'
/api/test/testmap/detail/by-testmap/{testMapID}:
get:
tags: [Tests]
summary: Get test mapping details by test map ID
security:
- bearerAuth: []
parameters:
- name: testMapID
in: path
required: true
schema:
type: integer
description: Test Map ID
responses:
'200':
description: List of test mapping details
content:
application/json:
schema:
@ -316,4 +423,85 @@
data:
type: array
items:
$ref: '../components/schemas/tests.yaml#/TestMap'
$ref: '../components/schemas/tests.yaml#/TestMapDetail'
/api/test/testmap/detail/batch:
post:
tags: [Tests]
summary: Batch create test mapping details
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: array
items:
type: object
properties:
TestMapID:
type: integer
HostTestCode:
type: string
HostTestName:
type: string
ConDefID:
type: integer
ClientTestCode:
type: string
ClientTestName:
type: string
responses:
'200':
description: Batch create results
patch:
tags: [Tests]
summary: Batch update test mapping details
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: array
items:
type: object
properties:
TestMapDetailID:
type: integer
TestMapID:
type: integer
HostTestCode:
type: string
HostTestName:
type: string
ConDefID:
type: integer
ClientTestCode:
type: string
ClientTestName:
type: string
responses:
'200':
description: Batch update results
delete:
tags: [Tests]
summary: Batch delete test mapping details
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: array
items:
type: integer
description: TestMapDetailIDs to delete
responses:
'200':
description: Batch delete results