fix(abdm): finalize and sync OPD consult and prescription FHIR bundles only on print
This commit is contained in:
parent
ca7d7be953
commit
a454bd178c
|
|
@ -8140,7 +8140,12 @@ class AbdmGateway extends BaseController
|
|||
$display = $compStr . ' - ' . $dateStr;
|
||||
}
|
||||
|
||||
$isReady = $hasStoredDoc || $hasHr || $isVisitDone || $hasFindings || $hasMeds || $hasVitals;
|
||||
// For ABDM discovery (readyOnly=true), require finalized FHIR bundle to exist.
|
||||
// Prevents advertising draft / incomplete consultations before the doctor prints the prescription.
|
||||
$isReady = ($hasStoredDoc || $hasHr);
|
||||
if (! $readyOnly) {
|
||||
$isReady = $isReady || $isVisitDone || $hasFindings || $hasMeds || $hasVitals;
|
||||
}
|
||||
|
||||
// 1. Doctor Visit / OPD Consultation record
|
||||
$addContext([
|
||||
|
|
|
|||
|
|
@ -2189,9 +2189,9 @@ class Opd extends BaseController
|
|||
if ($includeContent) {
|
||||
try {
|
||||
$rxCtrl = new \App\Controllers\Opd_prescription();
|
||||
$rxCtrl->storePrescriptionFhirBundle($opdId, $sessionId);
|
||||
$rxCtrl->finalizeOpdPrescriptionOnPrint($opdId, $sessionId);
|
||||
} catch (\Throwable $e) {
|
||||
log_message('warning', '[opd_lettre_pdf] storePrescriptionFhirBundle error: ' . $e->getMessage());
|
||||
log_message('warning', '[opd_lettre_pdf] finalizeOpdPrescriptionOnPrint error: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -707,13 +707,10 @@ class Opd_prescription extends BaseController
|
|||
}
|
||||
}
|
||||
|
||||
// Do not generate or push FHIR records during intermediate form draft/save.
|
||||
// FHIR records are only generated and finalized on Print action (opd_prescription_print),
|
||||
// ensuring all medications, diagnoses, and final prescription PDF are complete.
|
||||
$fhirStored = false;
|
||||
try {
|
||||
$fhirStored = $this->storePrescriptionFhirBundle((int) $opdId, (int) $recordId, $patientRow, (array) $opdRow);
|
||||
$this->createOpdPrescriptionWorkTask((int) $opdId, (int) $recordId);
|
||||
} catch (\Throwable $fhirEx) {
|
||||
log_message('error', 'FHIR bundle store failed during opd save: ' . $fhirEx->getMessage() . ' in ' . $fhirEx->getFile() . ':' . $fhirEx->getLine());
|
||||
}
|
||||
|
||||
$this->syncPatientAddictionFlags((int) ($opdRow->p_id ?? 0), [
|
||||
'is_smoking' => (int) $this->request->getPost('is_smoking'),
|
||||
|
|
@ -13949,15 +13946,8 @@ OPD SNAPSHOT JSON: " . $payload;
|
|||
|
||||
public function opd_prescription_print(int $opdId, int $opdSessionId, int $printType = 0)
|
||||
{
|
||||
$this->createOpdPrescriptionWorkTask($opdId, $opdSessionId);
|
||||
$this->markOpdVisitedOnConsultCompletion((int) $opdId);
|
||||
|
||||
// Regenerate complete FHIR records with latest medications and PDF on print action
|
||||
try {
|
||||
$this->storePrescriptionFhirBundle($opdId, $opdSessionId);
|
||||
} catch (\Throwable $e) {
|
||||
log_message('warning', '[opd_prescription_print] storePrescriptionFhirBundle error: ' . $e->getMessage());
|
||||
}
|
||||
// Finalize FHIR records with latest medications and prescription PDF on print action
|
||||
$this->finalizeOpdPrescriptionOnPrint($opdId, $opdSessionId);
|
||||
|
||||
$printConfig = $this->resolvePrescriptionLayoutByDoctorField($opdId, $printType);
|
||||
$layoutMode = (string) ($printConfig['layout'] ?? 'content_only');
|
||||
|
|
@ -13974,8 +13964,35 @@ OPD SNAPSHOT JSON: " . $payload;
|
|||
return redirect()->to($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize OPD Consultation and Prescription FHIR bundles upon Print.
|
||||
* Marks visit complete, generates complete FHIR bundles with all medicines & PDF,
|
||||
* enqueues work task, and immediately pushes/syncs to ABDM Bridge.
|
||||
*/
|
||||
public function finalizeOpdPrescriptionOnPrint(int $opdId, int $opdSessionId): void
|
||||
{
|
||||
$this->db = $this->db ?? \Config\Database::connect();
|
||||
$this->markOpdVisitedOnConsultCompletion((int) $opdId);
|
||||
|
||||
try {
|
||||
$this->storePrescriptionFhirBundle($opdId, $opdSessionId);
|
||||
$this->createOpdPrescriptionWorkTask($opdId, $opdSessionId);
|
||||
|
||||
// Immediately push finalized records (OPConsultRecord + PrescriptionRecord) to Bridge
|
||||
try {
|
||||
$syncService = new \App\Libraries\Abdm\Sync\AbdmTaskBoardSyncService();
|
||||
$syncService->syncOpdConsultRecords(1, false, $opdId);
|
||||
} catch (\Throwable $syncEx) {
|
||||
log_message('warning', '[finalizeOpdPrescriptionOnPrint] immediate bridge sync error: ' . $syncEx->getMessage());
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
log_message('warning', '[finalizeOpdPrescriptionOnPrint] storePrescriptionFhirBundle error: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private function markOpdVisitedOnConsultCompletion(int $opdId): void
|
||||
{
|
||||
$this->db = $this->db ?? \Config\Database::connect();
|
||||
if ($opdId <= 0 || ! $this->db->tableExists('opd_master')) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -14049,6 +14066,7 @@ OPD SNAPSHOT JSON: " . $payload;
|
|||
|
||||
private function createOpdPrescriptionWorkTask(int $opdId, int $opdSessionId): void
|
||||
{
|
||||
$this->db = $this->db ?? \Config\Database::connect();
|
||||
if ($opdId <= 0 || ! $this->db->tableExists('opd_master') || ! $this->db->tableExists('patient_master')) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -14072,9 +14090,24 @@ OPD SNAPSHOT JSON: " . $payload;
|
|||
->get(1)
|
||||
->getRowArray();
|
||||
|
||||
$abhaId = trim((string) ($patient['abha_id'] ?? $patient['abha_no'] ?? $patient['abha_address'] ?? $patient['abha'] ?? ''));
|
||||
if (! $this->isValidAbhaNumber($abhaId)) {
|
||||
return;
|
||||
$abhaId = '';
|
||||
foreach (['abha_address', 'abha_id', 'abha_no', 'abha'] as $col) {
|
||||
$cand = trim((string) ($patient[$col] ?? ''));
|
||||
if ($cand !== '' && $this->isValidAbhaNumber($cand)) {
|
||||
$abhaId = $cand;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($abhaId === '') {
|
||||
$cand = trim((string) ($patient['abha_address'] ?? $patient['abha_id'] ?? $patient['abha_no'] ?? $patient['abha'] ?? ''));
|
||||
$clean14 = preg_replace('/\D/', '', $cand);
|
||||
if (strlen($clean14) === 14) {
|
||||
$abhaId = $clean14;
|
||||
} elseif ($this->isValidAbhaNumber($cand)) {
|
||||
$abhaId = $cand;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$taskService = new AbdmWorkTaskService();
|
||||
|
|
@ -14097,6 +14130,11 @@ OPD SNAPSHOT JSON: " . $payload;
|
|||
|
||||
private function isValidAbhaNumber(string $abhaId): bool
|
||||
{
|
||||
return preg_match('/^\d{14}$/', trim($abhaId)) === 1;
|
||||
$clean = preg_replace('/\D/', '', $abhaId);
|
||||
if (strlen($clean) === 14) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return str_contains($abhaId, '@') && preg_match('/^[a-zA-Z0-9.\-_]{3,}@[a-zA-Z]{3,}$/', $abhaId) === 1;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ class AbdmTaskBoardSyncService
|
|||
* details: array<int, array<string, mixed>>
|
||||
* }
|
||||
*/
|
||||
public function syncOpdConsultRecords(int $limit = 20, bool $dryRun = false): array
|
||||
public function syncOpdConsultRecords(int $limit = 20, bool $dryRun = false, int $targetOpdId = 0): array
|
||||
{
|
||||
$summary = [
|
||||
'eligible' => 0,
|
||||
|
|
@ -71,19 +71,32 @@ class AbdmTaskBoardSyncService
|
|||
}
|
||||
|
||||
$patientFields = $this->db->getFieldNames('patient_master') ?? [];
|
||||
$abhaCol = $this->resolveFirstExistingColumn($patientFields, ['abha_id', 'abha_no', 'abha_address', 'abha']);
|
||||
if ($abhaCol === null) {
|
||||
$abhaCandidates = array_values(array_filter(['abha_address', 'abha_id', 'abha_no', 'abha'], fn ($c) => in_array($c, $patientFields, true)));
|
||||
if (empty($abhaCandidates)) {
|
||||
return $summary;
|
||||
}
|
||||
|
||||
// 1. Fetch done appointments from the last 30 days where patient has ABHA
|
||||
$rows = $this->db->table('opd_master o')
|
||||
->select('o.opd_id, o.p_id, o.P_name, o.apointment_date, o.opd_status, o.doc_name, p.' . $abhaCol . ' as abha_id, p.p_fname, p.gender, p.dob', false)
|
||||
->join('patient_master p', 'p.id = o.p_id', 'left')
|
||||
->where('o.opd_status', 2)
|
||||
->where('DATE(o.apointment_date) >=', date('Y-m-d', strtotime('-30 days')), false)
|
||||
->where('p.' . $abhaCol . ' !=', '')
|
||||
->orderBy('o.opd_id', 'DESC')
|
||||
$selectAbha = 'COALESCE(' . implode(', ', array_map(fn ($c) => "NULLIF(p.{$c}, '')", $abhaCandidates)) . ", '') as abha_id";
|
||||
|
||||
// 1. Fetch appointments where patient has ABHA (either Done or targetOpdId specified on print)
|
||||
$query = $this->db->table('opd_master o')
|
||||
->select('o.opd_id, o.p_id, o.P_name, o.apointment_date, o.opd_status, o.doc_name, ' . $selectAbha . ', p.p_fname, p.gender, p.dob', false)
|
||||
->join('patient_master p', 'p.id = o.p_id', 'left');
|
||||
|
||||
if ($targetOpdId > 0) {
|
||||
$query->where('o.opd_id', $targetOpdId);
|
||||
} else {
|
||||
$query->where('o.opd_status', 2)
|
||||
->where('DATE(o.apointment_date) >=', date('Y-m-d', strtotime('-30 days')), false);
|
||||
}
|
||||
|
||||
$query->groupStart();
|
||||
foreach ($abhaCandidates as $c) {
|
||||
$query->orWhere("p.{$c} !=", '');
|
||||
}
|
||||
$query->groupEnd();
|
||||
|
||||
$rows = $query->orderBy('o.opd_id', 'DESC')
|
||||
->limit(max(50, $limit * 3))
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
|
@ -200,7 +213,7 @@ class AbdmTaskBoardSyncService
|
|||
$lastModified = ! empty($allTimestamps) ? max($allTimestamps) : ($row['apointment_date'] ?? date('Y-m-d H:i:s'));
|
||||
|
||||
$cooling = self::calculateCooling('opd_prescription_publish', $lastModified);
|
||||
if ($cooling['is_cooling_active']) {
|
||||
if ($targetOpdId <= 0 && $cooling['is_cooling_active']) {
|
||||
$summary['cooling']++;
|
||||
$summary['details'][] = [
|
||||
'opd_id' => $opdId,
|
||||
|
|
@ -261,7 +274,8 @@ class AbdmTaskBoardSyncService
|
|||
$pushStatus = strtolower(trim((string) ($hrToUse['push_status'] ?? '')));
|
||||
|
||||
// Already pushed/submitted/linked successfully?
|
||||
if (in_array($pushStatus, ['queued', 'linked', 'pushed'], true)) {
|
||||
// When targeting a specific OPD on print, re-push to ensure Bridge receives the finalized bundle
|
||||
if ($targetOpdId <= 0 && in_array($pushStatus, ['queued', 'linked', 'pushed'], true)) {
|
||||
$summary['skipped']++;
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user