From 8aefeaca017aafb429944274b7240c73a18adcf0 Mon Sep 17 00:00:00 2001 From: mahdahar <89adham@gmail.com> Date: Wed, 1 Apr 2026 13:28:44 +0700 Subject: [PATCH] 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. --- app/Controllers/Test/TestsController.php | 129 +++++++++--------- ...026-01-01-000004_CreateTestDefinitions.php | 14 +- app/Models/PatResultModel.php | 26 ++-- app/Models/RefRange/RefNumModel.php | 21 ++- app/Models/RefRange/RefTxtModel.php | 19 ++- public/api-docs.bundled.yaml | 56 ++++---- public/components/schemas/tests.yaml | 26 ++-- public/paths/tests.yaml | 38 +++--- tests/feature/Test/TestCreateVariantsTest.php | 96 ++++++++++++- 9 files changed, 265 insertions(+), 160 deletions(-) diff --git a/app/Controllers/Test/TestsController.php b/app/Controllers/Test/TestsController.php index 8da3acb..da9e70c 100644 --- a/app/Controllers/Test/TestsController.php +++ b/app/Controllers/Test/TestsController.php @@ -30,12 +30,11 @@ class TestsController extends BaseController $this->modelRefNum = new \App\Models\RefRange\RefNumModel; $this->modelRefTxt = new \App\Models\RefRange\RefTxtModel; - $this->rules = [ - 'TestSiteCode' => 'required', - 'TestSiteName' => 'required', - 'TestType' => 'required', - 'SiteID' => 'required', - ]; + $this->rules = [ + 'TestSiteCode' => 'required', + 'TestSiteName' => 'required', + 'TestType' => 'required', + ]; } public function index() @@ -148,15 +147,15 @@ class TestsController extends BaseController $db->transStart(); try { - $testSiteData = [ - 'SiteID' => $input['SiteID'], - 'TestSiteCode'=> $input['TestSiteCode'], - 'TestSiteName'=> $input['TestSiteName'], - 'TestType' => $input['TestType'], - 'Description' => $input['Description'] ?? null, - 'SeqScr' => $input['SeqScr'] ?? 0, - 'SeqRpt' => $input['SeqRpt'] ?? 0, - 'IndentLeft' => $input['IndentLeft'] ?? 0, + $testSiteData = [ + 'SiteID' => array_key_exists('SiteID', $input) ? $input['SiteID'] : null, + 'TestSiteCode'=> $input['TestSiteCode'], + 'TestSiteName'=> $input['TestSiteName'], + 'TestType' => $input['TestType'], + 'Description' => $input['Description'] ?? null, + 'SeqScr' => array_key_exists('SeqScr', $input) ? $input['SeqScr'] : null, + 'SeqRpt' => array_key_exists('SeqRpt', $input) ? $input['SeqRpt'] : null, + 'IndentLeft' => $input['IndentLeft'] ?? 0, 'FontStyle' => $input['FontStyle'] ?? null, 'isVisibleScr' => $input['isVisibleScr'] ?? 1, 'isVisibleRpt' => $input['isVisibleRpt'] ?? 1, @@ -372,9 +371,9 @@ class TestsController extends BaseController $typeCode = $testTypeID; - $details = $input['details'] ?? $input; - $details['TestSiteID'] = $testSiteID; - $details['SiteID'] = $input['SiteID'] ?? 1; + $details = $input['details'] ?? $input; + $details['TestSiteID'] = $testSiteID; + $details['SiteID'] = array_key_exists('SiteID', $input) ? $input['SiteID'] : null; switch ($typeCode) { case 'CALC': @@ -403,14 +402,14 @@ class TestsController extends BaseController $refType = (string) $details['RefType']; $resultType = $details['ResultType'] ?? ''; - if (TestValidationService::usesRefNum($resultType, $refType) && isset($input['refnum']) && is_array($input['refnum'])) { - $this->saveRefNumRanges($testSiteID, $input['refnum'], $action, $input['SiteID'] ?? 1); - } - - if (TestValidationService::usesRefTxt($resultType, $refType) && isset($input['reftxt']) && is_array($input['reftxt'])) { - $this->saveRefTxtRanges($testSiteID, $input['reftxt'], $action, $input['SiteID'] ?? 1); - } - } + if (TestValidationService::usesRefNum($resultType, $refType) && isset($input['refnum']) && is_array($input['refnum'])) { + $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'])) { + $this->saveRefTxtRanges($testSiteID, $input['reftxt'], $action, array_key_exists('SiteID', $input) ? $input['SiteID'] : null); + } + } break; } @@ -420,30 +419,30 @@ class TestsController extends BaseController } } - private function saveTechDetails($testSiteID, $data, $action, $typeCode) - { - $techData = [ - 'DisciplineID' => $data['DisciplineID'] ?? null, - 'DepartmentID' => $data['DepartmentID'] ?? null, - 'ResultType' => $data['ResultType'] ?? null, - 'RefType' => $data['RefType'] ?? null, - 'VSet' => $data['VSet'] ?? null, - 'ReqQty' => $data['ReqQty'] ?? null, - 'ReqQtyUnit' => $data['ReqQtyUnit'] ?? null, - 'Unit1' => $data['Unit1'] ?? null, - 'Factor' => $data['Factor'] ?? null, - 'Unit2' => $data['Unit2'] ?? null, - 'Decimal' => $data['Decimal'] ?? 2, - 'CollReq' => $data['CollReq'] ?? null, - 'Method' => $data['Method'] ?? null, - 'ExpectedTAT' => $data['ExpectedTAT'] ?? null, - ]; - - $this->model->update($testSiteID, $techData); - } - - private function saveRefNumRanges($testSiteID, $ranges, $action, $siteID) - { + private function saveTechDetails($testSiteID, $data, $action, $typeCode) + { + $techData = [ + 'DisciplineID' => $data['DisciplineID'] ?? null, + 'DepartmentID' => $data['DepartmentID'] ?? null, + 'ResultType' => $data['ResultType'] ?? null, + 'RefType' => $data['RefType'] ?? null, + 'VSet' => $data['VSet'] ?? null, + 'ReqQty' => $data['ReqQty'] ?? null, + 'ReqQtyUnit' => $data['ReqQtyUnit'] ?? null, + 'Unit1' => $data['Unit1'] ?? null, + 'Factor' => $data['Factor'] ?? null, + 'Unit2' => $data['Unit2'] ?? null, + 'Decimal' => array_key_exists('Decimal', $data) ? $data['Decimal'] : null, + 'CollReq' => $data['CollReq'] ?? null, + 'Method' => $data['Method'] ?? null, + 'ExpectedTAT' => $data['ExpectedTAT'] ?? null, + ]; + + $this->model->update($testSiteID, $techData); + } + + private function saveRefNumRanges($testSiteID, $ranges, $action, $siteID) + { if ($action === 'update') { $this->modelRefNum->disableByTestSiteID($testSiteID); } @@ -460,21 +459,21 @@ class TestsController extends BaseController $this->modelRefTxt->batchInsert($testSiteID, $siteID, $ranges); } - private function saveCalcDetails($testSiteID, $data, $input, $action) - { - $calcData = [ - 'TestSiteID' => $testSiteID, - 'DisciplineID' => $data['DisciplineID'] ?? null, - 'DepartmentID' => $data['DepartmentID'] ?? null, - 'FormulaCode' => $data['FormulaCode'] ?? $data['Formula'] ?? null, - 'ResultType' => 'NMRIC', - 'RefType' => $data['RefType'] ?? 'RANGE', - 'Unit1' => $data['Unit1'] ?? $data['ResultUnit'] ?? null, - 'Factor' => $data['Factor'] ?? null, - 'Unit2' => $data['Unit2'] ?? null, - 'Decimal' => $data['Decimal'] ?? 2, - 'Method' => $data['Method'] ?? null, - ]; + private function saveCalcDetails($testSiteID, $data, $input, $action) + { + $calcData = [ + 'TestSiteID' => $testSiteID, + 'DisciplineID' => $data['DisciplineID'] ?? null, + 'DepartmentID' => $data['DepartmentID'] ?? null, + 'FormulaCode' => $data['FormulaCode'] ?? $data['Formula'] ?? null, + 'ResultType' => 'NMRIC', + 'RefType' => $data['RefType'] ?? 'RANGE', + 'Unit1' => $data['Unit1'] ?? $data['ResultUnit'] ?? null, + 'Factor' => $data['Factor'] ?? null, + 'Unit2' => $data['Unit2'] ?? null, + 'Decimal' => array_key_exists('Decimal', $data) ? $data['Decimal'] : null, + 'Method' => $data['Method'] ?? null, + ]; if ($action === 'update') { $exists = $this->modelCal->existsByTestSiteID($testSiteID); diff --git a/app/Database/Migrations/2026-01-01-000004_CreateTestDefinitions.php b/app/Database/Migrations/2026-01-01-000004_CreateTestDefinitions.php index 0174ee1..064028f 100644 --- a/app/Database/Migrations/2026-01-01-000004_CreateTestDefinitions.php +++ b/app/Database/Migrations/2026-01-01-000004_CreateTestDefinitions.php @@ -8,7 +8,7 @@ class CreateTestDefinitions extends Migration { public function up() { $this->forge->addField([ '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], 'TestSiteName' => ['type' => 'varchar', 'constraint'=> 100, 'null' => false], 'TestType' => ['type' => 'VARCHAR', 'constraint' => 10, 'null' => false], @@ -23,7 +23,7 @@ class CreateTestDefinitions extends Migration { 'Unit1' => ['type' => 'varchar', 'constraint'=> 20, 'null' => true], 'Factor' => ['type' => 'DECIMAL', 'constraint'=> '10,4', 'null' => true], 'Unit2' => ['type' => 'varchar', 'constraint'=> 20, 'null' => true], - 'Decimal' => ['type' => 'int', 'null' => true, 'default' => 2], + 'Decimal' => ['type' => 'int', 'null' => true ], 'CollReq' => ['type' => 'varchar', 'constraint'=> 255, 'null' => true], 'Method' => ['type' => 'varchar', 'constraint'=> 50, 'null' => true], 'ExpectedTAT' => ['type' => 'INT', 'null' => true], @@ -31,11 +31,11 @@ class CreateTestDefinitions extends Migration { 'SeqRpt' => ['type' => 'int', 'null' => true], 'IndentLeft' => ['type' => 'int', 'null' => true, 'default' => 0], 'FontStyle' => ['type' => 'varchar', 'constraint'=> 50, 'null' => true], - 'isVisibleScr' => ['type' => 'int', 'null' => true, 'default' => 1], - 'isVisibleRpt' => ['type' => 'int', 'null' => true, 'default' => 1], - 'isCountStat' => ['type' => 'int', 'null' => true, 'default' => 1], - 'Level' => ['type' => 'int', 'null' => true], - 'isRequestable' => [ 'type' => 'TINYINT', 'constraint' => 1, 'null' => true, 'default' => 1, 'comment' => 'Flag indicating if test can be requested (1=yes, 0=no)' ], + 'isVisibleScr' => ['type' => 'int', 'null' => true, 'default' => 1], + 'isVisibleRpt' => ['type' => 'int', 'null' => true, 'default' => 1], + 'isCountStat' => ['type' => 'int', 'null' => true, 'default' => 1], + 'Level' => ['type' => 'int', 'null' => true], + 'isRequestable' => [ 'type' => 'TINYINT', 'constraint' => 1, 'null' => true, 'default' => 1, 'comment' => 'Flag indicating if test can be requested (1=yes, 0=no)' ], 'CreateDate' => ['type' => 'Datetime', 'null' => true], 'StartDate' => ['type' => 'Datetime', 'null' => true], 'EndDate' => ['type' => 'Datetime', 'null' => true], diff --git a/app/Models/PatResultModel.php b/app/Models/PatResultModel.php index 2a979c4..b95b71c 100644 --- a/app/Models/PatResultModel.php +++ b/app/Models/PatResultModel.php @@ -72,19 +72,19 @@ class PatResultModel extends BaseModel { } } - // Check age criteria - if ($ref['AgeStart'] !== null || $ref['AgeEnd'] !== null) { - $birthdate = new \DateTime($patient['Birthdate']); - $today = new \DateTime(); - $age = $birthdate->diff($today)->y; - - if ($ref['AgeStart'] !== null && $age < $ref['AgeStart']) { - return null; - } - if ($ref['AgeEnd'] !== null && $age > $ref['AgeEnd']) { - return null; - } - } + // Check age criteria (AgeStart/AgeEnd stored in days) + if ($ref['AgeStart'] !== null || $ref['AgeEnd'] !== null) { + $birthdate = new \DateTime($patient['Birthdate']); + $today = new \DateTime(); + $ageInDays = $birthdate->diff($today, true)->days; + + if ($ref['AgeStart'] !== null && $ageInDays < (int) $ref['AgeStart']) { + return null; + } + if ($ref['AgeEnd'] !== null && $ageInDays > (int) $ref['AgeEnd']) { + return null; + } + } $value = floatval($resultValue); $low = floatval($ref['Low']); diff --git a/app/Models/RefRange/RefNumModel.php b/app/Models/RefRange/RefNumModel.php index 48230c8..b1fdae2 100644 --- a/app/Models/RefRange/RefNumModel.php +++ b/app/Models/RefRange/RefNumModel.php @@ -74,8 +74,8 @@ class RefNumModel extends BaseModel 'HighSignLabel' => $r['HighSign'] ? \App\Libraries\ValueSet::getLabel('math_sign', $r['HighSign']) : '', 'High' => $r['High'] !== null ? (float) $r['High'] : null, 'Low' => $r['Low'] !== null ? (float) $r['Low'] : null, - 'AgeStart' => (int) $r['AgeStart'], - 'AgeEnd' => (int) $r['AgeEnd'], + 'AgeStart' => $r['AgeStart'] !== null ? (int) $r['AgeStart'] : null, + 'AgeEnd' => $r['AgeEnd'] !== null ? (int) $r['AgeEnd'] : null, 'Flag' => $r['Flag'], 'Interpretation' => $r['Interpretation'], 'Notes' => $r['Notes'], @@ -104,9 +104,16 @@ class RefNumModel extends BaseModel * @param array $ranges * @return void */ - public function batchInsert($testSiteID, $siteID, $ranges) - { - foreach ($ranges as $index => $range) { + public function batchInsert($testSiteID, $siteID, $ranges) + { + 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([ 'TestSiteID' => $testSiteID, 'SiteID' => $siteID, @@ -114,8 +121,8 @@ class RefNumModel extends BaseModel 'NumRefType' => $range['NumRefType'], 'RangeType' => $range['RangeType'], 'Sex' => $range['Sex'], - 'AgeStart' => (int) ($range['AgeStart'] ?? 0), - 'AgeEnd' => (int) ($range['AgeEnd'] ?? 150), + 'AgeStart' => $ageStart, + 'AgeEnd' => $ageEnd, 'LowSign' => !empty($range['LowSign']) ? $range['LowSign'] : null, 'Low' => !empty($range['Low']) ? (float) $range['Low'] : null, 'HighSign' => !empty($range['HighSign']) ? $range['HighSign'] : null, diff --git a/app/Models/RefRange/RefTxtModel.php b/app/Models/RefRange/RefTxtModel.php index f3d0b42..7d3a9a5 100644 --- a/app/Models/RefRange/RefTxtModel.php +++ b/app/Models/RefRange/RefTxtModel.php @@ -59,8 +59,8 @@ class RefTxtModel extends BaseModel 'TxtRefTypeLabel'=> $r['TxtRefType'] ? \App\Libraries\ValueSet::getLabel('text_ref_type', $r['TxtRefType']) : '', 'Sex' => $r['Sex'], 'SexLabel' => $r['Sex'] ? \App\Libraries\ValueSet::getLabel('gender', $r['Sex']) : '', - 'AgeStart' => (int) $r['AgeStart'], - 'AgeEnd' => (int) $r['AgeEnd'], + 'AgeStart' => $r['AgeStart'] !== null ? (int) $r['AgeStart'] : null, + 'AgeEnd' => $r['AgeEnd'] !== null ? (int) $r['AgeEnd'] : null, 'RefTxt' => $r['RefTxt'], 'Flag' => $r['Flag'], ]; @@ -88,17 +88,24 @@ class RefTxtModel extends BaseModel * @param array $ranges * @return void */ - public function batchInsert($testSiteID, $siteID, $ranges) - { + public function batchInsert($testSiteID, $siteID, $ranges) + { 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([ 'TestSiteID' => $testSiteID, 'SiteID' => $siteID, 'SpcType' => $range['SpcType'] ?? 'GEN', 'TxtRefType' => $range['TxtRefType'], 'Sex' => $range['Sex'], - 'AgeStart' => (int) ($range['AgeStart'] ?? 0), - 'AgeEnd' => (int) ($range['AgeEnd'] ?? 150), + 'AgeStart' => $ageStart, + 'AgeEnd' => $ageEnd, 'RefTxt' => $range['RefTxt'] ?? '', 'Flag' => $range['Flag'] ?? null, 'CreateDate' => date('Y-m-d H:i:s'), diff --git a/public/api-docs.bundled.yaml b/public/api-docs.bundled.yaml index 7975d54..c41effa 100644 --- a/public/api-docs.bundled.yaml +++ b/public/api-docs.bundled.yaml @@ -4756,8 +4756,8 @@ paths: Low: 70 HighSign: LE High: 100 - AgeStart: 18 - AgeEnd: 99 + AgeStart: 6570 + AgeEnd: 36135 Flag: 'N' DisciplineID: 2 DepartmentID: 2 @@ -4785,8 +4785,8 @@ paths: Low: 70 HighSign: LE High: 100 - AgeStart: 18 - AgeEnd: 99 + AgeStart: 6570 + AgeEnd: 36135 Flag: 'N' - NumRefType: NMRC RangeType: REF @@ -4795,8 +4795,8 @@ paths: Low: 75 HighSign: < High: 105 - AgeStart: 18 - AgeEnd: 99 + AgeStart: 6570 + AgeEnd: 36135 Flag: 'N' testmap: - HostType: SITE @@ -4849,7 +4849,7 @@ paths: LowSign: LT Low: 120 AgeStart: 0 - AgeEnd: 125 + AgeEnd: 45625 Flag: H DisciplineID: 2 DepartmentID: 2 @@ -4876,7 +4876,7 @@ paths: LowSign: LT Low: 120 AgeStart: 0 - AgeEnd: 125 + AgeEnd: 45625 Flag: H - NumRefType: THOLD RangeType: PANIC @@ -4884,7 +4884,7 @@ paths: LowSign: < Low: 121 AgeStart: 0 - AgeEnd: 125 + AgeEnd: 45625 Flag: H testmap: - HostType: SITE @@ -4924,8 +4924,8 @@ paths: - SpcType: GEN TxtRefType: TEXT Sex: '2' - AgeStart: 18 - AgeEnd: 99 + AgeStart: 6570 + AgeEnd: 36135 RefTxt: NORM=Normal;HIGH=High Flag: 'N' DisciplineID: 1 @@ -4949,15 +4949,15 @@ paths: - SpcType: GEN TxtRefType: TEXT Sex: '2' - AgeStart: 18 - AgeEnd: 99 + AgeStart: 6570 + AgeEnd: 36135 RefTxt: NORM=Normal Flag: 'N' - SpcType: GEN TxtRefType: TEXT Sex: '1' - AgeStart: 18 - AgeEnd: 99 + AgeStart: 6570 + AgeEnd: 36135 RefTxt: ABN=Abnormal Flag: 'N' testmap: @@ -4992,7 +4992,7 @@ paths: TxtRefType: VSET Sex: '2' AgeStart: 0 - AgeEnd: 120 + AgeEnd: 43800 RefTxt: NORM=Normal;MACRO=Macro Flag: 'N' DisciplineID: 4 @@ -5017,7 +5017,7 @@ paths: TxtRefType: VSET Sex: '2' AgeStart: 0 - AgeEnd: 120 + AgeEnd: 43800 RefTxt: NORM=Normal;ABN=Abnormal Flag: 'N' testmap: @@ -5102,8 +5102,8 @@ paths: Low: 10 HighSign: LE High: 20 - AgeStart: 18 - AgeEnd: 120 + AgeStart: 6570 + AgeEnd: 43800 Flag: 'N' testmap: - HostType: SITE @@ -7102,8 +7102,10 @@ components: format: float AgeStart: type: integer + description: Minimum patient age in days AgeEnd: type: integer + description: Maximum patient age in days Flag: type: string Interpretation: @@ -7133,8 +7135,10 @@ components: type: string AgeStart: type: integer + description: Minimum patient age in days AgeEnd: type: integer + description: Maximum patient age in days RefTxt: type: string Flag: @@ -7176,8 +7180,8 @@ components: HighSignLabel: <= Low: 70 High: 100 - AgeStart: 18 - AgeEnd: 99 + AgeStart: 6570 + AgeEnd: 36135 Flag: 'N' Interpretation: Normal TEST_threshold: @@ -7212,7 +7216,7 @@ components: LowSignLabel: < High: 40 AgeStart: 0 - AgeEnd: 120 + AgeEnd: 43800 Flag: L Interpretation: Critical Low TEST_text: @@ -7238,8 +7242,8 @@ components: TxtRefTypeLabel: Text Sex: '2' SexLabel: Male - AgeStart: 18 - AgeEnd: 99 + AgeStart: 6570 + AgeEnd: 36135 RefTxt: NORM=Normal;HYPO=Hypochromic;MACRO=Macrocytic Flag: 'N' PARAM: @@ -7306,8 +7310,8 @@ components: HighSignLabel: <= Low: 10 High: 20 - AgeStart: 18 - AgeEnd: 120 + AgeStart: 6570 + AgeEnd: 43800 Flag: 'N' Interpretation: Normal GROUP: diff --git a/public/components/schemas/tests.yaml b/public/components/schemas/tests.yaml index 7890d6c..32aef2a 100644 --- a/public/components/schemas/tests.yaml +++ b/public/components/schemas/tests.yaml @@ -264,8 +264,10 @@ TestDefinition: format: float AgeStart: type: integer + description: Minimum patient age in days AgeEnd: type: integer + description: Maximum patient age in days Flag: type: string Interpretation: @@ -291,10 +293,12 @@ TestDefinition: type: string SexLabel: type: string - AgeStart: - type: integer - AgeEnd: - type: integer + AgeStart: + type: integer + description: Minimum patient age in days + AgeEnd: + type: integer + description: Maximum patient age in days RefTxt: type: string Flag: @@ -336,8 +340,8 @@ TestDefinition: HighSignLabel: "<=" Low: 70 High: 100 - AgeStart: 18 - AgeEnd: 99 + AgeStart: 6570 + AgeEnd: 36135 Flag: N Interpretation: Normal TEST_threshold: @@ -372,7 +376,7 @@ TestDefinition: LowSignLabel: "<" High: 40 AgeStart: 0 - AgeEnd: 120 + AgeEnd: 43800 Flag: L Interpretation: Critical Low TEST_text: @@ -398,8 +402,8 @@ TestDefinition: TxtRefTypeLabel: Text Sex: '2' SexLabel: Male - AgeStart: 18 - AgeEnd: 99 + AgeStart: 6570 + AgeEnd: 36135 RefTxt: 'NORM=Normal;HYPO=Hypochromic;MACRO=Macrocytic' Flag: N PARAM: @@ -466,8 +470,8 @@ TestDefinition: HighSignLabel: "<=" Low: 10 High: 20 - AgeStart: 18 - AgeEnd: 120 + AgeStart: 6570 + AgeEnd: 43800 Flag: N Interpretation: Normal GROUP: diff --git a/public/paths/tests.yaml b/public/paths/tests.yaml index 2378522..4eb18a4 100644 --- a/public/paths/tests.yaml +++ b/public/paths/tests.yaml @@ -270,8 +270,8 @@ Low: 70 HighSign: LE High: 100 - AgeStart: 18 - AgeEnd: 99 + AgeStart: 6570 + AgeEnd: 36135 Flag: N DisciplineID: 2 DepartmentID: 2 @@ -299,8 +299,8 @@ Low: 70 HighSign: LE High: 100 - AgeStart: 18 - AgeEnd: 99 + AgeStart: 6570 + AgeEnd: 36135 Flag: N - NumRefType: NMRC RangeType: REF @@ -309,8 +309,8 @@ Low: 75 HighSign: < High: 105 - AgeStart: 18 - AgeEnd: 99 + AgeStart: 6570 + AgeEnd: 36135 Flag: N testmap: - HostType: SITE @@ -363,7 +363,7 @@ LowSign: LT Low: 120 AgeStart: 0 - AgeEnd: 125 + AgeEnd: 45625 Flag: H DisciplineID: 2 DepartmentID: 2 @@ -390,7 +390,7 @@ LowSign: LT Low: 120 AgeStart: 0 - AgeEnd: 125 + AgeEnd: 45625 Flag: H - NumRefType: THOLD RangeType: PANIC @@ -398,7 +398,7 @@ LowSign: < Low: 121 AgeStart: 0 - AgeEnd: 125 + AgeEnd: 45625 Flag: H testmap: - HostType: SITE @@ -438,8 +438,8 @@ - SpcType: GEN TxtRefType: TEXT Sex: '2' - AgeStart: 18 - AgeEnd: 99 + AgeStart: 6570 + AgeEnd: 36135 RefTxt: NORM=Normal;HIGH=High Flag: N DisciplineID: 1 @@ -463,15 +463,15 @@ - SpcType: GEN TxtRefType: TEXT Sex: '2' - AgeStart: 18 - AgeEnd: 99 + AgeStart: 6570 + AgeEnd: 36135 RefTxt: NORM=Normal Flag: N - SpcType: GEN TxtRefType: TEXT Sex: '1' - AgeStart: 18 - AgeEnd: 99 + AgeStart: 6570 + AgeEnd: 36135 RefTxt: ABN=Abnormal Flag: N testmap: @@ -506,7 +506,7 @@ TxtRefType: VSET Sex: '2' AgeStart: 0 - AgeEnd: 120 + AgeEnd: 43800 RefTxt: NORM=Normal;MACRO=Macro Flag: N DisciplineID: 4 @@ -531,7 +531,7 @@ TxtRefType: VSET Sex: '2' AgeStart: 0 - AgeEnd: 120 + AgeEnd: 43800 RefTxt: NORM=Normal;ABN=Abnormal Flag: N testmap: @@ -616,8 +616,8 @@ Low: 10 HighSign: LE High: 20 - AgeStart: 18 - AgeEnd: 120 + AgeStart: 6570 + AgeEnd: 43800 Flag: N testmap: - HostType: SITE diff --git a/tests/feature/Test/TestCreateVariantsTest.php b/tests/feature/Test/TestCreateVariantsTest.php index aaa2c32..a7fee03 100644 --- a/tests/feature/Test/TestCreateVariantsTest.php +++ b/tests/feature/Test/TestCreateVariantsTest.php @@ -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 { $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 { $notes = 'Auto note ' . uniqid(); @@ -339,10 +423,10 @@ class TestCreateVariantsTest extends CIUnitTestCase 'DepartmentID' => $details['DepartmentID'] ?? 2, 'Method' => $details['Method'] ?? 'Automated test', '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)) { $normalized[$key] = $details[$key]; } @@ -364,7 +448,7 @@ class TestCreateVariantsTest extends CIUnitTestCase 'HighSign' => 'LE', 'High' => $numRefType === 'THOLD' ? 40 : 20, 'AgeStart' => 0, - 'AgeEnd' => 120, + 'AgeEnd' => 43800, 'Flag' => 'N', 'Interpretation' => 'Normal range', 'SpcType' => 'GEN', @@ -382,7 +466,7 @@ class TestCreateVariantsTest extends CIUnitTestCase 'HighSign' => '<', 'High' => $numRefType === 'THOLD' ? 50 : 15, 'AgeStart' => 0, - 'AgeEnd' => 99, + 'AgeEnd' => 36135, 'Flag' => 'N', 'Interpretation' => 'Alternate range', 'SpcType' => 'GEN', @@ -401,7 +485,7 @@ class TestCreateVariantsTest extends CIUnitTestCase 'TxtRefType' => $txtRefType, 'Sex' => '2', 'AgeStart' => 0, - 'AgeEnd' => 120, + 'AgeEnd' => 43800, 'RefTxt' => $txtRefType === 'VSET' ? 'NORM=Normal;ABN=Abnormal' : 'NORM=Normal', 'Flag' => 'N', ], @@ -413,7 +497,7 @@ class TestCreateVariantsTest extends CIUnitTestCase 'TxtRefType' => $txtRefType, 'Sex' => '1', 'AgeStart' => 0, - 'AgeEnd' => 120, + 'AgeEnd' => 43800, 'RefTxt' => $txtRefType === 'VSET' ? 'HIGH=High;LOW=Low' : 'ABN=Abnormal', 'Flag' => 'N', ];