feat(abdm): enable live fresh FHIR fetch on pull, remove old push options, and keep care context linking

This commit is contained in:
DevSoftTech Server 2026-09-28 03:25:04 +05:30
parent 90e63d2d58
commit d0f7aa4542
3 changed files with 109 additions and 31 deletions

View File

@ -4901,6 +4901,41 @@ class AbdmGateway extends BaseController
continue;
}
// Strategy 0: Live On-Demand Fresh Generation for OPD Consult Record
if (preg_match('/(?:OPD|PRESCRIPTION)-(\d+)(?:-S(\d+))?/i', $ref, $m)) {
$targetPatientId = (int) $m[1];
$targetSessionId = (int) ($m[2] ?? 0);
$targetOpdId = 0;
if ($targetSessionId > 0 && $db->tableExists('opd_prescription')) {
$pRow = $db->table('opd_prescription')->select('opd_id')->where('id', $targetSessionId)->get(1)->getRowArray();
$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);
}
if ($targetOpdId > 0) {
try {
$opdCtrl = new \App\Controllers\Opd_prescription();
$opdCtrl->initController($this->request, $this->response, service('logger'));
$regen = $opdCtrl->regenerateFhirBundleInternal($targetOpdId, $targetSessionId);
if (!empty($regen['ok']) && !empty($regen['bundle'])) {
$records[] = [
'careContextReference' => $ref,
'hiType' => 'OPConsultRecord',
'display' => 'Consultation Record - ' . date('d M Y'),
'bundle' => $regen['bundle'],
];
continue;
}
} catch (\Throwable $ex) {
log_message('warning', 'Live FHIR bundle 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')) {
@ -4962,8 +4997,8 @@ class AbdmGateway extends BaseController
if (is_array($bundleData)) {
$records[] = [
'careContextReference' => $ref,
'hiType' => 'PrescriptionRecord',
'display' => 'Prescription - ' . date('d M Y', strtotime($docRow['generated_at'] ?? 'now')),
'hiType' => $docRow['bundle_type'] ?? 'OPConsultRecord',
'display' => 'Consultation Record - ' . date('d M Y', strtotime($docRow['generated_at'] ?? 'now')),
'bundle' => $bundleData,
];
continue;

View File

@ -4843,7 +4843,7 @@ class Opd_prescription extends BaseController
*
* @return array<string, mixed>
*/
private function regenerateFhirBundleInternal(int $opdId, int $sessionId): array
public function regenerateFhirBundleInternal(int $opdId, int $sessionId): array
{
if ($opdId <= 0) {
@ -8799,6 +8799,14 @@ class Opd_prescription extends BaseController
$this->trackMedicineUsage($medId);
}
if ($opdId > 0 && $sessionId > 0) {
try {
$this->regenerateFhirBundleInternal($opdId, $sessionId);
} catch (\Throwable $fhirEx) {
log_message('warning', 'FHIR bundle auto-update on medicine_add failed: ' . $fhirEx->getMessage());
}
}
$message = 'Medicine added';
if (! empty($masterInfo['is_new'])) {
$message .= ' | New medicine saved in OPD master';
@ -9210,6 +9218,20 @@ class Opd_prescription extends BaseController
$this->trackMedicineUsage($masterMedId);
}
$sessionId = (int) ($current['opd_pre_id'] ?? 0);
$opdId = 0;
if ($sessionId > 0 && $this->db->tableExists('opd_prescription')) {
$pRow = $this->db->table('opd_prescription')->select('opd_id')->where('id', $sessionId)->get(1)->getRowArray();
$opdId = (int) ($pRow['opd_id'] ?? 0);
}
if ($opdId > 0 && $sessionId > 0) {
try {
$this->regenerateFhirBundleInternal($opdId, $sessionId);
} catch (\Throwable $fhirEx) {
log_message('warning', 'FHIR bundle auto-update on medicine_update failed: ' . $fhirEx->getMessage());
}
}
$message = 'Medicine updated';
if (! empty($masterInfo['is_new'])) {
$message .= ' | New medicine saved in OPD master';
@ -9309,6 +9331,21 @@ class Opd_prescription extends BaseController
$row = $this->db->table($table)->where('id', (int) $id)->get(1)->getRowArray() ?? [];
$this->db->table($table)->where('id', (int) $id)->delete();
$this->auditClinicalUpdate('opd_prescription_medicine', 'removed', (int) $id, $row, null);
$sessionId = (int) ($row['opd_pre_id'] ?? 0);
$opdId = 0;
if ($sessionId > 0 && $this->db->tableExists('opd_prescription')) {
$pRow = $this->db->table('opd_prescription')->select('opd_id')->where('id', $sessionId)->get(1)->getRowArray();
$opdId = (int) ($pRow['opd_id'] ?? 0);
}
if ($opdId > 0 && $sessionId > 0) {
try {
$this->regenerateFhirBundleInternal($opdId, $sessionId);
} catch (\Throwable $fhirEx) {
log_message('warning', 'FHIR bundle auto-update on medicine_remove failed: ' . $fhirEx->getMessage());
}
}
return $this->response->setJSON(['update' => 1, 'error_text' => 'Medicine removed']);
}

View File

@ -62,8 +62,8 @@
['Patients Without ABHA', 'without_abha', 'danger'],
['ABHA Verified', 'abha_verified', 'success'],
['Verified In Range', 'verified_in_range', 'info'],
['Records Pushed', 'records_pushed', 'warning'],
['Pushed In Range', 'records_pushed_in_range', 'primary'],
['Care Contexts Linked', 'records_pushed', 'success'],
['Linked In Range', 'records_pushed_in_range', 'primary'],
['OPD Tokens', 'opd_tokens', 'secondary'],
['Tokens In Range', 'opd_tokens_in_range', 'success'],
] as [$label, $key, $tone]): ?>
@ -178,22 +178,14 @@
<button type="button" class="btn btn-sm btn-outline-success action-btn" data-action="create_abha">Create ABHA</button>
<?php elseif ($type === 'patient_abha_update'): ?>
<button type="button" class="btn btn-sm btn-outline-primary action-btn" data-action="update_abha">Update ABHA</button>
<?php elseif ($type === 'immunization_record_publish'): ?>
<?php if ($bridgeSubmitted): ?>
<button type="button" class="btn btn-sm btn-outline-success" disabled title="Submitted to ABDM Bridge<?= $bridgeCareContext !== '' ? ': ' . esc($bridgeCareContext) : '' ?>">Submitted to Bridge</button>
<?php else: ?>
<button type="button" class="btn btn-sm btn-outline-success immunization-bridge-btn" title="Push this ImmunizationRecord FHIR bundle to ABDM Bridge.">Push to Bridge</button>
<?php endif; ?>
<?php else: ?>
<button type="button" class="btn btn-sm btn-outline-warning action-btn" data-action="submit">Submit</button>
<?php endif; ?>
<button type="button" class="btn btn-sm btn-outline-success btn-row-hip-link" title="HIP-Initiated Care Context Linking"><i class="bi bi-link-45deg"></i> Link Care Context</button>
<?php if ($showPreview): ?>
<button type="button" class="btn btn-sm btn-outline-primary preview-fhir-btn">Preview FHIR</button>
<?php endif; ?>
<?php if ($showSandbox): ?>
<button type="button" class="btn btn-sm btn-outline-info sandbox-btn">Sandbox</button>
<?php endif; ?>
<?php if ($showPreview): ?>
<button type="button" class="btn btn-sm btn-outline-primary preview-fhir-btn">Preview FHIR</button>
<?php endif; ?>
<button type="button" class="btn btn-sm btn-outline-success btn-row-hip-link" title="HIP-Initiated Care Context Linking"><i class="bi bi-link-45deg"></i> Link</button>
<button type="button" class="btn btn-sm btn-outline-dark close-btn">Close</button>
</div>
</td>
@ -376,13 +368,7 @@
$bridgeRecordId = trim((string) ($r['bridge_record_id'] ?? ''));
$hasFhir = (int) ($r['has_fhir'] ?? 0) === 1;
$pushStatusRaw = strtolower(trim((string) ($r['push_status'] ?? '')));
$canSubmitGateway = $hasFhir;
$submitButtonLabel = in_array($pushStatusRaw, ['queued', 'linked', 'failed'], true)
? 'Submit Again'
: 'Submit to ABDM';
$submitButtonClass = in_array($pushStatusRaw, ['queued', 'linked'], true)
? 'btn-outline-secondary'
: 'btn-outline-warning';
$isCareContextLinked = in_array(strtolower($linkStatus), ['linked', 'registered'], true) || in_array($pushStatusRaw, ['linked', 'pushed', 'queued'], true);
$statusBadgeClass = 'badge bg-' . $statusTone;
if ($statusTone === 'warning') {
$statusBadgeClass .= ' text-dark';
@ -417,13 +403,18 @@
</td>
<td>
<button type="button" class="btn btn-sm btn-outline-primary btn-opd-consult-fhir" data-opd-id="<?= (int) ($r['opd_id'] ?? 0) ?>">Preview FHIR</button>
<?php if ($canSubmitGateway): ?>
<?php if ($isCareContextLinked): ?>
<span class="badge bg-success ms-1"><i class="bi bi-check-circle"></i> Linked</span>
<?php else: ?>
<button
type="button"
class="btn btn-sm <?= esc($submitButtonClass) ?> btn-opd-consult-submit-gateway ms-1"
class="btn btn-sm btn-outline-success btn-opd-care-context-link ms-1"
data-opd-id="<?= (int) ($r['opd_id'] ?? 0) ?>"
data-patient-id="<?= (int) ($r['p_id'] ?? $r['patient_id'] ?? 0) ?>"
data-abha-id="<?= esc((string) ($r['abha_id'] ?? '')) ?>"
><?= esc($submitButtonLabel) ?></button>
data-patient-name="<?= esc((string) ($r['P_name'] ?? '')) ?>"
title="Link Care Context with patient ABHA (M2)"
><i class="bi bi-link-45deg"></i> Link Context</button>
<?php endif; ?>
</td>
</tr>
@ -490,7 +481,11 @@
</td>
<td class="text-nowrap">
<button type="button" class="btn btn-sm btn-outline-primary btn-invoice-preview">Preview</button>
<button type="button" class="btn btn-sm btn-outline-warning btn-invoice-push" <?= $alreadySubmitted ? 'disabled' : '' ?>>Push</button>
<?php if ($alreadySubmitted): ?>
<span class="badge bg-success ms-1"><i class="bi bi-check-circle"></i> Linked</span>
<?php else: ?>
<button type="button" class="btn btn-sm btn-outline-success btn-invoice-care-link ms-1" data-patient-id="<?= (int) ($invoice['patient_id'] ?? 0) ?>" data-abha-id="<?= esc((string) ($invoice['abha_id'] ?? '')) ?>" data-patient-name="<?= esc((string) ($invoice['patient_name'] ?? '')) ?>" title="Link Care Context with patient ABHA"><i class="bi bi-link-45deg"></i> Link Context</button>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
@ -538,8 +533,6 @@
<button type="button" class="btn btn-sm btn-outline-warning" id="btnRegenerateFhirModal" title="Rebuild FHIR bundle from current clinical data"><i class="bi bi-arrow-clockwise"></i> Regenerate</button>
<button type="button" class="btn btn-sm btn-primary" id="btnUpdateFhirJson" title="Validate and apply edited JSON changes to preview and database"><i class="bi bi-check2-circle"></i> Update JSON</button>
<div>
<label class="me-3 small text-muted"><input type="checkbox" id="chkForceNewRecord" checked> New Record ID (for PHR testing)</label>
<button type="button" class="btn btn-sm btn-outline-success" id="btnSubmitFhirToAbdm" title="Push FHIR bundle to ABDM bridge"><i class="bi bi-cloud-upload"></i> Submit to ABDM</button>
<button type="button" class="btn btn-sm btn-outline-secondary" id="btnCopyFhirModal">Copy JSON</button>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
@ -2999,7 +2992,8 @@
});
})();
btnSubmitFhirToAbdm.addEventListener('click', function () {
if (btnSubmitFhirToAbdm) {
btnSubmitFhirToAbdm.addEventListener('click', function () {
var btn = this;
if (_fhirSubmitMode === 'invoice') {
@ -3374,6 +3368,7 @@
alert(' Error: ' + msg);
});
});
}
document.getElementById('btnCopyFhirModal').addEventListener('click', function () {
var btn = this;
@ -3484,6 +3479,17 @@
}
});
});
document.querySelectorAll('.btn-opd-care-context-link, .btn-invoice-care-link').forEach(function (btn) {
btn.addEventListener('click', function () {
var patientId = parseInt(btn.getAttribute('data-patient-id') || '0', 10);
var abhaId = (btn.getAttribute('data-abha-id') || '').trim();
var patientName = (btn.getAttribute('data-patient-name') || '').trim();
if (typeof window.openAbdmHipLinkModal === 'function') {
window.openAbdmHipLinkModal(patientId, abhaId, { patient_name: patientName });
}
});
});
})();
</script>
<?= view('partials/abdm_hip_link_modal') ?>