fix(abdm): unify immunization payload, resolve CC IMM-53, add on-demand fetch and sync bundle

This commit is contained in:
DevSoftTech Server 2026-10-08 11:07:08 +05:30
parent 91d7fe77d2
commit 75e3a820cc
3 changed files with 129 additions and 82 deletions

View File

@ -241,9 +241,13 @@ class AbdmFhirInspector extends BaseController
return $gw->invoiceFhirPreview();
}
// Immunization: IMM-{id}
if (preg_match('/^IMM-(\d+)/i', $ref, $m)) {
$immId = (int) $m[1];
// Immunization: IMM-{id} or IMM-{patientId}-V{id}-{date}
if (preg_match('/^IMM-(?:PAT-(\d+)|(?:(\d+)-V)?(\d+))/i', $ref, $m)) {
$immId = (int) (! empty($m[3]) ? $m[3] : (! empty($m[2]) ? $m[2] : ($m[1] ?? 0)));
if ($patientId <= 0 && $this->db->tableExists('immunization_records')) {
$immRec = $this->db->table('immunization_records')->select('patient_id')->where('id', $immId)->get(1)->getRowArray();
$patientId = (int) ($immRec['patient_id'] ?? 0);
}
$gw = new \App\Controllers\AbdmGateway();
$gw->initController($this->request, $this->response, service('logger'));
$_GET['record_id'] = $immId;

View File

@ -2581,15 +2581,16 @@ class AbdmGateway extends BaseController
public function immunizationFhirPreview()
{
if (! $this->request->isAJAX()) {
return $this->response->setStatusCode(403)->setJSON(['status' => 'error', 'message' => 'AJAX only']);
}
$patientId = (int) ($this->request->getGet('patient_id') ?? $this->request->getPost('patient_id') ?? 0);
$recordId = (int) ($this->request->getGet('record_id') ?? $this->request->getPost('record_id') ?? 0);
$abhaId = trim((string) ($this->request->getGet('abha_id') ?? $this->request->getPost('abha_id') ?? ''));
$forceNewRecord = (int) ($this->request->getGet('force_new_record') ?? $this->request->getGet('force_rebuild') ?? $this->request->getPost('force_new_record') ?? 0) === 1;
if ($patientId <= 0 && $recordId > 0 && $this->db->tableExists('immunization_records')) {
$immRec = $this->db->table('immunization_records')->select('patient_id')->where('id', $recordId)->get(1)->getRowArray();
$patientId = (int) ($immRec['patient_id'] ?? 0);
}
if ($patientId <= 0) {
return $this->response->setStatusCode(400)->setJSON([
'status' => 'error',
@ -3974,10 +3975,14 @@ class AbdmGateway extends BaseController
'care_context_reference' => $ccRef,
]);
$vaccineDisplay = ! empty($records[0]['vaccine_name']) ? trim((string) $records[0]['vaccine_name']) : 'Vaccine';
$dateStr = date('d M Y', strtotime($visitDate));
$ccDisplay = 'ImmunizationRecord - ' . $vaccineDisplay . ' (' . $dateStr . ')';
return [
'bundle' => $bundle,
'care_context_reference' => $ccRef,
'care_context_display' => 'Immunization Record - ' . $visitDate,
'care_context_display' => $ccDisplay,
'patient_name' => $patient['name'],
'doctor_name' => (string) ($practitioner['name'] ?? ''),
'year_of_birth' => $this->resolvePatientBirthYear($patientRow, str_contains($rawAbha, '@') ? $rawAbha : '', str_contains($rawAbha, '@') ? '' : $rawAbha),
@ -5371,6 +5376,60 @@ class AbdmGateway extends BaseController
}
}
// Strategy 0e: Live On-Demand Fresh Generation for Immunization Record (IMM-{id})
if (preg_match('/^IMM-(?:PAT-(\d+)|(?:(\d+)-V)?(\d+))/i', $ref, $immM)) {
$immTargetId = (int) (! empty($immM[3]) ? $immM[3] : (! empty($immM[2]) ? $immM[2] : ($immM[1] ?? 0)));
$immPatientId = (int) (! empty($immM[1]) ? $immM[1] : (! empty($immM[2]) ? $immM[2] : 0));
try {
$immPayload = $this->buildImmunizationGatewayPayload($immPatientId, $immTargetId, '', false);
if ($immPayload !== null && ! empty($immPayload['bundle'])) {
$vaccineName = 'Vaccine';
if (! empty($immPayload['records'][0]['vaccine_name'])) {
$vaccineName = trim((string) $immPayload['records'][0]['vaccine_name']);
}
$dateStr = date('d M Y', strtotime((string) ($immPayload['visit_date'] ?? date('Y-m-d'))));
$immDisplay = 'ImmunizationRecord - ' . $vaccineName . ' (' . $dateStr . ')';
$records[] = [
'careContextReference' => $ref,
'hiType' => 'ImmunizationRecord',
'display' => $immDisplay,
'bundle' => $immPayload['bundle'],
];
// Persist or cache to health_records for rapid subsequent fetches
if ($db->tableExists('health_records')) {
$exHr = $db->table('health_records')->where('care_context_reference', $ref)->get(1)->getRowArray();
if ($exHr) {
if (empty($exHr['record_data'])) {
$db->table('health_records')->where('id', (int) $exHr['id'])->update([
'record_data' => json_encode($immPayload['bundle'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
'push_status' => 'linked',
'updated_at' => date('Y-m-d H:i:s'),
]);
}
} else {
$resolvedPatId = $immPatientId > 0 ? $immPatientId : (int) ($immPayload['records'][0]['patient_id'] ?? 0);
$db->table('health_records')->insert([
'patient_id' => $resolvedPatId,
'abha_id' => (string) ($immPayload['records'][0]['abha_id'] ?? $this->resolvePatientAbhaIdentifier($resolvedPatId)),
'hi_type' => 'ImmunizationRecord',
'entity_type' => 'immunization',
'entity_id' => (string) $immTargetId,
'care_context_reference' => $ref,
'record_data' => json_encode($immPayload['bundle'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
'push_status' => 'linked',
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
}
}
continue;
}
} catch (\Throwable $ex) {
log_message('warning', 'Live Immunization 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')) {
@ -7494,6 +7553,15 @@ class AbdmGateway extends BaseController
$ccRef = ! empty($imm['abdm_care_context_reference'])
? trim((string) $imm['abdm_care_context_reference'])
: ('IMM-' . $imm['id']);
if (empty($imm['abdm_care_context_reference'])) {
$this->db->table('immunization_records')
->where('id', (int) $imm['id'])
->groupStart()
->where('abdm_care_context_reference', null)
->orWhere('abdm_care_context_reference', '')
->groupEnd()
->update(['abdm_care_context_reference' => $ccRef]);
}
$display = 'ImmunizationRecord - ' . ($imm['vaccine_name'] ?? 'Vaccine') . ' (' . $dateStr . ')';
$addContext([
@ -10326,95 +10394,48 @@ class AbdmGateway extends BaseController
return null;
}
$immRows = [];
if ($this->db->tableExists('immunization_records')) {
$builder = $this->db->table('immunization_records');
if ($recordId > 0) {
$builder->where('id', $recordId);
} elseif ($patientId > 0) {
$builder->where('patient_id', $patientId)->orderBy('id', 'DESC')->limit(10);
}
$immRows = $builder->get()->getResultArray();
if ($patientId <= 0 && $recordId > 0 && $this->db->tableExists('immunization_records')) {
$rec = $this->db->table('immunization_records')->select('patient_id')->where('id', $recordId)->get(1)->getRowArray();
$patientId = (int) ($rec['patient_id'] ?? 0);
}
if (empty($immRows)) {
return null;
}
$firstRow = $immRows[0];
if ($patientId <= 0) {
$patientId = (int) ($firstRow['patient_id'] ?? 0);
}
$patientRow = $this->loadPatientRow($patientId);
if (empty($patientRow)) {
return null;
}
if ($abhaId === '') {
$abhaId = $this->resolvePatientAbhaIdentifier($patientId);
$gatewayPayload = $this->buildImmunizationGatewayPayload($patientId, $recordId, $abhaId, false);
if ($gatewayPayload === null) {
return null;
}
$vaccines = [];
$visitDate = date('Y-m-d');
foreach ($immRows as $row) {
$vaccineName = trim((string) ($row['vaccine_name'] ?? $row['vaccine_display'] ?? ''));
if ($vaccineName === '' && ! empty($row['vaccine_master_id']) && $this->db->tableExists('immunization_vaccine_master')) {
$vm = $this->db->table('immunization_vaccine_master')->where('id', (int) $row['vaccine_master_id'])->get(1)->getRowArray();
$vaccineName = trim((string) ($vm['vaccine_name'] ?? ''));
}
if ($vaccineName === '') {
$vaccineName = 'Vaccine Dose';
}
$occDate = ! empty($row['given_date']) ? (string) $row['given_date'] : (! empty($row['scheduled_date']) ? (string) $row['scheduled_date'] : date('Y-m-d H:i:s'));
$visitDate = date('Y-m-d', strtotime($occDate));
$vaccines[] = [
'vaccine_name' => $vaccineName,
'name' => $vaccineName,
'snomed_code' => trim((string) ($row['vaccine_code'] ?? '')),
'occurrence_date' => date(DATE_ATOM, strtotime($occDate)),
'status' => trim((string) ($row['status'] ?? 'completed')),
'lot_number' => trim((string) ($row['lot_number'] ?? '')),
];
$vaccineName = 'Vaccine';
if (! empty($gatewayPayload['records'][0]['vaccine_name'])) {
$vaccineName = trim((string) $gatewayPayload['records'][0]['vaccine_name']);
}
$dateStr = date('d M Y', strtotime((string) ($gatewayPayload['visit_date'] ?? date('Y-m-d'))));
$ccDisplay = 'ImmunizationRecord - ' . $vaccineName . ' (' . $dateStr . ')';
$ccRef = (string) ($gatewayPayload['care_context_reference'] ?? ('IMM-' . $recordId));
$primaryRecordId = (int) ($firstRow['id'] ?? $recordId);
$cleanDate = date('Ymd', strtotime($visitDate));
$ccRef = 'IMM-' . $patientId . '-V' . $primaryRecordId . '-' . $cleanDate;
$ccDisplay = 'Immunization - ' . date('d/m/Y', strtotime($visitDate));
$source = [
'record_id' => (string) $primaryRecordId,
'visit_date' => $visitDate,
'completed_at' => date(DATE_ATOM, strtotime($visitDate)),
'patient' => [
'id' => (string) $patientId,
'name' => $this->patientDisplayName($patientRow),
'gender' => strtolower(trim((string) ($patientRow['gender'] ?? 'unknown'))),
'birth_date' => ! empty($patientRow['dob']) ? date('Y-m-d', strtotime((string) $patientRow['dob'])) : '',
'abha_id' => preg_replace('/\D/', '', $abhaId),
'abha_address' => str_contains($abhaId, '@') ? $abhaId : '',
],
'immunizations' => $vaccines,
'vaccines' => $vaccines,
'hfr_id' => $this->connector->getHfrId() ?: 'HFR-IN-HMS',
];
$factory = new FhirGeneratorFactory();
$generatorOutput = $factory->immunization()->generate($source);
$adapter = new \App\Libraries\Abdm\Fhir\Support\GatewayPayloadAdapter();
$gatewayPayload = $adapter->toGatewayPayload($generatorOutput, $source, (string) ($source['hfr_id']));
if ($recordId > 0 && $this->db->tableExists('immunization_records')) {
$this->db->table('immunization_records')
->where('id', $recordId)
->groupStart()
->where('abdm_care_context_reference', null)
->orWhere('abdm_care_context_reference', '')
->groupEnd()
->update(['abdm_care_context_reference' => $ccRef]);
}
return [
'hi_type' => 'ImmunizationRecord',
'entity_type' => 'immunization',
'entity_id' => (string) $primaryRecordId,
'entity_id' => (string) ($recordId > 0 ? $recordId : $patientId),
'patient_id' => $patientId,
'patient_name' => $this->patientDisplayName($patientRow),
'visit_date' => $visitDate,
'patient_name' => (string) ($gatewayPayload['patient_name'] ?? ('PATIENT-' . $patientId)),
'visit_date' => (string) ($gatewayPayload['visit_date'] ?? date('Y-m-d')),
'care_context_reference' => $ccRef,
'care_context_display' => $ccDisplay,
'bundle' => (array) ($gatewayPayload['fhir_bundle'] ?? []),
'bundle' => (array) ($gatewayPayload['bundle'] ?? []),
];
}

View File

@ -831,9 +831,11 @@ class AbdmTaskBoardSyncService
$cleanVisitDate = str_replace('-', '', $visitDate);
$careContextRef = $prefix . $entityId . '-' . $cleanVisitDate;
if ($taskType === 'immunization_record_publish' && $this->db->tableExists('immunization_records')) {
$immRec = $this->db->table('immunization_records')->select('abdm_care_context_reference')->where('id', (int) $entityId)->get(1)->getRowArray();
$immRec = $this->db->table('immunization_records')->select('abdm_care_context_reference, vaccine_name')->where('id', (int) $entityId)->get(1)->getRowArray();
if (! empty($immRec['abdm_care_context_reference'])) {
$careContextRef = trim((string) $immRec['abdm_care_context_reference']);
} else {
$careContextRef = 'IMM-' . $entityId;
}
}
if ($taskType === 'wellness_record_publish' && $this->db->tableExists('patient_wellness_records')) {
@ -846,7 +848,12 @@ class AbdmTaskBoardSyncService
}
}
}
$careContextDisplay = $hiType . ' ' . $visitDate;
if ($taskType === 'immunization_record_publish' && ! empty($immRec['vaccine_name'])) {
$careContextDisplay = 'ImmunizationRecord - ' . trim((string) $immRec['vaccine_name']) . ' (' . date('d M Y', strtotime($visitDate)) . ')';
} else {
$careContextDisplay = $hiType . ' ' . $visitDate;
}
$pushData = [
'patient_id' => (string) $patientId,
@ -913,6 +920,21 @@ class AbdmTaskBoardSyncService
}
}
}
} elseif ($taskType === 'immunization_record_publish') {
if (empty($pushData['bundle']) && class_exists('\App\Controllers\AbdmGateway')) {
try {
$gw = new \App\Controllers\AbdmGateway();
$immPayload = $gw->buildImmunizationRecordPayload((int) $entityId, $patientId, $abhaAddress !== '' ? $abhaAddress : $abhaNumber);
if (! empty($immPayload['bundle'])) {
$pushData['bundle'] = $immPayload['bundle'];
$pushData['fhir_bundle'] = $immPayload['bundle'];
$pushData['record_data'] = $immPayload['bundle'];
$bundleJson = (string) json_encode($immPayload['bundle'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}
} catch (\Throwable $ie) {
log_message('warning', '[processIndividualWorkTask] Immunization bundle generation error: ' . $ie->getMessage());
}
}
}
try {