ABDM: guard repeat OPD submits and dedupe health records

This commit is contained in:
DevSoftTech Server 2026-06-27 00:32:14 +05:30
parent 9b00974e4e
commit e651b18a01
2 changed files with 62 additions and 14 deletions

View File

@ -1164,17 +1164,55 @@ class AbdmGateway extends BaseController
'bundle' => $bundle, 'bundle' => $bundle,
]; ];
// Store FHIR payload in health_records before pushing // Reuse the latest matching OPD health record to avoid duplicate rows on repeat submit.
$healthRecordId = $this->storeHealthRecord([ $healthRecordId = 0;
'patient_id' => $patientId, if ($this->db->tableExists('health_records')) {
'abha_id' => $hasAbha ? $abhaId : '', $hrFields = $this->db->getFieldNames('health_records') ?? [];
'hi_type' => 'OPConsultRecord', $hrSelectFields = array_values(array_unique(array_filter([
'entity_type' => 'opd', 'id',
'entity_id' => (string) $opdId, 'push_status',
'fhir_bundle' => $bundleJson, 'care_context_reference',
'care_context_reference' => $careContextRef, in_array('record_data', $hrFields, true) ? 'record_data' : null,
'consent_handle' => $consentHandleResolved, in_array('updated_at', $hrFields, true) ? 'updated_at' : null,
]); in_array('created_at', $hrFields, true) ? 'created_at' : null,
])));
$hrBuilder = $this->db->table('health_records')
->select(implode(',', $hrSelectFields))
->where('entity_type', 'opd')
->where('entity_id', (string) $opdId);
if (in_array('care_context_reference', $hrFields, true)) {
$hrBuilder->where('care_context_reference', $careContextRef);
}
$existingHr = $hrBuilder
->orderBy('id', 'DESC')
->get(1)
->getRowArray();
if (! empty($existingHr)) {
$existingPush = strtolower(trim((string) ($existingHr['push_status'] ?? '')));
$existingRecordData = (string) ($existingHr['record_data'] ?? '');
$isSameBundle = $existingRecordData !== '' ? hash('sha256', $existingRecordData) === hash('sha256', $bundleJson) : false;
if ($isSameBundle || in_array($existingPush, ['queued', 'linked', 'local_discovery_ready', 'local_only'], true)) {
$healthRecordId = (int) ($existingHr['id'] ?? 0);
}
}
}
if ($healthRecordId <= 0) {
// Store FHIR payload in health_records before pushing
$healthRecordId = $this->storeHealthRecord([
'patient_id' => $patientId,
'abha_id' => $hasAbha ? $abhaId : '',
'hi_type' => 'OPConsultRecord',
'entity_type' => 'opd',
'entity_id' => (string) $opdId,
'fhir_bundle' => $bundleJson,
'care_context_reference' => $careContextRef,
'consent_handle' => $consentHandleResolved,
]);
}
if (! $hasAbha) { if (! $hasAbha) {
if ($healthRecordId > 0 && $this->db->tableExists('health_records')) { if ($healthRecordId > 0 && $this->db->tableExists('health_records')) {

View File

@ -56,7 +56,7 @@
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-sm table-striped mb-0" id="taskTable"> <table class="table table-sm table-striped mb-0" id="taskTable">
<thead class="table-light"> <thead class="table-light">
<tr> <tr data-push-status="<?= esc((string) ($r['push_status'] ?? '')) ?>">
<th>ID</th> <th>ID</th>
<th>Task</th> <th>Task</th>
<th>Patient</th> <th>Patient</th>
@ -318,7 +318,7 @@
</td> </td>
<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> <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 (in_array((string) ($r['push_status'] ?? ''), ['failed', 'local_only'], true)): ?> <?php if ((string) ($r['push_status'] ?? '') === 'failed'): ?>
<button <button
type="button" type="button"
class="btn btn-sm btn-outline-warning btn-opd-consult-submit-gateway ms-1" class="btn btn-sm btn-outline-warning btn-opd-consult-submit-gateway ms-1"
@ -2030,6 +2030,7 @@
_fhirSubmitMode = options.submitMode || 'opd'; _fhirSubmitMode = options.submitMode || 'opd';
_fhirSubmitTaskRow = options.taskRow || null; _fhirSubmitTaskRow = options.taskRow || null;
_fhirSubmitAbha = (options.abhaId || '').trim(); _fhirSubmitAbha = (options.abhaId || '').trim();
var rowPushStatus = (options.pushStatus || '').toString().trim().toLowerCase();
_fhirOpdId = 0; _fhirSessionId = 0; _fhirOpdId = 0; _fhirSessionId = 0;
if (fhirModalTitle) fhirModalTitle.textContent = title || 'FHIR Preview'; if (fhirModalTitle) fhirModalTitle.textContent = title || 'FHIR Preview';
@ -2044,6 +2045,7 @@
if (fhirModalHttpBadge) { fhirModalHttpBadge.className = 'badge bg-warning text-dark'; fhirModalHttpBadge.textContent = 'HTTP ...'; } if (fhirModalHttpBadge) { fhirModalHttpBadge.className = 'badge bg-warning text-dark'; fhirModalHttpBadge.textContent = 'HTTP ...'; }
if (btnSubmitFhirToAbdm) { if (btnSubmitFhirToAbdm) {
btnSubmitFhirToAbdm.disabled = false;
if (_fhirSubmitMode === 'task') { if (_fhirSubmitMode === 'task') {
btnSubmitFhirToAbdm.className = 'btn btn-sm btn-outline-warning'; btnSubmitFhirToAbdm.className = 'btn btn-sm btn-outline-warning';
btnSubmitFhirToAbdm.innerHTML = '<i class="bi bi-check2-square"></i> Verify & Submit to Gateway'; btnSubmitFhirToAbdm.innerHTML = '<i class="bi bi-check2-square"></i> Verify & Submit to Gateway';
@ -2056,6 +2058,12 @@
btnSubmitFhirToAbdm.className = defaultSubmitBtnClass; btnSubmitFhirToAbdm.className = defaultSubmitBtnClass;
btnSubmitFhirToAbdm.innerHTML = defaultSubmitBtnHtml; btnSubmitFhirToAbdm.innerHTML = defaultSubmitBtnHtml;
btnSubmitFhirToAbdm.title = defaultSubmitBtnTitle; btnSubmitFhirToAbdm.title = defaultSubmitBtnTitle;
if (['linked', 'queued', 'local_discovery_ready'].indexOf(rowPushStatus) !== -1) {
btnSubmitFhirToAbdm.disabled = true;
btnSubmitFhirToAbdm.className = 'btn btn-sm btn-outline-secondary';
btnSubmitFhirToAbdm.innerHTML = '<i class="bi bi-check2-circle"></i> Already Submitted';
btnSubmitFhirToAbdm.title = 'Record is already queued/linked/discovery-ready. Use retry only on failed rows.';
}
} }
} }
@ -2300,8 +2308,10 @@
document.querySelectorAll('.btn-opd-consult-fhir').forEach(function (btn) { document.querySelectorAll('.btn-opd-consult-fhir').forEach(function (btn) {
btn.addEventListener('click', function () { btn.addEventListener('click', function () {
var opdId = parseInt(btn.getAttribute('data-opd-id') || '0', 10); var opdId = parseInt(btn.getAttribute('data-opd-id') || '0', 10);
var tr = btn.closest('tr');
var pushStatus = tr ? (tr.getAttribute('data-push-status') || '') : '';
if (opdId <= 0) return; if (opdId <= 0) return;
openFhirModal('<?= base_url('Opd_prescription/fhir_bundle_preview') ?>/' + opdId, 'FHIR Preview — OPD #' + opdId); openFhirModal('<?= base_url('Opd_prescription/fhir_bundle_preview') ?>/' + opdId, 'FHIR Preview — OPD #' + opdId, { pushStatus: pushStatus });
}); });
}); });