diff --git a/Test Management.docx b/Test Management.docx new file mode 100644 index 0000000..04bc352 Binary files /dev/null and b/Test Management.docx differ diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 0d32f37..e6ad184 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -150,7 +150,7 @@ $routes->group('api', function ($routes) { $routes->group('valueset', function ($routes) { $routes->get('/', 'ValueSet\ValueSet::index'); $routes->get('(:num)', 'ValueSet\ValueSet::show/$1'); - $routes->get('valuesetdef/(:segment)', 'ValueSet\ValueSet::showByValueSetDef/$1'); + $routes->get('valuesetdef/(:num)', 'ValueSet\ValueSet::showByValueSetDef/$1'); $routes->post('/', 'ValueSet\ValueSet::create'); $routes->patch('/', 'ValueSet\ValueSet::update'); $routes->delete('/', 'ValueSet\ValueSet::delete'); diff --git a/app/Controllers/Tests.php b/app/Controllers/Tests.php index 5569dfc..a8629b7 100644 --- a/app/Controllers/Tests.php +++ b/app/Controllers/Tests.php @@ -13,6 +13,7 @@ class Tests extends BaseController { protected $modelCal; protected $modelTech; protected $modelGrp; + protected $modelMap; protected $modelValueSet; public function __construct() { @@ -21,29 +22,138 @@ class Tests extends BaseController { $this->modelCal = new \App\Models\Test\TestDefCalModel; $this->modelTech = new \App\Models\Test\TestDefTechModel; $this->modelGrp = new \App\Models\Test\TestDefGrpModel; + $this->modelMap = new \App\Models\Test\TestMapModel; $this->modelValueSet = new \App\Models\ValueSet\ValueSetModel; - // Basic validation for the main part + // Validation rules for main test definition $this->rules = [ - 'TestSiteCode' => 'required', + 'TestSiteCode' => 'required|min_length[3]|max_length[6]', 'TestSiteName' => 'required', - 'TestType' => 'required' + 'TestType' => 'required', + 'SiteID' => 'required' ]; } + /** + * GET /api/tests + * List all tests with optional filtering + */ public function index() { - $rows = $this->model->getTests(); - if (empty($rows)) { return $this->respond([ 'status' => 'success', 'message' => "no Data.", 'data' => [] ], 200); } + $siteId = $this->request->getGet('SiteID'); + $testType = $this->request->getGet('TestType'); + $visibleScr = $this->request->getGet('VisibleScr'); + $visibleRpt = $this->request->getGet('VisibleRpt'); + $keyword = $this->request->getGet('TestSiteName'); + + $builder = $this->db->table('testdefsite') + ->select("testdefsite.TestSiteID, testdefsite.TestSiteCode, testdefsite.TestSiteName, testdefsite.TestType, + testdefsite.SeqScr, testdefsite.SeqRpt, testdefsite.VisibleScr, testdefsite.VisibleRpt, + testdefsite.CountStat, testdefsite.StartDate, testdefsite.EndDate, + valueset.VValue as TypeCode, valueset.VDesc as TypeName") + ->join("valueset", "valueset.VID=testdefsite.TestType", "left") + ->where('testdefsite.EndDate IS NULL'); + + if ($siteId) { + $builder->where('testdefsite.SiteID', $siteId); + } + + if ($testType) { + $builder->where('testdefsite.TestType', $testType); + } + + if ($visibleScr !== null) { + $builder->where('testdefsite.VisibleScr', $visibleScr); + } + + if ($visibleRpt !== null) { + $builder->where('testdefsite.VisibleRpt', $visibleRpt); + } + + if ($keyword) { + $builder->like('testdefsite.TestSiteName', $keyword); + } + + $rows = $builder->orderBy('testdefsite.SeqScr', 'ASC')->get()->getResultArray(); + + 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); } + /** + * GET /api/tests/{id} + * Get single test by ID with all related details + */ public function show($id = null) { if (!$id) return $this->failValidationErrors('ID is required'); - $row = $this->model->getTest($id); - if (empty($row)) { return $this->respond([ 'status' => 'success', 'message' => "no Data.", 'data' => null ], 200); } + + $row = $this->model->select("testdefsite.*, valueset.VValue as TypeCode, valueset.VDesc as TypeName") + ->join("valueset", "valueset.VID=testdefsite.TestType", "left") + ->where("testdefsite.TestSiteID", $id) + ->find($id); + + if (!$row) { + return $this->respond([ 'status' => 'success', 'message' => "No data.", 'data' => null ], 200); + } + + // Load related details based on TestType + $typeCode = $row['TypeCode'] ?? ''; + + if ($typeCode === 'CALC') { + // Load calculation details + $row['testdefcal'] = $this->db->table('testdefcal') + ->select('testdefcal.*, d.DisciplineName, dept.DepartmentName') + ->join('discipline d', 'd.DisciplineID=testdefcal.DisciplineID', 'left') + ->join('department dept', 'dept.DepartmentID=testdefcal.DepartmentID', 'left') + ->where('testdefcal.TestSiteID', $id) + ->where('testdefcal.EndDate IS NULL') + ->get()->getResultArray(); + + // Load test mappings + $row['testmap'] = $this->modelMap->where('TestSiteID', $id)->where('EndDate IS NULL')->findAll(); + + } elseif ($typeCode === 'GROUP') { + // Load group members with test details + $row['testdefgrp'] = $this->db->table('testdefgrp') + ->select('testdefgrp.*, t.TestSiteCode, t.TestSiteName, t.TestType, vs.VValue as MemberTypeCode') + ->join('testdefsite t', 't.TestSiteID=testdefgrp.Member', 'left') + ->join('valueset vs', 'vs.VID=t.TestType', 'left') + ->where('testdefgrp.TestSiteID', $id) + ->where('testdefgrp.EndDate IS NULL') + ->orderBy('testdefgrp.TestGrpID', 'ASC') + ->get()->getResultArray(); + + // Load test mappings + $row['testmap'] = $this->modelMap->where('TestSiteID', $id)->where('EndDate IS NULL')->findAll(); + + } elseif ($typeCode === 'TITLE') { + // Load test mappings only for TITLE type + $row['testmap'] = $this->modelMap->where('TestSiteID', $id)->where('EndDate IS NULL')->findAll(); + + } else { + // TEST or PARAM - load technical details + $row['testdeftech'] = $this->db->table('testdeftech') + ->select('testdeftech.*, d.DisciplineName, dept.DepartmentName, w.WorkstationName, e.EquipmentName') + ->join('discipline d', 'd.DisciplineID=testdeftech.DisciplineID', 'left') + ->join('department dept', 'dept.DepartmentID=testdeftech.DepartmentID', 'left') + ->join('workstation w', 'w.WorkstationID=testdeftech.WorkstationID', 'left') + ->join('equipment e', 'e.EquipmentID=testdeftech.EquipmentID', 'left') + ->where('testdeftech.TestSiteID', $id) + ->where('testdeftech.EndDate IS NULL') + ->get()->getResultArray(); + + // Load test mappings + $row['testmap'] = $this->modelMap->where('TestSiteID', $id)->where('EndDate IS NULL')->findAll(); + } + return $this->respond([ 'status' => 'success', 'message'=> "Data fetched successfully", 'data' => $row ], 200); } + /** + * POST /api/tests + * Create new test definition + */ public function create() { $input = $this->request->getJSON(true); @@ -55,7 +165,23 @@ class Tests extends BaseController { try { // 1. Insert into Main Table (testdefsite) - $id = $this->model->insert($input); + $testSiteData = [ + 'SiteID' => $input['SiteID'], + 'TestSiteCode' => $input['TestSiteCode'], + 'TestSiteName' => $input['TestSiteName'], + 'TestType' => $input['TestType'], + 'Description' => $input['Description'] ?? null, + 'SeqScr' => $input['SeqScr'] ?? 0, + 'SeqRpt' => $input['SeqRpt'] ?? 0, + 'IndentLeft' => $input['IndentLeft'] ?? 0, + 'FontStyle' => $input['FontStyle'] ?? null, + 'VisibleScr' => $input['VisibleScr'] ?? 1, + 'VisibleRpt' => $input['VisibleRpt'] ?? 1, + 'CountStat' => $input['CountStat'] ?? 1, + 'StartDate' => $input['StartDate'] ?? date('Y-m-d H:i:s') + ]; + + $id = $this->model->insert($testSiteData); if (!$id) { throw new \Exception("Failed to insert main test definition"); } @@ -69,28 +195,52 @@ class Tests extends BaseController { return $this->failServerError('Transaction failed'); } - return $this->respondCreated([ 'status' => 'success', 'message' => "data created successfully", 'data'=> $id ]); + return $this->respondCreated([ + 'status' => 'success', + 'message' => "Test created successfully", + 'data' => ['TestSiteID' => $id] + ]); } catch (\Exception $e) { $this->db->transRollback(); return $this->failServerError('Something went wrong: ' . $e->getMessage()); } } + /** + * PUT/PATCH /api/tests/{id} + * Update existing test definition + */ public function update($id = null) { $input = $this->request->getJSON(true); // Determine ID if (!$id && isset($input["TestSiteID"])) { $id = $input["TestSiteID"]; } if (!$id) { return $this->failValidationErrors('TestSiteID is required.'); } - - // Optional validation - // if (!$this->validateData($input, $this->rules)) { ... } + + // Verify record exists + $existing = $this->model->find($id); + if (!$existing) { + return $this->failNotFound('Test not found'); + } $this->db->transStart(); try { // 1. Update Main Table - $this->model->update($id, $input); + $testSiteData = []; + $allowedUpdateFields = ['TestSiteCode', 'TestSiteName', 'TestType', 'Description', + 'SeqScr', 'SeqRpt', 'IndentLeft', 'FontStyle', + 'VisibleScr', 'VisibleRpt', 'CountStat', 'StartDate']; + + foreach ($allowedUpdateFields as $field) { + if (isset($input[$field])) { + $testSiteData[$field] = $input[$field]; + } + } + + if (!empty($testSiteData)) { + $this->model->update($id, $testSiteData); + } // 2. Handle Details $this->handleDetails($id, $input, 'update'); @@ -101,7 +251,83 @@ class Tests extends BaseController { return $this->failServerError('Transaction failed'); } - return $this->respondCreated([ 'status' => 'success', 'message' => "data updated successfully", 'data'=> $id ]); + return $this->respond([ + 'status' => 'success', + 'message' => "Test updated successfully", + 'data' => ['TestSiteID' => $id] + ]); + } catch (\Exception $e) { + $this->db->transRollback(); + return $this->failServerError('Something went wrong: ' . $e->getMessage()); + } + } + + /** + * DELETE /api/tests/{id} + * Soft delete test by setting EndDate + */ + public function delete($id = null) { + $input = $this->request->getJSON(true); + + // Determine ID + if (!$id && isset($input["TestSiteID"])) { $id = $input["TestSiteID"]; } + if (!$id) { return $this->failValidationErrors('TestSiteID is required.'); } + + // Verify record exists + $existing = $this->model->find($id); + if (!$existing) { + return $this->failNotFound('Test not found'); + } + + // Check if already disabled + if (!empty($existing['EndDate'])) { + return $this->failValidationErrors('Test is already disabled'); + } + + $this->db->transStart(); + + try { + $now = date('Y-m-d H:i:s'); + + // 1. Soft delete main record + $this->model->update($id, ['EndDate' => $now]); + + // 2. Get TestType to handle related records + $testType = $existing['TestType']; + $vs = $this->modelValueSet->find($testType); + $typeCode = $vs['VValue'] ?? ''; + + // 3. Soft delete related records based on TestType + if ($typeCode === 'CALC') { + $this->db->table('testdefcal') + ->where('TestSiteID', $id) + ->update(['EndDate' => $now]); + } elseif ($typeCode === 'GROUP') { + $this->db->table('testdefgrp') + ->where('TestSiteID', $id) + ->update(['EndDate' => $now]); + } elseif (in_array($typeCode, ['TEST', 'PARAM'])) { + $this->db->table('testdeftech') + ->where('TestSiteID', $id) + ->update(['EndDate' => $now]); + } + + // 4. Soft delete test mappings + $this->db->table('testmap') + ->where('TestSiteID', $id) + ->update(['EndDate' => $now]); + + $this->db->transComplete(); + + if ($this->db->transStatus() === false) { + return $this->failServerError('Transaction failed'); + } + + return $this->respond([ + 'status' => 'success', + 'message' => "Test disabled successfully", + 'data' => ['TestSiteID' => $id, 'EndDate' => $now] + ]); } catch (\Exception $e) { $this->db->transRollback(); return $this->failServerError('Something went wrong: ' . $e->getMessage()); @@ -120,67 +346,182 @@ class Tests extends BaseController { $testTypeID = $existing['TestType'] ?? null; } - if (!$testTypeID) return; // Should not happen if required + if (!$testTypeID) return; // Get Type Code (TEST, PARAM, CALC, GROUP, TITLE) $vs = $this->modelValueSet->find($testTypeID); $typeCode = $vs['VValue'] ?? ''; - // Get details data if present (for 'details' key in unified JSON) - // We accept both flat (top-level) and nested 'details' for flexibility, prefer 'details' + // Get details data from input $details = $input['details'] ?? $input; - $details['TestSiteID'] = $testSiteID; // Ensure foreign key is set + $details['TestSiteID'] = $testSiteID; $details['SiteID'] = $input['SiteID'] ?? 1; switch ($typeCode) { case 'CALC': - $this->saveSubTable($this->modelCal, $testSiteID, $details, $action, 'TestCalID'); + $this->saveCalcDetails($testSiteID, $details, $action); break; case 'GROUP': - // Groups are special: List of members - // Payload expected: details: { members: [{Member: 1}, {Member: 2}] } - if ($action === 'update') { - $this->modelGrp->where('TestSiteID', $testSiteID)->delete(); - } - - $members = $details['members'] ?? ($input['Members'] ?? []); - if (is_array($members)) { - foreach ($members as $m) { - $memberID = is_array($m) ? ($m['Member'] ?? null) : $m; - if ($memberID) { - $this->modelGrp->insert([ - 'SiteID' => $details['SiteID'], - 'TestSiteID' => $testSiteID, - 'Member' => $memberID - ]); - } - } - } + $this->saveGroupDetails($testSiteID, $details, $input, $action); break; - + case 'TITLE': + // TITLE type only has testdefsite, no additional details needed + // But we should save test mappings if provided + if (isset($input['testmap']) && is_array($input['testmap'])) { + $this->saveTestMap($testSiteID, $input['testmap'], $action); + } + break; case 'TEST': case 'PARAM': default: - // Default to TestDefTech for 'TEST' and 'PARAM' - $this->saveSubTable($this->modelTech, $testSiteID, $details, $action, 'TestTechID'); + $this->saveTechDetails($testSiteID, $details, $action, $typeCode); break; } + + // Save test mappings for TEST and CALC types as well + if (in_array($typeCode, ['TEST', 'CALC']) && isset($input['testmap']) && is_array($input['testmap'])) { + $this->saveTestMap($testSiteID, $input['testmap'], $action); + } } - private function saveSubTable($model, $testSiteID, $data, $action, $pkName) { + /** + * Save technical details for TEST and PARAM types + */ + private function saveTechDetails($testSiteID, $data, $action, $typeCode) { + $techData = [ + 'SiteID' => $data['SiteID'], + 'TestSiteID' => $testSiteID, + 'DisciplineID' => $data['DisciplineID'] ?? null, + 'DepartmentID' => $data['DepartmentID'] ?? null, + 'WorkstationID' => $data['WorkstationID'] ?? null, + 'EquipmentID' => $data['EquipmentID'] ?? null, + 'ResultType' => $data['ResultType'] ?? ($typeCode === 'PARAM' ? 'Numeric' : null), + 'RefType' => $data['RefType'] ?? null, + 'VSet' => $data['VSet'] ?? null, + 'SpcType' => $data['SpcType'] ?? null, + 'SpcDesc' => $data['SpcDesc'] ?? null, + 'ReqQty' => $data['ReqQty'] ?? null, + 'ReqQtyUnit' => $data['ReqQtyUnit'] ?? null, + 'Unit1' => $data['Unit1'] ?? null, + 'Factor' => $data['Factor'] ?? null, + 'Unit2' => $data['Unit2'] ?? null, + 'Decimal' => $data['Decimal'] ?? 2, + 'CollReq' => $data['CollReq'] ?? null, + 'Method' => $data['Method'] ?? null, + 'ExpectedTAT' => $data['ExpectedTAT'] ?? null + ]; + if ($action === 'update') { - // Check existence - $exists = $model->where('TestSiteID', $testSiteID)->first(); + $exists = $this->db->table('testdeftech') + ->where('TestSiteID', $testSiteID) + ->where('EndDate IS NULL') + ->get()->getRowArray(); + if ($exists) { - $model->update($exists[$pkName], $data); + $this->modelTech->update($exists['TestTechID'], $techData); } else { - $model->insert($data); + $this->modelTech->insert($techData); } } else { - $model->insert($data); + $this->modelTech->insert($techData); } } -} \ No newline at end of file + + /** + * Save calculation details for CALC type + */ + private function saveCalcDetails($testSiteID, $data, $action) { + $calcData = [ + 'SiteID' => $data['SiteID'], + 'TestSiteID' => $testSiteID, + 'DisciplineID' => $data['DisciplineID'] ?? null, + 'DepartmentID' => $data['DepartmentID'] ?? null, + 'FormulaInput' => $data['FormulaInput'] ?? null, + 'FormulaCode' => $data['FormulaCode'] ?? $data['Formula'] ?? null, + 'RefType' => $data['RefType'] ?? 'NMRC', + 'Unit1' => $data['Unit1'] ?? $data['ResultUnit'] ?? null, + 'Factor' => $data['Factor'] ?? null, + 'Unit2' => $data['Unit2'] ?? null, + 'Decimal' => $data['Decimal'] ?? 2, + 'Method' => $data['Method'] ?? null + ]; + + if ($action === 'update') { + $exists = $this->db->table('testdefcal') + ->where('TestSiteID', $testSiteID) + ->where('EndDate IS NULL') + ->get()->getRowArray(); + + if ($exists) { + $this->modelCal->update($exists['TestCalID'], $calcData); + } else { + $this->modelCal->insert($calcData); + } + } else { + $this->modelCal->insert($calcData); + } + } + + /** + * Save group details for GROUP type + */ + private function saveGroupDetails($testSiteID, $data, $input, $action) { + if ($action === 'update') { + // Soft delete existing members + $this->db->table('testdefgrp') + ->where('TestSiteID', $testSiteID) + ->update(['EndDate' => date('Y-m-d H:i:s')]); + } + + // Get members from details or input + $members = $data['members'] ?? ($input['Members'] ?? []); + + if (is_array($members)) { + foreach ($members as $m) { + $memberID = is_array($m) ? ($m['Member'] ?? ($m['TestSiteID'] ?? null)) : $m; + if ($memberID) { + $this->modelGrp->insert([ + 'SiteID' => $data['SiteID'], + 'TestSiteID' => $testSiteID, + 'Member' => $memberID + ]); + } + } + } + } + + /** + * Save test mappings + */ + private function saveTestMap($testSiteID, $mappings, $action) { + if ($action === 'update') { + // Soft delete existing mappings + $this->db->table('testmap') + ->where('TestSiteID', $testSiteID) + ->update(['EndDate' => date('Y-m-d H:i:s')]); + } + + if (is_array($mappings)) { + foreach ($mappings as $map) { + $mapData = [ + 'TestSiteID' => $testSiteID, + 'HostType' => $map['HostType'] ?? null, + 'HostID' => $map['HostID'] ?? null, + 'HostDataSource' => $map['HostDataSource'] ?? null, + 'HostTestCode' => $map['HostTestCode'] ?? null, + 'HostTestName' => $map['HostTestName'] ?? null, + 'ClientType' => $map['ClientType'] ?? null, + 'ClientID' => $map['ClientID'] ?? null, + 'ClientDataSource' => $map['ClientDataSource'] ?? null, + 'ConDefID' => $map['ConDefID'] ?? null, + 'ClientTestCode' => $map['ClientTestCode'] ?? null, + 'ClientTestName' => $map['ClientTestName'] ?? null + ]; + $this->modelMap->insert($mapData); + } + } + } +} diff --git a/app/Database/Migrations/2025-10-11-100001_Test.php b/app/Database/Migrations/2025-10-11-100001_Test.php index 00c978b..ad5656a 100644 --- a/app/Database/Migrations/2025-10-11-100001_Test.php +++ b/app/Database/Migrations/2025-10-11-100001_Test.php @@ -6,95 +6,114 @@ use CodeIgniter\Database\Migration; class CreateTestsTable extends Migration { public function up() { + // testdefsite - Main test definition table $this->forge->addField([ 'TestSiteID' => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true], 'SiteID' => ['type' => 'INT', 'null' => false], 'TestSiteCode' => ['type' => 'varchar', 'constraint'=> 6, 'null' => false], - 'TestSiteName' => ['type' => 'varchar', 'constraint'=> 50, 'null' => false], + 'TestSiteName' => ['type' => 'varchar', 'constraint'=> 100, 'null' => false], 'TestType' => ['type' => 'int', 'null' => false], - 'Description' => ['type' => 'varchar', 'constraint'=> 150, 'null' => true], + 'Description' => ['type' => 'varchar', 'constraint'=> 255, 'null' => true], 'SeqScr' => ['type' => 'int', 'null' => true], 'SeqRpt' => ['type' => 'int', 'null' => true], - 'IndentLeft' => ['type' => 'int', 'null' => true], - 'VisibleScr' => ['type' => 'int', 'null' => true], - 'VisibleRpt' => ['type' => 'int', 'null' => true], - 'CountStat' => ['type' => 'int', 'null' => true], + 'IndentLeft' => ['type' => 'int', 'null' => true, 'default' => 0], + 'FontStyle' => ['type' => 'varchar', 'constraint'=> 50, 'null' => true], + 'VisibleScr' => ['type' => 'int', 'null' => true, 'default' => 1], + 'VisibleRpt' => ['type' => 'int', 'null' => true, 'default' => 1], + 'CountStat' => ['type' => 'int', 'null' => true, 'default' => 1], 'CreateDate' => ['type' => 'Datetime', 'null' => true], - 'StartDate' => ['type' => 'Datetime', 'null' => true], + 'StartDate' => ['type' => 'Datetime', 'null' => true], 'EndDate' => ['type' => 'Datetime', 'null' => true], ]); $this->forge->addKey('TestSiteID', true); $this->forge->createTable('testdefsite'); + // testdeftech - Technical definition for TEST and PARAM types $this->forge->addField([ 'TestTechID' => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true], + 'SiteID' => ['type' => 'INT', 'null' => false], 'TestSiteID' => ['type' => 'INT', 'null' => false], 'DisciplineID' => ['type' => 'int', 'null' => true], 'DepartmentID' => ['type' => 'int', 'null' => true], - 'ResultType' => ['type' => 'int', 'null' => true], - 'RefType' => ['type' => 'int', 'null' => true], + 'WorkstationID' => ['type' => 'INT', 'null' => true], + 'EquipmentID' => ['type' => 'INT', 'null' => true], + 'ResultType' => ['type' => 'varchar', 'constraint'=> 20, 'null' => true], + 'RefType' => ['type' => 'varchar', 'constraint'=> 10, 'null' => true], 'VSet' => ['type' => 'int', 'null' => true], - 'SpcType' => ['type' => 'int', 'null' => true], - 'ReqQty' => ['type' => 'int', 'null' => true], - 'ReqQtyUnit' => ['type' => 'varchar', 'constraint'=>20, 'null' => true], - 'Unit1' => ['type' => 'varchar', 'constraint'=>20, 'null' => true], - 'Factor' => ['type' => 'int', 'null' => true], - 'Unit2' => ['type' => 'varchar', 'constraint'=>20, 'null' => true], - 'Decimal' => ['type' => 'int', 'null' => true], - 'CollReq' => ['type' => 'varchar', 'constraint'=>50, 'null' => true], - 'Method' => ['type' => 'varchar', 'constraint'=>50, 'null' => true], + 'SpcType' => ['type' => 'varchar', 'constraint'=> 10, 'null' => true], + 'SpcDesc' => ['type' => 'varchar', 'constraint'=> 100, 'null' => true], + 'ReqQty' => ['type' => 'DECIMAL', 'constraint'=> '10,2', 'null' => true], + 'ReqQtyUnit' => ['type' => 'varchar', 'constraint'=> 20, 'null' => true], + 'Unit1' => ['type' => 'varchar', 'constraint'=> 20, 'null' => true], + 'Factor' => ['type' => 'DECIMAL', 'constraint'=> '10,4', 'null' => true], + 'Unit2' => ['type' => 'varchar', 'constraint'=> 20, 'null' => true], + 'Decimal' => ['type' => 'int', 'null' => true, 'default' => 2], + 'CollReq' => ['type' => 'varchar', 'constraint'=> 255, 'null' => true], + 'Method' => ['type' => 'varchar', 'constraint'=> 50, 'null' => true], 'ExpectedTAT' => ['type' => 'INT', 'null' => true], 'CreateDate' => ['type' => 'Datetime', 'null' => true], 'EndDate' => ['type' => 'Datetime', 'null' => true] ]); $this->forge->addKey('TestTechID', true); + $this->forge->addForeignKey('TestSiteID', 'testdefsite', 'TestSiteID', 'CASCADE', 'CASCADE'); $this->forge->createTable('testdeftech'); + // testdefcal - Calculation definition for CALC type $this->forge->addField([ 'TestCalID' => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true], + 'SiteID' => ['type' => 'INT', 'null' => false], 'TestSiteID' => ['type' => 'INT', 'null' => false], 'DisciplineID' => ['type' => 'INT', 'null' => true], 'DepartmentID' => ['type' => 'INT', 'null' => true], - 'FormulaInput' => ['type' => 'varchar', 'constraint'=>20, 'null' => true], - 'FormulaCode' => ['type' => 'varchar', 'constraint'=>150, 'null' => true], - 'Unit1' => ['type' => 'varchar', 'constraint'=>20, 'null' => true], - 'Factor' => ['type' => 'int', 'null' => true], - 'Unit2' => ['type' => 'varchar', 'constraint'=>20, 'null' => true], - 'Decimal' => ['type' => 'int', 'null' => true], + 'FormulaInput' => ['type' => 'varchar', 'constraint'=> 255, 'null' => true], + 'FormulaCode' => ['type' => 'text', 'null' => true], + 'RefType' => ['type' => 'varchar', 'constraint'=> 10, 'null' => true, 'default' => 'NMRC'], + 'Unit1' => ['type' => 'varchar', 'constraint'=> 20, 'null' => true], + 'Factor' => ['type' => 'DECIMAL', 'constraint'=> '10,4', 'null' => true], + 'Unit2' => ['type' => 'varchar', 'constraint'=> 20, 'null' => true], + 'Decimal' => ['type' => 'int', 'null' => true, 'default' => 2], + 'Method' => ['type' => 'varchar', 'constraint'=> 50, 'null' => true], 'CreateDate' => ['type' => 'Datetime', 'null' => true], 'EndDate' => ['type' => 'Datetime', 'null' => true] ]); $this->forge->addKey('TestCalID', true); + $this->forge->addForeignKey('TestSiteID', 'testdefsite', 'TestSiteID', 'CASCADE', 'CASCADE'); $this->forge->createTable('testdefcal'); + // testdefgrp - Group definition for GROUP type $this->forge->addField([ 'TestGrpID' => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true], + 'SiteID' => ['type' => 'INT', 'null' => false], 'TestSiteID' => ['type' => 'INT', 'null' => false], 'Member' => ['type' => 'INT', 'null' => true], 'CreateDate' => ['type' => 'Datetime', 'null' => true], 'EndDate' => ['type' => 'Datetime', 'null' => true] ]); $this->forge->addKey('TestGrpID', true); + $this->forge->addForeignKey('TestSiteID', 'testdefsite', 'TestSiteID', 'CASCADE', 'CASCADE'); + $this->forge->addForeignKey('Member', 'testdefsite', 'TestSiteID', 'CASCADE', 'CASCADE'); $this->forge->createTable('testdefgrp'); + // testmap - Test mapping for all types $this->forge->addField([ 'TestMapID' => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true], 'TestSiteID' => ['type' => 'INT', 'null' => false], - 'HostType' => ['type' => 'int', 'null' => true], - 'HostID' => ['type' => 'int', 'null' => true], - 'HostDataSource' => ['type' => 'varchar', 'constraint'=>50, 'null' => true], - 'HostTestCode' => ['type' => 'varchar', 'constraint'=>10, 'null' => true], - 'HostTestName' => ['type' => 'varchar', 'constraint'=>50, 'null' => true], - 'ClientType' => ['type' => 'int', 'null' => true], - 'ClientID' => ['type' => 'int', 'null' => true], - 'ClientDataSource' => ['type' => 'varchar', 'constraint'=>50, 'null' => true], - 'ConDefID' => ['type' => 'int', 'null' => true], - 'ClientTestCode' => ['type' => 'varchar', 'constraint'=>10, 'null' => true], - 'ClientTestName' => ['type' => 'varchar', 'constraint'=>50, 'null' => true], + 'HostType' => ['type' => 'varchar', 'constraint'=> 20, 'null' => true], + 'HostID' => ['type' => 'varchar', 'constraint'=> 50, 'null' => true], + 'HostDataSource' => ['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], + 'ClientDataSource' => ['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'); } @@ -105,4 +124,4 @@ class CreateTestsTable extends Migration { $this->forge->dropTable('testdefgrp'); $this->forge->dropTable('testmap'); } -} \ No newline at end of file +} diff --git a/app/Models/Test/TestDefCalModel.php b/app/Models/Test/TestDefCalModel.php index 192b2dc..219c3ab 100644 --- a/app/Models/Test/TestDefCalModel.php +++ b/app/Models/Test/TestDefCalModel.php @@ -7,8 +7,22 @@ use App\Models\BaseModel; class TestDefCalModel extends BaseModel { protected $table = 'testdefcal'; protected $primaryKey = 'TestCalID'; - protected $allowedFields = ['SiteID', 'TestSiteID', 'DisciplineID', 'DepartmentID','FormulaCode', 'FormulaInput', - 'Unit1', 'Factor', 'Unit2', 'Decimal' ,'CreateDate', 'EndDate']; + protected $allowedFields = [ + 'SiteID', + 'TestSiteID', + 'DisciplineID', + 'DepartmentID', + 'FormulaInput', + 'FormulaCode', + 'RefType', + 'Unit1', + 'Factor', + 'Unit2', + 'Decimal', + 'Method', + 'CreateDate', + 'EndDate' + ]; protected $useTimestamps = true; protected $createdField = 'CreateDate'; diff --git a/app/Models/Test/TestDefSiteModel.php b/app/Models/Test/TestDefSiteModel.php index 4608f52..52986d8 100644 --- a/app/Models/Test/TestDefSiteModel.php +++ b/app/Models/Test/TestDefSiteModel.php @@ -7,23 +7,70 @@ use App\Models\BaseModel; class TestDefSiteModel extends BaseModel { protected $table = 'testdefsite'; protected $primaryKey = 'TestSiteID'; - protected $allowedFields = ['SiteID', 'TestSiteCode', 'TestSiteName', 'TestType', 'Description', 'SeqScr', 'SeqRpt', 'IndentLeft', - 'VisibleScr', 'VisibleRpt', 'CountStat', 'CreateDate', 'EndDate']; - + protected $allowedFields = [ + 'SiteID', + 'TestSiteCode', + 'TestSiteName', + 'TestType', + 'Description', + 'SeqScr', + 'SeqRpt', + 'IndentLeft', + 'FontStyle', + 'VisibleScr', + 'VisibleRpt', + 'CountStat', + 'CreateDate', + 'StartDate', + 'EndDate' + ]; + protected $useTimestamps = true; protected $createdField = 'CreateDate'; - protected $updatedField = ''; + protected $updatedField = 'StartDate'; protected $useSoftDeletes = true; protected $deletedField = "EndDate"; - public function getTests() { - $rows = $this->select("TestSiteID, TestSiteCode, TestSiteName, TestType, valueset.VValue as TypeCode, valueset.VDesc as TypeName ") + /** + * Get all tests with type information + */ + public function getTests($siteId = null, $testType = null, $visibleScr = null, $visibleRpt = null, $keyword = null) { + $builder = $this->select("testdefsite.TestSiteID, testdefsite.TestSiteCode, testdefsite.TestSiteName, testdefsite.TestType, + testdefsite.SeqScr, testdefsite.SeqRpt, testdefsite.VisibleScr, testdefsite.VisibleRpt, + testdefsite.CountStat, testdefsite.StartDate, testdefsite.EndDate, + valueset.VValue as TypeCode, valueset.VDesc as TypeName") ->join("valueset", "valueset.VID=testdefsite.TestType", "left") - ->findAll(); - return $rows; + ->where('testdefsite.EndDate IS NULL'); + + if ($siteId) { + $builder->where('testdefsite.SiteID', $siteId); + } + + if ($testType) { + $builder->where('testdefsite.TestType', $testType); + } + + if ($visibleScr !== null) { + $builder->where('testdefsite.VisibleScr', $visibleScr); + } + + if ($visibleRpt !== null) { + $builder->where('testdefsite.VisibleRpt', $visibleRpt); + } + + if ($keyword) { + $builder->like('testdefsite.TestSiteName', $keyword); + } + + return $builder->orderBy('testdefsite.SeqScr', 'ASC')->findAll(); } + /** + * Get single test with all related details based on TestType + */ public function getTest($TestSiteID) { + $db = \Config\Database::connect(); + $row = $this->select("testdefsite.*, valueset.VValue as TypeCode, valueset.VDesc as TypeName") ->join("valueset", "valueset.VID=testdefsite.TestType", "left") ->where("testdefsite.TestSiteID", $TestSiteID) @@ -31,14 +78,58 @@ class TestDefSiteModel extends BaseModel { if (!$row) return null; - if ($row['TypeCode'] == 'Calculated') { - $row['testdefcal'] = $this->db->query("select * from testdefcal where TestSiteID='$TestSiteID'")->getResultArray(); - } elseif ($row['TypeCode'] == 'GROUP') { - $row['testdefgrp'] = $this->db->query("select testdefgrp.*, t.TestSiteCode, t.TestSiteName from testdefgrp left join testdefsite t on t.TestSiteID=testdefgrp.Member where testdefgrp.TestSiteID='$TestSiteID'")->getResultArray(); - } else { - $row['testdeftech'] = $this->db->query("select * from testdeftech where TestSiteID='$TestSiteID'")->getResultArray(); + $typeCode = $row['TypeCode'] ?? ''; + + // Load related details based on TestType + if ($typeCode === 'CALC') { + // Load calculation details with joined discipline and department + $row['testdefcal'] = $db->table('testdefcal') + ->select('testdefcal.*, d.DisciplineName, dept.DepartmentName') + ->join('discipline d', 'd.DisciplineID=testdefcal.DisciplineID', 'left') + ->join('department dept', 'dept.DepartmentID=testdefcal.DepartmentID', 'left') + ->where('testdefcal.TestSiteID', $TestSiteID) + ->where('testdefcal.EndDate IS NULL') + ->get()->getResultArray(); + + // Load test mappings + $testMapModel = new \App\Models\Test\TestMapModel(); + $row['testmap'] = $testMapModel->where('TestSiteID', $TestSiteID)->where('EndDate IS NULL')->findAll(); + + } elseif ($typeCode === 'GROUP') { + // Load group members with test details + $row['testdefgrp'] = $db->table('testdefgrp') + ->select('testdefgrp.*, t.TestSiteCode, t.TestSiteName, t.TestType, vs.VValue as MemberTypeCode') + ->join('testdefsite t', 't.TestSiteID=testdefgrp.Member', 'left') + ->join('valueset vs', 'vs.VID=t.TestType', 'left') + ->where('testdefgrp.TestSiteID', $TestSiteID) + ->where('testdefgrp.EndDate IS NULL') + ->orderBy('testdefgrp.TestGrpID', 'ASC') + ->get()->getResultArray(); + + // Load test mappings + $testMapModel = new \App\Models\Test\TestMapModel(); + $row['testmap'] = $testMapModel->where('TestSiteID', $TestSiteID)->where('EndDate IS NULL')->findAll(); + + } elseif ($typeCode === 'TITLE') { + // Load test mappings only for TITLE type + $testMapModel = new \App\Models\Test\TestMapModel(); + $row['testmap'] = $testMapModel->where('TestSiteID', $TestSiteID)->where('EndDate IS NULL')->findAll(); + + } elseif (in_array($typeCode, ['TEST', 'PARAM'])) { + // TEST or PARAM - load technical details with joined tables + $row['testdeftech'] = $db->table('testdeftech') + ->select('testdeftech.*, d.DisciplineName, dept.DepartmentName') + ->join('discipline d', 'd.DisciplineID=testdeftech.DisciplineID', 'left') + ->join('department dept', 'dept.DepartmentID=testdeftech.DepartmentID', 'left') + ->where('testdeftech.TestSiteID', $TestSiteID) + ->where('testdeftech.EndDate IS NULL') + ->get()->getResultArray(); + + // Load test mappings + $testMapModel = new \App\Models\Test\TestMapModel(); + $row['testmap'] = $testMapModel->where('TestSiteID', $TestSiteID)->where('EndDate IS NULL')->findAll(); } - + return $row; } -} \ No newline at end of file +} diff --git a/app/Models/Test/TestDefTechModel.php b/app/Models/Test/TestDefTechModel.php index 68da643..876d0f8 100644 --- a/app/Models/Test/TestDefTechModel.php +++ b/app/Models/Test/TestDefTechModel.php @@ -7,8 +7,30 @@ use App\Models\BaseModel; class TestDefTechModel extends BaseModel { protected $table = 'testdeftech'; protected $primaryKey = 'TestTechID'; - protected $allowedFields = ['SiteID', 'TestSiteID', 'DisciplineID', 'DepartmentID', 'WorkstationID', 'EquipmentID', 'VSet', 'SpcType', - 'ReqQty', 'ReqQtyUnit', 'Unit1', 'Factor', 'Unit2', 'Decimal', 'CollReq', 'Method', 'ExpectedTAT', 'CreateDate', 'EndDate']; + protected $allowedFields = [ + 'SiteID', + 'TestSiteID', + 'DisciplineID', + 'DepartmentID', + 'WorkstationID', + 'EquipmentID', + 'ResultType', + 'RefType', + 'VSet', + 'SpcType', + 'SpcDesc', + 'ReqQty', + 'ReqQtyUnit', + 'Unit1', + 'Factor', + 'Unit2', + 'Decimal', + 'CollReq', + 'Method', + 'ExpectedTAT', + 'CreateDate', + 'EndDate' + ]; protected $useTimestamps = true; protected $createdField = 'CreateDate'; diff --git a/app/Models/Test/TestMapModel.php b/app/Models/Test/TestMapModel.php index 47afcec..9a02c1e 100644 --- a/app/Models/Test/TestMapModel.php +++ b/app/Models/Test/TestMapModel.php @@ -7,8 +7,22 @@ use App\Models\BaseModel; class TestMapModel extends BaseModel { protected $table = 'testmap'; protected $primaryKey = 'TestMapID'; - protected $allowedFields = ['HostType', 'HostID', 'HostDataSource', 'HostTestCode', 'HostTestName', - 'ClientType', 'ClientID', 'ClientTestCode', 'ClientTestName', 'CreateDate', 'EndDate' ]; + protected $allowedFields = [ + 'TestSiteID', + 'HostType', + 'HostID', + 'HostDataSource', + 'HostTestCode', + 'HostTestName', + 'ClientType', + 'ClientID', + 'ClientDataSource', + 'ConDefID', + 'ClientTestCode', + 'ClientTestName', + 'CreateDate', + 'EndDate' + ]; protected $useTimestamps = true; protected $createdField = 'CreateDate'; diff --git a/app/Views/v2/layout/main_layout.php b/app/Views/v2/layout/main_layout.php index 31a6615..de86511 100644 --- a/app/Views/v2/layout/main_layout.php +++ b/app/Views/v2/layout/main_layout.php @@ -20,6 +20,7 @@ + diff --git a/app/Views/v2/master/tests/calc_dialog.php b/app/Views/v2/master/tests/calc_dialog.php new file mode 100644 index 0000000..5c5d6e5 --- /dev/null +++ b/app/Views/v2/master/tests/calc_dialog.php @@ -0,0 +1,249 @@ + + diff --git a/app/Views/v2/master/tests/group_dialog.php b/app/Views/v2/master/tests/group_dialog.php new file mode 100644 index 0000000..f657ba1 --- /dev/null +++ b/app/Views/v2/master/tests/group_dialog.php @@ -0,0 +1,241 @@ + + diff --git a/app/Views/v2/master/tests/param_dialog.php b/app/Views/v2/master/tests/param_dialog.php new file mode 100644 index 0000000..d047548 --- /dev/null +++ b/app/Views/v2/master/tests/param_dialog.php @@ -0,0 +1,223 @@ + + diff --git a/app/Views/v2/master/lab_tests/test_dialog.php b/app/Views/v2/master/tests/test_dialog.php similarity index 99% rename from app/Views/v2/master/lab_tests/test_dialog.php rename to app/Views/v2/master/tests/test_dialog.php index de91604..107c2c5 100644 --- a/app/Views/v2/master/lab_tests/test_dialog.php +++ b/app/Views/v2/master/tests/test_dialog.php @@ -1,6 +1,6 @@