format('y'); $month = $date->format('m'); $day = $date->format('d'); $counter = $this->db->table('counter') ->where('CounterName', 'ORDER') ->get() ->getRow(); if (!$counter) { $this->db->table('counter')->insert([ 'CounterName' => 'ORDER', 'CounterValue' => 1 ]); $seq = 1; } else { $seq = $counter->CounterValue + 1; $this->db->table('counter') ->where('CounterName', 'ORDER') ->update(['CounterValue' => $seq]); } $seqStr = str_pad($seq, 5, '0', STR_PAD_LEFT); return $siteCode . $year . $month . $day . $seqStr; } public function generateSpecimenID(string $orderID, int $seq): string { return $orderID . '-S' . str_pad($seq, 2, '0', STR_PAD_LEFT); } public function createOrder(array $data): string { $this->db->transStart(); try { $orderID = !empty($data['OrderID']) ? $data['OrderID'] : $this->generateOrderID($data['SiteCode'] ?? '00'); $orderData = [ 'OrderID' => $orderID, 'PlacerID' => $data['PlacerID'] ?? null, 'InternalPID' => $data['InternalPID'], 'SiteID' => $data['SiteID'] ?? '1', 'PVADTID' => $data['PatVisitID'] ?? $data['PVADTID'] ?? 0, 'ReqApp' => $data['ReqApp'] ?? null, 'Priority' => $data['Priority'] ?? 'R', 'TrnDate' => $data['OrderDateTime'] ?? $data['TrnDate'] ?? date('Y-m-d H:i:s'), 'EffDate' => $data['EffDate'] ?? date('Y-m-d H:i:s'), 'CreateDate' => date('Y-m-d H:i:s') ]; $internalOID = $this->insert($orderData); if (!$internalOID) { throw new \Exception('Failed to create order'); } // Handle Order Comments if (!empty($data['Comment'])) { $this->db->table('ordercom')->insert([ 'InternalOID' => $internalOID, 'Comment' => $data['Comment'], 'CreateDate' => date('Y-m-d H:i:s') ]); } // Process Tests Expansion $testToOrder = []; $specimenConDefMap = []; // Map ConDefID to specimen info if (isset($data['Tests']) && is_array($data['Tests'])) { $testModel = new \App\Models\Test\TestDefSiteModel(); $grpModel = new \App\Models\Test\TestDefGrpModel(); $calModel = new \App\Models\Test\TestDefCalModel(); $testMapDetailModel = new \App\Models\Test\TestMapDetailModel(); $containerDefModel = new \App\Models\Specimen\ContainerDefModel(); foreach ($data['Tests'] as $test) { $testSiteID = $test['TestSiteID'] ?? $test['TestID'] ?? null; if ($testSiteID) { $this->expandTest($testSiteID, $testToOrder, $testModel, $grpModel, $calModel); } } // Group tests by container requirement $testsByContainer = []; foreach ($testToOrder as $tid => $tinfo) { // Find container requirement for this test $containerReq = $this->getContainerRequirement($tid, $testMapDetailModel, $containerDefModel); $conDefID = $containerReq['ConDefID'] ?? null; if (!isset($testsByContainer[$conDefID])) { $testsByContainer[$conDefID] = [ 'tests' => [], 'containerInfo' => $containerReq ]; } $testsByContainer[$conDefID]['tests'][$tid] = $tinfo; } // Create specimens for each unique container requirement $specimenSeq = 1; foreach ($testsByContainer as $conDefID => $containerData) { $specimenID = $this->generateSpecimenID($orderID, $specimenSeq++); $specimenData = [ 'SID' => $specimenID, 'SiteID' => $data['SiteID'] ?? '1', 'OrderID' => $internalOID, 'ConDefID' => $conDefID, 'Qty' => 1, 'Unit' => 'tube', 'GenerateBy' => 'ORDER', 'CreateDate' => date('Y-m-d H:i:s') ]; $this->db->table('specimen')->insert($specimenData); $internalSID = $this->db->insertID(); // Create specimen status $this->db->table('specimenstatus')->insert([ 'SID' => $specimenID, 'OrderID' => $internalOID, 'SpcStatus' => 'PENDING', 'CreateDate' => date('Y-m-d H:i:s') ]); // Store mapping for patres creation foreach ($containerData['tests'] as $tid => $tinfo) { $specimenConDefMap[$tid] = [ 'InternalSID' => $internalSID, 'SID' => $specimenID, 'ConDefID' => $conDefID ]; } } // Insert unique tests into patres with specimen links if (!empty($testToOrder)) { $resModel = new \App\Models\PatResultModel(); foreach ($testToOrder as $tid => $tinfo) { $specimenInfo = $specimenConDefMap[$tid] ?? null; $patResData = [ 'OrderID' => $internalOID, 'TestSiteID' => $tid, 'TestSiteCode' => $tinfo['TestSiteCode'], 'SID' => $orderID, 'SampleID' => $orderID, 'ResultDateTime' => $orderData['TrnDate'], 'CreateDate' => date('Y-m-d H:i:s') ]; if ($specimenInfo) { $patResData['InternalSID'] = $specimenInfo['InternalSID']; } $resModel->insert($patResData); } } } $this->db->transComplete(); if ($this->db->transStatus() === false) { throw new \Exception('Transaction failed'); } return $orderID; } catch (\Exception $e) { $this->db->transRollback(); throw $e; } } private function getContainerRequirement($testSiteID, $testMapDetailModel, $containerDefModel): array { // Try to find container requirement from test mapping $containerDef = $this->db->table('testmapdetail tmd') ->select('tmd.ConDefID, cd.ConCode, cd.ConName') ->join('containerdef cd', 'cd.ConDefID = tmd.ConDefID', 'left') ->where('tmd.ClientTestCode', function($builder) use ($testSiteID) { return $builder->select('TestSiteCode') ->from('testdefsite') ->where('TestSiteID', $testSiteID); }) ->where('tmd.EndDate IS NULL') ->get() ->getRowArray(); if ($containerDef) { return [ 'ConDefID' => $containerDef['ConDefID'], 'ConCode' => $containerDef['ConCode'], 'ConName' => $containerDef['ConName'] ]; } return [ 'ConDefID' => null, 'ConCode' => 'DEFAULT', 'ConName' => 'Default Container' ]; } private function expandTest($testSiteID, &$testToOrder, $testModel, $grpModel, $calModel) { if (isset($testToOrder[$testSiteID])) return; $testInfo = $testModel->find($testSiteID); if (!$testInfo) return; $testToOrder[$testSiteID] = [ 'TestSiteCode' => $testInfo['TestSiteCode'], 'TestType' => $testInfo['TestType'] ]; // Handle Group Expansion if ($testInfo['TestType'] === 'GROUP') { $members = $grpModel->where('TestSiteID', $testSiteID)->findAll(); foreach ($members as $m) { $this->expandTest($m['Member'], $testToOrder, $testModel, $grpModel, $calModel); } } // Handle Calculated Test Dependencies if ($testInfo['TestType'] === 'CALC') { $members = $grpModel->getGroupMembers($testSiteID); foreach ($members as $member) { $memberID = $member['TestSiteID'] ?? null; if ($memberID) { $this->expandTest($memberID, $testToOrder, $testModel, $grpModel, $calModel); } } } } public function getOrder(string $orderID): ?array { return $this->select('*') ->where('OrderID', $orderID) ->where('DelDate', null) ->get() ->getRowArray(); } public function getOrdersByPatient(int $internalPID): array { return $this->select('*') ->where('InternalPID', $internalPID) ->where('DelDate', null) ->orderBy('TrnDate', 'DESC') ->get() ->getResultArray(); } public function updateStatus(string $orderID, string $status): bool { $order = $this->getOrder($orderID); if (!$order) return false; return (bool)$this->db->table('orderstatus')->insert([ 'InternalOID' => $order['InternalOID'], 'OrderStatus' => $status, 'CreateDate' => date('Y-m-d H:i:s') ]); } public function softDelete(string $orderID): bool { return $this->where('OrderID', $orderID)->update(null, ['DelDate' => date('Y-m-d H:i:s')]); } }