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,
];
// 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,
]);
// Reuse the latest matching OPD health record to avoid duplicate rows on repeat submit.
$healthRecordId = 0;
if ($this->db->tableExists('health_records')) {
$hrFields = $this->db->getFieldNames('health_records') ?? [];
$hrSelectFields = array_values(array_unique(array_filter([
'id',
'push_status',
'care_context_reference',
in_array('record_data', $hrFields, true) ? 'record_data' : null,
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 ($healthRecordId > 0 && $this->db->tableExists('health_records')) {

View File

@ -56,7 +56,7 @@
<div class="table-responsive">
<table class="table table-sm table-striped mb-0" id="taskTable">
<thead class="table-light">
<tr>
<tr data-push-status="<?= esc((string) ($r['push_status'] ?? '')) ?>">
<th>ID</th>
<th>Task</th>
<th>Patient</th>
@ -318,7 +318,7 @@
</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 (in_array((string) ($r['push_status'] ?? ''), ['failed', 'local_only'], true)): ?>
<?php if ((string) ($r['push_status'] ?? '') === 'failed'): ?>
<button
type="button"
class="btn btn-sm btn-outline-warning btn-opd-consult-submit-gateway ms-1"
@ -2030,6 +2030,7 @@
_fhirSubmitMode = options.submitMode || 'opd';
_fhirSubmitTaskRow = options.taskRow || null;
_fhirSubmitAbha = (options.abhaId || '').trim();
var rowPushStatus = (options.pushStatus || '').toString().trim().toLowerCase();
_fhirOpdId = 0; _fhirSessionId = 0;
if (fhirModalTitle) fhirModalTitle.textContent = title || 'FHIR Preview';
@ -2044,6 +2045,7 @@
if (fhirModalHttpBadge) { fhirModalHttpBadge.className = 'badge bg-warning text-dark'; fhirModalHttpBadge.textContent = 'HTTP ...'; }
if (btnSubmitFhirToAbdm) {
btnSubmitFhirToAbdm.disabled = false;
if (_fhirSubmitMode === 'task') {
btnSubmitFhirToAbdm.className = 'btn btn-sm btn-outline-warning';
btnSubmitFhirToAbdm.innerHTML = '<i class="bi bi-check2-square"></i> Verify & Submit to Gateway';
@ -2056,6 +2058,12 @@
btnSubmitFhirToAbdm.className = defaultSubmitBtnClass;
btnSubmitFhirToAbdm.innerHTML = defaultSubmitBtnHtml;
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) {
btn.addEventListener('click', function () {
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;
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 });
});
});