todo : fixing testmap detail
This commit is contained in:
parent
9946978487
commit
84cfff2201
@ -17,6 +17,10 @@ class TestMapController extends BaseController {
|
||||
protected $patchRules;
|
||||
protected $model;
|
||||
protected $modelDetail;
|
||||
protected array $headerFields = ['HostType', 'HostID', 'ClientType', 'ClientID'];
|
||||
protected array $detailFields = ['HostTestCode', 'HostTestName', 'ConDefID', 'ClientTestCode', 'ClientTestName'];
|
||||
protected array $detailRules;
|
||||
protected array $detailPatchRules;
|
||||
|
||||
public function __construct() {
|
||||
$this->db = \Config\Database::connect();
|
||||
@ -29,7 +33,17 @@ class TestMapController extends BaseController {
|
||||
$this->patchRules = [
|
||||
'HostID' => 'permit_empty|integer',
|
||||
'ClientID' => 'permit_empty|integer',
|
||||
'HostType' => 'permit_empty|string',
|
||||
'ClientType' => 'permit_empty|string',
|
||||
];
|
||||
$this->detailRules = [
|
||||
'HostTestCode' => 'permit_empty|max_length[10]',
|
||||
'HostTestName' => 'permit_empty|max_length[100]',
|
||||
'ConDefID' => 'permit_empty|integer',
|
||||
'ClientTestCode' => 'permit_empty|max_length[10]',
|
||||
'ClientTestName' => 'permit_empty|max_length[100]',
|
||||
];
|
||||
$this->detailPatchRules = $this->detailRules;
|
||||
}
|
||||
|
||||
public function index() {
|
||||
@ -65,11 +79,37 @@ class TestMapController extends BaseController {
|
||||
|
||||
public function create() {
|
||||
$input = $this->request->getJSON(true);
|
||||
if (!$this->validateData($input, $this->rules)) { return $this->failValidationErrors($this->validator->getErrors()); }
|
||||
$detailsPayload = null;
|
||||
if (array_key_exists('details', $input)) {
|
||||
$detailsPayload = $this->resolveDetailOperations($input['details']);
|
||||
if ($detailsPayload === null) { return; }
|
||||
}
|
||||
|
||||
$headerInput = array_intersect_key($input, array_flip($this->headerFields));
|
||||
if (!$this->validateData($headerInput, $this->rules)) {
|
||||
log_message('error', 'TestMap create validation failed: ' . json_encode($this->validator->getErrors()));
|
||||
return $this->failValidationErrors($this->validator->getErrors());
|
||||
}
|
||||
|
||||
$this->db->transStart();
|
||||
try {
|
||||
$id = $this->model->insert($input);
|
||||
$id = $this->model->insert($headerInput);
|
||||
|
||||
if ($detailsPayload !== null && !empty($detailsPayload['new'])) {
|
||||
if (!$this->insertDetailRows($id, $detailsPayload['new'])) {
|
||||
$this->db->transRollback();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->transComplete();
|
||||
if ($this->db->transStatus() === false) {
|
||||
return $this->failServerError('Something went wrong while saving the test map.');
|
||||
}
|
||||
|
||||
return $this->respondCreated([ 'status' => 'success', 'message' => "data created successfully", 'data' => $id ]);
|
||||
} catch (\Exception $e) {
|
||||
$this->db->transRollback();
|
||||
return $this->failServerError('Something went wrong: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
@ -80,6 +120,14 @@ class TestMapController extends BaseController {
|
||||
return;
|
||||
}
|
||||
|
||||
$detailsPayload = null;
|
||||
if (array_key_exists('details', $input)) {
|
||||
$detailsPayload = $this->resolveDetailOperations($input['details']);
|
||||
if ($detailsPayload === null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$id = $this->requirePatchId($TestMapID, 'TestMapID');
|
||||
if ($id === null) {
|
||||
return;
|
||||
@ -94,16 +142,31 @@ class TestMapController extends BaseController {
|
||||
return $this->failValidationErrors('TestMapID in URL does not match body.');
|
||||
}
|
||||
|
||||
$validationInput = array_intersect_key($input, $this->patchRules);
|
||||
$validationInput = array_intersect_key($headerInput = array_intersect_key($input, array_flip($this->headerFields)), $this->patchRules);
|
||||
if (!empty($validationInput) && !$this->validateData($validationInput, $this->patchRules)) {
|
||||
return $this->failValidationErrors($this->validator->getErrors());
|
||||
}
|
||||
|
||||
$input['TestMapID'] = $id;
|
||||
$this->db->transStart();
|
||||
try {
|
||||
$this->model->update($id,$input);
|
||||
if (!empty($headerInput)) {
|
||||
$this->model->update($id, $headerInput);
|
||||
}
|
||||
|
||||
if ($detailsPayload !== null && !$this->applyDetailOperations($id, $detailsPayload)) {
|
||||
$this->db->transRollback();
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->transComplete();
|
||||
if ($this->db->transStatus() === false) {
|
||||
return $this->failServerError('Something went wrong while updating the test map.');
|
||||
}
|
||||
|
||||
return $this->respond([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 200);
|
||||
} catch (\Exception $e) {
|
||||
$this->db->transRollback();
|
||||
return $this->failServerError('Something went wrong: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
@ -118,18 +181,20 @@ class TestMapController extends BaseController {
|
||||
if (empty($row)) { return $this->respond([ 'status' => 'failed', 'message' => "Data not found or already deleted.", 'data' => null ], 404); }
|
||||
|
||||
$this->db->transStart();
|
||||
$timestamp = date('Y-m-d H:i:s');
|
||||
$this->model->update($id, ['EndDate' => $timestamp]);
|
||||
|
||||
// Soft delete the testmap
|
||||
$this->model->update($id, ['EndDate' => date('Y-m-d H:i:s')]);
|
||||
|
||||
// Soft delete all related details
|
||||
$this->modelDetail->where('TestMapID', $id)
|
||||
->where('EndDate', null)
|
||||
->set('EndDate', date('Y-m-d H:i:s'))
|
||||
->set('EndDate', $timestamp)
|
||||
->update();
|
||||
|
||||
$this->db->transComplete();
|
||||
|
||||
if ($this->db->transStatus() === false) {
|
||||
return $this->failServerError('Something went wrong while deleting the test map.');
|
||||
}
|
||||
|
||||
return $this->respond([ 'status' => 'success', 'message' => "data deleted successfully", 'data' => $id ], 200);
|
||||
} catch (\Exception $e) {
|
||||
return $this->failServerError('Something went wrong: ' . $e->getMessage());
|
||||
@ -158,6 +223,214 @@ class TestMapController extends BaseController {
|
||||
return $row;
|
||||
}
|
||||
|
||||
private function resolveDetailOperations(mixed $detailsPayload): ?array
|
||||
{
|
||||
if ($detailsPayload === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!is_array($detailsPayload)) {
|
||||
$this->failValidationErrors('details must be an array or object.');
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->isDetailOpsPayload($detailsPayload)) {
|
||||
$newItems = $this->normalizeDetailList($detailsPayload['new'] ?? []);
|
||||
if ($newItems === null) { return null; }
|
||||
$editItems = $this->normalizeDetailList($detailsPayload['edit'] ?? []);
|
||||
if ($editItems === null) { return null; }
|
||||
$deletedIds = $this->normalizeDetailIds($detailsPayload['deleted'] ?? []);
|
||||
if ($deletedIds === null) { return null; }
|
||||
|
||||
return ['new' => $newItems, 'edit' => $editItems, 'deleted' => $deletedIds];
|
||||
}
|
||||
|
||||
if ($this->isListPayload($detailsPayload)) {
|
||||
$items = $this->normalizeDetailList($detailsPayload);
|
||||
if ($items === null) { return null; }
|
||||
return ['new' => $items, 'edit' => [], 'deleted' => []];
|
||||
}
|
||||
|
||||
if ($this->isAssocArray($detailsPayload)) {
|
||||
$items = $this->normalizeDetailList([$detailsPayload]);
|
||||
if ($items === null) { return null; }
|
||||
return ['new' => $items, 'edit' => [], 'deleted' => []];
|
||||
}
|
||||
|
||||
$this->failValidationErrors('details must be an array of objects or contain new/edit/deleted arrays.');
|
||||
return null;
|
||||
}
|
||||
|
||||
private function applyDetailOperations(int $testMapID, array $operations): bool
|
||||
{
|
||||
if (!empty($operations['edit']) && !$this->updateDetails($testMapID, $operations['edit'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!empty($operations['deleted']) && !$this->softDeleteDetails($testMapID, $operations['deleted'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!empty($operations['new']) && !$this->insertDetailRows($testMapID, $operations['new'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function insertDetailRows(int $testMapID, array $items): bool
|
||||
{
|
||||
if (empty($items)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$prepared = [];
|
||||
foreach ($items as $index => $item) {
|
||||
if (!$this->validateData($item, $this->detailRules)) {
|
||||
$this->failValidationErrors(['details.new' => $this->validator->getErrors()]);
|
||||
return false;
|
||||
}
|
||||
$prepared[] = array_merge(['TestMapID' => $testMapID], $item);
|
||||
}
|
||||
|
||||
$this->modelDetail->insertBatch($prepared);
|
||||
return true;
|
||||
}
|
||||
|
||||
private function updateDetails(int $testMapID, array $items): bool
|
||||
{
|
||||
foreach ($items as $index => $detail) {
|
||||
$detailID = $detail['TestMapDetailID'] ?? null;
|
||||
if (!$detailID || !ctype_digit((string) $detailID)) {
|
||||
$this->failValidationErrors("details.edit[{$index}].TestMapDetailID is required and must be an integer.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (array_key_exists('TestMapID', $detail) && (int) $detail['TestMapID'] !== $testMapID) {
|
||||
$this->failValidationErrors("details.edit[{$index}] must belong to TestMap {$testMapID}.");
|
||||
return false;
|
||||
}
|
||||
|
||||
$existing = $this->modelDetail->where('TestMapDetailID', $detailID)
|
||||
->where('TestMapID', $testMapID)
|
||||
->where('EndDate', null)
|
||||
->first();
|
||||
|
||||
if (empty($existing)) {
|
||||
$this->failValidationErrors("Detail record {$detailID} not found for this test map.");
|
||||
return false;
|
||||
}
|
||||
|
||||
$updateData = array_intersect_key($detail, array_flip($this->detailFields));
|
||||
if ($updateData === []) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->validateData($updateData, $this->detailPatchRules)) {
|
||||
$this->failValidationErrors($this->validator->getErrors());
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->modelDetail->update($detailID, $updateData);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function softDeleteDetails(int $testMapID, array $ids): bool
|
||||
{
|
||||
if (empty($ids)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$existing = $this->modelDetail->select('TestMapDetailID')
|
||||
->whereIn('TestMapDetailID', $ids)
|
||||
->where('TestMapID', $testMapID)
|
||||
->where('EndDate', null)
|
||||
->findAll();
|
||||
|
||||
$foundIds = array_column($existing, 'TestMapDetailID');
|
||||
$missing = array_diff($ids, $foundIds);
|
||||
if (!empty($missing)) {
|
||||
$this->failValidationErrors('Some detail IDs do not exist or belong to another test map: ' . implode(', ', $missing));
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->modelDetail->whereIn('TestMapDetailID', $ids)
|
||||
->where('TestMapID', $testMapID)
|
||||
->where('EndDate', null)
|
||||
->set('EndDate', date('Y-m-d H:i:s'))
|
||||
->update();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function isDetailOpsPayload(array $payload): bool
|
||||
{
|
||||
return (bool) array_intersect(array_keys($payload), ['new', 'edit', 'deleted']);
|
||||
}
|
||||
|
||||
private function isListPayload(array $payload): bool
|
||||
{
|
||||
if ($payload === []) {
|
||||
return true;
|
||||
}
|
||||
return array_keys($payload) === range(0, count($payload) - 1);
|
||||
}
|
||||
|
||||
private function isAssocArray(array $payload): bool
|
||||
{
|
||||
if ($payload === []) {
|
||||
return false;
|
||||
}
|
||||
return array_keys($payload) !== range(0, count($payload) - 1);
|
||||
}
|
||||
|
||||
private function normalizeDetailList(mixed $value): ?array
|
||||
{
|
||||
if ($value === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!is_array($value)) {
|
||||
$this->failValidationErrors('Details must be provided as an array of objects.');
|
||||
return null;
|
||||
}
|
||||
|
||||
$results = [];
|
||||
foreach ($value as $index => $item) {
|
||||
if (!is_array($item)) {
|
||||
$this->failValidationErrors("details[{$index}] must be an object.");
|
||||
return null;
|
||||
}
|
||||
$results[] = $item;
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
private function normalizeDetailIds(mixed $value): ?array
|
||||
{
|
||||
if ($value === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!is_array($value)) {
|
||||
$value = [$value];
|
||||
}
|
||||
|
||||
$results = [];
|
||||
foreach ($value as $index => $item) {
|
||||
if (!ctype_digit((string) $item)) {
|
||||
$this->failValidationErrors("details.deleted[{$index}] must be an integer.");
|
||||
return null;
|
||||
}
|
||||
$results[] = (int) $item;
|
||||
}
|
||||
|
||||
return array_values(array_unique($results));
|
||||
}
|
||||
|
||||
public function batchCreate() {
|
||||
$items = $this->request->getJSON(true);
|
||||
if (!is_array($items)) { return $this->failValidationErrors('Expected array of items'); }
|
||||
|
||||
@ -4089,7 +4089,7 @@ paths:
|
||||
description: Client identifier
|
||||
details:
|
||||
type: array
|
||||
description: Optional detail records to create
|
||||
description: Optional detail records to create alongside the header
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
@ -4199,21 +4199,6 @@ paths:
|
||||
type: integer
|
||||
description: Test Map ID
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
HostType:
|
||||
type: string
|
||||
HostID:
|
||||
type: string
|
||||
ClientType:
|
||||
type: string
|
||||
ClientID:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Test mapping updated
|
||||
content:
|
||||
@ -4229,6 +4214,64 @@ paths:
|
||||
data:
|
||||
type: integer
|
||||
description: Updated TestMapID
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
HostType:
|
||||
type: string
|
||||
HostID:
|
||||
type: string
|
||||
ClientType:
|
||||
type: string
|
||||
ClientID:
|
||||
type: string
|
||||
details:
|
||||
type: object
|
||||
description: Apply detail-level changes together with the header update
|
||||
properties:
|
||||
new:
|
||||
type: array
|
||||
description: New detail records to insert
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
HostTestCode:
|
||||
type: string
|
||||
HostTestName:
|
||||
type: string
|
||||
ConDefID:
|
||||
type: integer
|
||||
ClientTestCode:
|
||||
type: string
|
||||
ClientTestName:
|
||||
type: string
|
||||
edit:
|
||||
type: array
|
||||
description: Existing detail records to update
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
TestMapDetailID:
|
||||
type: integer
|
||||
HostTestCode:
|
||||
type: string
|
||||
HostTestName:
|
||||
type: string
|
||||
ConDefID:
|
||||
type: integer
|
||||
ClientTestCode:
|
||||
type: string
|
||||
ClientTestName:
|
||||
type: string
|
||||
deleted:
|
||||
type: array
|
||||
description: TestMapDetailIDs to soft delete
|
||||
items:
|
||||
type: integer
|
||||
responses: null
|
||||
/api/test/testmap/by-testcode/{testCode}:
|
||||
get:
|
||||
tags:
|
||||
|
||||
@ -63,7 +63,7 @@
|
||||
description: Client identifier
|
||||
details:
|
||||
type: array
|
||||
description: Optional detail records to create
|
||||
description: Optional detail records to create alongside the header
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
@ -188,7 +188,50 @@
|
||||
type: string
|
||||
ClientID:
|
||||
type: string
|
||||
responses:
|
||||
details:
|
||||
type: object
|
||||
description: Apply detail-level changes together with the header update
|
||||
properties:
|
||||
new:
|
||||
type: array
|
||||
description: New detail records to insert
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
HostTestCode:
|
||||
type: string
|
||||
HostTestName:
|
||||
type: string
|
||||
ConDefID:
|
||||
type: integer
|
||||
ClientTestCode:
|
||||
type: string
|
||||
ClientTestName:
|
||||
type: string
|
||||
edit:
|
||||
type: array
|
||||
description: Existing detail records to update
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
TestMapDetailID:
|
||||
type: integer
|
||||
HostTestCode:
|
||||
type: string
|
||||
HostTestName:
|
||||
type: string
|
||||
ConDefID:
|
||||
type: integer
|
||||
ClientTestCode:
|
||||
type: string
|
||||
ClientTestName:
|
||||
type: string
|
||||
deleted:
|
||||
type: array
|
||||
description: TestMapDetailIDs to soft delete
|
||||
items:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: Test mapping updated
|
||||
content:
|
||||
|
||||
@ -39,15 +39,24 @@ class TestMapPatchTest extends CIUnitTestCase
|
||||
$payload = array_merge([
|
||||
'TestMapCode' => 'TM_' . uniqid(),
|
||||
'TestMapName' => 'Test Map ' . uniqid(),
|
||||
'HostType' => 'SITE',
|
||||
'HostID' => 1,
|
||||
'ClientType' => 'SITE',
|
||||
'ClientID' => 1,
|
||||
], $data);
|
||||
|
||||
$response = $this->withHeaders($this->authHeaders())
|
||||
->withBodyFormat('json')
|
||||
->call('post', $this->endpoint, $payload);
|
||||
|
||||
fwrite(STDERR, 'Create response: ' . $response->getStatusCode() . ' ' . $response->getBody() . PHP_EOL);
|
||||
$response->assertStatus(201);
|
||||
$decoded = json_decode($response->getJSON(), true);
|
||||
return $decoded['data'];
|
||||
$created = json_decode($response->getJSON(), true);
|
||||
$id = $created['data'];
|
||||
|
||||
$show = $this->withHeaders($this->authHeaders())->call('get', "{$this->endpoint}/{$id}");
|
||||
$show->assertStatus(200);
|
||||
return json_decode($show->getJSON(), true)['data'];
|
||||
}
|
||||
|
||||
public function testPartialUpdateTestMapSuccess()
|
||||
@ -118,4 +127,133 @@ class TestMapPatchTest extends CIUnitTestCase
|
||||
$this->assertNotEquals($testMap['TestMapCode'], $showData['TestMapCode']);
|
||||
$this->assertEquals($testMap['TestMapName'], $showData['TestMapName']);
|
||||
}
|
||||
|
||||
public function testCreateTestMapWithDetails()
|
||||
{
|
||||
$details = [
|
||||
[
|
||||
'HostTestCode' => 'HB_' . uniqid(),
|
||||
'HostTestName' => 'Hemoglobin',
|
||||
'ClientTestCode' => '2',
|
||||
'ClientTestName' => 'Hemoglobin',
|
||||
],
|
||||
[
|
||||
'HostTestCode' => 'HCT_' . uniqid(),
|
||||
'HostTestName' => 'Hematocrit',
|
||||
'ClientTestCode' => '3',
|
||||
'ClientTestName' => 'Hematocrit',
|
||||
],
|
||||
];
|
||||
|
||||
$testMap = $this->createTestMap([
|
||||
'HostType' => 'SITE',
|
||||
'HostID' => 1,
|
||||
'ClientType' => 'SITE',
|
||||
'ClientID' => 2,
|
||||
'details' => $details,
|
||||
]);
|
||||
|
||||
$this->assertCount(2, $testMap['details']);
|
||||
$this->assertEquals('2', $testMap['details'][0]['ClientTestCode']);
|
||||
}
|
||||
|
||||
public function testPatchTestMapDetailOperations()
|
||||
{
|
||||
$initialDetails = [
|
||||
[
|
||||
'HostTestCode' => 'HB_' . uniqid(),
|
||||
'HostTestName' => 'Hemoglobin',
|
||||
'ClientTestCode' => '2',
|
||||
'ClientTestName' => 'Hemoglobin',
|
||||
],
|
||||
[
|
||||
'HostTestCode' => 'HCT_' . uniqid(),
|
||||
'HostTestName' => 'Hematocrit',
|
||||
'ClientTestCode' => '3',
|
||||
'ClientTestName' => 'Hematocrit',
|
||||
],
|
||||
];
|
||||
|
||||
$testMap = $this->createTestMap([
|
||||
'HostType' => 'SITE',
|
||||
'HostID' => 1,
|
||||
'ClientType' => 'SITE',
|
||||
'ClientID' => 1,
|
||||
'details' => $initialDetails,
|
||||
]);
|
||||
|
||||
$existingDetails = $testMap['details'];
|
||||
$editDetail = $existingDetails[0];
|
||||
$deleteDetail = $existingDetails[1];
|
||||
|
||||
$patch = $this->withHeaders($this->authHeaders())
|
||||
->withBodyFormat('json')
|
||||
->call('patch', "{$this->endpoint}/{$testMap['TestMapID']}", [
|
||||
'ClientType' => 'WST',
|
||||
'details' => [
|
||||
'edit' => [
|
||||
[
|
||||
'TestMapDetailID' => $editDetail['TestMapDetailID'],
|
||||
'ClientTestName' => 'Hemoglobin Updated',
|
||||
],
|
||||
],
|
||||
'new' => [
|
||||
[
|
||||
'HostTestCode' => 'MCV_' . uniqid(),
|
||||
'HostTestName' => 'MCV',
|
||||
'ClientTestCode' => '4',
|
||||
'ClientTestName' => 'MCV',
|
||||
],
|
||||
],
|
||||
'deleted' => [$deleteDetail['TestMapDetailID']],
|
||||
],
|
||||
]);
|
||||
|
||||
$patch->assertStatus(200);
|
||||
$patchData = json_decode($patch->getJSON(), true);
|
||||
$this->assertEquals('success', $patchData['status']);
|
||||
|
||||
$show = $this->withHeaders($this->authHeaders())->call('get', "{$this->endpoint}/{$testMap['TestMapID']}");
|
||||
$show->assertStatus(200);
|
||||
$showData = json_decode($show->getJSON(), true)['data'];
|
||||
|
||||
$this->assertEquals('WST', $showData['ClientType']);
|
||||
$this->assertCount(2, $showData['details']);
|
||||
$detailIds = array_column($showData['details'], 'TestMapDetailID');
|
||||
$this->assertContains($editDetail['TestMapDetailID'], $detailIds);
|
||||
$this->assertNotContains($deleteDetail['TestMapDetailID'], $detailIds);
|
||||
|
||||
$updatedDetails = array_values(array_filter($showData['details'], static fn ($row) => $row['TestMapDetailID'] === $editDetail['TestMapDetailID']));
|
||||
$this->assertNotEmpty($updatedDetails);
|
||||
$this->assertEquals('Hemoglobin Updated', $updatedDetails[0]['ClientTestName']);
|
||||
}
|
||||
|
||||
public function testDeleteTestMapRemovesDetails()
|
||||
{
|
||||
$testMap = $this->createTestMap([
|
||||
'HostType' => 'SITE',
|
||||
'HostID' => 2,
|
||||
'ClientType' => 'SITE',
|
||||
'ClientID' => 3,
|
||||
'details' => [
|
||||
[
|
||||
'HostTestCode' => 'PLT_' . uniqid(),
|
||||
'HostTestName' => 'Platelet',
|
||||
'ClientTestCode' => '5',
|
||||
'ClientTestName' => 'Platelet',
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$delete = $this->withHeaders($this->authHeaders())
|
||||
->withBodyFormat('json')
|
||||
->call('delete', $this->endpoint, ['TestMapID' => $testMap['TestMapID']]);
|
||||
|
||||
$delete->assertStatus(200);
|
||||
|
||||
$details = $this->withHeaders($this->authHeaders())
|
||||
->call('get', "{$this->endpoint}/detail/by-testmap/{$testMap['TestMapID']}");
|
||||
$details->assertStatus(200);
|
||||
$this->assertEquals([], json_decode($details->getJSON(), true)['data']);
|
||||
}
|
||||
}
|
||||
|
||||
5
todo.md
Normal file
5
todo.md
Normal file
@ -0,0 +1,5 @@
|
||||
### TestMap detail sync fix
|
||||
- Investigate why `TestMapController::create` and `patch` still reject payloads (400) despite passing required fields; log output hints validation errors.
|
||||
- Complete detail operation helpers (new/edit/deleted) so frontend payload works end-to-end and rerun feature tests.
|
||||
- Update tests once endpoints behave (remove stderr logging) and verify `phpunit tests/feature/Test/TestMapPatchTest.php` passes.
|
||||
- Confirm OpenAPI docs reflect final behavior and bundle output already up-to-date.
|
||||
Loading…
x
Reference in New Issue
Block a user