diff --git a/app/Controllers/AbdmGateway.php b/app/Controllers/AbdmGateway.php index 0dbdb5a..ec3d188 100644 --- a/app/Controllers/AbdmGateway.php +++ b/app/Controllers/AbdmGateway.php @@ -4942,8 +4942,13 @@ class AbdmGateway extends BaseController $targetOpdId = (int) ($pRow['opd_id'] ?? 0); } if ($targetOpdId <= 0 && $targetPatientId > 0 && $db->tableExists('opd_master')) { - $opdRow = $db->table('opd_master')->select('opd_id')->where('p_id', $targetPatientId)->orderBy('opd_id', 'DESC')->get(1)->getRowArray(); - $targetOpdId = (int) ($opdRow['opd_id'] ?? 0); + $opdRow = $db->table('opd_master')->select('opd_id')->where('opd_id', $targetPatientId)->get(1)->getRowArray(); + if ($opdRow) { + $targetOpdId = (int) $opdRow['opd_id']; + } else { + $opdRow = $db->table('opd_master')->select('opd_id')->where('p_id', $targetPatientId)->orderBy('opd_id', 'DESC')->get(1)->getRowArray(); + $targetOpdId = (int) ($opdRow['opd_id'] ?? 0); + } } if ($targetOpdId > 0) { @@ -4966,6 +4971,30 @@ class AbdmGateway extends BaseController } } + // Strategy 0b: Live On-Demand Fresh Generation for Invoice Record + if (preg_match('/^INVOICE-(CHG|OPD|IPD)-(\d+)/i', $ref, $invM)) { + $invSource = match(strtoupper($invM[1])) { + 'CHG' => 'charges_invoice', + 'OPD' => 'opd_invoice', + 'IPD' => 'ipd_invoice', + }; + $invId = (int) $invM[2]; + try { + $invPayload = $this->buildInvoiceSourceRecordPayload($invSource, $invId, 0, ''); + if ($invPayload !== null && !empty($invPayload['bundle'])) { + $records[] = [ + 'careContextReference' => $ref, + 'hiType' => 'InvoiceRecord', + 'display' => $invPayload['care_context_display'] ?? 'Invoice Record', + 'bundle' => $invPayload['bundle'], + ]; + continue; + } + } catch (\Throwable $ex) { + log_message('warning', 'Live Invoice FHIR generation in recordsFetch failed: ' . $ex->getMessage()); + } + } + // Strategy A: Check if health_records already has stored FHIR record_data $hrRow = null; if ($db->tableExists('health_records')) { @@ -5017,14 +5046,18 @@ class AbdmGateway extends BaseController // Strategy B: Build compliant FHIR Document Bundle from patient_master & OPD - $bundle = $this->assembleFhirBundleForCareContext($ref); - if ($bundle !== null) { - $records[] = [ - 'careContextReference' => $ref, - 'hiType' => 'OPConsultRecord', - 'display' => 'OPConsultRecord - ' . date('d M Y'), - 'bundle' => $bundle, - ]; + // STRICTLY restricted to OPD/consultation care contexts. + // NEVER synthesize dummy consultation records for invoices, discharge, labs, radiology, etc. + if (preg_match('/^(?:OPD|PRESCRIPTION|CONSULT)/i', $ref)) { + $bundle = $this->assembleFhirBundleForCareContext($ref); + if ($bundle !== null) { + $records[] = [ + 'careContextReference' => $ref, + 'hiType' => 'OPConsultRecord', + 'display' => 'OPConsultRecord - ' . date('d M Y'), + 'bundle' => $bundle, + ]; + } } } @@ -5049,10 +5082,25 @@ class AbdmGateway extends BaseController { $db = \Config\Database::connect(); - // Extract patient ID from reference (e.g. "OPD-16-S1-20260909" -> patient_id 16) + // Extract patient ID and doctor from reference (e.g. "OPD-16-S1-20260909" -> patient_id 16 or opd_id) $patientId = 0; + $doctorName = 'Treating Doctor'; if (preg_match('/OPD-(\d+)/i', $careContextRef, $matches)) { - $patientId = (int) $matches[1]; + $extractedId = (int) $matches[1]; + if ($db->tableExists('opd_master')) { + $opd = $db->table('opd_master')->where('opd_id', $extractedId)->get(1)->getRowArray(); + if (!empty($opd)) { + if (!empty($opd['p_id'])) { + $patientId = (int) $opd['p_id']; + } + if (!empty($opd['doc_name'])) { + $doctorName = trim((string) $opd['doc_name']); + } + } + } + if ($patientId === 0) { + $patientId = $extractedId; + } } elseif (preg_match('/(?:HR|PAT|REG|P)-(\d+)/i', $careContextRef, $matches)) { $patientId = (int) $matches[1]; } @@ -5121,7 +5169,7 @@ class AbdmGateway extends BaseController ], 'date' => $nowIso, 'author' => [ - ['reference' => 'Practitioner/prac-1', 'display' => 'Treating Doctor'], + ['reference' => 'Practitioner/prac-1', 'display' => $doctorName], ], 'title' => 'Consultation Record', 'section' => [ @@ -5155,7 +5203,7 @@ class AbdmGateway extends BaseController 'resource' => [ 'resourceType' => 'Practitioner', 'id' => 'prac-1', - 'name' => [['text' => 'Treating Doctor']], + 'name' => [['text' => $doctorName]], ], ], ], diff --git a/app/Controllers/Opd_prescription.php b/app/Controllers/Opd_prescription.php index b8b4a05..9585471 100644 --- a/app/Controllers/Opd_prescription.php +++ b/app/Controllers/Opd_prescription.php @@ -4936,8 +4936,15 @@ class Opd_prescription extends BaseController $bundle = $this->fhirR4Builder->buildOpConsultBundle($patient, $encounter, $medications, $conditions, $clinicalContext); $bundleJson = (string) json_encode($bundle, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); - $user = function_exists('auth') ? auth()->user() : null; - $generatedBy = (string) ($user->id ?? session('user_id') ?? 'system'); + $generatedBy = 'system'; + try { + if (session_status() === PHP_SESSION_ACTIVE) { + $user = function_exists('auth') ? auth()->user() : null; + $generatedBy = (string) ($user->id ?? session('user_id') ?? 'system'); + } + } catch (\Throwable) { + $generatedBy = 'system'; + } $generatedAt = Time::now('Asia/Kolkata')->toDateTimeString(); // Overwrite the latest stored bundle for this opd_id / session @@ -4975,6 +4982,7 @@ class Opd_prescription extends BaseController 'ok' => 1, 'message' => 'FHIR bundle regenerated successfully', 'document_id' => $documentId, + 'bundle' => $bundle, 'generated_at' => $generatedAt, 'entry_count' => isset($bundle['entry']) ? count($bundle['entry']) : 0, ]; diff --git a/app/Libraries/Abdm/M3HiuWorkflowService.php b/app/Libraries/Abdm/M3HiuWorkflowService.php index 81eee31..d979bb5 100644 --- a/app/Libraries/Abdm/M3HiuWorkflowService.php +++ b/app/Libraries/Abdm/M3HiuWorkflowService.php @@ -243,11 +243,11 @@ class M3HiuWorkflowService $consentRowId = (int) ($row['id'] ?? 0); - // Fast-check: if a child reconcile/data_fetch workflow for this request_id has already reached terminal state + // Fast-check: if a child reconcile/data_fetch workflow for this request_id has reached terminal state (EXPIRED, REVOKED, DENIED) $terminalCheck = $this->db->table('abdm_hiu_workflows') ->select('id, workflow_state, operation') ->where('request_id', $requestId) - ->whereIn('workflow_state', ['COMPLETED', 'EXPIRED', 'REVOKED', 'DENIED']) + ->whereIn('workflow_state', ['EXPIRED', 'REVOKED', 'DENIED']) ->orderBy('id', 'DESC') ->get(1) ->getRowArray(); @@ -289,9 +289,27 @@ class M3HiuWorkflowService ?? '' ))); + $allArtifactsReceived = true; + $resolvedConsentId = trim((string) ( + $consentResult['abdm_consent_request_id'] + ?? $consentResult['consent_request_id'] + ?? $consentResult['abdm_consent_artifact_id'] + ?? $consentResult['consent_id'] + ?? '' + )); + if ($resolvedConsentId !== '' && $this->db->tableExists('abdm_hiu_consent_artifacts')) { + $unreceived = $this->db->table('abdm_hiu_consent_artifacts') + ->where('consent_request_id', $resolvedConsentId) + ->whereNotIn('last_status', ['RECEIVED', 'DATA_RECEIVED', 'EXPIRED', 'REVOKED', 'DENIED', 'COMPLETED']) + ->countAllResults(); + if ($unreceived > 0) { + $allArtifactsReceived = false; + } + } + if (in_array($consentState, ['revoked', 'denied', 'expired'], true)) { $this->markConsentRequestTerminal($consentRowId, strtoupper($consentState)); - } elseif ($cascade['granted'] && $cascade['failed'] === 0 && $cascade['data_updates'] > 0) { + } elseif ($cascade['granted'] && $cascade['failed'] === 0 && $cascade['data_updates'] > 0 && $allArtifactsReceived) { $this->markConsentRequestTerminal($consentRowId, 'COMPLETED'); } } else { @@ -432,7 +450,8 @@ class M3HiuWorkflowService $artifactResult = $this->runOperation('data_fetch', $artifactPayload); if ((int) ($artifactResult['ok'] ?? 0) === 1) { $result['data_updates']++; - $this->markArtifactPolled($resolvedConsentId, $artifactId, (string) ($artifactResult['consent_status'] ?? '')); + $status = (string) ($artifactResult['workflow_state'] ?? $artifactResult['status'] ?? $artifactResult['consent_status'] ?? ''); + $this->markArtifactPolled($resolvedConsentId, $artifactId, $status); } else { $result['failed']++; }