diff --git a/app/Config/Routes.php b/app/Config/Routes.php index df5c88c..dd391df 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -19,12 +19,13 @@ $routes->group('api', ['filter' => 'auth'], function ($routes) { $routes->get('audit-logs', 'Audit\AuditLogController::index'); // Results CRUD - $routes->group('result', function ($routes) { - $routes->get('/', 'ResultController::index'); - $routes->get('(:num)', 'ResultController::show/$1'); + $routes->group('result', function ($routes) { + $routes->get('/', 'ResultController::index'); + $routes->post('/', 'ResultController::create'); + $routes->get('(:num)', 'ResultController::show/$1'); $routes->patch('(:any)', 'ResultController::update/$1'); - $routes->delete('(:num)', 'ResultController::delete/$1'); - }); + $routes->delete('(:num)', 'ResultController::delete/$1'); + }); // Reports $routes->get('report/(:num)', 'ReportController::view/$1'); diff --git a/app/Controllers/Contact/ContactController.php b/app/Controllers/Contact/ContactController.php index 090f2fe..02e1c8e 100644 --- a/app/Controllers/Contact/ContactController.php +++ b/app/Controllers/Contact/ContactController.php @@ -1,13 +1,15 @@ request->getJSON(true); - if (!$ContactID || !ctype_digit((string) $ContactID)) { + $input = $this->requirePatchPayload($this->request->getJSON(true)); + if ($input === null) { + return; + } + + $id = $this->requirePatchId($ContactID, 'ContactID'); + if ($id === null) { + return; + } + + $existing = $this->model->find($id); + if (!$existing) { return $this->respond([ 'status' => 'failed', - 'message' => 'ContactID is required and must be a valid integer', + 'message' => 'Contact not found', 'data' => [] - ], 400); - } - if (empty($input) || !is_array($input)) { - return $this->failValidationErrors('No data provided for update.'); + ], 404); } $validationInput = array_intersect_key($input, $this->patchRules); @@ -95,13 +104,13 @@ class ContactController extends BaseController { return $this->failValidationErrors($this->validator->getErrors()); } - $input['ContactID'] = (int) $ContactID; + $input['ContactID'] = $id; try { $this->model->saveContact($input); $id = $input['ContactID']; - return $this->respondCreated([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 201); - } catch (\Throwable $e) { - return $this->failServerError('Something went wrong: ' . $e->getMessage()); - } - } -} + return $this->respond([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 200); + } catch (\Throwable $e) { + return $this->failServerError('Something went wrong: ' . $e->getMessage()); + } + } +} diff --git a/app/Controllers/LocationController.php b/app/Controllers/LocationController.php index c43f753..11970aa 100644 --- a/app/Controllers/LocationController.php +++ b/app/Controllers/LocationController.php @@ -1,12 +1,14 @@ 'required', ]; $this->patchRules = [ + 'SiteID' => 'permit_empty|is_natural_no_zero', 'LocCode' => 'permit_empty|max_length[6]', + 'Parent' => 'permit_empty|is_natural', 'LocFull' => 'permit_empty', + 'Description' => 'permit_empty|max_length[255]', + 'LocType' => 'permit_empty', + 'Street1' => 'permit_empty|max_length[255]', + 'Street2' => 'permit_empty|max_length[255]', + 'City' => 'permit_empty|max_length[255]', + 'Province' => 'permit_empty|max_length[255]', + 'PostCode' => 'permit_empty|max_length[20]', + 'GeoLocationSystem' => 'permit_empty|max_length[255]', + 'GeoLocationData' => 'permit_empty|max_length[255]', + 'Phone' => 'permit_empty|max_length[20]', + 'Email' => 'permit_empty|valid_email|max_length[255]', ]; } @@ -56,30 +71,45 @@ class LocationController extends BaseController { } public function update($LocationID = null) { - $input = $this->request->getJSON(true); - if (!$LocationID || !ctype_digit((string) $LocationID)) { + $input = $this->requirePatchPayload($this->request->getJSON(true)); + if ($input === null) { + return; + } + + $id = $this->requirePatchId($LocationID, 'LocationID'); + if ($id === null) { + return; + } + + $existing = $this->model->find($id); + if (!$existing) { return $this->respond([ 'status' => 'failed', - 'message' => 'LocationID is required and must be a valid integer', + 'message' => 'Location not found', 'data' => [] - ], 400); - } - if (empty($input) || !is_array($input)) { - return $this->failValidationErrors('No data provided for update.'); + ], 404); } $validationInput = array_intersect_key($input, $this->patchRules); - if (!empty($validationInput) && !$this->validateData($validationInput, $this->patchRules)) { + if ($validationInput === []) { + return $this->respond([ + 'status' => 'failed', + 'message' => 'No valid fields provided for update.', + 'data' => [] + ], 422); + } + + if (!$this->validateData($validationInput, $this->patchRules)) { return $this->failValidationErrors($this->validator->getErrors()); } - $input['LocationID'] = (int) $LocationID; + $input['LocationID'] = $id; try { - $result = $this->model->saveLocation($input, true); - return $this->respondCreated([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $result ], 201); + $result = $this->model->saveLocation($input); + return $this->respond([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $result ], 200); } catch (\Throwable $e) { - return $this->failServerError('Something went wrong: ' . $e->getMessage()); - } + return $this->failServerError('Something went wrong: ' . $e->getMessage()); + } } public function delete() { diff --git a/app/Controllers/Organization/AccountController.php b/app/Controllers/Organization/AccountController.php index 1247220..0b03672 100644 --- a/app/Controllers/Organization/AccountController.php +++ b/app/Controllers/Organization/AccountController.php @@ -1,13 +1,15 @@ request->getJSON(true); - if (!$AccountID || !ctype_digit((string) $AccountID)) { - return $this->failValidationErrors('ID is required.'); + $input = $this->requirePatchPayload($this->request->getJSON(true)); + if ($input === null) { + return; } - $input['AccountID'] = (int) $AccountID; + + $id = $this->requirePatchId($AccountID, 'AccountID'); + if ($id === null) { + return; + } + + $existing = $this->model->find($id); + if (!$existing) { + return $this->respond([ 'status' => 'failed', 'message' => 'Account not found', 'data' => [] ], 404); + } + + $input['AccountID'] = $id; try { - $id = $input['AccountID']; - if (!$id) { return $this->failValidationErrors('ID is required.'); } - $this->model->update($id, $input); - return $this->respondCreated([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 201); - } catch (\Throwable $e) { - return $this->failServerError('Something went wrong: ' . $e->getMessage()); - } - } -} + $this->model->update($id, $input); + return $this->respond([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 200); + } catch (\Throwable $e) { + return $this->failServerError('Something went wrong: ' . $e->getMessage()); + } + } +} diff --git a/app/Controllers/Organization/CodingSysController.php b/app/Controllers/Organization/CodingSysController.php index a0abe02..c5ee62e 100644 --- a/app/Controllers/Organization/CodingSysController.php +++ b/app/Controllers/Organization/CodingSysController.php @@ -2,12 +2,14 @@ namespace App\Controllers\Organization; +use App\Traits\PatchValidationTrait; use App\Traits\ResponseTrait; use App\Controllers\BaseController; use App\Models\Organization\CodingSysModel; class CodingSysController extends BaseController { use ResponseTrait; + use PatchValidationTrait; protected $db; protected $model; @@ -79,22 +81,30 @@ class CodingSysController extends BaseController { } public function update($CodingSysID = null) { - $input = $this->request->getJSON(true); + $input = $this->requirePatchPayload($this->request->getJSON(true)); + if ($input === null) { + return; + } + + $id = $this->requirePatchId($CodingSysID, 'CodingSysID'); + if ($id === null) { + return; + } + + $existing = $this->model->find($id); + if (!$existing) { + return $this->respond(['status' => 'failed', 'message' => 'CodingSys not found', 'data' => []], 404); + } + + if (isset($input['CodingSysID']) && (string) $input['CodingSysID'] !== (string) $id) { + return $this->failValidationErrors('CodingSysID in URL does not match body.'); + } + + $input['CodingSysID'] = $id; try { - if (!$CodingSysID || !ctype_digit((string) $CodingSysID)) { - return $this->failValidationErrors('CodingSysID is required.'); - } - - if (isset($input['CodingSysID']) && (string) $input['CodingSysID'] !== (string) $CodingSysID) { - return $this->failValidationErrors('CodingSysID in URL does not match body.'); - } - - $id = (int) $CodingSysID; - $input['CodingSysID'] = $id; - $this->model->update($id, $input); - return $this->respondCreated(['status' => 'success', 'message' => 'data updated successfully', 'data' => $id], 201); + return $this->respond(['status' => 'success', 'message' => 'data updated successfully', 'data' => $id], 200); } catch (\Throwable $e) { return $this->failServerError('Something went wrong: ' . $e->getMessage()); } diff --git a/app/Controllers/Organization/DepartmentController.php b/app/Controllers/Organization/DepartmentController.php index 6c33301..12911b8 100644 --- a/app/Controllers/Organization/DepartmentController.php +++ b/app/Controllers/Organization/DepartmentController.php @@ -1,13 +1,15 @@ request->getJSON(true); + $input = $this->requirePatchPayload($this->request->getJSON(true)); + if ($input === null) { + return; + } + + $id = $this->requirePatchId($DepartmentID, 'DepartmentID'); + if ($id === null) { + return; + } + + $existing = $this->model->find($id); + if (!$existing) { + return $this->respond([ 'status' => 'failed', 'message' => 'Department not found', 'data' => [] ], 404); + } + + $input['DepartmentID'] = $id; try { - if (!$DepartmentID || !ctype_digit((string) $DepartmentID)) { - return $this->failValidationErrors('ID is required.'); - } - $input['DepartmentID'] = (int) $DepartmentID; - $id = $input['DepartmentID']; $this->model->update($id, $input); - return $this->respondCreated([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 201); - } catch (\Throwable $e) { - return $this->failServerError('Something went wrong: ' . $e->getMessage()); - } + return $this->respond([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 200); + } catch (\Throwable $e) { + return $this->failServerError('Something went wrong: ' . $e->getMessage()); + } } } diff --git a/app/Controllers/Organization/DisciplineController.php b/app/Controllers/Organization/DisciplineController.php index 241729c..2798b17 100644 --- a/app/Controllers/Organization/DisciplineController.php +++ b/app/Controllers/Organization/DisciplineController.php @@ -1,13 +1,15 @@ request->getJSON(true); - if (!$DisciplineID || !ctype_digit((string) $DisciplineID)) { return $this->failValidationErrors('ID is required.'); } - $input['DisciplineID'] = (int) $DisciplineID; - $id = $input['DisciplineID']; + $input = $this->requirePatchPayload($this->request->getJSON(true)); + if ($input === null) { + return; + } + + $id = $this->requirePatchId($DisciplineID, 'DisciplineID'); + if ($id === null) { + return; + } + + $existing = $this->model->find($id); + if (!$existing) { + return $this->respond([ 'status' => 'failed', 'message' => 'Discipline not found', 'data' => [] ], 404); + } + + $input['DisciplineID'] = $id; $this->model->update($id, $input); - return $this->respondCreated([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 201); + return $this->respond([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 200); /* try { $id = $input['DisciplineID']; diff --git a/app/Controllers/Organization/HostAppController.php b/app/Controllers/Organization/HostAppController.php index 9848715..d467edd 100644 --- a/app/Controllers/Organization/HostAppController.php +++ b/app/Controllers/Organization/HostAppController.php @@ -2,12 +2,14 @@ namespace App\Controllers\Organization; +use App\Traits\PatchValidationTrait; use App\Traits\ResponseTrait; use App\Controllers\BaseController; use App\Models\Organization\HostAppModel; class HostAppController extends BaseController { use ResponseTrait; + use PatchValidationTrait; protected $db; protected $model; @@ -83,21 +85,29 @@ class HostAppController extends BaseController { } public function update($HostAppID = null) { - $input = $this->request->getJSON(true); + $input = $this->requirePatchPayload($this->request->getJSON(true)); + if ($input === null) { + return; + } + $id = $this->requirePatchId($HostAppID, 'HostAppID'); + if ($id === null) { + return; + } + + $existing = $this->model->find($id); + if (!$existing) { + return $this->respond(['status' => 'failed', 'message' => 'HostApp not found', 'data' => []], 404); + } + + if (isset($input['HostAppID']) && (string) $input['HostAppID'] !== (string) $id) { + return $this->failValidationErrors('HostAppID in URL does not match body.'); + } + + $input['HostAppID'] = $id; try { - if ($HostAppID === null || $HostAppID === '') { - return $this->failValidationErrors('HostAppID is required.'); - } - - if (isset($input['HostAppID']) && (string) $input['HostAppID'] !== (string) $HostAppID) { - return $this->failValidationErrors('HostAppID in URL does not match body.'); - } - - $id = $HostAppID; - $input['HostAppID'] = $id; $this->model->update($id, $input); - return $this->respondCreated(['status' => 'success', 'message' => 'data updated successfully', 'data' => $id], 201); + return $this->respond(['status' => 'success', 'message' => 'data updated successfully', 'data' => $id], 200); } catch (\Throwable $e) { return $this->failServerError('Something went wrong: ' . $e->getMessage()); } diff --git a/app/Controllers/Organization/HostComParaController.php b/app/Controllers/Organization/HostComParaController.php index d2e695e..c7a72c6 100644 --- a/app/Controllers/Organization/HostComParaController.php +++ b/app/Controllers/Organization/HostComParaController.php @@ -2,12 +2,14 @@ namespace App\Controllers\Organization; +use App\Traits\PatchValidationTrait; use App\Traits\ResponseTrait; use App\Controllers\BaseController; use App\Models\Organization\HostComParaModel; class HostComParaController extends BaseController { use ResponseTrait; + use PatchValidationTrait; protected $db; protected $model; @@ -83,22 +85,30 @@ class HostComParaController extends BaseController { } public function update($HostAppID = null) { - $input = $this->request->getJSON(true); + $input = $this->requirePatchPayload($this->request->getJSON(true)); + if ($input === null) { + return; + } + + $id = $this->requirePatchId($HostAppID, 'HostAppID'); + if ($id === null) { + return; + } + + $existing = $this->model->find($id); + if (!$existing) { + return $this->respond(['status' => 'failed', 'message' => 'HostComPara not found', 'data' => []], 404); + } + + if (isset($input['HostAppID']) && (string) $input['HostAppID'] !== (string) $id) { + return $this->failValidationErrors('HostAppID in URL does not match body.'); + } + + $input['HostAppID'] = $id; try { - if ($HostAppID === null || $HostAppID === '') { - return $this->failValidationErrors('HostAppID is required.'); - } - - if (isset($input['HostAppID']) && (string) $input['HostAppID'] !== (string) $HostAppID) { - return $this->failValidationErrors('HostAppID in URL does not match body.'); - } - - $id = $HostAppID; - $input['HostAppID'] = $id; - $this->model->update($id, $input); - return $this->respondCreated(['status' => 'success', 'message' => 'data updated successfully', 'data' => $id], 201); + return $this->respond(['status' => 'success', 'message' => 'data updated successfully', 'data' => $id], 200); } catch (\Throwable $e) { return $this->failServerError('Something went wrong: ' . $e->getMessage()); } diff --git a/app/Controllers/Organization/SiteController.php b/app/Controllers/Organization/SiteController.php index 2d884d8..99c86fe 100644 --- a/app/Controllers/Organization/SiteController.php +++ b/app/Controllers/Organization/SiteController.php @@ -1,13 +1,15 @@ request->getJSON(true); + $input = $this->requirePatchPayload($this->request->getJSON(true)); + if ($input === null) { + return; + } - if (!$SiteID || !ctype_digit((string) $SiteID)) { return $this->failValidationErrors('ID is required.'); } - $input['SiteID'] = (int) $SiteID; - - $id = $input['SiteID']; - + $id = $this->requirePatchId($SiteID, 'SiteID'); + if ($id === null) { + return; + } + + $existing = $this->model->find($id); + if (!$existing) { + return $this->respond([ 'status' => 'failed', 'message' => 'Site not found', 'data' => [] ], 404); + } + + $input['SiteID'] = $id; + if (!empty($input['SiteCode'])) { $validation = service('validation'); $validation->setRules([ @@ -94,11 +106,11 @@ class SiteController extends BaseController { } } - try { - $this->model->update($id, $input); - return $this->respondCreated([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 201); - } catch (\Throwable $e) { - return $this->failServerError('Something went wrong: ' . $e->getMessage()); - } - } -} + try { + $this->model->update($id, $input); + return $this->respond([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 200); + } catch (\Throwable $e) { + return $this->failServerError('Something went wrong: ' . $e->getMessage()); + } + } +} diff --git a/app/Controllers/Organization/WorkstationController.php b/app/Controllers/Organization/WorkstationController.php index 175c012..2d13890 100644 --- a/app/Controllers/Organization/WorkstationController.php +++ b/app/Controllers/Organization/WorkstationController.php @@ -1,13 +1,15 @@ request->getJSON(true); + $input = $this->requirePatchPayload($this->request->getJSON(true)); + if ($input === null) { + return; + } + + $id = $this->requirePatchId($WorkstationID, 'WorkstationID'); + if ($id === null) { + return; + } + + $existing = $this->model->find($id); + if (!$existing) { + return $this->respond([ 'status' => 'failed', 'message' => 'Workstation not found', 'data' => [] ], 404); + } + + $input['WorkstationID'] = $id; try { - if (!$WorkstationID || !ctype_digit((string) $WorkstationID)) { - return $this->failValidationErrors('ID is required.'); - } - $input['WorkstationID'] = (int) $WorkstationID; - $id = $input['WorkstationID']; $this->model->update($id, $input); - return $this->respondCreated([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 201); - } catch (\Throwable $e) { - return $this->failServerError('Something went wrong: ' . $e->getMessage()); - } - } -} + return $this->respond([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 200); + } catch (\Throwable $e) { + return $this->failServerError('Something went wrong: ' . $e->getMessage()); + } + } +} diff --git a/app/Controllers/PatVisitController.php b/app/Controllers/PatVisitController.php index 7764747..ce1ba0b 100644 --- a/app/Controllers/PatVisitController.php +++ b/app/Controllers/PatVisitController.php @@ -1,14 +1,16 @@ request->getJSON(true); - if (!$InternalPVID || !is_numeric($InternalPVID)) { - return $this->respond(['status' => 'error', 'message' => 'Invalid or missing ID'], 400); + $input = $this->requirePatchPayload($this->request->getJSON(true)); + if ($input === null) { + return; } - $input['InternalPVID'] = $InternalPVID; + + $id = $this->requirePatchId($InternalPVID, 'InternalPVID'); + if ($id === null) { + return; + } + + $visit = $this->model->find($id); + if (!$visit) { + return $this->respond(['status' => 'failed', 'message' => 'Visit not found', 'data' => []], 404); + } + $input['InternalPVID'] = $id; try { - // Check if visit exists - $visit = $this->model->find($input["InternalPVID"]); - if (!$visit) { - return $this->respond(['status' => 'error', 'message' => 'Visit not found'], 404); - } - - $data = $this->model->updatePatVisit($input); - return $this->respond(['status' => 'success', 'message' => 'Data updated successfully', 'data' => $data], 200); - } catch (\Exception $e) { - return $this->failServerError('Something went wrong: ' . $e->getMessage()); - } - } + $data = $this->model->updatePatVisit($input); + return $this->respond(['status' => 'success', 'message' => 'Data updated successfully', 'data' => $data], 200); + } catch (\Exception $e) { + return $this->failServerError('Something went wrong: ' . $e->getMessage()); + } + } public function create() { $input = $this->request->getJSON(true); @@ -175,17 +181,34 @@ class PatVisitController extends BaseController { } public function updateADT($PVADTID = null) { - $input = $this->request->getJSON(true); - if (!$PVADTID || !is_numeric($PVADTID)) { return $this->respond(['status' => 'error', 'message' => 'Invalid or missing ID'], 400); } - $input['PVADTID'] = $PVADTID; + $input = $this->requirePatchPayload($this->request->getJSON(true)); + if ($input === null) { + return; + } + + $id = $this->requirePatchId($PVADTID, 'PVADTID'); + if ($id === null) { + return; + } + $modelPVA = new PatVisitADTModel(); + $adt = $modelPVA->find($id); + if (!$adt) { + return $this->respond(['status' => 'failed', 'message' => 'ADT record not found', 'data' => []], 404); + } + + if (!isset($input['InternalPVID'])) { + $input['InternalPVID'] = $adt['InternalPVID']; + } + + $input['PVADTID'] = $id; try { - $data = $modelPVA->update($input['PVADTID'], $input); - return $this->respond(['status' => 'success', 'message' => 'Data updated successfully', 'data' => $data], 200); - } catch (\Exception $e) { - return $this->failServerError('Something went wrong: ' . $e->getMessage()); - } - } + $data = $modelPVA->update($id, $input); + return $this->respond(['status' => 'success', 'message' => 'Data updated successfully', 'data' => $data], 200); + } catch (\Exception $e) { + return $this->failServerError('Something went wrong: ' . $e->getMessage()); + } + } public function getADTByVisit($InternalPVID = null) { try { diff --git a/app/Controllers/ResultController.php b/app/Controllers/ResultController.php index c9c6429..b81accc 100644 --- a/app/Controllers/ResultController.php +++ b/app/Controllers/ResultController.php @@ -2,12 +2,14 @@ namespace App\Controllers; -use App\Traits\ResponseTrait; +use App\Traits\PatchValidationTrait; +use App\Traits\ResponseTrait; use CodeIgniter\Controller; use App\Models\PatResultModel; -class ResultController extends Controller { - use ResponseTrait; +class ResultController extends Controller { + use ResponseTrait; + use PatchValidationTrait; protected $model; @@ -24,24 +26,28 @@ class ResultController extends Controller { $orderID = $this->request->getGet('order_id'); $patientID = $this->request->getGet('patient_id'); - if ($orderID) { - $results = $this->model->getByOrder((int)$orderID); - } elseif ($patientID) { - $results = $this->model->getByPatient((int)$patientID); - } else { - // Get all results with pagination - $page = (int)($this->request->getGet('page') ?? 1); - $perPage = (int)($this->request->getGet('per_page') ?? 20); - - $results = $this->model - ->where('DelDate', null) - ->orderBy('ResultID', 'DESC') - ->paginate($perPage, 'default', $page); - } - - return $this->respond([ - 'status' => 'success', - 'message' => 'Results retrieved successfully', + if ($orderID) { + $results = $this->model->getByOrder((int)$orderID); + } elseif ($patientID) { + $results = $this->model->getByPatient((int)$patientID); + } else { + // Get all results with pagination + $page = (int)($this->request->getGet('page') ?? 1); + $perPage = (int)($this->request->getGet('per_page') ?? 20); + + $results = $this->model + ->where('DelDate', null) + ->orderBy('ResultID', 'DESC') + ->paginate($perPage, 'default', $page); + } + + $results = is_array($results) + ? array_map([$this, 'hydrateResultPayload'], $results) + : $results; + + return $this->respond([ + 'status' => 'success', + 'message' => 'Results retrieved successfully', 'data' => $results ], 200); @@ -59,51 +65,110 @@ class ResultController extends Controller { * Get single result * GET /api/result/{id} */ - public function show($id) { - try { - $result = $this->model->getWithRelations((int)$id); - - if (!$result) { - return $this->respond([ - 'status' => 'failed', - 'message' => 'Result not found', - 'data' => [] - ], 404); - } - - return $this->respond([ - 'status' => 'success', - 'message' => 'Result retrieved successfully', - 'data' => $result - ], 200); - - } catch (\Exception $e) { - log_message('error', 'ResultController::show error: ' . $e->getMessage()); - return $this->respond([ - 'status' => 'failed', - 'message' => 'Failed to retrieve result', - 'data' => [] - ], 500); - } - } + public function show($id) { + try { + $result = $this->model->getWithRelations((int)$id); + + if (!$result) { + return $this->respond([ + 'status' => 'failed', + 'message' => 'Result not found', + 'data' => [] + ], 404); + } + + $result = $this->hydrateResultPayload($result); + + return $this->respond([ + 'status' => 'success', + 'message' => 'Result retrieved successfully', + 'data' => $result + ], 200); + + } catch (\Exception $e) { + log_message('error', 'ResultController::show error: ' . $e->getMessage()); + return $this->respond([ + 'status' => 'failed', + 'message' => 'Failed to retrieve result', + 'data' => [] + ], 500); + } + } + + /** + * Create a new result entry + * POST /api/result + */ + public function create() { + $payload = $this->request->getJSON(true); + if (!is_array($payload) || empty($payload)) { + return $this->respond([ + 'status' => 'failed', + 'message' => 'No data provided', + 'data' => [] + ], 400); + } + + if (isset($payload['ResultValue'])) { + $payload['Result'] = $payload['ResultValue']; + } + + try { + $resultId = $this->model->insert($payload, true); + + if (!$resultId) { + return $this->respond([ + 'status' => 'failed', + 'message' => 'Failed to create result', + 'data' => [] + ], 500); + } + + return $this->respondCreated([ + 'status' => 'success', + 'message' => 'Result created successfully', + 'data' => [ + 'ResultID' => $resultId, + 'ResultValue' => $payload['ResultValue'] ?? ($payload['Result'] ?? null), + 'ResultCode' => $payload['ResultCode'] ?? null, + ] + ], 201); + } catch (\Exception $e) { + log_message('error', 'ResultController::create error: ' . $e->getMessage()); + return $this->respond([ + 'status' => 'failed', + 'message' => 'Failed to create result', + 'data' => [] + ], 500); + } + } /** * Update result with validation * PATCH /api/result/{id} */ - public function update($id) { - try { - $data = $this->request->getJSON(true); - - if (empty($data)) { - return $this->respond([ - 'status' => 'failed', - 'message' => 'No data provided', - 'data' => [] - ], 400); - } - - $result = $this->model->updateWithValidation((int)$id, $data); + public function update($id) { + try { + $data = $this->requirePatchPayload($this->request->getJSON(true)); + if ($data === null) { + return; + } + + $validatedId = $this->requirePatchId($id, 'ResultID'); + if ($validatedId === null) { + return; + } + + $existing = $this->model->find($validatedId); + if (!$existing) { + return $this->respond([ + 'status' => 'failed', + 'message' => 'Result not found', + 'data' => [] + ], 404); + } + + $result = $this->model->updateWithValidation($validatedId, $data); if (!$result['success']) { return $this->respond([ @@ -114,16 +179,16 @@ class ResultController extends Controller { } // Get updated result with relations - $updatedResult = $this->model->getWithRelations((int)$id); - - return $this->respond([ - 'status' => 'success', - 'message' => $result['message'], - 'data' => [ - 'result' => $updatedResult, - 'flag' => $result['flag'] - ] - ], 200); + $updatedResult = $this->model->getWithRelations($validatedId); + + return $this->respond([ + 'status' => 'success', + 'message' => $result['message'], + 'data' => [ + 'result' => $updatedResult ? $this->hydrateResultPayload($updatedResult) : [], + 'flag' => $result['flag'] + ] + ], 200); } catch (\Exception $e) { log_message('error', 'ResultController::update error: ' . $e->getMessage()); @@ -139,7 +204,7 @@ class ResultController extends Controller { * Soft delete result * DELETE /api/result/{id} */ - public function delete($id) { + public function delete($id) { try { $result = $this->model->find((int)$id); @@ -174,6 +239,13 @@ class ResultController extends Controller { 'message' => 'Failed to delete result', 'data' => [] ], 500); - } - } -} + } + } + + private function hydrateResultPayload(array $payload): array { + if (!array_key_exists('ResultValue', $payload) && array_key_exists('Result', $payload)) { + $payload['ResultValue'] = $payload['Result']; + } + return $payload; + } +} diff --git a/app/Controllers/Rule/RuleController.php b/app/Controllers/Rule/RuleController.php index be0a00d..14de3c6 100644 --- a/app/Controllers/Rule/RuleController.php +++ b/app/Controllers/Rule/RuleController.php @@ -6,11 +6,13 @@ use App\Controllers\BaseController; use App\Models\Rule\RuleDefModel; use App\Models\Test\TestDefSiteModel; use App\Services\RuleExpressionService; +use App\Traits\PatchValidationTrait; use App\Traits\ResponseTrait; -class RuleController extends BaseController -{ +class RuleController extends BaseController +{ use ResponseTrait; + use PatchValidationTrait; protected RuleDefModel $ruleDefModel; @@ -98,7 +100,10 @@ class RuleController extends BaseController public function create() { - $input = $this->request->getJSON(true) ?? []; + $input = $this->requirePatchPayload($this->request->getJSON(true) ?? []); + if ($input === null) { + return; + } $validation = service('validation'); $validation->setRules([ diff --git a/app/Controllers/Specimen/ContainerDefController.php b/app/Controllers/Specimen/ContainerDefController.php index 5531dd4..64e3962 100644 --- a/app/Controllers/Specimen/ContainerDefController.php +++ b/app/Controllers/Specimen/ContainerDefController.php @@ -2,13 +2,15 @@ namespace App\Controllers\Specimen; -use App\Traits\ResponseTrait; -use App\Controllers\BaseController; -use App\Libraries\ValueSet; -use App\Models\Specimen\ContainerDefModel; +use App\Traits\PatchValidationTrait; +use App\Traits\ResponseTrait; +use App\Controllers\BaseController; +use App\Libraries\ValueSet; +use App\Models\Specimen\ContainerDefModel; class ContainerDefController extends BaseController { - use ResponseTrait; + use ResponseTrait; + use PatchValidationTrait; protected $db; protected $model; @@ -79,13 +81,19 @@ class ContainerDefController extends BaseController { } public function update($ConDefID = null) { - $input = $this->request->getJSON(true); - if (!$ConDefID || !ctype_digit((string) $ConDefID)) { - return $this->failValidationErrors('ConDefID is required.'); + $input = $this->requirePatchPayload($this->request->getJSON(true)); + if ($input === null) { + return; } - if (empty($input) || !is_array($input)) { - return $this->failValidationErrors('No data provided for update.'); + $id = $this->requirePatchId($ConDefID, 'ConDefID'); + if ($id === null) { + return; + } + + $existing = $this->model->find($id); + if (!$existing) { + return $this->respond([ 'status' => 'failed', 'message' => 'Container definition not found', 'data' => [] ], 404); } $validationInput = array_intersect_key($input, $this->patchRules); @@ -93,13 +101,13 @@ class ContainerDefController extends BaseController { return $this->failValidationErrors($this->validator->getErrors()); } - $input['ConDefID'] = (int) $ConDefID; + $input['ConDefID'] = $id; try { - $ConDefID = $this->model->update($input['ConDefID'], $input); - return $this->respondCreated([ 'status' => 'success', 'message' => "data $ConDefID updated successfully" ]); - } catch (\Exception $e) { - return $this->failServerError('Something went wrong: ' . $e->getMessage()); - } - } + $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()); + } + } } diff --git a/app/Controllers/Specimen/SpecimenCollectionController.php b/app/Controllers/Specimen/SpecimenCollectionController.php index 917257c..6e97c5c 100644 --- a/app/Controllers/Specimen/SpecimenCollectionController.php +++ b/app/Controllers/Specimen/SpecimenCollectionController.php @@ -2,13 +2,15 @@ namespace App\Controllers\Specimen; -use App\Traits\ResponseTrait; -use App\Controllers\BaseController; -use App\Libraries\ValueSet; -use App\Models\Specimen\SpecimenCollectionModel; +use App\Traits\PatchValidationTrait; +use App\Traits\ResponseTrait; +use App\Controllers\BaseController; +use App\Libraries\ValueSet; +use App\Models\Specimen\SpecimenCollectionModel; -class SpecimenCollectionController extends BaseController { - use ResponseTrait; +class SpecimenCollectionController extends BaseController { + use ResponseTrait; + use PatchValidationTrait; protected $db; protected $model; @@ -67,18 +69,29 @@ class SpecimenCollectionController extends BaseController { } public function update($SpcColID = null) { - $input = $this->request->getJSON(true); - if (!$SpcColID || !ctype_digit((string) $SpcColID)) { - return $this->failValidationErrors('SpcColID is required.'); + $input = $this->requirePatchPayload($this->request->getJSON(true)); + if ($input === null) { + return; } - $input['SpcColID'] = (int) $SpcColID; + + $id = $this->requirePatchId($SpcColID, 'SpcColID'); + if ($id === null) { + return; + } + + $existing = $this->model->where('SpcColID', $id)->first(); + if (!$existing) { + return $this->respond([ 'status' => 'failed', 'message' => 'Specimen collection not found', 'data' => [] ], 404); + } + + $input['SpcColID'] = $id; if (!$this->validateData($input, $this->rules)) { return $this->failValidationErrors($this->validator->getErrors()); } try { - $id = $this->model->update($input['SpcColID'], $input); - return $this->respondCreated([ 'status' => 'success', 'message' => "data $id updated successfully" ]); - } catch (\Exception $e) { - return $this->failServerError('Something went wrong: ' . $e->getMessage()); - } - } + $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()); + } + } } diff --git a/app/Controllers/Specimen/SpecimenController.php b/app/Controllers/Specimen/SpecimenController.php index 7b3e1ca..36b0d63 100644 --- a/app/Controllers/Specimen/SpecimenController.php +++ b/app/Controllers/Specimen/SpecimenController.php @@ -2,13 +2,15 @@ namespace App\Controllers\Specimen; -use App\Traits\ResponseTrait; -use App\Controllers\BaseController; -use App\Libraries\ValueSet; -use App\Models\Specimen\SpecimenModel; +use App\Traits\PatchValidationTrait; +use App\Traits\ResponseTrait; +use App\Controllers\BaseController; +use App\Libraries\ValueSet; +use App\Models\Specimen\SpecimenModel; -class SpecimenController extends BaseController { - use ResponseTrait; +class SpecimenController extends BaseController { + use ResponseTrait; + use PatchValidationTrait; protected $db; protected $model; @@ -67,19 +69,30 @@ class SpecimenController extends BaseController { } public function update($SID = null) { - $input = $this->request->getJSON(true); - if (!$SID || !ctype_digit((string) $SID)) { - return $this->failValidationErrors('SID is required.'); + $input = $this->requirePatchPayload($this->request->getJSON(true)); + if ($input === null) { + return; } - $input['SID'] = (int) $SID; + + $id = $this->requirePatchId($SID, 'SID'); + if ($id === null) { + return; + } + + $existing = $this->model->where('SID', $id)->first(); + if (!$existing) { + return $this->respond([ 'status' => 'failed', 'message' => 'Specimen not found', 'data' => [] ], 404); + } + + $input['SID'] = $id; if (!$this->validateData($input, $this->rules)) { return $this->failValidationErrors($this->validator->getErrors()); } try { - $id = $this->model->update($input['SID'], $input); - return $this->respondCreated([ 'status' => 'success', 'message' => "data $id updated successfully" ]); - } catch (\Exception $e) { - return $this->failServerError('Something went wrong: ' . $e->getMessage()); - } - } + $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()); + } + } /** * Delete a specimen (soft delete) diff --git a/app/Controllers/Specimen/SpecimenPrepController.php b/app/Controllers/Specimen/SpecimenPrepController.php index 2798cf3..6151eb3 100644 --- a/app/Controllers/Specimen/SpecimenPrepController.php +++ b/app/Controllers/Specimen/SpecimenPrepController.php @@ -2,12 +2,14 @@ namespace App\Controllers\Specimen; -use App\Traits\ResponseTrait; -use App\Controllers\BaseController; -use App\Models\Specimen\SpecimenPrepModel; +use App\Traits\PatchValidationTrait; +use App\Traits\ResponseTrait; +use App\Controllers\BaseController; +use App\Models\Specimen\SpecimenPrepModel; -class SpecimenPrepController extends BaseController { - use ResponseTrait; +class SpecimenPrepController extends BaseController { + use ResponseTrait; + use PatchValidationTrait; protected $db; protected $model; @@ -52,18 +54,29 @@ class SpecimenPrepController extends BaseController { } public function update($SpcPrpID = null) { - $input = $this->request->getJSON(true); - if (!$SpcPrpID || !ctype_digit((string) $SpcPrpID)) { - return $this->failValidationErrors('SpcPrpID is required.'); + $input = $this->requirePatchPayload($this->request->getJSON(true)); + if ($input === null) { + return; } - $input['SpcPrpID'] = (int) $SpcPrpID; + + $id = $this->requirePatchId($SpcPrpID, 'SpcPrpID'); + if ($id === null) { + return; + } + + $existing = $this->model->where('SpcPrpID', $id)->first(); + if (!$existing) { + return $this->respond([ 'status' => 'failed', 'message' => 'Specimen prep not found', 'data' => [] ], 404); + } + + $input['SpcPrpID'] = $id; if (!$this->validateData($input, $this->rules)) { return $this->failValidationErrors($this->validator->getErrors()); } try { - $id = $this->model->update($input['SpcPrpID'], $input); - return $this->respondCreated([ 'status' => 'success', 'message' => "data $id updated successfully" ]); - } catch (\Exception $e) { - return $this->failServerError('Something went wrong: ' . $e->getMessage()); - } - } + $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()); + } + } } diff --git a/app/Controllers/Specimen/SpecimenStatusController.php b/app/Controllers/Specimen/SpecimenStatusController.php index 03a793d..cbf2d97 100644 --- a/app/Controllers/Specimen/SpecimenStatusController.php +++ b/app/Controllers/Specimen/SpecimenStatusController.php @@ -2,13 +2,15 @@ namespace App\Controllers\Specimen; -use App\Traits\ResponseTrait; -use App\Controllers\BaseController; -use App\Libraries\ValueSet; -use App\Models\Specimen\SpecimenStatusModel; +use App\Traits\PatchValidationTrait; +use App\Traits\ResponseTrait; +use App\Controllers\BaseController; +use App\Libraries\ValueSet; +use App\Models\Specimen\SpecimenStatusModel; class SpecimenStatusController extends BaseController { - use ResponseTrait; + use ResponseTrait; + use PatchValidationTrait; protected $db; protected $model; @@ -65,18 +67,29 @@ class SpecimenStatusController extends BaseController { } public function update($SpcStaID = null) { - $input = $this->request->getJSON(true); - if (!$SpcStaID || !ctype_digit((string) $SpcStaID)) { - return $this->failValidationErrors('SpcStaID is required.'); + $input = $this->requirePatchPayload($this->request->getJSON(true)); + if ($input === null) { + return; } - $input['SpcStaID'] = (int) $SpcStaID; + + $id = $this->requirePatchId($SpcStaID, 'SpcStaID'); + if ($id === null) { + return; + } + + $existing = $this->model->where('SpcStaID', $id)->first(); + if (!$existing) { + return $this->respond([ 'status' => 'failed', 'message' => 'Specimen status not found', 'data' => [] ], 404); + } + + $input['SpcStaID'] = $id; if (!$this->validateData($input, $this->rules)) { return $this->failValidationErrors($this->validator->getErrors()); } try { - $id = $this->model->update($input['SpcStaID'], $input); - return $this->respondCreated([ 'status' => 'success', 'message' => "data $id updated successfully" ]); - } catch (\Exception $e) { - return $this->failServerError('Something went wrong: ' . $e->getMessage()); - } - } + $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()); + } + } } diff --git a/app/Controllers/Test/TestMapController.php b/app/Controllers/Test/TestMapController.php index 68081fb..4aad6d7 100644 --- a/app/Controllers/Test/TestMapController.php +++ b/app/Controllers/Test/TestMapController.php @@ -1,14 +1,16 @@ request->getJSON(true); - if (!$TestMapID || !ctype_digit((string) $TestMapID)) { return $this->failValidationErrors('TestMapID is required.'); } - if (empty($input) || !is_array($input)) { - return $this->failValidationErrors('No data provided for update.'); + $input = $this->requirePatchPayload($this->request->getJSON(true)); + if ($input === null) { + return; } - $id = (int) $TestMapID; + + $id = $this->requirePatchId($TestMapID, 'TestMapID'); + if ($id === null) { + return; + } + + $existing = $this->model->where('TestMapID', $id)->where('EndDate', null)->first(); + if (!$existing) { + return $this->respond([ 'status' => 'failed', 'message' => 'Test map not found', 'data' => [] ], 404); + } + if (isset($input['TestMapID']) && (string) $input['TestMapID'] !== (string) $id) { return $this->failValidationErrors('TestMapID in URL does not match body.'); } @@ -91,10 +102,10 @@ class TestMapController extends BaseController { $input['TestMapID'] = $id; try { $this->model->update($id,$input); - return $this->respondCreated([ 'status' => 'success', 'message' => "data updated successfully", 'data' => $id ]); - } catch (\Exception $e) { - return $this->failServerError('Something went wrong: ' . $e->getMessage()); - } + 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() { diff --git a/app/Controllers/Test/TestMapDetailController.php b/app/Controllers/Test/TestMapDetailController.php index f698e76..a2df57d 100644 --- a/app/Controllers/Test/TestMapDetailController.php +++ b/app/Controllers/Test/TestMapDetailController.php @@ -2,12 +2,14 @@ namespace App\Controllers\Test; -use App\Controllers\BaseController; -use App\Traits\ResponseTrait; -use App\Models\Test\TestMapDetailModel; +use App\Controllers\BaseController; +use App\Traits\PatchValidationTrait; +use App\Traits\ResponseTrait; +use App\Models\Test\TestMapDetailModel; -class TestMapDetailController extends BaseController { - use ResponseTrait; +class TestMapDetailController extends BaseController { + use ResponseTrait; + use PatchValidationTrait; protected $db; protected $rules; @@ -99,35 +101,42 @@ class TestMapDetailController extends BaseController { } public function update($TestMapDetailID = null) { - $input = $this->request->getJSON(true); - if (!$TestMapDetailID || !ctype_digit((string) $TestMapDetailID)) { - return $this->failValidationErrors('TestMapDetailID is required.'); + $input = $this->requirePatchPayload($this->request->getJSON(true)); + if ($input === null) { + return; } - if (empty($input) || !is_array($input)) { - return $this->failValidationErrors('No data provided for update.'); + + $id = $this->requirePatchId($TestMapDetailID, 'TestMapDetailID'); + if ($id === null) { + return; } - $id = (int) $TestMapDetailID; + + $existing = $this->model->where('TestMapDetailID', $id)->where('EndDate', null)->first(); + if (!$existing) { + return $this->respond([ 'status' => 'failed', 'message' => 'Test map detail not found', 'data' => [] ], 404); + } + if (isset($input['TestMapDetailID']) && (string) $input['TestMapDetailID'] !== (string) $id) { return $this->failValidationErrors('TestMapDetailID in URL does not match body.'); } + $input['TestMapDetailID'] = $id; - $validationInput = array_intersect_key($input, $this->patchRules); if (!empty($validationInput) && !$this->validateData($validationInput, $this->patchRules)) { 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()); - } - } + + 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); diff --git a/app/Controllers/Test/TestsController.php b/app/Controllers/Test/TestsController.php index 05a9704..28d87c6 100644 --- a/app/Controllers/Test/TestsController.php +++ b/app/Controllers/Test/TestsController.php @@ -2,14 +2,16 @@ namespace App\Controllers\Test; -use App\Controllers\BaseController; -use App\Libraries\TestValidationService; -use App\Libraries\ValueSet; -use App\Traits\ResponseTrait; +use App\Controllers\BaseController; +use App\Libraries\TestValidationService; +use App\Libraries\ValueSet; +use App\Traits\PatchValidationTrait; +use App\Traits\ResponseTrait; -class TestsController extends BaseController -{ - use ResponseTrait; +class TestsController extends BaseController +{ + use ResponseTrait; + use PatchValidationTrait; protected $model; protected $modelCal; @@ -205,21 +207,30 @@ class TestsController extends BaseController } } - public function update($id = null) - { - $input = $this->request->getJSON(true); - - if (!$id && isset($input['TestSiteID'])) { - $id = $input['TestSiteID']; - } - if (!$id) { - return $this->failValidationErrors('TestSiteID is required.'); - } - - $existing = $this->model->find($id); - if (!$existing) { - return $this->failNotFound('Test not found'); - } + public function update($id = null) + { + $input = $this->requirePatchPayload($this->request->getJSON(true)); + if ($input === null) { + return; + } + + if (!$id && isset($input['TestSiteID'])) { + $id = $input['TestSiteID']; + } + + $id = $this->requirePatchId($id, 'TestSiteID'); + if ($id === null) { + return; + } + + $existing = $this->model->find($id); + if (!$existing) { + return $this->respond([ + 'status' => 'failed', + 'message' => 'Test not found', + 'data' => [] + ], 404); + } $testType = $input['TestType'] ?? $existing['TestType'] ?? ''; $details = $input['details'] ?? $input; diff --git a/app/Controllers/User/UserController.php b/app/Controllers/User/UserController.php index 04cbb59..0b7c357 100644 --- a/app/Controllers/User/UserController.php +++ b/app/Controllers/User/UserController.php @@ -4,6 +4,7 @@ namespace App\Controllers\User; use App\Controllers\BaseController; use App\Models\User\UserModel; +use App\Traits\PatchValidationTrait; use App\Traits\ResponseTrait; /** @@ -13,6 +14,7 @@ use App\Traits\ResponseTrait; class UserController extends BaseController { use ResponseTrait; + use PatchValidationTrait; protected $model; protected $db; @@ -178,7 +180,10 @@ class UserController extends BaseController public function update($id) { try { - $data = $this->request->getJSON(true); + $data = $this->requirePatchPayload($this->request->getJSON(true)); + if ($data === null) { + return; + } if (empty($id) || !ctype_digit((string) $id)) { return $this->respond([ diff --git a/app/Models/Location/LocationModel.php b/app/Models/Location/LocationModel.php index 88af01b..59cee7f 100644 --- a/app/Models/Location/LocationModel.php +++ b/app/Models/Location/LocationModel.php @@ -1,21 +1,25 @@ select("LocationID, LocCode, Parent, LocFull, LocType"); + public function getLocations($LocCode, $LocName) { + $sql = $this->select("LocationID, LocCode, Parent, LocFull, LocType"); if($LocName != '') { $sql->like('LocFull', $LocName, 'both'); } if($LocCode != '') { $sql->like('LocCode', $LocCode, 'both'); } $rows = $sql->findAll(); @@ -25,9 +29,9 @@ class LocationModel extends BaseModel { return $rows; } - public function getLocation($LocationID) { - $row = $this->select("location.*, la.Street1, la.Street2, la.PostCode, la.GeoLocationSystem, la.GeoLocationData, - la.Province as Province, prop.AreaName as ProvinceLabel, la.City as City, city.AreaName as CityLabel, site.SiteID, site.SiteName") + public function getLocation($LocationID) { + $row = $this->select("location.*, la.Street1, la.Street2, la.PostCode, la.GeoLocationSystem, la.GeoLocationData, + la.Province as Province, prop.AreaName as ProvinceLabel, la.City as City, city.AreaName as CityLabel, site.SiteID, site.SiteName") ->join("locationaddress la", "location.LocationID=la.LocationID", "left") ->join("areageo prop", "la.Province=prop.AreaGeoID", "left") ->join("areageo city", "la.City=city.AreaGeoID", "left") @@ -40,29 +44,69 @@ class LocationModel extends BaseModel { 'LocType' => 'location_type', ])[0]; - return $row; - } + return $row; + } + + private function extractLocationFields(array $data): array + { + return $this->filterFields($data, $this->locationEditableFields); + } + + private function extractAddressFields(array $data): array + { + return $this->filterFields($data, $this->addressEditableFields); + } + + private function filterFields(array $data, array $fields): array + { + return array_intersect_key($data, array_flip($fields)); + } + + private function persistAddressFields(int $LocationID, array $data, LocationAddressModel $modelAddress, bool $forceInsert = false): void + { + $addressPayload = $this->extractAddressFields($data); + if ($addressPayload === [] && !$forceInsert) { + return; + } + + $payloadWithId = array_merge(['LocationID' => $LocationID], $addressPayload); + + if ($forceInsert) { + $modelAddress->insert($payloadWithId); + return; + } + + $existingAddress = $modelAddress->find($LocationID); + if ($existingAddress === null) { + $modelAddress->insert($payloadWithId); + return; + } + + $modelAddress->update($LocationID, $addressPayload); + } - public function saveLocation(array $data): array { - $modelAddress = new \App\Models\Location\LocationAddressModel(); - $db = \Config\Database::connect(); - $db->transBegin(); - try { - if (!empty($data['LocationID'])) { - $LocationID = $data['LocationID']; - $this->update($LocationID, $data); - $modelAddress->update($LocationID, $data); - } else { - $LocationID = $this->insert($data, true); - $data['LocationID'] = $LocationID; - $modelAddress->insert($data); - } - if ($db->transStatus() === false) { - $error = $db->error(); - $db->transRollback(); - throw new \Exception($error['message'] ?? 'Transaction failed'); - } - $db->transCommit(); + public function saveLocation(array $data): array { + $modelAddress = new LocationAddressModel(); + $db = \Config\Database::connect(); + $db->transBegin(); + try { + if (!empty($data['LocationID'])) { + $LocationID = $data['LocationID']; + $locationPayload = $this->extractLocationFields($data); + if ($locationPayload !== []) { + $this->update($LocationID, $locationPayload); + } + $this->persistAddressFields($LocationID, $data, $modelAddress); + } else { + $LocationID = $this->insert($data, true); + $this->persistAddressFields($LocationID, $data, $modelAddress, true); + } + if ($db->transStatus() === false) { + $error = $db->error(); + $db->transRollback(); + throw new \Exception($error['message'] ?? 'Transaction failed'); + } + $db->transCommit(); return [ 'status' => 'success', 'LocationID' => $LocationID ]; } catch (\Throwable $e) { $db->transRollback(); diff --git a/app/Models/PatResultModel.php b/app/Models/PatResultModel.php index b95b71c..683d51a 100644 --- a/app/Models/PatResultModel.php +++ b/app/Models/PatResultModel.php @@ -8,17 +8,19 @@ use App\Models\Patient\PatientModel; class PatResultModel extends BaseModel { protected $table = 'patres'; protected $primaryKey = 'ResultID'; - protected $allowedFields = [ - 'SiteID', - 'OrderID', - 'InternalSID', - 'SID', - 'SampleID', - 'TestSiteID', - 'TestSiteCode', - 'AspCnt', - 'Result', - 'SampleType', + protected $allowedFields = [ + 'SiteID', + 'OrderID', + 'InternalSID', + 'SID', + 'SampleID', + 'TestSiteID', + 'TestSiteCode', + 'AspCnt', + 'ResultCode', + 'Result', + 'ResultValue', + 'SampleType', 'ResultDateTime', 'WorkstationID', 'EquipmentID', diff --git a/app/Traits/PatchValidationTrait.php b/app/Traits/PatchValidationTrait.php new file mode 100644 index 0000000..42b9dff --- /dev/null +++ b/app/Traits/PatchValidationTrait.php @@ -0,0 +1,20 @@ +failValidationErrors("{$label} is required and must be a valid integer."); + return null; + } + return (int) $id; + } + + protected function requirePatchPayload(mixed $payload): ?array { + if (!is_array($payload) || empty($payload)) { + $this->failValidationErrors('No data provided for update.'); + return null; + } + return $payload; + } +} diff --git a/tests/feature/Location/LocationPatchTest.php b/tests/feature/Location/LocationPatchTest.php index cdc9e6a..852be95 100644 --- a/tests/feature/Location/LocationPatchTest.php +++ b/tests/feature/Location/LocationPatchTest.php @@ -37,8 +37,8 @@ class LocationPatchTest extends CIUnitTestCase private function createLocation(array $data = []): array { $payload = array_merge([ - 'LocationCode' => 'LOC_' . uniqid(), - 'LocationName' => 'Test Location ' . uniqid(), + 'LocCode' => 'LC' . substr(uniqid(), -4), + 'LocFull' => 'Test Location ' . uniqid(), ], $data); $response = $this->withHeaders($this->authHeaders()) @@ -47,7 +47,12 @@ class LocationPatchTest extends CIUnitTestCase $response->assertStatus(201); $decoded = json_decode($response->getJSON(), true); - return $decoded['data']; + $locationId = $decoded['data']['LocationID']; + $show = $this->withHeaders($this->authHeaders()) + ->call('get', "{$this->endpoint}/{$locationId}"); + $show->assertStatus(200); + $showData = json_decode($show->getJSON(), true)['data']; + return $showData; } public function testPartialUpdateLocationSuccess() @@ -57,7 +62,7 @@ class LocationPatchTest extends CIUnitTestCase $patch = $this->withHeaders($this->authHeaders()) ->withBodyFormat('json') - ->call('patch', "{$this->endpoint}/{$id}", ['LocationName' => 'Updated Location']); + ->call('patch', "{$this->endpoint}/{$id}", ['LocFull' => 'Updated Location']); $patch->assertStatus(200); $patchData = json_decode($patch->getJSON(), true); @@ -67,15 +72,15 @@ class LocationPatchTest extends CIUnitTestCase $show->assertStatus(200); $showData = json_decode($show->getJSON(), true)['data']; - $this->assertEquals('Updated Location', $showData['LocationName']); - $this->assertEquals($location['LocationCode'], $showData['LocationCode']); + $this->assertEquals('Updated Location', $showData['LocFull']); + $this->assertEquals($location['LocCode'], $showData['LocCode']); } public function testPartialUpdateLocationNotFound() { $patch = $this->withHeaders($this->authHeaders()) ->withBodyFormat('json') - ->call('patch', "{$this->endpoint}/999999", ['LocationName' => 'Updated']); + ->call('patch', "{$this->endpoint}/999999", ['LocFull' => 'Updated']); $patch->assertStatus(404); } @@ -84,7 +89,7 @@ class LocationPatchTest extends CIUnitTestCase { $patch = $this->withHeaders($this->authHeaders()) ->withBodyFormat('json') - ->call('patch', "{$this->endpoint}/invalid", ['LocationName' => 'Updated']); + ->call('patch', "{$this->endpoint}/invalid", ['LocFull' => 'Updated']); $patch->assertStatus(400); } @@ -108,14 +113,32 @@ class LocationPatchTest extends CIUnitTestCase $patch = $this->withHeaders($this->authHeaders()) ->withBodyFormat('json') - ->call('patch', "{$this->endpoint}/{$id}", ['LocationCode' => 'NEW_CODE_' . uniqid()]); + ->call('patch', "{$this->endpoint}/{$id}", ['LocCode' => 'LC' . substr(uniqid(), -4)]); $patch->assertStatus(200); $showData = json_decode($this->withHeaders($this->authHeaders()) ->call('get', "{$this->endpoint}/{$id}") ->getJSON(), true)['data']; - $this->assertNotEquals($location['LocationCode'], $showData['LocationCode']); - $this->assertEquals($location['LocationName'], $showData['LocationName']); + $this->assertNotEquals($location['LocCode'], $showData['LocCode']); + $this->assertEquals($location['LocFull'], $showData['LocFull']); + + } + + public function testPartialUpdateLocationAddressField() + { + $location = $this->createLocation(); + $id = $location['LocationID']; + + $patch = $this->withHeaders($this->authHeaders()) + ->withBodyFormat('json') + ->call('patch', "{$this->endpoint}/{$id}", ['Street1' => '123 Market St']); + + $patch->assertStatus(200); + $showData = json_decode($this->withHeaders($this->authHeaders()) + ->call('get', "{$this->endpoint}/{$id}") + ->getJSON(), true)['data']; + + $this->assertEquals('123 Market St', $showData['Street1']); } }