feat: add equipment list management API with CRUD operations

This commit is contained in:
mahdahar 2026-02-24 16:53:36 +07:00
parent 3a30629e15
commit d3668fe2b3
13 changed files with 966 additions and 41 deletions

View File

@ -228,6 +228,35 @@ $routes->group('api/patient', function ($routes) {
## Project-Specific Conventions
### API Documentation Sync
**CRITICAL**: When updating any controller, you MUST also update the corresponding OpenAPI YAML documentation:
- **Paths**: `public/paths/<resource>.yaml` (e.g., `patients.yaml`, `orders.yaml`)
- **Schemas**: `public/components/schemas/<resource>.yaml`
- **Main file**: `public/api-docs.yaml` (for tags and schema references)
**After updating YAML files**, regenerate the bundled documentation:
```bash
node public/bundle-api-docs.js
```
This produces `public/api-docs.bundled.yaml` which is used by Swagger UI/Redoc.
### Controller-to-YAML Mapping
| Controller | YAML Path File | YAML Schema File |
|-----------|----------------|------------------|
| `PatientController` | `paths/patients.yaml` | `components/schemas/patient.yaml` |
| `PatVisitController` | `paths/patient-visits.yaml` | `components/schemas/patient-visit.yaml` |
| `OrderTestController` | `paths/orders.yaml` | `components/schemas/orders.yaml` |
| `SpecimenController` | `paths/specimen.yaml` | `components/schemas/specimen.yaml` |
| `TestsController` | `paths/tests.yaml` | `components/schemas/tests.yaml` |
| `AuthController` | `paths/authentication.yaml` | `components/schemas/authentication.yaml` |
| `ResultController` | `paths/results.yaml` | `components/schemas/*.yaml` |
| `EdgeController` | `paths/edge-api.yaml` | `components/schemas/edge-api.yaml` |
| `LocationController` | `paths/locations.yaml` | `components/schemas/master-data.yaml` |
| `ValueSetController` | `paths/valuesets.yaml` | `components/schemas/valuesets.yaml` |
| `ContactController` | `paths/contact.yaml` | (inline schemas) |
### Legacy Field Naming
Database uses PascalCase columns: `PatientID`, `NameFirst`, `Birthdate`, `CreatedAt`, `UpdatedAt`

View File

@ -208,6 +208,15 @@ $routes->group('api', function ($routes) {
});
});
// Infrastructure
$routes->group('equipmentlist', function ($routes) {
$routes->get('/', 'Infrastructure\EquipmentListController::index');
$routes->get('(:num)', 'Infrastructure\EquipmentListController::show/$1');
$routes->post('/', 'Infrastructure\EquipmentListController::create');
$routes->patch('/', 'Infrastructure\EquipmentListController::update');
$routes->delete('/', 'Infrastructure\EquipmentListController::delete');
});
// Specimen
$routes->group('specimen', function ($routes) {
// Container aliases - 'container' and 'containerdef' both point to ContainerDefController

View File

@ -0,0 +1,113 @@
<?php
namespace App\Controllers\Infrastructure;
use App\Controllers\BaseController;
use App\Traits\ResponseTrait;
use App\Models\Infrastructure\EquipmentListModel;
class EquipmentListController extends BaseController {
use ResponseTrait;
protected $db;
protected $model;
public function __construct() {
$this->db = \Config\Database::connect();
$this->model = new EquipmentListModel();
}
public function index() {
$filter = [
'IEID' => $this->request->getVar('IEID'),
'InstrumentName' => $this->request->getVar('InstrumentName'),
'DepartmentID' => $this->request->getVar('DepartmentID'),
'WorkstationID' => $this->request->getVar('WorkstationID'),
'Enable' => $this->request->getVar('Enable'),
];
$rows = $this->model->getEquipmentLists($filter);
if (empty($rows)) {
return $this->respond([
'status' => 'success',
'message' => 'no Data.',
'data' => []
], 200);
}
return $this->respond([
'status' => 'success',
'message' => 'fetch success',
'data' => $rows
], 200);
}
public function show($EID = null) {
$row = $this->model->getEquipmentList($EID);
if (empty($row)) {
return $this->respond([
'status' => 'success',
'message' => 'no Data.',
'data' => null
], 200);
}
return $this->respond([
'status' => 'success',
'message' => 'fetch success',
'data' => $row
], 200);
}
public function create() {
$input = $this->request->getJSON(true);
try {
$EID = $this->model->insert($input, true);
return $this->respondCreated([
'status' => 'success',
'message' => 'data created successfully',
'data' => $EID
], 201);
} catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
public function update() {
$input = $this->request->getJSON(true);
try {
$EID = $input['EID'];
$this->model->update($EID, $input);
return $this->respond([
'status' => 'success',
'message' => 'data updated successfully',
'data' => $EID
], 200);
} catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
public function delete() {
try {
$input = $this->request->getJSON(true);
$EID = $input['EID'];
if (!$EID) {
return $this->failValidationErrors('EID is required.');
}
$this->model->delete($EID);
return $this->respondDeleted([
'status' => 'success',
'message' => "{$EID} deleted successfully."
]);
} catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
}

View File

@ -80,9 +80,20 @@ class CreateLookups extends Migration {
]);
$this->forge->addKey('SpecialtyID', true);
$this->forge->createTable('medicalspecialty');
$this->forge->addField([
'AreaGeoID' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
'AreaCode' => ['type' => 'varchar', 'constraint' => 20, 'null' => true],
'Class' => ['type' => 'int', 'null' => true],
'AreaName' => ['type' => 'VARCHAR', 'constraint' => 100, 'null' => false],
'Parent' => ['type' => 'int', 'null' => true],
]);
$this->forge->addKey('AreaGeoID', true);
$this->forge->createTable('areageo');
}
public function down() {
$this->forge->dropTable('areageo');
$this->forge->dropTable('medicalspecialty');
$this->forge->dropTable('occupation');
$this->forge->dropTable('containerdef');

View File

@ -98,9 +98,24 @@ class CreateOrganization extends Migration {
]);
$this->forge->addKey('DepartmentID', true);
$this->forge->createTable('department');
$this->forge->addField([
'WorkstationID' => ['type' => 'int', 'unsigned' => true, 'auto_increment'=> true],
'DepartmentID' => ['type' => 'int', 'null'=> false],
'WorkstationCode' => ['type' => 'varchar', 'constraint'=>10, 'null'=> false],
'WorkstationName' => ['type' => 'varchar', 'constraint'=> 150, 'null'=> true],
'Type' => ['type' => 'VARCHAR', 'constraint' => 10, 'null'=> true],
'LinkTo' => ['type' => 'int', 'null'=> true],
'Enable' => ['type' => 'VARCHAR', 'constraint' => 10, 'null'=> true],
'CreateDate' => ['type'=>'DATETIME', 'null' => true],
'EndDate' => ['type'=>'DATETIME', 'null' => true]
]);
$this->forge->addKey('WorkstationID', true);
$this->forge->createTable('workstation');
}
public function down() {
$this->forge->dropTable('workstation');
$this->forge->dropTable('department');
$this->forge->dropTable('discipline');
$this->forge->dropTable('locationaddress');

View File

@ -7,30 +7,18 @@ use CodeIgniter\Database\Migration;
class CreateLabInfrastructure extends Migration {
public function up() {
$this->forge->addField([
'WorkstationID' => ['type' => 'int', 'unsigned' => true, 'auto_increment'=> true],
'DepartmentID' => ['type' => 'int', 'null'=> false],
'WorkstationCode' => ['type' => 'varchar', 'constraint'=>10, 'null'=> false],
'WorkstationName' => ['type' => 'varchar', 'constraint'=> 150, 'null'=> true],
'Type' => ['type' => 'VARCHAR', 'constraint' => 10, 'null'=> true],
'LinkTo' => ['type' => 'int', 'null'=> true],
'Enable' => ['type' => 'VARCHAR', 'constraint' => 10, 'null'=> true],
'CreateDate' => ['type'=>'DATETIME', 'null' => true],
'EndDate' => ['type'=>'DATETIME', 'null' => true]
]);
$this->forge->addKey('WorkstationID', true);
$this->forge->createTable('workstation');
$this->forge->addField([
'EquipmentID' => ['type' => 'int', 'unsigned' => true, 'auto_increment'=> true],
'EID' => ['type' => 'int', 'unsigned' => true, 'auto_increment'=> true],
'IEID' => ['type' => 'varchar', 'constraint' => 50],
'DepartmentID' => ['type' => 'int', 'constraint'=> 10, 'null'=> false],
'InstrumentID' => ['type' => 'varchar', 'constraint'=> 150, 'null'=> true],
'InstrumentName' => ['type' => 'varchar', 'constraint'=> 150, 'null'=> true],
'WorkstationID' => ['type' => 'int', 'unsigned' => true ],
'Enable' => ['type' => 'bit', 'null'=> false],
'EquipmentRole' => ['type' => 'varchar', 'constraint' => 1, 'null'=> false],
'CreateDate' => ['type'=>'DATETIME', 'null' => true],
'EndDate' => ['type'=>'DATETIME', 'null' => true]
]);
$this->forge->addKey('EquipmentID', true);
$this->forge->addKey('EID', true);
$this->forge->createTable('equipmentlist');
$this->forge->addField([
@ -68,23 +56,11 @@ class CreateLabInfrastructure extends Migration {
]);
$this->forge->addKey('EquipmentID', true);
$this->forge->createTable('devicelist');
$this->forge->addField([
'AreaGeoID' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
'AreaCode' => ['type' => 'varchar', 'constraint' => 20, 'null' => true],
'Class' => ['type' => 'int', 'null' => true],
'AreaName' => ['type' => 'VARCHAR', 'constraint' => 100, 'null' => false],
'Parent' => ['type' => 'int', 'null' => true],
]);
$this->forge->addKey('AreaGeoID', true);
$this->forge->createTable('areageo');
}
public function down() {
$this->forge->dropTable('areageo');
$this->forge->dropTable('devicelist');
$this->forge->dropTable('comparameters');
$this->forge->dropTable('equipmentlist');
$this->forge->dropTable('workstation');
}
}

View File

@ -64,5 +64,38 @@ class OrganizationSeeder extends Seeder
['WorkstationID' => '9', 'DepartmentID' => '6', 'WorkstationCode' => 'UMAN', 'WorkstationName' => 'Urin Manual', 'Type' => '0', 'LinkTo' => '', 'Enable' => '1', 'CreateDate' => "$now"],
];
$this->db->table('workstation')->insertBatch($data);
// Equipment/Instruments for each workstation
$data = [
// Hematology Auto (WorkstationID: 1)
['EID' => 1, 'IEID' => 'EQ-HEM-001', 'DepartmentID' => '1', 'InstrumentID' => 'SYSMEX_XN1000', 'InstrumentName' => 'Sysmex XN-1000 Hematology Analyzer', 'WorkstationID' => 1, 'Enable' => 1, 'EquipmentRole' => 'A', 'CreateDate' => "$now"],
['EID' => 2, 'IEID' => 'EQ-HEM-002', 'DepartmentID' => '1', 'InstrumentID' => 'SYSMEX_XS1000', 'InstrumentName' => 'Sysmex XS-1000i', 'WorkstationID' => 1, 'Enable' => 1, 'EquipmentRole' => 'A', 'CreateDate' => "$now"],
// Hematology Backup (WorkstationID: 2)
['EID' => 3, 'IEID' => 'EQ-HEM-003', 'DepartmentID' => '1', 'InstrumentID' => 'SYSMEX_XN550', 'InstrumentName' => 'Sysmex XN-550 Backup', 'WorkstationID' => 2, 'Enable' => 1, 'EquipmentRole' => 'B', 'CreateDate' => "$now"],
['EID' => 4, 'IEID' => 'EQ-HEM-004', 'DepartmentID' => '1', 'InstrumentID' => 'MANUAL_DIFF', 'InstrumentName' => 'Manual Differential Counter', 'WorkstationID' => 2, 'Enable' => 1, 'EquipmentRole' => 'M', 'CreateDate' => "$now"],
// Chemistry Auto (WorkstationID: 3)
['EID' => 5, 'IEID' => 'EQ-CHEM-001', 'DepartmentID' => '3', 'InstrumentID' => 'COBAS_C501', 'InstrumentName' => 'Roche Cobas C501 Chemistry Analyzer', 'WorkstationID' => 3, 'Enable' => 1, 'EquipmentRole' => 'A', 'CreateDate' => "$now"],
['EID' => 6, 'IEID' => 'EQ-CHEM-002', 'DepartmentID' => '3', 'InstrumentID' => 'COBAS_C311', 'InstrumentName' => 'Roche Cobas C311', 'WorkstationID' => 3, 'Enable' => 1, 'EquipmentRole' => 'A', 'CreateDate' => "$now"],
// Chemistry Backup (WorkstationID: 4)
['EID' => 7, 'IEID' => 'EQ-CHEM-003', 'DepartmentID' => '3', 'InstrumentID' => 'COBAS_E411', 'InstrumentName' => 'Roche Cobas E411 Immunoassay', 'WorkstationID' => 4, 'Enable' => 1, 'EquipmentRole' => 'B', 'CreateDate' => "$now"],
// Chemistry Manual (WorkstationID: 5)
['EID' => 8, 'IEID' => 'EQ-CHEM-004', 'DepartmentID' => '3', 'InstrumentID' => 'SPEC_MANUAL', 'InstrumentName' => 'Spectrophotometer Manual', 'WorkstationID' => 5, 'Enable' => 1, 'EquipmentRole' => 'M', 'CreateDate' => "$now"],
// Immunology Auto (WorkstationID: 6)
['EID' => 9, 'IEID' => 'EQ-IMM-001', 'DepartmentID' => '4', 'InstrumentID' => 'ARCH_I2000', 'InstrumentName' => 'Architect i2000 Immunoassay', 'WorkstationID' => 6, 'Enable' => 1, 'EquipmentRole' => 'A', 'CreateDate' => "$now"],
['EID' => 10, 'IEID' => 'EQ-IMM-002', 'DepartmentID' => '4', 'InstrumentID' => 'COBAS_E601', 'InstrumentName' => 'Roche Cobas E601', 'WorkstationID' => 6, 'Enable' => 1, 'EquipmentRole' => 'A', 'CreateDate' => "$now"],
// Immunology Manual (WorkstationID: 7)
['EID' => 11, 'IEID' => 'EQ-IMM-003', 'DepartmentID' => '4', 'InstrumentID' => 'ELISA_READER', 'InstrumentName' => 'ELISA Plate Reader', 'WorkstationID' => 7, 'Enable' => 1, 'EquipmentRole' => 'M', 'CreateDate' => "$now"],
['EID' => 12, 'IEID' => 'EQ-IMM-004', 'DepartmentID' => '4', 'InstrumentID' => 'FLUORIMETER', 'InstrumentName' => 'Fluorimeter Manual', 'WorkstationID' => 7, 'Enable' => 1, 'EquipmentRole' => 'M', 'CreateDate' => "$now"],
// Urinalysis Auto (WorkstationID: 8)
['EID' => 13, 'IEID' => 'EQ-URI-001', 'DepartmentID' => '6', 'InstrumentID' => 'URISYS_1100', 'InstrumentName' => 'Urisys 1100 Urine Analyzer', 'WorkstationID' => 8, 'Enable' => 1, 'EquipmentRole' => 'A', 'CreateDate' => "$now"],
['EID' => 14, 'IEID' => 'EQ-URI-002', 'DepartmentID' => '6', 'InstrumentID' => 'CLINITEK', 'InstrumentName' => 'Clinitek Urine Chemistry', 'WorkstationID' => 8, 'Enable' => 1, 'EquipmentRole' => 'A', 'CreateDate' => "$now"],
// Urinalysis Manual (WorkstationID: 9)
['EID' => 15, 'IEID' => 'EQ-URI-003', 'DepartmentID' => '6', 'InstrumentID' => 'MICROSCOPE', 'InstrumentName' => 'Microscope Manual', 'WorkstationID' => 9, 'Enable' => 1, 'EquipmentRole' => 'M', 'CreateDate' => "$now"],
['EID' => 16, 'IEID' => 'EQ-URI-004', 'DepartmentID' => '6', 'InstrumentID' => 'CENTRIFUGE', 'InstrumentName' => 'Centrifuge Urine', 'WorkstationID' => 9, 'Enable' => 1, 'EquipmentRole' => 'M', 'CreateDate' => "$now"],
// Additional equipment (disabled)
['EID' => 17, 'IEID' => 'EQ-HEM-005', 'DepartmentID' => '1', 'InstrumentID' => 'OLD_ANALYZER', 'InstrumentName' => 'Old Hematology Analyzer', 'WorkstationID' => 1, 'Enable' => 0, 'EquipmentRole' => 'B', 'CreateDate' => "$now"],
['EID' => 18, 'IEID' => 'EQ-CHEM-005', 'DepartmentID' => '3', 'InstrumentID' => 'MAINTENANCE', 'InstrumentName' => 'Chemistry Analyzer Under Maintenance', 'WorkstationID' => 3, 'Enable' => 0, 'EquipmentRole' => 'B', 'CreateDate' => "$now"],
];
$this->db->table('equipmentlist')->insertBatch($data);
}
}

View File

@ -225,7 +225,11 @@ class TestSeeder extends Seeder
$testMaps = [];
// Hematology tests → HAUTO workstation + EDTA container
// ============================================
// SITE → WORKSTATION mappings (no ConDefID)
// ============================================
// Hematology tests → HAUTO workstation
$hematologyTests = ['HB', 'HCT', 'RBC', 'WBC', 'PLT', 'MCV', 'MCH', 'MCHC'];
foreach ($hematologyTests as $testCode) {
$testMaps[] = [
@ -236,14 +240,14 @@ class TestSeeder extends Seeder
'HostTestName' => $testCode,
'ClientType' => 'WORKSTATION',
'ClientID' => (string)$wsHAuto,
'ConDefID' => $conEDTA,
'ConDefID' => null,
'ClientTestCode' => $testCode,
'ClientTestName' => $testCode,
'CreateDate' => $now
];
}
// Chemistry tests → CAUTO workstation + SST container
// Chemistry tests → CAUTO workstation
$chemistryTests = ['GLU', 'CREA', 'UREA', 'SGOT', 'SGPT', 'CHOL', 'TG', 'HDL', 'LDL'];
foreach ($chemistryTests as $testCode) {
$testMaps[] = [
@ -254,14 +258,14 @@ class TestSeeder extends Seeder
'HostTestName' => $testCode,
'ClientType' => 'WORKSTATION',
'ClientID' => (string)$wsCAuto,
'ConDefID' => $conSST,
'ConDefID' => null,
'ClientTestCode' => $testCode,
'ClientTestName' => $testCode,
'CreateDate' => $now
];
}
// Calculated chemistry tests → CAUTO workstation + SST container (inherited from parents)
// Calculated chemistry tests → CAUTO workstation
$calcChemistryTests = ['EGFR', 'LDLCALC'];
foreach ($calcChemistryTests as $testCode) {
$testMaps[] = [
@ -272,14 +276,14 @@ class TestSeeder extends Seeder
'HostTestName' => $testCode,
'ClientType' => 'WORKSTATION',
'ClientID' => (string)$wsCAuto,
'ConDefID' => $conSST,
'ConDefID' => null,
'ClientTestCode' => $testCode,
'ClientTestName' => $testCode,
'CreateDate' => $now
];
}
// Urinalysis tests → UAUTO workstation + Pot Urin container
// Urinalysis tests → UAUTO workstation
$urineTests = ['UCOLOR', 'UGLUC', 'UPROT', 'PH'];
foreach ($urineTests as $testCode) {
$testMaps[] = [
@ -290,14 +294,14 @@ class TestSeeder extends Seeder
'HostTestName' => $testCode,
'ClientType' => 'WORKSTATION',
'ClientID' => (string)$wsUAuto,
'ConDefID' => $conPotUrin,
'ConDefID' => null,
'ClientTestCode' => $testCode,
'ClientTestName' => $testCode,
'CreateDate' => $now
];
}
// Panel/Group tests - map to primary container (EDTA for CBC, SST for LIPID/LFT/RFT)
// Panel/Group tests
$testMaps[] = [
'TestSiteID' => $tIDs['CBC'],
'HostType' => 'SITE',
@ -306,7 +310,7 @@ class TestSeeder extends Seeder
'HostTestName' => 'CBC',
'ClientType' => 'WORKSTATION',
'ClientID' => (string)$wsHAuto,
'ConDefID' => $conEDTA,
'ConDefID' => null,
'ClientTestCode' => 'CBC',
'ClientTestName' => 'CBC',
'CreateDate' => $now
@ -320,7 +324,7 @@ class TestSeeder extends Seeder
'HostTestName' => 'LIPID',
'ClientType' => 'WORKSTATION',
'ClientID' => (string)$wsCAuto,
'ConDefID' => $conSST,
'ConDefID' => null,
'ClientTestCode' => 'LIPID',
'ClientTestName' => 'LIPID',
'CreateDate' => $now
@ -334,7 +338,7 @@ class TestSeeder extends Seeder
'HostTestName' => 'LFT',
'ClientType' => 'WORKSTATION',
'ClientID' => (string)$wsCAuto,
'ConDefID' => $conSST,
'ConDefID' => null,
'ClientTestCode' => 'LFT',
'ClientTestName' => 'LFT',
'CreateDate' => $now
@ -348,7 +352,7 @@ class TestSeeder extends Seeder
'HostTestName' => 'RFT',
'ClientType' => 'WORKSTATION',
'ClientID' => (string)$wsCAuto,
'ConDefID' => $conSST,
'ConDefID' => null,
'ClientTestCode' => 'RFT',
'ClientTestName' => 'RFT',
'CreateDate' => $now
@ -369,6 +373,141 @@ class TestSeeder extends Seeder
'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,
'CreateDate' => $now
];
}
// 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
];
}
// 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

@ -0,0 +1,65 @@
<?php
namespace App\Models\Infrastructure;
use App\Models\BaseModel;
class EquipmentListModel extends BaseModel {
protected $table = 'equipmentlist';
protected $primaryKey = 'EID';
protected $allowedFields = [
'IEID',
'DepartmentID',
'InstrumentID',
'InstrumentName',
'WorkstationID',
'Enable',
'EquipmentRole',
'CreateDate',
'EndDate'
];
protected $useTimestamps = true;
protected $createdField = 'CreateDate';
protected $updatedField = '';
protected $useSoftDeletes = true;
protected $deletedField = 'EndDate';
public function getEquipmentLists($filter = []) {
$builder = $this->builder();
$builder->select('equipmentlist.*, department.DepartmentName, workstation.WorkstationName')
->join('department', 'department.DepartmentID = equipmentlist.DepartmentID', 'left')
->join('workstation', 'workstation.WorkstationID = equipmentlist.WorkstationID', 'left');
if (!empty($filter['IEID'])) {
$builder->like('equipmentlist.IEID', $filter['IEID'], 'both');
}
if (!empty($filter['InstrumentName'])) {
$builder->like('equipmentlist.InstrumentName', $filter['InstrumentName'], 'both');
}
if (!empty($filter['DepartmentID'])) {
$builder->where('equipmentlist.DepartmentID', $filter['DepartmentID']);
}
if (!empty($filter['WorkstationID'])) {
$builder->where('equipmentlist.WorkstationID', $filter['WorkstationID']);
}
if (isset($filter['Enable'])) {
$builder->where('equipmentlist.Enable', $filter['Enable']);
}
$rows = $builder->get()->getResultArray();
return $rows;
}
public function getEquipmentList($EID) {
$builder = $this->builder();
$row = $builder->select('equipmentlist.*, department.DepartmentName, workstation.WorkstationName')
->join('department', 'department.DepartmentID = equipmentlist.DepartmentID', 'left')
->join('workstation', 'workstation.WorkstationID = equipmentlist.WorkstationID', 'left')
->where('equipmentlist.EID', $EID)
->get()
->getRowArray();
return $row;
}
}

View File

@ -47,6 +47,8 @@ tags:
description: Value set definitions and items
- name: Demo
description: Demo/test endpoints (no authentication)
- name: EquipmentList
description: Laboratory equipment and instrument management
paths:
/api/auth/login:
post:
@ -594,6 +596,225 @@ paths:
responses:
'200':
description: Status logged
/api/equipmentlist:
get:
tags:
- EquipmentList
summary: List equipment
description: Get list of equipment with optional filters
security:
- bearerAuth: []
parameters:
- name: IEID
in: query
schema:
type: string
description: Filter by IEID
- name: InstrumentName
in: query
schema:
type: string
description: Filter by instrument name
- name: DepartmentID
in: query
schema:
type: integer
description: Filter by department ID
- name: WorkstationID
in: query
schema:
type: integer
description: Filter by workstation ID
- name: Enable
in: query
schema:
type: integer
enum:
- 0
- 1
description: Filter by enable status
responses:
'200':
description: List of equipment
content:
application/json:
schema:
type: object
properties:
status:
type: string
message:
type: string
data:
type: array
items:
$ref: '#/components/schemas/EquipmentList'
post:
tags:
- EquipmentList
summary: Create equipment
description: Create a new equipment entry
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- IEID
- DepartmentID
- Enable
- EquipmentRole
properties:
IEID:
type: string
maxLength: 50
DepartmentID:
type: integer
InstrumentID:
type: string
maxLength: 150
InstrumentName:
type: string
maxLength: 150
WorkstationID:
type: integer
Enable:
type: integer
enum:
- 0
- 1
EquipmentRole:
type: string
maxLength: 1
responses:
'201':
description: Equipment created
content:
application/json:
schema:
type: object
properties:
status:
type: string
message:
type: string
data:
type: integer
patch:
tags:
- EquipmentList
summary: Update equipment
description: Update an existing equipment entry
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- EID
properties:
EID:
type: integer
IEID:
type: string
maxLength: 50
DepartmentID:
type: integer
InstrumentID:
type: string
maxLength: 150
InstrumentName:
type: string
maxLength: 150
WorkstationID:
type: integer
Enable:
type: integer
enum:
- 0
- 1
EquipmentRole:
type: string
maxLength: 1
responses:
'200':
description: Equipment updated
content:
application/json:
schema:
type: object
properties:
status:
type: string
message:
type: string
data:
type: integer
delete:
tags:
- EquipmentList
summary: Delete equipment
description: Soft delete an equipment entry
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- EID
properties:
EID:
type: integer
responses:
'200':
description: Equipment deleted
content:
application/json:
schema:
type: object
properties:
status:
type: string
message:
type: string
/api/equipmentlist/{id}:
get:
tags:
- EquipmentList
summary: Get equipment by ID
description: Get a single equipment entry by its EID
security:
- bearerAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: integer
description: Equipment ID
responses:
'200':
description: Equipment details
content:
application/json:
schema:
type: object
properties:
status:
type: string
message:
type: string
data:
$ref: '#/components/schemas/EquipmentList'
/api/location:
get:
tags:
@ -5163,6 +5384,55 @@ components:
type: string
format: date-time
nullable: true
EquipmentList:
type: object
properties:
EID:
type: integer
description: Equipment ID (auto-increment)
IEID:
type: string
maxLength: 50
description: Internal Equipment ID
DepartmentID:
type: integer
description: Reference to department
InstrumentID:
type: string
maxLength: 150
description: Instrument identifier
InstrumentName:
type: string
maxLength: 150
description: Instrument display name
WorkstationID:
type: integer
description: Reference to workstation
Enable:
type: integer
enum:
- 0
- 1
description: Equipment status (0=disabled, 1=enabled)
EquipmentRole:
type: string
maxLength: 1
description: Equipment role code
CreateDate:
type: string
format: date-time
description: Creation timestamp
EndDate:
type: string
format: date-time
nullable: true
description: Deletion timestamp (soft delete)
DepartmentName:
type: string
description: Joined department name
WorkstationName:
type: string
description: Joined workstation name
Contact:
type: object
properties:

View File

@ -49,6 +49,8 @@ tags:
description: Value set definitions and items
- name: Demo
description: Demo/test endpoints (no authentication)
- name: EquipmentList
description: Laboratory equipment and instrument management
components:
securitySchemes:
@ -156,6 +158,10 @@ components:
Location:
$ref: './components/schemas/master-data.yaml#/Location'
# EquipmentList schemas
EquipmentList:
$ref: './components/schemas/equipmentlist.yaml#/EquipmentList'
# Paths are in separate files in the paths/ directory
# To view the complete API with all paths, use: api-docs.bundled.yaml
# To rebuild the bundle after changes: python bundle-api-docs.py

View File

@ -0,0 +1,47 @@
EquipmentList:
type: object
properties:
EID:
type: integer
description: Equipment ID (auto-increment)
IEID:
type: string
maxLength: 50
description: Internal Equipment ID
DepartmentID:
type: integer
description: Reference to department
InstrumentID:
type: string
maxLength: 150
description: Instrument identifier
InstrumentName:
type: string
maxLength: 150
description: Instrument display name
WorkstationID:
type: integer
description: Reference to workstation
Enable:
type: integer
enum: [0, 1]
description: Equipment status (0=disabled, 1=enabled)
EquipmentRole:
type: string
maxLength: 1
description: Equipment role code
CreateDate:
type: string
format: date-time
description: Creation timestamp
EndDate:
type: string
format: date-time
nullable: true
description: Deletion timestamp (soft delete)
DepartmentName:
type: string
description: Joined department name
WorkstationName:
type: string
description: Joined workstation name

View File

@ -0,0 +1,212 @@
/api/equipmentlist:
get:
tags: [EquipmentList]
summary: List equipment
description: Get list of equipment with optional filters
security:
- bearerAuth: []
parameters:
- name: IEID
in: query
schema:
type: string
description: Filter by IEID
- name: InstrumentName
in: query
schema:
type: string
description: Filter by instrument name
- name: DepartmentID
in: query
schema:
type: integer
description: Filter by department ID
- name: WorkstationID
in: query
schema:
type: integer
description: Filter by workstation ID
- name: Enable
in: query
schema:
type: integer
enum: [0, 1]
description: Filter by enable status
responses:
'200':
description: List of equipment
content:
application/json:
schema:
type: object
properties:
status:
type: string
message:
type: string
data:
type: array
items:
$ref: '../components/schemas/equipmentlist.yaml#/EquipmentList'
post:
tags: [EquipmentList]
summary: Create equipment
description: Create a new equipment entry
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- IEID
- DepartmentID
- Enable
- EquipmentRole
properties:
IEID:
type: string
maxLength: 50
DepartmentID:
type: integer
InstrumentID:
type: string
maxLength: 150
InstrumentName:
type: string
maxLength: 150
WorkstationID:
type: integer
Enable:
type: integer
enum: [0, 1]
EquipmentRole:
type: string
maxLength: 1
responses:
'201':
description: Equipment created
content:
application/json:
schema:
type: object
properties:
status:
type: string
message:
type: string
data:
type: integer
patch:
tags: [EquipmentList]
summary: Update equipment
description: Update an existing equipment entry
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- EID
properties:
EID:
type: integer
IEID:
type: string
maxLength: 50
DepartmentID:
type: integer
InstrumentID:
type: string
maxLength: 150
InstrumentName:
type: string
maxLength: 150
WorkstationID:
type: integer
Enable:
type: integer
enum: [0, 1]
EquipmentRole:
type: string
maxLength: 1
responses:
'200':
description: Equipment updated
content:
application/json:
schema:
type: object
properties:
status:
type: string
message:
type: string
data:
type: integer
delete:
tags: [EquipmentList]
summary: Delete equipment
description: Soft delete an equipment entry
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- EID
properties:
EID:
type: integer
responses:
'200':
description: Equipment deleted
content:
application/json:
schema:
type: object
properties:
status:
type: string
message:
type: string
/api/equipmentlist/{id}:
get:
tags: [EquipmentList]
summary: Get equipment by ID
description: Get a single equipment entry by its EID
security:
- bearerAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: integer
description: Equipment ID
responses:
'200':
description: Equipment details
content:
application/json:
schema:
type: object
properties:
status:
type: string
message:
type: string
data:
$ref: '../components/schemas/equipmentlist.yaml#/EquipmentList'