fix: preserve nullable test metadata and day-based age ranges

Avoid coercing missing SiteID, Decimal, and age boundaries to hardcoded defaults so payload intent is retained across test creation and reference range inserts. Align patient result age checks and OpenAPI examples with day-based age bounds, with feature coverage for create variants.
This commit is contained in:
mahdahar 2026-04-01 13:28:44 +07:00
parent 366572a0cb
commit 8aefeaca01
9 changed files with 265 additions and 160 deletions

View File

@ -34,7 +34,6 @@ class TestsController extends BaseController
'TestSiteCode' => 'required', 'TestSiteCode' => 'required',
'TestSiteName' => 'required', 'TestSiteName' => 'required',
'TestType' => 'required', 'TestType' => 'required',
'SiteID' => 'required',
]; ];
} }
@ -149,13 +148,13 @@ class TestsController extends BaseController
try { try {
$testSiteData = [ $testSiteData = [
'SiteID' => $input['SiteID'], 'SiteID' => array_key_exists('SiteID', $input) ? $input['SiteID'] : null,
'TestSiteCode'=> $input['TestSiteCode'], 'TestSiteCode'=> $input['TestSiteCode'],
'TestSiteName'=> $input['TestSiteName'], 'TestSiteName'=> $input['TestSiteName'],
'TestType' => $input['TestType'], 'TestType' => $input['TestType'],
'Description' => $input['Description'] ?? null, 'Description' => $input['Description'] ?? null,
'SeqScr' => $input['SeqScr'] ?? 0, 'SeqScr' => array_key_exists('SeqScr', $input) ? $input['SeqScr'] : null,
'SeqRpt' => $input['SeqRpt'] ?? 0, 'SeqRpt' => array_key_exists('SeqRpt', $input) ? $input['SeqRpt'] : null,
'IndentLeft' => $input['IndentLeft'] ?? 0, 'IndentLeft' => $input['IndentLeft'] ?? 0,
'FontStyle' => $input['FontStyle'] ?? null, 'FontStyle' => $input['FontStyle'] ?? null,
'isVisibleScr' => $input['isVisibleScr'] ?? 1, 'isVisibleScr' => $input['isVisibleScr'] ?? 1,
@ -374,7 +373,7 @@ class TestsController extends BaseController
$details = $input['details'] ?? $input; $details = $input['details'] ?? $input;
$details['TestSiteID'] = $testSiteID; $details['TestSiteID'] = $testSiteID;
$details['SiteID'] = $input['SiteID'] ?? 1; $details['SiteID'] = array_key_exists('SiteID', $input) ? $input['SiteID'] : null;
switch ($typeCode) { switch ($typeCode) {
case 'CALC': case 'CALC':
@ -404,11 +403,11 @@ class TestsController extends BaseController
$resultType = $details['ResultType'] ?? ''; $resultType = $details['ResultType'] ?? '';
if (TestValidationService::usesRefNum($resultType, $refType) && isset($input['refnum']) && is_array($input['refnum'])) { if (TestValidationService::usesRefNum($resultType, $refType) && isset($input['refnum']) && is_array($input['refnum'])) {
$this->saveRefNumRanges($testSiteID, $input['refnum'], $action, $input['SiteID'] ?? 1); $this->saveRefNumRanges($testSiteID, $input['refnum'], $action, array_key_exists('SiteID', $input) ? $input['SiteID'] : null);
} }
if (TestValidationService::usesRefTxt($resultType, $refType) && isset($input['reftxt']) && is_array($input['reftxt'])) { if (TestValidationService::usesRefTxt($resultType, $refType) && isset($input['reftxt']) && is_array($input['reftxt'])) {
$this->saveRefTxtRanges($testSiteID, $input['reftxt'], $action, $input['SiteID'] ?? 1); $this->saveRefTxtRanges($testSiteID, $input['reftxt'], $action, array_key_exists('SiteID', $input) ? $input['SiteID'] : null);
} }
} }
@ -433,7 +432,7 @@ class TestsController extends BaseController
'Unit1' => $data['Unit1'] ?? null, 'Unit1' => $data['Unit1'] ?? null,
'Factor' => $data['Factor'] ?? null, 'Factor' => $data['Factor'] ?? null,
'Unit2' => $data['Unit2'] ?? null, 'Unit2' => $data['Unit2'] ?? null,
'Decimal' => $data['Decimal'] ?? 2, 'Decimal' => array_key_exists('Decimal', $data) ? $data['Decimal'] : null,
'CollReq' => $data['CollReq'] ?? null, 'CollReq' => $data['CollReq'] ?? null,
'Method' => $data['Method'] ?? null, 'Method' => $data['Method'] ?? null,
'ExpectedTAT' => $data['ExpectedTAT'] ?? null, 'ExpectedTAT' => $data['ExpectedTAT'] ?? null,
@ -472,7 +471,7 @@ class TestsController extends BaseController
'Unit1' => $data['Unit1'] ?? $data['ResultUnit'] ?? null, 'Unit1' => $data['Unit1'] ?? $data['ResultUnit'] ?? null,
'Factor' => $data['Factor'] ?? null, 'Factor' => $data['Factor'] ?? null,
'Unit2' => $data['Unit2'] ?? null, 'Unit2' => $data['Unit2'] ?? null,
'Decimal' => $data['Decimal'] ?? 2, 'Decimal' => array_key_exists('Decimal', $data) ? $data['Decimal'] : null,
'Method' => $data['Method'] ?? null, 'Method' => $data['Method'] ?? null,
]; ];

View File

@ -8,7 +8,7 @@ class CreateTestDefinitions extends Migration {
public function up() { public function up() {
$this->forge->addField([ $this->forge->addField([
'TestSiteID' => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true], 'TestSiteID' => ['type' => 'INT', 'auto_increment' => true, 'unsigned' => true],
'SiteID' => ['type' => 'INT', 'null' => false], 'SiteID' => ['type' => 'INT', 'null' => true],
'TestSiteCode' => ['type' => 'varchar', 'constraint'=> 10, 'null' => false], 'TestSiteCode' => ['type' => 'varchar', 'constraint'=> 10, 'null' => false],
'TestSiteName' => ['type' => 'varchar', 'constraint'=> 100, 'null' => false], 'TestSiteName' => ['type' => 'varchar', 'constraint'=> 100, 'null' => false],
'TestType' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => false], 'TestType' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => false],
@ -23,7 +23,7 @@ class CreateTestDefinitions extends Migration {
'Unit1' => ['type' => 'varchar', 'constraint'=> 20, 'null' => true], 'Unit1' => ['type' => 'varchar', 'constraint'=> 20, 'null' => true],
'Factor' => ['type' => 'DECIMAL', 'constraint'=> '10,4', 'null' => true], 'Factor' => ['type' => 'DECIMAL', 'constraint'=> '10,4', 'null' => true],
'Unit2' => ['type' => 'varchar', 'constraint'=> 20, 'null' => true], 'Unit2' => ['type' => 'varchar', 'constraint'=> 20, 'null' => true],
'Decimal' => ['type' => 'int', 'null' => true, 'default' => 2], 'Decimal' => ['type' => 'int', 'null' => true ],
'CollReq' => ['type' => 'varchar', 'constraint'=> 255, 'null' => true], 'CollReq' => ['type' => 'varchar', 'constraint'=> 255, 'null' => true],
'Method' => ['type' => 'varchar', 'constraint'=> 50, 'null' => true], 'Method' => ['type' => 'varchar', 'constraint'=> 50, 'null' => true],
'ExpectedTAT' => ['type' => 'INT', 'null' => true], 'ExpectedTAT' => ['type' => 'INT', 'null' => true],

View File

@ -72,16 +72,16 @@ class PatResultModel extends BaseModel {
} }
} }
// Check age criteria // Check age criteria (AgeStart/AgeEnd stored in days)
if ($ref['AgeStart'] !== null || $ref['AgeEnd'] !== null) { if ($ref['AgeStart'] !== null || $ref['AgeEnd'] !== null) {
$birthdate = new \DateTime($patient['Birthdate']); $birthdate = new \DateTime($patient['Birthdate']);
$today = new \DateTime(); $today = new \DateTime();
$age = $birthdate->diff($today)->y; $ageInDays = $birthdate->diff($today, true)->days;
if ($ref['AgeStart'] !== null && $age < $ref['AgeStart']) { if ($ref['AgeStart'] !== null && $ageInDays < (int) $ref['AgeStart']) {
return null; return null;
} }
if ($ref['AgeEnd'] !== null && $age > $ref['AgeEnd']) { if ($ref['AgeEnd'] !== null && $ageInDays > (int) $ref['AgeEnd']) {
return null; return null;
} }
} }

View File

@ -74,8 +74,8 @@ class RefNumModel extends BaseModel
'HighSignLabel' => $r['HighSign'] ? \App\Libraries\ValueSet::getLabel('math_sign', $r['HighSign']) : '', 'HighSignLabel' => $r['HighSign'] ? \App\Libraries\ValueSet::getLabel('math_sign', $r['HighSign']) : '',
'High' => $r['High'] !== null ? (float) $r['High'] : null, 'High' => $r['High'] !== null ? (float) $r['High'] : null,
'Low' => $r['Low'] !== null ? (float) $r['Low'] : null, 'Low' => $r['Low'] !== null ? (float) $r['Low'] : null,
'AgeStart' => (int) $r['AgeStart'], 'AgeStart' => $r['AgeStart'] !== null ? (int) $r['AgeStart'] : null,
'AgeEnd' => (int) $r['AgeEnd'], 'AgeEnd' => $r['AgeEnd'] !== null ? (int) $r['AgeEnd'] : null,
'Flag' => $r['Flag'], 'Flag' => $r['Flag'],
'Interpretation' => $r['Interpretation'], 'Interpretation' => $r['Interpretation'],
'Notes' => $r['Notes'], 'Notes' => $r['Notes'],
@ -107,6 +107,13 @@ class RefNumModel extends BaseModel
public function batchInsert($testSiteID, $siteID, $ranges) public function batchInsert($testSiteID, $siteID, $ranges)
{ {
foreach ($ranges as $index => $range) { foreach ($ranges as $index => $range) {
$ageStart = array_key_exists('AgeStart', $range) && $range['AgeStart'] !== '' && $range['AgeStart'] !== null
? (int) $range['AgeStart']
: null;
$ageEnd = array_key_exists('AgeEnd', $range) && $range['AgeEnd'] !== '' && $range['AgeEnd'] !== null
? (int) $range['AgeEnd']
: null;
$this->insert([ $this->insert([
'TestSiteID' => $testSiteID, 'TestSiteID' => $testSiteID,
'SiteID' => $siteID, 'SiteID' => $siteID,
@ -114,8 +121,8 @@ class RefNumModel extends BaseModel
'NumRefType' => $range['NumRefType'], 'NumRefType' => $range['NumRefType'],
'RangeType' => $range['RangeType'], 'RangeType' => $range['RangeType'],
'Sex' => $range['Sex'], 'Sex' => $range['Sex'],
'AgeStart' => (int) ($range['AgeStart'] ?? 0), 'AgeStart' => $ageStart,
'AgeEnd' => (int) ($range['AgeEnd'] ?? 150), 'AgeEnd' => $ageEnd,
'LowSign' => !empty($range['LowSign']) ? $range['LowSign'] : null, 'LowSign' => !empty($range['LowSign']) ? $range['LowSign'] : null,
'Low' => !empty($range['Low']) ? (float) $range['Low'] : null, 'Low' => !empty($range['Low']) ? (float) $range['Low'] : null,
'HighSign' => !empty($range['HighSign']) ? $range['HighSign'] : null, 'HighSign' => !empty($range['HighSign']) ? $range['HighSign'] : null,

View File

@ -59,8 +59,8 @@ class RefTxtModel extends BaseModel
'TxtRefTypeLabel'=> $r['TxtRefType'] ? \App\Libraries\ValueSet::getLabel('text_ref_type', $r['TxtRefType']) : '', 'TxtRefTypeLabel'=> $r['TxtRefType'] ? \App\Libraries\ValueSet::getLabel('text_ref_type', $r['TxtRefType']) : '',
'Sex' => $r['Sex'], 'Sex' => $r['Sex'],
'SexLabel' => $r['Sex'] ? \App\Libraries\ValueSet::getLabel('gender', $r['Sex']) : '', 'SexLabel' => $r['Sex'] ? \App\Libraries\ValueSet::getLabel('gender', $r['Sex']) : '',
'AgeStart' => (int) $r['AgeStart'], 'AgeStart' => $r['AgeStart'] !== null ? (int) $r['AgeStart'] : null,
'AgeEnd' => (int) $r['AgeEnd'], 'AgeEnd' => $r['AgeEnd'] !== null ? (int) $r['AgeEnd'] : null,
'RefTxt' => $r['RefTxt'], 'RefTxt' => $r['RefTxt'],
'Flag' => $r['Flag'], 'Flag' => $r['Flag'],
]; ];
@ -91,14 +91,21 @@ class RefTxtModel extends BaseModel
public function batchInsert($testSiteID, $siteID, $ranges) public function batchInsert($testSiteID, $siteID, $ranges)
{ {
foreach ($ranges as $range) { foreach ($ranges as $range) {
$ageStart = array_key_exists('AgeStart', $range) && $range['AgeStart'] !== '' && $range['AgeStart'] !== null
? (int) $range['AgeStart']
: null;
$ageEnd = array_key_exists('AgeEnd', $range) && $range['AgeEnd'] !== '' && $range['AgeEnd'] !== null
? (int) $range['AgeEnd']
: null;
$this->insert([ $this->insert([
'TestSiteID' => $testSiteID, 'TestSiteID' => $testSiteID,
'SiteID' => $siteID, 'SiteID' => $siteID,
'SpcType' => $range['SpcType'] ?? 'GEN', 'SpcType' => $range['SpcType'] ?? 'GEN',
'TxtRefType' => $range['TxtRefType'], 'TxtRefType' => $range['TxtRefType'],
'Sex' => $range['Sex'], 'Sex' => $range['Sex'],
'AgeStart' => (int) ($range['AgeStart'] ?? 0), 'AgeStart' => $ageStart,
'AgeEnd' => (int) ($range['AgeEnd'] ?? 150), 'AgeEnd' => $ageEnd,
'RefTxt' => $range['RefTxt'] ?? '', 'RefTxt' => $range['RefTxt'] ?? '',
'Flag' => $range['Flag'] ?? null, 'Flag' => $range['Flag'] ?? null,
'CreateDate' => date('Y-m-d H:i:s'), 'CreateDate' => date('Y-m-d H:i:s'),

View File

@ -4756,8 +4756,8 @@ paths:
Low: 70 Low: 70
HighSign: LE HighSign: LE
High: 100 High: 100
AgeStart: 18 AgeStart: 6570
AgeEnd: 99 AgeEnd: 36135
Flag: 'N' Flag: 'N'
DisciplineID: 2 DisciplineID: 2
DepartmentID: 2 DepartmentID: 2
@ -4785,8 +4785,8 @@ paths:
Low: 70 Low: 70
HighSign: LE HighSign: LE
High: 100 High: 100
AgeStart: 18 AgeStart: 6570
AgeEnd: 99 AgeEnd: 36135
Flag: 'N' Flag: 'N'
- NumRefType: NMRC - NumRefType: NMRC
RangeType: REF RangeType: REF
@ -4795,8 +4795,8 @@ paths:
Low: 75 Low: 75
HighSign: < HighSign: <
High: 105 High: 105
AgeStart: 18 AgeStart: 6570
AgeEnd: 99 AgeEnd: 36135
Flag: 'N' Flag: 'N'
testmap: testmap:
- HostType: SITE - HostType: SITE
@ -4849,7 +4849,7 @@ paths:
LowSign: LT LowSign: LT
Low: 120 Low: 120
AgeStart: 0 AgeStart: 0
AgeEnd: 125 AgeEnd: 45625
Flag: H Flag: H
DisciplineID: 2 DisciplineID: 2
DepartmentID: 2 DepartmentID: 2
@ -4876,7 +4876,7 @@ paths:
LowSign: LT LowSign: LT
Low: 120 Low: 120
AgeStart: 0 AgeStart: 0
AgeEnd: 125 AgeEnd: 45625
Flag: H Flag: H
- NumRefType: THOLD - NumRefType: THOLD
RangeType: PANIC RangeType: PANIC
@ -4884,7 +4884,7 @@ paths:
LowSign: < LowSign: <
Low: 121 Low: 121
AgeStart: 0 AgeStart: 0
AgeEnd: 125 AgeEnd: 45625
Flag: H Flag: H
testmap: testmap:
- HostType: SITE - HostType: SITE
@ -4924,8 +4924,8 @@ paths:
- SpcType: GEN - SpcType: GEN
TxtRefType: TEXT TxtRefType: TEXT
Sex: '2' Sex: '2'
AgeStart: 18 AgeStart: 6570
AgeEnd: 99 AgeEnd: 36135
RefTxt: NORM=Normal;HIGH=High RefTxt: NORM=Normal;HIGH=High
Flag: 'N' Flag: 'N'
DisciplineID: 1 DisciplineID: 1
@ -4949,15 +4949,15 @@ paths:
- SpcType: GEN - SpcType: GEN
TxtRefType: TEXT TxtRefType: TEXT
Sex: '2' Sex: '2'
AgeStart: 18 AgeStart: 6570
AgeEnd: 99 AgeEnd: 36135
RefTxt: NORM=Normal RefTxt: NORM=Normal
Flag: 'N' Flag: 'N'
- SpcType: GEN - SpcType: GEN
TxtRefType: TEXT TxtRefType: TEXT
Sex: '1' Sex: '1'
AgeStart: 18 AgeStart: 6570
AgeEnd: 99 AgeEnd: 36135
RefTxt: ABN=Abnormal RefTxt: ABN=Abnormal
Flag: 'N' Flag: 'N'
testmap: testmap:
@ -4992,7 +4992,7 @@ paths:
TxtRefType: VSET TxtRefType: VSET
Sex: '2' Sex: '2'
AgeStart: 0 AgeStart: 0
AgeEnd: 120 AgeEnd: 43800
RefTxt: NORM=Normal;MACRO=Macro RefTxt: NORM=Normal;MACRO=Macro
Flag: 'N' Flag: 'N'
DisciplineID: 4 DisciplineID: 4
@ -5017,7 +5017,7 @@ paths:
TxtRefType: VSET TxtRefType: VSET
Sex: '2' Sex: '2'
AgeStart: 0 AgeStart: 0
AgeEnd: 120 AgeEnd: 43800
RefTxt: NORM=Normal;ABN=Abnormal RefTxt: NORM=Normal;ABN=Abnormal
Flag: 'N' Flag: 'N'
testmap: testmap:
@ -5102,8 +5102,8 @@ paths:
Low: 10 Low: 10
HighSign: LE HighSign: LE
High: 20 High: 20
AgeStart: 18 AgeStart: 6570
AgeEnd: 120 AgeEnd: 43800
Flag: 'N' Flag: 'N'
testmap: testmap:
- HostType: SITE - HostType: SITE
@ -7102,8 +7102,10 @@ components:
format: float format: float
AgeStart: AgeStart:
type: integer type: integer
description: Minimum patient age in days
AgeEnd: AgeEnd:
type: integer type: integer
description: Maximum patient age in days
Flag: Flag:
type: string type: string
Interpretation: Interpretation:
@ -7133,8 +7135,10 @@ components:
type: string type: string
AgeStart: AgeStart:
type: integer type: integer
description: Minimum patient age in days
AgeEnd: AgeEnd:
type: integer type: integer
description: Maximum patient age in days
RefTxt: RefTxt:
type: string type: string
Flag: Flag:
@ -7176,8 +7180,8 @@ components:
HighSignLabel: <= HighSignLabel: <=
Low: 70 Low: 70
High: 100 High: 100
AgeStart: 18 AgeStart: 6570
AgeEnd: 99 AgeEnd: 36135
Flag: 'N' Flag: 'N'
Interpretation: Normal Interpretation: Normal
TEST_threshold: TEST_threshold:
@ -7212,7 +7216,7 @@ components:
LowSignLabel: < LowSignLabel: <
High: 40 High: 40
AgeStart: 0 AgeStart: 0
AgeEnd: 120 AgeEnd: 43800
Flag: L Flag: L
Interpretation: Critical Low Interpretation: Critical Low
TEST_text: TEST_text:
@ -7238,8 +7242,8 @@ components:
TxtRefTypeLabel: Text TxtRefTypeLabel: Text
Sex: '2' Sex: '2'
SexLabel: Male SexLabel: Male
AgeStart: 18 AgeStart: 6570
AgeEnd: 99 AgeEnd: 36135
RefTxt: NORM=Normal;HYPO=Hypochromic;MACRO=Macrocytic RefTxt: NORM=Normal;HYPO=Hypochromic;MACRO=Macrocytic
Flag: 'N' Flag: 'N'
PARAM: PARAM:
@ -7306,8 +7310,8 @@ components:
HighSignLabel: <= HighSignLabel: <=
Low: 10 Low: 10
High: 20 High: 20
AgeStart: 18 AgeStart: 6570
AgeEnd: 120 AgeEnd: 43800
Flag: 'N' Flag: 'N'
Interpretation: Normal Interpretation: Normal
GROUP: GROUP:

View File

@ -264,8 +264,10 @@ TestDefinition:
format: float format: float
AgeStart: AgeStart:
type: integer type: integer
description: Minimum patient age in days
AgeEnd: AgeEnd:
type: integer type: integer
description: Maximum patient age in days
Flag: Flag:
type: string type: string
Interpretation: Interpretation:
@ -293,8 +295,10 @@ TestDefinition:
type: string type: string
AgeStart: AgeStart:
type: integer type: integer
description: Minimum patient age in days
AgeEnd: AgeEnd:
type: integer type: integer
description: Maximum patient age in days
RefTxt: RefTxt:
type: string type: string
Flag: Flag:
@ -336,8 +340,8 @@ TestDefinition:
HighSignLabel: "<=" HighSignLabel: "<="
Low: 70 Low: 70
High: 100 High: 100
AgeStart: 18 AgeStart: 6570
AgeEnd: 99 AgeEnd: 36135
Flag: N Flag: N
Interpretation: Normal Interpretation: Normal
TEST_threshold: TEST_threshold:
@ -372,7 +376,7 @@ TestDefinition:
LowSignLabel: "<" LowSignLabel: "<"
High: 40 High: 40
AgeStart: 0 AgeStart: 0
AgeEnd: 120 AgeEnd: 43800
Flag: L Flag: L
Interpretation: Critical Low Interpretation: Critical Low
TEST_text: TEST_text:
@ -398,8 +402,8 @@ TestDefinition:
TxtRefTypeLabel: Text TxtRefTypeLabel: Text
Sex: '2' Sex: '2'
SexLabel: Male SexLabel: Male
AgeStart: 18 AgeStart: 6570
AgeEnd: 99 AgeEnd: 36135
RefTxt: 'NORM=Normal;HYPO=Hypochromic;MACRO=Macrocytic' RefTxt: 'NORM=Normal;HYPO=Hypochromic;MACRO=Macrocytic'
Flag: N Flag: N
PARAM: PARAM:
@ -466,8 +470,8 @@ TestDefinition:
HighSignLabel: "<=" HighSignLabel: "<="
Low: 10 Low: 10
High: 20 High: 20
AgeStart: 18 AgeStart: 6570
AgeEnd: 120 AgeEnd: 43800
Flag: N Flag: N
Interpretation: Normal Interpretation: Normal
GROUP: GROUP:

View File

@ -270,8 +270,8 @@
Low: 70 Low: 70
HighSign: LE HighSign: LE
High: 100 High: 100
AgeStart: 18 AgeStart: 6570
AgeEnd: 99 AgeEnd: 36135
Flag: N Flag: N
DisciplineID: 2 DisciplineID: 2
DepartmentID: 2 DepartmentID: 2
@ -299,8 +299,8 @@
Low: 70 Low: 70
HighSign: LE HighSign: LE
High: 100 High: 100
AgeStart: 18 AgeStart: 6570
AgeEnd: 99 AgeEnd: 36135
Flag: N Flag: N
- NumRefType: NMRC - NumRefType: NMRC
RangeType: REF RangeType: REF
@ -309,8 +309,8 @@
Low: 75 Low: 75
HighSign: < HighSign: <
High: 105 High: 105
AgeStart: 18 AgeStart: 6570
AgeEnd: 99 AgeEnd: 36135
Flag: N Flag: N
testmap: testmap:
- HostType: SITE - HostType: SITE
@ -363,7 +363,7 @@
LowSign: LT LowSign: LT
Low: 120 Low: 120
AgeStart: 0 AgeStart: 0
AgeEnd: 125 AgeEnd: 45625
Flag: H Flag: H
DisciplineID: 2 DisciplineID: 2
DepartmentID: 2 DepartmentID: 2
@ -390,7 +390,7 @@
LowSign: LT LowSign: LT
Low: 120 Low: 120
AgeStart: 0 AgeStart: 0
AgeEnd: 125 AgeEnd: 45625
Flag: H Flag: H
- NumRefType: THOLD - NumRefType: THOLD
RangeType: PANIC RangeType: PANIC
@ -398,7 +398,7 @@
LowSign: < LowSign: <
Low: 121 Low: 121
AgeStart: 0 AgeStart: 0
AgeEnd: 125 AgeEnd: 45625
Flag: H Flag: H
testmap: testmap:
- HostType: SITE - HostType: SITE
@ -438,8 +438,8 @@
- SpcType: GEN - SpcType: GEN
TxtRefType: TEXT TxtRefType: TEXT
Sex: '2' Sex: '2'
AgeStart: 18 AgeStart: 6570
AgeEnd: 99 AgeEnd: 36135
RefTxt: NORM=Normal;HIGH=High RefTxt: NORM=Normal;HIGH=High
Flag: N Flag: N
DisciplineID: 1 DisciplineID: 1
@ -463,15 +463,15 @@
- SpcType: GEN - SpcType: GEN
TxtRefType: TEXT TxtRefType: TEXT
Sex: '2' Sex: '2'
AgeStart: 18 AgeStart: 6570
AgeEnd: 99 AgeEnd: 36135
RefTxt: NORM=Normal RefTxt: NORM=Normal
Flag: N Flag: N
- SpcType: GEN - SpcType: GEN
TxtRefType: TEXT TxtRefType: TEXT
Sex: '1' Sex: '1'
AgeStart: 18 AgeStart: 6570
AgeEnd: 99 AgeEnd: 36135
RefTxt: ABN=Abnormal RefTxt: ABN=Abnormal
Flag: N Flag: N
testmap: testmap:
@ -506,7 +506,7 @@
TxtRefType: VSET TxtRefType: VSET
Sex: '2' Sex: '2'
AgeStart: 0 AgeStart: 0
AgeEnd: 120 AgeEnd: 43800
RefTxt: NORM=Normal;MACRO=Macro RefTxt: NORM=Normal;MACRO=Macro
Flag: N Flag: N
DisciplineID: 4 DisciplineID: 4
@ -531,7 +531,7 @@
TxtRefType: VSET TxtRefType: VSET
Sex: '2' Sex: '2'
AgeStart: 0 AgeStart: 0
AgeEnd: 120 AgeEnd: 43800
RefTxt: NORM=Normal;ABN=Abnormal RefTxt: NORM=Normal;ABN=Abnormal
Flag: N Flag: N
testmap: testmap:
@ -616,8 +616,8 @@
Low: 10 Low: 10
HighSign: LE HighSign: LE
High: 20 High: 20
AgeStart: 18 AgeStart: 6570
AgeEnd: 120 AgeEnd: 43800
Flag: N Flag: N
testmap: testmap:
- HostType: SITE - HostType: SITE

View File

@ -31,6 +31,54 @@ class TestCreateVariantsTest extends CIUnitTestCase
} }
} }
public function testCreateTechnicalCanAcceptNullSiteAndNumericFields(): void
{
$payload = $this->buildTechnicalPayload('TEST', [
'ResultType' => 'NMRIC',
'RefType' => 'RANGE',
'Decimal' => null,
'Factor' => 2.5,
'ReqQty' => 1.75,
'ReqQtyUnit' => 'uL',
]);
$payload['SiteID'] = null;
$payload['SeqScr'] = null;
$payload['SeqRpt'] = null;
$payload['refnum'] = [
[
'NumRefType' => 'NMRC',
'RangeType' => 'REF',
'Sex' => '2',
'LowSign' => 'GE',
'Low' => 10,
'HighSign' => 'LE',
'High' => 20,
'AgeStart' => null,
'AgeEnd' => null,
'Flag' => 'N',
'Interpretation' => 'Nullable range',
'SpcType' => 'GEN',
],
];
$response = $this->withBodyFormat('json')->call('post', $this->endpoint, $payload);
$response->assertStatus(201);
$testSiteId = json_decode($response->getJSON(), true)['data']['TestSiteId'];
$show = $this->call('get', $this->endpoint . '/' . $testSiteId);
$show->assertStatus(200);
$data = json_decode($show->getJSON(), true)['data'];
$this->assertNull($data['SiteID']);
$this->assertNull($data['SeqScr']);
$this->assertNull($data['SeqRpt']);
$this->assertNull($data['Decimal']);
$this->assertSame(2.5, (float) $data['Factor']);
$this->assertSame(1.75, (float) $data['ReqQty']);
$this->assertNull($data['refnum'][0]['AgeStart']);
$this->assertNull($data['refnum'][0]['AgeEnd']);
}
public function testCreateTechnicalWithNumericReference(): void public function testCreateTechnicalWithNumericReference(): void
{ {
$refnum = $this->buildRefNumEntries('NMRC', true); $refnum = $this->buildRefNumEntries('NMRC', true);
@ -42,6 +90,42 @@ class TestCreateVariantsTest extends CIUnitTestCase
} }
} }
public function testCreateTechnicalNumericReferenceReturnsAgeInDays(): void
{
$payload = $this->buildTechnicalPayload('TEST', [
'ResultType' => 'NMRIC',
'RefType' => 'RANGE',
]);
$payload['refnum'] = [
[
'NumRefType' => 'NMRC',
'RangeType' => 'REF',
'Sex' => '2',
'LowSign' => 'GE',
'Low' => 10,
'HighSign' => 'LE',
'High' => 20,
'AgeStart' => 6570,
'AgeEnd' => 36135,
'Flag' => 'N',
'Interpretation' => 'Adult range in days',
'SpcType' => 'GEN',
],
];
$response = $this->withBodyFormat('json')->call('post', $this->endpoint, $payload);
$response->assertStatus(201);
$testSiteId = json_decode($response->getJSON(), true)['data']['TestSiteId'];
$show = $this->call('get', $this->endpoint . '/' . $testSiteId);
$show->assertStatus(200);
$data = json_decode($show->getJSON(), true)['data'];
$this->assertSame(6570, (int) $data['refnum'][0]['AgeStart']);
$this->assertSame(36135, (int) $data['refnum'][0]['AgeEnd']);
}
public function testNumericRefRangeNotesPersistAfterCreate(): void public function testNumericRefRangeNotesPersistAfterCreate(): void
{ {
$notes = 'Auto note ' . uniqid(); $notes = 'Auto note ' . uniqid();
@ -339,10 +423,10 @@ class TestCreateVariantsTest extends CIUnitTestCase
'DepartmentID' => $details['DepartmentID'] ?? 2, 'DepartmentID' => $details['DepartmentID'] ?? 2,
'Method' => $details['Method'] ?? 'Automated test', 'Method' => $details['Method'] ?? 'Automated test',
'Unit1' => $details['Unit1'] ?? 'mg/dL', 'Unit1' => $details['Unit1'] ?? 'mg/dL',
'Decimal' => $details['Decimal'] ?? 0, 'Decimal' => array_key_exists('Decimal', $details) ? $details['Decimal'] : 0,
]; ];
foreach (['ResultType', 'RefType', 'FormulaCode', 'members', 'ExpectedTAT'] as $key) { foreach (['ResultType', 'RefType', 'FormulaCode', 'members', '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];
} }
@ -364,7 +448,7 @@ class TestCreateVariantsTest extends CIUnitTestCase
'HighSign' => 'LE', 'HighSign' => 'LE',
'High' => $numRefType === 'THOLD' ? 40 : 20, 'High' => $numRefType === 'THOLD' ? 40 : 20,
'AgeStart' => 0, 'AgeStart' => 0,
'AgeEnd' => 120, 'AgeEnd' => 43800,
'Flag' => 'N', 'Flag' => 'N',
'Interpretation' => 'Normal range', 'Interpretation' => 'Normal range',
'SpcType' => 'GEN', 'SpcType' => 'GEN',
@ -382,7 +466,7 @@ class TestCreateVariantsTest extends CIUnitTestCase
'HighSign' => '<', 'HighSign' => '<',
'High' => $numRefType === 'THOLD' ? 50 : 15, 'High' => $numRefType === 'THOLD' ? 50 : 15,
'AgeStart' => 0, 'AgeStart' => 0,
'AgeEnd' => 99, 'AgeEnd' => 36135,
'Flag' => 'N', 'Flag' => 'N',
'Interpretation' => 'Alternate range', 'Interpretation' => 'Alternate range',
'SpcType' => 'GEN', 'SpcType' => 'GEN',
@ -401,7 +485,7 @@ class TestCreateVariantsTest extends CIUnitTestCase
'TxtRefType' => $txtRefType, 'TxtRefType' => $txtRefType,
'Sex' => '2', 'Sex' => '2',
'AgeStart' => 0, 'AgeStart' => 0,
'AgeEnd' => 120, 'AgeEnd' => 43800,
'RefTxt' => $txtRefType === 'VSET' ? 'NORM=Normal;ABN=Abnormal' : 'NORM=Normal', 'RefTxt' => $txtRefType === 'VSET' ? 'NORM=Normal;ABN=Abnormal' : 'NORM=Normal',
'Flag' => 'N', 'Flag' => 'N',
], ],
@ -413,7 +497,7 @@ class TestCreateVariantsTest extends CIUnitTestCase
'TxtRefType' => $txtRefType, 'TxtRefType' => $txtRefType,
'Sex' => '1', 'Sex' => '1',
'AgeStart' => 0, 'AgeStart' => 0,
'AgeEnd' => 120, 'AgeEnd' => 43800,
'RefTxt' => $txtRefType === 'VSET' ? 'HIGH=High;LOW=Low' : 'ABN=Abnormal', 'RefTxt' => $txtRefType === 'VSET' ? 'HIGH=High;LOW=Low' : 'ABN=Abnormal',
'Flag' => 'N', 'Flag' => 'N',
]; ];