ABDM: persist IPD discharge FHIR to sync outbox

This commit is contained in:
DevSoftTech Server 2026-07-17 17:42:17 +05:30
parent e9b31e8475
commit 3b3e71341d

View File

@ -3,6 +3,9 @@
namespace App\Controllers;
use App\Controllers\BaseController;
use App\Libraries\Abdm\Fhir\FhirGeneratorFactory;
use App\Libraries\Abdm\Fhir\Support\GatewayPayloadAdapter;
use App\Libraries\Abdm\Sync\AbdmSyncOutboxService;
use App\Libraries\AbdmWorkTaskService;
use App\Libraries\BridgeSyncService;
use App\Models\IpdBillingModel;
@ -7602,6 +7605,203 @@ class Ipd_discharge extends BaseController
} catch (\Throwable $e) {
// Do not block discharge workflows if queue service is unavailable.
}
// Also persist ABDM-compatible FHIR bundle in sync tables/outbox for M2 push.
// Fail-open: discharge save flow must never fail due to ABDM sync issues.
try {
$this->enqueueIpdDischargeFhirSync($ipdId, $patientId, $ipdRow, $patientRow);
} catch (\Throwable $e) {
// Intentionally ignored.
}
}
private function enqueueIpdDischargeFhirSync(int $ipdId, int $patientId, array $ipdRow, array $patientRow): void
{
if ($ipdId <= 0 || $patientId <= 0) {
return;
}
$hfrId = trim($this->getHospitalSettingValue('ABDM_HFR_ID'));
if ($hfrId === '') {
return;
}
$patientName = trim(trim((string) ($patientRow['p_fname'] ?? '')) . ' ' . trim((string) ($patientRow['p_lname'] ?? '')));
if ($patientName === '') {
$patientName = trim((string) ($ipdRow['P_name'] ?? ''));
}
$abhaIdRaw = '';
foreach (['abha_id', 'abha_no', 'abha'] as $field) {
$value = trim((string) ($patientRow[$field] ?? ''));
if ($value !== '') {
$abhaIdRaw = $value;
break;
}
}
if ($abhaIdRaw === '') {
$abhaIdRaw = trim((string) ($patientRow['abha_address'] ?? ''));
}
$abhaDigits = preg_replace('/\D/', '', $abhaIdRaw);
$abhaDigits = is_string($abhaDigits) ? $abhaDigits : '';
$abhaAddress = trim((string) ($patientRow['abha_address'] ?? ''));
if ($abhaAddress === '' && strpos($abhaIdRaw, '@') !== false) {
$abhaAddress = $abhaIdRaw;
}
$admissionRaw = trim((string) ($ipdRow['register_date'] ?? ''));
$dischargeRaw = trim((string) ($ipdRow['discharge_date'] ?? ''));
$admissionIso = $this->toIsoDateTimeOrNow($admissionRaw);
$dischargeIso = $this->toIsoDateTimeOrNow($dischargeRaw);
$visitDate = $dischargeRaw !== ''
? date('Y-m-d', strtotime($dischargeRaw))
: ($admissionRaw !== '' ? date('Y-m-d', strtotime($admissionRaw)) : date('Y-m-d'));
$diagnosisRows = $this->byIpdRows('ipd_discharge_diagnosis', ['comp_report'], 'id ASC', $ipdId);
$conditionRows = [];
foreach ($diagnosisRows as $row) {
$text = trim((string) ($row['comp_report'] ?? ''));
if ($text !== '') {
$conditionRows[] = ['text' => $text, 'code' => ''];
}
}
$procedureRows = [];
foreach ($this->byIpdRows('ipd_discharge_surgery', ['surgery_name', 'surgery_date'], 'id ASC', $ipdId) as $row) {
$text = trim((string) ($row['surgery_name'] ?? ''));
if ($text !== '') {
$procedureRows[] = [
'text' => $text,
'code' => '',
'performed_at' => $this->toIsoDateTimeOrNow(trim((string) ($row['surgery_date'] ?? ''))),
];
}
}
foreach ($this->byIpdRows('ipd_discharge_procedure', ['procedure_name', 'procedure_date'], 'id ASC', $ipdId) as $row) {
$text = trim((string) ($row['procedure_name'] ?? ''));
if ($text !== '') {
$procedureRows[] = [
'text' => $text,
'code' => '',
'performed_at' => $this->toIsoDateTimeOrNow(trim((string) ($row['procedure_date'] ?? ''))),
];
}
}
$medicationRows = [];
$legacyMeds = $this->byIpdRows('ipd_discharge_prescrption_prescribed', ['med_name', 'dosage', 'dosage_when', 'dosage_freq', 'no_of_days'], 'id ASC', $ipdId);
if (empty($legacyMeds)) {
$legacyMeds = $this->byIpdRows('ipd_discharge_prescription_prescribed', ['med_name', 'dosage', 'dosage_when', 'dosage_freq', 'no_of_days'], 'id ASC', $ipdId);
}
foreach ($legacyMeds as $row) {
$name = trim((string) ($row['med_name'] ?? ''));
if ($name === '') {
continue;
}
$dose = trim(implode(' ', array_filter([
(string) ($row['dosage'] ?? ''),
(string) ($row['dosage_when'] ?? ''),
(string) ($row['dosage_freq'] ?? ''),
(string) ($row['no_of_days'] ?? ''),
], static fn ($v) => trim((string) $v) !== '')));
$medicationRows[] = ['name' => $name, 'dosage' => $dose];
}
if (empty($medicationRows)) {
foreach ($this->byIpdRows('ipd_discharge_drug', ['drug_name', 'drug_dose', 'drug_day'], 'id ASC', $ipdId) as $row) {
$name = trim((string) ($row['drug_name'] ?? ''));
if ($name === '') {
continue;
}
$dose = trim(implode(' ', array_filter([
(string) ($row['drug_dose'] ?? ''),
(string) ($row['drug_day'] ?? ''),
], static fn ($v) => trim((string) $v) !== '')));
$medicationRows[] = ['name' => $name, 'dosage' => $dose];
}
}
$source = [
'record_id' => (string) $ipdId,
'session_id' => (string) $ipdId,
'visit_date' => $visitDate,
'completed_at' => $dischargeIso,
'department' => trim((string) ($ipdRow['dept_id'] ?? '')),
'doctor_name' => trim((string) ($ipdRow['r_doc_name'] ?? '')),
'patient' => [
'id' => (string) $patientId,
'name' => $patientName,
'gender' => $this->normalizeFhirGender((string) ($patientRow['gender'] ?? $patientRow['xgender'] ?? '')),
'dob' => ! empty($patientRow['dob']) ? date('Y-m-d', strtotime((string) $patientRow['dob'])) : '',
'abha_id' => $abhaDigits,
'abha_address' => $abhaAddress,
],
'encounter' => [
'id' => (string) $ipdId,
'start' => $admissionIso,
'end' => $dischargeIso,
],
'conditions' => $conditionRows,
'procedures' => $procedureRows,
'medications' => $medicationRows,
];
$factory = new FhirGeneratorFactory();
$generatorOutput = $factory->discharge()->generate($source);
$adapter = new GatewayPayloadAdapter();
$gatewayPayload = $adapter->toGatewayPayload($generatorOutput, $source, $hfrId);
$sourceUpdatedAt = date('Y-m-d H:i:s', strtotime($dischargeRaw !== '' ? $dischargeRaw : 'now'));
$syncPayload = [
'local_record_id' => 'ipd-discharge-' . $ipdId,
'local_patient_id' => $patientId,
'hi_type' => (string) ($gatewayPayload['hi_type'] ?? 'DischargeSummaryRecord'),
'care_context_reference' => (string) ($gatewayPayload['care_context_reference'] ?? ''),
'care_context_display' => (string) ($gatewayPayload['care_context_display'] ?? ''),
'visit_date' => (string) ($gatewayPayload['visit_date'] ?? $visitDate),
'department' => (string) ($gatewayPayload['department'] ?? ''),
'doctor_name' => (string) ($gatewayPayload['doctor_name'] ?? ''),
'consent_id' => '',
'hfr_id' => $hfrId,
'source_updated_at' => $sourceUpdatedAt,
'patient_name' => (string) ($gatewayPayload['patient_name'] ?? $patientName),
'abha_id' => (string) ($gatewayPayload['abha_id'] ?? $abhaDigits),
'abha_address' => (string) ($gatewayPayload['abha_address'] ?? $abhaAddress),
'fhir_bundle' => (array) ($gatewayPayload['fhir_bundle'] ?? []),
];
$outbox = new AbdmSyncOutboxService();
$outbox->enqueueRecordSync($syncPayload);
}
private function normalizeFhirGender(string $gender): string
{
$value = strtolower(trim($gender));
if ($value === 'm' || $value === 'male') {
return 'male';
}
if ($value === 'f' || $value === 'female') {
return 'female';
}
if ($value === 'other') {
return 'other';
}
return 'unknown';
}
private function toIsoDateTimeOrNow(string $value): string
{
$value = trim($value);
if ($value === '') {
return date(DATE_ATOM);
}
$ts = strtotime($value);
if ($ts === false) {
return date(DATE_ATOM);
}
return date(DATE_ATOM, $ts);
}
private function getNextVisitOptions(string $baseDate): array