fix: align test members payload with top-level API contract

This commit is contained in:
mahdahar 2026-04-02 04:52:50 +07:00
parent 399f4d615b
commit eeaed768c9
5 changed files with 142 additions and 160 deletions

View File

@ -92,9 +92,9 @@ class TestsController extends BaseController
if ($typeCode === 'CALC') { if ($typeCode === 'CALC') {
$row['testdefcal'] = $this->modelCal->getByTestSiteID($id); $row['testdefcal'] = $this->modelCal->getByTestSiteID($id);
$row['testdefgrp'] = $this->modelGrp->getGroupMembers($id); $row['members'] = $this->modelGrp->getGroupMembers($id);
} elseif ($typeCode === 'GROUP') { } elseif ($typeCode === 'GROUP') {
$row['testdefgrp'] = $this->modelGrp->getGroupMembers($id); $row['members'] = $this->modelGrp->getGroupMembers($id);
} elseif ($typeCode !== 'TITLE') { } elseif ($typeCode !== 'TITLE') {
$refType = $row['RefType'] ?? ''; $refType = $row['RefType'] ?? '';
$resultType = $row['ResultType'] ?? ''; $resultType = $row['ResultType'] ?? '';
@ -491,7 +491,7 @@ class TestsController extends BaseController
$this->modelGrp->disableByTestSiteID($testSiteID); $this->modelGrp->disableByTestSiteID($testSiteID);
} }
$memberIDs = $this->resolveCalcMemberIDs($data, $input); $memberIDs = $this->resolveMemberIDs($input);
// Validate member IDs before insertion // Validate member IDs before insertion
$validation = $this->validateMemberIDs($memberIDs); $validation = $this->validateMemberIDs($memberIDs);
@ -507,15 +507,14 @@ class TestsController extends BaseController
} }
} }
private function resolveCalcMemberIDs(array $data, array $input): array private function resolveMemberIDs(array $input): array
{ {
$memberIDs = []; $memberIDs = [];
$rawMembers = $data['members'] ?? ($input['members'] ?? []); $rawMembers = $input['members'] ?? [];
if (is_array($rawMembers)) { if (is_array($rawMembers)) {
foreach ($rawMembers as $member) { foreach ($rawMembers as $member) {
if (is_array($member)) { if (is_array($member)) {
// Only accept TestSiteID, not Member (which might be SeqScr)
$rawID = $member['TestSiteID'] ?? null; $rawID = $member['TestSiteID'] ?? null;
} else { } else {
$rawID = is_numeric($member) ? $member : null; $rawID = is_numeric($member) ? $member : null;
@ -563,20 +562,7 @@ class TestsController extends BaseController
$this->modelGrp->disableByTestSiteID($testSiteID); $this->modelGrp->disableByTestSiteID($testSiteID);
} }
$members = $data['members'] ?? ($input['Members'] ?? []); $memberIDs = $this->resolveMemberIDs($input);
$memberIDs = [];
if (is_array($members)) {
foreach ($members as $m) {
// Only accept TestSiteID, not Member (which might be SeqScr)
$memberID = is_array($m) ? ($m['TestSiteID'] ?? null) : $m;
if ($memberID && is_numeric($memberID)) {
$memberIDs[] = (int) $memberID;
}
}
}
$memberIDs = array_values(array_unique(array_filter($memberIDs)));
// Validate member IDs before insertion // Validate member IDs before insertion
$validation = $this->validateMemberIDs($memberIDs); $validation = $this->validateMemberIDs($memberIDs);

View File

@ -4671,21 +4671,17 @@ paths:
FormulaCode: FormulaCode:
type: string type: string
description: Formula expression for calculated tests (e.g., "{TBIL} - {DBIL}") description: Formula expression for calculated tests (e.g., "{TBIL} - {DBIL}")
testdefgrp: members:
type: object type: array
description: Group member payload stored in the `testdefgrp` table. description: Array of member TestSiteIDs for CALC/GROUP definitions.
properties: items:
members: type: object
type: array properties:
description: Array of member TestSiteIDs for CALC/GROUP definitions. TestSiteID:
items: type: integer
type: object description: Foreign key referencing the member test's TestSiteID.
properties: required:
TestSiteID: - TestSiteID
type: integer
description: Foreign key referencing the member test's TestSiteID.
required:
- TestSiteID
refnum: refnum:
type: array type: array
items: items:
@ -5100,10 +5096,9 @@ paths:
DepartmentID: 2 DepartmentID: 2
testdefcal: testdefcal:
FormulaCode: CKD_EPI(CREA,AGE,GENDER) FormulaCode: CKD_EPI(CREA,AGE,GENDER)
testdefgrp: members:
members: - TestSiteID: 21
- TestSiteID: 21 - TestSiteID: 22
- TestSiteID: 22
CALC_full: CALC_full:
summary: Calculated test with numeric reference ranges and map summary: Calculated test with numeric reference ranges and map
value: value:
@ -5142,10 +5137,9 @@ paths:
DepartmentID: 2 DepartmentID: 2
testdefcal: testdefcal:
FormulaCode: CKD_EPI(CREA,AGE,GENDER) FormulaCode: CKD_EPI(CREA,AGE,GENDER)
testdefgrp: members:
members: - TestSiteID: 21
- TestSiteID: 21 - TestSiteID: 22
- TestSiteID: 22
GROUP_with_members: GROUP_with_members:
summary: Group/profile test with members and mapping summary: Group/profile test with members and mapping
value: value:
@ -5169,10 +5163,9 @@ paths:
ConDefID: 1 ConDefID: 1
ClientTestCode: LIPID_C ClientTestCode: LIPID_C
ClientTestName: Lipid Client ClientTestName: Lipid Client
testdefgrp: members:
members: - TestSiteID: 169
- TestSiteID: 169 - TestSiteID: 170
- TestSiteID: 170
responses: responses:
'201': '201':
description: Test definition created description: Test definition created
@ -5334,21 +5327,17 @@ paths:
FormulaCode: FormulaCode:
type: string type: string
description: Formula expression for calculated tests (e.g., "{TBIL} - {DBIL}") description: Formula expression for calculated tests (e.g., "{TBIL} - {DBIL}")
testdefgrp: members:
type: object type: array
description: Group member payload stored in the `testdefgrp` table. description: Array of member TestSiteIDs for CALC/GROUP definitions.
properties: items:
members: type: object
type: array properties:
description: Array of member TestSiteIDs for CALC/GROUP definitions. TestSiteID:
items: type: integer
type: object description: Foreign key referencing the member test's TestSiteID.
properties: required:
TestSiteID: - TestSiteID
type: integer
description: Foreign key referencing the member test's TestSiteID.
required:
- TestSiteID
refnum: refnum:
type: array type: array
items: items:
@ -7061,12 +7050,12 @@ components:
description: Calculated test details (only for CALC type) description: Calculated test details (only for CALC type)
items: items:
type: object type: object
testdefgrp: members:
type: array type: array
description: | description: |
Group members (for GROUP and CALC types). Group members (for GROUP and CALC types).
When creating or updating, provide members in details.members array with TestSiteID field. When creating or updating, provide members in members array with TestSiteID field.
Do NOT use Member or SeqScr fields when creating/updating. Do NOT use Member, SeqScr, or Members fields when creating/updating.
items: items:
type: object type: object
properties: properties:
@ -7080,7 +7069,7 @@ components:
type: integer type: integer
description: | description: |
Member TestSiteID (foreign key to testdefsite). Member TestSiteID (foreign key to testdefsite).
**Note**: This field is in the response. When creating/updating, use TestSiteID in details.members array instead. **Note**: This field is in the response. When creating/updating, use TestSiteID in members array instead.
TestSiteCode: TestSiteCode:
type: string type: string
description: Member test code description: Member test code
@ -7349,7 +7338,7 @@ components:
FormulaCode: CKD_EPI(CREA,AGE,GENDER) FormulaCode: CKD_EPI(CREA,AGE,GENDER)
Unit1: mL/min/1.73m2 Unit1: mL/min/1.73m2
Decimal: 0 Decimal: 0
testdefgrp: members:
- TestSiteID: 21 - TestSiteID: 21
TestSiteCode: CREA TestSiteCode: CREA
TestSiteName: Creatinine TestSiteName: Creatinine
@ -7391,7 +7380,7 @@ components:
isVisibleScr: 1 isVisibleScr: 1
isVisibleRpt: 1 isVisibleRpt: 1
isCountStat: 1 isCountStat: 1
testdefgrp: members:
- TestGrpID: 1 - TestGrpID: 1
TestSiteID: 6 TestSiteID: 6
Member: 100 Member: 100

View File

@ -183,12 +183,12 @@ TestDefinition:
description: Calculated test details (only for CALC type) description: Calculated test details (only for CALC type)
items: items:
type: object type: object
testdefgrp: members:
type: array type: array
description: | description: |
Group members (for GROUP and CALC types). Group members (for GROUP and CALC types).
When creating or updating, provide members in details.members array with TestSiteID field. When creating or updating, provide members in members array with TestSiteID field.
Do NOT use Member or SeqScr fields when creating/updating. Do NOT use Member, SeqScr, or Members fields when creating/updating.
items: items:
type: object type: object
properties: properties:
@ -202,7 +202,7 @@ TestDefinition:
type: integer type: integer
description: | description: |
Member TestSiteID (foreign key to testdefsite). Member TestSiteID (foreign key to testdefsite).
**Note**: This field is in the response. When creating/updating, use TestSiteID in details.members array instead. **Note**: This field is in the response. When creating/updating, use TestSiteID in members array instead.
TestSiteCode: TestSiteCode:
type: string type: string
description: Member test code description: Member test code
@ -467,7 +467,7 @@ TestDefinition:
FormulaCode: CKD_EPI(CREA,AGE,GENDER) FormulaCode: CKD_EPI(CREA,AGE,GENDER)
Unit1: mL/min/1.73m2 Unit1: mL/min/1.73m2
Decimal: 0 Decimal: 0
testdefgrp: members:
- TestSiteID: 21 - TestSiteID: 21
TestSiteCode: CREA TestSiteCode: CREA
TestSiteName: Creatinine TestSiteName: Creatinine
@ -509,7 +509,7 @@ TestDefinition:
isVisibleScr: 1 isVisibleScr: 1
isVisibleRpt: 1 isVisibleRpt: 1
isCountStat: 1 isCountStat: 1
testdefgrp: members:
- TestGrpID: 1 - TestGrpID: 1
TestSiteID: 6 TestSiteID: 6
Member: 100 Member: 100

View File

@ -185,21 +185,17 @@
FormulaCode: FormulaCode:
type: string type: string
description: Formula expression for calculated tests (e.g., "{TBIL} - {DBIL}") description: Formula expression for calculated tests (e.g., "{TBIL} - {DBIL}")
testdefgrp: members:
type: object type: array
description: Group member payload stored in the `testdefgrp` table. description: Array of member TestSiteIDs for CALC/GROUP definitions.
properties: items:
members: type: object
type: array properties:
description: Array of member TestSiteIDs for CALC/GROUP definitions. TestSiteID:
items: type: integer
type: object description: Foreign key referencing the member test's TestSiteID.
properties: required:
TestSiteID: - TestSiteID
type: integer
description: Foreign key referencing the member test's TestSiteID.
required:
- TestSiteID
refnum: refnum:
type: array type: array
items: items:
@ -614,10 +610,9 @@
DepartmentID: 2 DepartmentID: 2
testdefcal: testdefcal:
FormulaCode: CKD_EPI(CREA,AGE,GENDER) FormulaCode: CKD_EPI(CREA,AGE,GENDER)
testdefgrp: members:
members: - TestSiteID: 21
- TestSiteID: 21 - TestSiteID: 22
- TestSiteID: 22
CALC_full: CALC_full:
summary: Calculated test with numeric reference ranges and map summary: Calculated test with numeric reference ranges and map
value: value:
@ -656,10 +651,9 @@
DepartmentID: 2 DepartmentID: 2
testdefcal: testdefcal:
FormulaCode: CKD_EPI(CREA,AGE,GENDER) FormulaCode: CKD_EPI(CREA,AGE,GENDER)
testdefgrp: members:
members: - TestSiteID: 21
- TestSiteID: 21 - TestSiteID: 22
- TestSiteID: 22
GROUP_with_members: GROUP_with_members:
summary: Group/profile test with members and mapping summary: Group/profile test with members and mapping
value: value:
@ -683,10 +677,9 @@
ConDefID: 1 ConDefID: 1
ClientTestCode: LIPID_C ClientTestCode: LIPID_C
ClientTestName: Lipid Client ClientTestName: Lipid Client
testdefgrp: members:
members: - TestSiteID: 169
- TestSiteID: 169 - TestSiteID: 170
- TestSiteID: 170
responses: responses:
'201': '201':
@ -834,21 +827,17 @@
FormulaCode: FormulaCode:
type: string type: string
description: Formula expression for calculated tests (e.g., "{TBIL} - {DBIL}") description: Formula expression for calculated tests (e.g., "{TBIL} - {DBIL}")
testdefgrp: members:
type: object type: array
description: Group member payload stored in the `testdefgrp` table. description: Array of member TestSiteIDs for CALC/GROUP definitions.
properties: items:
members: type: object
type: array properties:
description: Array of member TestSiteIDs for CALC/GROUP definitions. TestSiteID:
items: type: integer
type: object description: Foreign key referencing the member test's TestSiteID.
properties: required:
TestSiteID: - TestSiteID
type: integer
description: Foreign key referencing the member test's TestSiteID.
required:
- TestSiteID
refnum: refnum:
type: array type: array
items: items:

View File

@ -359,6 +359,17 @@ class TestCreateVariantsTest extends CIUnitTestCase
$this->assertArrayHasKey('data', $json); $this->assertArrayHasKey('data', $json);
$this->assertArrayHasKey('TestSiteId', $json['data']); $this->assertArrayHasKey('TestSiteId', $json['data']);
$this->assertIsInt($json['data']['TestSiteId']); $this->assertIsInt($json['data']['TestSiteId']);
$show = $this->call('get', $this->endpoint . '/' . $json['data']['TestSiteId']);
$show->assertStatus(200);
$showData = json_decode($show->getJSON(), true)['data'];
$this->assertArrayHasKey('members', $showData);
$this->assertArrayNotHasKey('testdefgrp', $showData);
if ($members !== []) {
$this->assertCount(count($members), $showData['members']);
}
} }
private function assertTechnicalCreated( private function assertTechnicalCreated(
@ -390,6 +401,7 @@ class TestCreateVariantsTest extends CIUnitTestCase
$this->assertArrayHasKey('data', $json); $this->assertArrayHasKey('data', $json);
$this->assertArrayHasKey('TestSiteId', $json['data']); $this->assertArrayHasKey('TestSiteId', $json['data']);
$this->assertIsInt($json['data']['TestSiteId']); $this->assertIsInt($json['data']['TestSiteId']);
} }
private function assertCalculatedCreated( private function assertCalculatedCreated(
@ -418,6 +430,14 @@ class TestCreateVariantsTest extends CIUnitTestCase
$this->assertArrayHasKey('data', $json); $this->assertArrayHasKey('data', $json);
$this->assertArrayHasKey('TestSiteId', $json['data']); $this->assertArrayHasKey('TestSiteId', $json['data']);
$this->assertIsInt($json['data']['TestSiteId']); $this->assertIsInt($json['data']['TestSiteId']);
$show = $this->call('get', $this->endpoint . '/' . $json['data']['TestSiteId']);
$show->assertStatus(200);
$showData = json_decode($show->getJSON(), true)['data'];
$this->assertArrayHasKey('members', $showData);
$this->assertArrayNotHasKey('testdefgrp', $showData);
$this->assertCount(count($members), $showData['members']);
} }
private function buildTechnicalPayload(string $testType, array $details = []): array private function buildTechnicalPayload(string $testType, array $details = []): array
@ -451,11 +471,11 @@ class TestCreateVariantsTest extends CIUnitTestCase
'isVisibleScr' => 1, 'isVisibleScr' => 1,
'isVisibleRpt' => 1, 'isVisibleRpt' => 1,
'isCountStat' => 0, 'isCountStat' => 0,
'members' => array_map(fn ($id) => ['TestSiteID' => $id], $members),
'details' => [ 'details' => [
'DisciplineID' => 2, 'DisciplineID' => 2,
'DepartmentID' => 2, 'DepartmentID' => 2,
'FormulaCode' => '{GLU} + {CREA}', 'FormulaCode' => '{GLU} + {CREA}',
'members' => array_map(fn ($id) => ['TestSiteID' => $id], $members),
], ],
]; ];
@ -474,9 +494,7 @@ class TestCreateVariantsTest extends CIUnitTestCase
'isVisibleScr' => 1, 'isVisibleScr' => 1,
'isVisibleRpt' => 1, 'isVisibleRpt' => 1,
'isCountStat' => 1, 'isCountStat' => 1,
'details' => [ 'members' => array_map(fn ($id) => ['TestSiteID' => $id], $members),
'members' => array_map(fn ($id) => ['TestSiteID' => $id], $members),
],
'testmap' => $testmap, 'testmap' => $testmap,
]; ];
} }
@ -491,7 +509,7 @@ class TestCreateVariantsTest extends CIUnitTestCase
'Decimal' => array_key_exists('Decimal', $details) ? $details['Decimal'] : 0, 'Decimal' => array_key_exists('Decimal', $details) ? $details['Decimal'] : 0,
]; ];
foreach (['ResultType', 'RefType', 'FormulaCode', 'members', 'ExpectedTAT', 'Factor', 'ReqQty', 'ReqQtyUnit', 'Unit2', 'VSet', 'CollReq'] as $key) { foreach (['ResultType', 'RefType', 'FormulaCode', 'ExpectedTAT', 'Factor', 'ReqQty', 'ReqQtyUnit', 'Unit2', 'VSet', 'CollReq'] as $key) {
if (array_key_exists($key, $details)) { if (array_key_exists($key, $details)) {
$normalized[$key] = $details[$key]; $normalized[$key] = $details[$key];
} }