fix(abdm): add datetime-local to consent request, prevent browser hang with non-blocking fetch, and fix DB column queries
This commit is contained in:
parent
d2dd84b27f
commit
5306271383
|
|
@ -1647,10 +1647,10 @@ $routes->get('app/nursing', 'Api\v1\NursingApi::pwaIndex');
|
|||
$routes->get('app/nursing/(:any)', 'Api\v1\NursingApi::pwaIndex');
|
||||
$routes->get('app/doctor', 'Api\v1\DoctorApi::pwaIndex');
|
||||
$routes->get('app/doctor/(:any)', 'Api\v1\DoctorApi::pwaIndex');
|
||||
$routes->match(['get', 'head'], 'app/queue', 'Api\v1\DoctorApi::queueIndex');
|
||||
$routes->match(['get', 'head'], 'app/queue/sw.js', 'Api\v1\DoctorApi::queueSw');
|
||||
$routes->match(['get', 'head'], 'app/queue/manifest.json', 'Api\v1\DoctorApi::queueManifest');
|
||||
$routes->match(['get', 'head'], 'app/queue/(:any)', 'Api\v1\DoctorApi::queueIndex');
|
||||
$routes->match(['GET', 'HEAD'], 'app/queue', 'Api\v1\DoctorApi::queueIndex');
|
||||
$routes->match(['GET', 'HEAD'], 'app/queue/sw.js', 'Api\v1\DoctorApi::queueSw');
|
||||
$routes->match(['GET', 'HEAD'], 'app/queue/manifest.json', 'Api\v1\DoctorApi::queueManifest');
|
||||
$routes->match(['GET', 'HEAD'], 'app/queue/(:any)', 'Api\v1\DoctorApi::queueIndex');
|
||||
|
||||
// Dedicated RESTful JSON API v1 Group for React PWAs & Mobile Apps
|
||||
$routes->group('api/v1', ['namespace' => 'App\Controllers\Api\v1'], static function ($routes) {
|
||||
|
|
|
|||
|
|
@ -9845,10 +9845,17 @@ class Ipd_discharge extends BaseController
|
|||
'updated_at' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
|
||||
$hrFields = $this->db->getFieldNames('health_records') ?? [];
|
||||
if (! empty($hrFields)) {
|
||||
$hrData = array_intersect_key($hrData, array_flip($hrFields));
|
||||
}
|
||||
|
||||
if (! empty($existingHr)) {
|
||||
$this->db->table('health_records')->where('id', (int) $existingHr['id'])->update($hrData);
|
||||
} else {
|
||||
$hrData['created_at'] = date('Y-m-d H:i:s');
|
||||
if (in_array('created_at', $hrFields, true)) {
|
||||
$hrData['created_at'] = date('Y-m-d H:i:s');
|
||||
}
|
||||
$this->db->table('health_records')->insert($hrData);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2116,19 +2116,30 @@ class Patient extends BaseController
|
|||
$nowIst = new \DateTimeImmutable('now', $istTz);
|
||||
|
||||
try {
|
||||
$fromIst = $dateFromInput !== '' ? new \DateTimeImmutable($dateFromInput, $istTz) : $nowIst->modify('-365 days');
|
||||
if ($dateFromInput !== '') {
|
||||
$fromIst = new \DateTimeImmutable($dateFromInput, $istTz);
|
||||
if (strpos($dateFromInput, 'T') === false && strpos($dateFromInput, ' ') === false) {
|
||||
$fromIst = $fromIst->setTime(0, 0, 0);
|
||||
}
|
||||
} else {
|
||||
$fromIst = $nowIst->modify('-365 days')->setTime(0, 0, 0);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return $this->response->setStatusCode(422)->setJSON([
|
||||
'ok' => 0,
|
||||
'error' => 'Invalid "Date From" date.',
|
||||
'error' => 'Invalid "Date From" date/time.',
|
||||
]);
|
||||
}
|
||||
try {
|
||||
$toIst = $dateToInput !== '' ? new \DateTimeImmutable($dateToInput, $istTz) : $nowIst;
|
||||
if ($dateToInput !== '') {
|
||||
$toIst = new \DateTimeImmutable($dateToInput, $istTz);
|
||||
} else {
|
||||
$toIst = $nowIst;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return $this->response->setStatusCode(422)->setJSON([
|
||||
'ok' => 0,
|
||||
'error' => 'Invalid "Date To" date.',
|
||||
'error' => 'Invalid "Date To" date/time.',
|
||||
]);
|
||||
}
|
||||
if ($fromIst->getTimestamp() >= $toIst->getTimestamp()) {
|
||||
|
|
@ -2139,14 +2150,21 @@ class Patient extends BaseController
|
|||
}
|
||||
|
||||
try {
|
||||
$eraseIst = $eraseDateInput !== '' ? new \DateTimeImmutable($eraseDateInput, $istTz) : $nowIst->modify('+1 year');
|
||||
if ($eraseDateInput !== '') {
|
||||
$eraseIst = new \DateTimeImmutable($eraseDateInput, $istTz);
|
||||
if (strpos($eraseDateInput, 'T') === false && strpos($eraseDateInput, ' ') === false) {
|
||||
$eraseIst = $eraseIst->setTime(23, 59, 59);
|
||||
}
|
||||
} else {
|
||||
$eraseIst = $nowIst->modify('+1 year')->setTime(23, 59, 59);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return $this->response->setStatusCode(422)->setJSON([
|
||||
'ok' => 0,
|
||||
'error' => 'Invalid "Expiry Date" date.',
|
||||
'error' => 'Invalid "Expiry Date" date/time.',
|
||||
]);
|
||||
}
|
||||
if ($eraseIst->setTime(23, 59, 59)->getTimestamp() < $toIst->getTimestamp()) {
|
||||
if ($eraseIst->getTimestamp() < $toIst->getTimestamp()) {
|
||||
return $this->response->setStatusCode(422)->setJSON([
|
||||
'ok' => 0,
|
||||
'error' => '"Expiry Date" must be on or after "Date To".',
|
||||
|
|
@ -2158,9 +2176,9 @@ class Patient extends BaseController
|
|||
$toIst = $safeNowIst;
|
||||
}
|
||||
|
||||
$fromUtc = $fromIst->setTime(0, 0, 0)->setTimezone($utcTz)->format('Y-m-d\TH:i:s.000\Z');
|
||||
$fromUtc = $fromIst->setTimezone($utcTz)->format('Y-m-d\TH:i:s.000\Z');
|
||||
$toUtc = $toIst->setTimezone($utcTz)->format('Y-m-d\TH:i:s.000\Z');
|
||||
$eraseAtUtc = $eraseIst->setTime(23, 59, 59)->setTimezone($utcTz)->format('Y-m-d\TH:i:s.000\Z');
|
||||
$eraseAtUtc = $eraseIst->setTimezone($utcTz)->format('Y-m-d\TH:i:s.000\Z');
|
||||
|
||||
$consent = [
|
||||
'purpose' => [
|
||||
|
|
@ -2384,52 +2402,41 @@ class Patient extends BaseController
|
|||
}
|
||||
|
||||
// If we already have a granted snapshot with a concrete consent reference,
|
||||
// continue directly with fetch instead of re-reconciling a stale request_id.
|
||||
// check if records exist or let background worker fetch them without blocking the browser.
|
||||
if ($resumedFromLastSync && strtoupper(trim((string) ($lastSync['phase'] ?? ''))) === 'GRANTED') {
|
||||
if ($consentArtifactRef === '' && $consentRequestRef === '') {
|
||||
$consentRequestRef = trim((string) ($lastSync['consent_request_id'] ?? ''));
|
||||
$consentArtifactRef = trim((string) ($lastSync['consent_id'] ?? ''));
|
||||
}
|
||||
|
||||
$canDirectFetch = ($consentArtifactRef !== '' && preg_match('/^REQ-/i', $consentArtifactRef) !== 1)
|
||||
|| ($consentRequestRef !== '' && preg_match('/^REQ-/i', $consentRequestRef) !== 1);
|
||||
|
||||
if ($canDirectFetch) {
|
||||
$fetchPayload = [
|
||||
'abha_address' => $abhaContext['abha_address'],
|
||||
];
|
||||
if ($consentArtifactRef !== '' && preg_match('/^REQ-/i', $consentArtifactRef) !== 1) {
|
||||
$fetchPayload['consentId'] = $consentArtifactRef;
|
||||
} elseif ($consentRequestRef !== '' && preg_match('/^REQ-/i', $consentRequestRef) !== 1) {
|
||||
$fetchPayload['consentRequestId'] = $consentRequestRef;
|
||||
}
|
||||
|
||||
try {
|
||||
$fetchResult = $service->runOperation('data_fetch', $fetchPayload);
|
||||
} catch (\Throwable $e) {
|
||||
return $this->response->setStatusCode(500)->setJSON([
|
||||
'ok' => 0,
|
||||
'phase' => 'FETCH_FAILED',
|
||||
'error' => 'Unable to fetch ABDM content: ' . $e->getMessage(),
|
||||
'request_id' => $flowRefId,
|
||||
]);
|
||||
}
|
||||
|
||||
if ((int) ($fetchResult['ok'] ?? 0) === 1) {
|
||||
return $this->response->setJSON([
|
||||
'ok' => 1,
|
||||
'phase' => 'COMPLETED',
|
||||
'poll_again' => 0,
|
||||
'request_id' => $flowRefId,
|
||||
'message' => 'Resumed previous granted consent flow and fetched data successfully.',
|
||||
'data' => [
|
||||
'consent_request' => $consentResult,
|
||||
'consent_reconcile' => $reconcileResult,
|
||||
'data_fetch' => $fetchResult,
|
||||
],
|
||||
]);
|
||||
}
|
||||
$docCount = $this->countAbdmDocumentsForPatient($pno, $consentRequestRef);
|
||||
if ($docCount > 0) {
|
||||
return $this->response->setJSON([
|
||||
'ok' => 1,
|
||||
'phase' => 'COMPLETED',
|
||||
'poll_again' => 0,
|
||||
'request_id' => $flowRefId,
|
||||
'message' => 'Resumed previous granted consent flow and ' . $docCount . ' record(s) are available.',
|
||||
'data' => [
|
||||
'consent_request' => $consentResult,
|
||||
'consent_reconcile' => $reconcileResult,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
// Records not yet stored locally; trigger background HIU poll so browser doesn't block
|
||||
$this->triggerBackgroundHiuPoll();
|
||||
return $this->response->setJSON([
|
||||
'ok' => 1,
|
||||
'phase' => 'GRANTED',
|
||||
'poll_again' => 1,
|
||||
'request_id' => $flowRefId,
|
||||
'message' => 'Consent is granted. Records are being fetched in the background...',
|
||||
'data' => [
|
||||
'consent_request' => $consentResult,
|
||||
'consent_reconcile' => $reconcileResult,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
$reconcilePayload = [
|
||||
|
|
@ -2497,88 +2504,37 @@ class Patient extends BaseController
|
|||
$reconcileResult = $fallbackReconcile;
|
||||
$state = $this->deriveAutoFlowState($reconcileResult);
|
||||
if ($state === 'GRANTED') {
|
||||
$fetchPayload = [
|
||||
'abha_address' => $abhaContext['abha_address'],
|
||||
'consentId' => $consentArtifactRef,
|
||||
];
|
||||
if ($fetchPayload['consentId'] === '') {
|
||||
unset($fetchPayload['consentId']);
|
||||
if ($consentRequestRef !== '') {
|
||||
$fetchPayload['consentRequestId'] = $consentRequestRef;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$fetchResult = $service->runOperation('data_fetch', $fetchPayload);
|
||||
} catch (\Throwable $e) {
|
||||
return $this->response->setStatusCode(500)->setJSON([
|
||||
'ok' => 0,
|
||||
'phase' => 'FETCH_FAILED',
|
||||
'error' => 'Unable to fetch ABDM content after fallback reconcile: ' . $e->getMessage(),
|
||||
'request_id' => $flowRefId,
|
||||
]);
|
||||
}
|
||||
|
||||
if ((int) ($fetchResult['ok'] ?? 0) === 1) {
|
||||
$docCount = $this->countAbdmDocumentsForPatient($pno, $consentRequestRef);
|
||||
if ($docCount > 0) {
|
||||
return $this->response->setJSON([
|
||||
'ok' => 1,
|
||||
'phase' => 'COMPLETED',
|
||||
'poll_again' => 0,
|
||||
'request_id' => $flowRefId,
|
||||
'message' => 'Consent status was resolved using fallback bridge reference and data fetched successfully.',
|
||||
'message' => 'Consent resolved and ' . $docCount . ' record(s) are available.',
|
||||
'data' => [
|
||||
'consent_request' => $consentResult,
|
||||
'consent_reconcile' => $reconcileResult,
|
||||
'data_fetch' => $fetchResult,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
$this->triggerBackgroundHiuPoll();
|
||||
return $this->response->setJSON([
|
||||
'ok' => 1,
|
||||
'phase' => 'GRANTED',
|
||||
'poll_again' => 1,
|
||||
'request_id' => $flowRefId,
|
||||
'message' => 'Consent granted. Records are being fetched in the background...',
|
||||
'data' => [
|
||||
'consent_request' => $consentResult,
|
||||
'consent_reconcile' => $reconcileResult,
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$altConsentRequestRef = trim((string) ($consentRequestRef !== '' ? $consentRequestRef : ($lastSync['consent_request_id'] ?? '')));
|
||||
$altConsentArtifactRef = trim((string) ($consentArtifactRef !== '' ? $consentArtifactRef : ($lastSync['consent_id'] ?? '')));
|
||||
$canDirectFetch = ($altConsentArtifactRef !== '' && preg_match('/^REQ-/i', $altConsentArtifactRef) !== 1)
|
||||
|| ($altConsentRequestRef !== '' && preg_match('/^REQ-/i', $altConsentRequestRef) !== 1);
|
||||
|
||||
if ($canDirectFetch) {
|
||||
$fetchPayload = [
|
||||
'abha_address' => $abhaContext['abha_address'],
|
||||
];
|
||||
if ($altConsentArtifactRef !== '' && preg_match('/^REQ-/i', $altConsentArtifactRef) !== 1) {
|
||||
$fetchPayload['consentId'] = $altConsentArtifactRef;
|
||||
} elseif ($altConsentRequestRef !== '' && preg_match('/^REQ-/i', $altConsentRequestRef) !== 1) {
|
||||
$fetchPayload['consentRequestId'] = $altConsentRequestRef;
|
||||
}
|
||||
|
||||
try {
|
||||
$fetchResult = $service->runOperation('data_fetch', $fetchPayload);
|
||||
} catch (\Throwable $e) {
|
||||
return $this->response->setStatusCode(500)->setJSON([
|
||||
'ok' => 0,
|
||||
'phase' => 'FETCH_FAILED',
|
||||
'error' => 'Unable to fetch ABDM content: ' . $e->getMessage(),
|
||||
'request_id' => $flowRefId,
|
||||
]);
|
||||
}
|
||||
|
||||
if ((int) ($fetchResult['ok'] ?? 0) === 1) {
|
||||
return $this->response->setJSON([
|
||||
'ok' => 1,
|
||||
'phase' => 'COMPLETED',
|
||||
'poll_again' => 0,
|
||||
'request_id' => $flowRefId,
|
||||
'message' => 'Consent status lookup returned not found for old request id, but data fetch succeeded using saved consent reference.',
|
||||
'data' => [
|
||||
'consent_request' => $consentResult,
|
||||
'consent_reconcile' => $reconcileResult,
|
||||
'data_fetch' => $fetchResult,
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->response->setJSON([
|
||||
'ok' => 1,
|
||||
'phase' => 'REQUESTED',
|
||||
|
|
@ -2613,64 +2569,28 @@ class Patient extends BaseController
|
|||
$pollAgain = in_array($state, ['REQUESTED', 'PENDING'], true);
|
||||
|
||||
if ($state === 'GRANTED') {
|
||||
$fetchPayload = [
|
||||
'abha_address' => $abhaContext['abha_address'],
|
||||
];
|
||||
if ($consentArtifactRef !== '') {
|
||||
$fetchPayload['consentId'] = $consentArtifactRef;
|
||||
} elseif ($consentRequestRef !== '') {
|
||||
$fetchPayload['consentRequestId'] = $consentRequestRef;
|
||||
$docCount = $this->countAbdmDocumentsForPatient($pno, $consentRequestRef);
|
||||
if ($docCount > 0) {
|
||||
$state = 'COMPLETED';
|
||||
$pollAgain = false;
|
||||
$message = 'Data fetch completed and ' . $docCount . ' document(s) are now available.';
|
||||
} else {
|
||||
$fetchPayload['request_id'] = $flowRefId;
|
||||
}
|
||||
try {
|
||||
$fetchResult = $service->runOperation('data_fetch', $fetchPayload);
|
||||
} catch (\Throwable $e) {
|
||||
return $this->response->setStatusCode(500)->setJSON([
|
||||
'ok' => 0,
|
||||
'phase' => 'FETCH_FAILED',
|
||||
'error' => 'Unable to fetch ABDM content: ' . $e->getMessage(),
|
||||
'request_id' => $flowRefId,
|
||||
]);
|
||||
}
|
||||
|
||||
if ((int) ($fetchResult['ok'] ?? 0) !== 1) {
|
||||
$httpCode = (int) ($fetchResult['http_code'] ?? 422);
|
||||
if ($httpCode < 100 || $httpCode > 599) {
|
||||
$httpCode = 422;
|
||||
}
|
||||
|
||||
$fetchError = (string) ($fetchResult['error_text'] ?? 'Data fetch failed.');
|
||||
if ($this->isPendingBridgeSessionError($fetchError, $httpCode)) {
|
||||
return $this->response->setJSON([
|
||||
'ok' => 1,
|
||||
'phase' => 'GRANTED',
|
||||
'poll_again' => 1,
|
||||
'request_id' => $flowRefId,
|
||||
'message' => 'Consent granted. Waiting for the health records to arrive from the facilities.',
|
||||
'data' => [
|
||||
'consent_request' => $consentResult,
|
||||
'consent_reconcile' => $reconcileResult,
|
||||
'data_fetch' => $fetchResult,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->response->setStatusCode($httpCode)->setJSON([
|
||||
'ok' => 0,
|
||||
'phase' => 'FETCH_FAILED',
|
||||
'error' => $fetchError,
|
||||
// Consent is granted in PHR, but documents are not yet downloaded/decrypted.
|
||||
// Trigger background HIU poll worker so the browser does NOT hang on synchronous data_fetch.
|
||||
$this->triggerBackgroundHiuPoll();
|
||||
|
||||
return $this->response->setJSON([
|
||||
'ok' => 1,
|
||||
'phase' => 'GRANTED',
|
||||
'poll_again' => 1,
|
||||
'request_id' => $flowRefId,
|
||||
'message' => 'Consent granted. Health records are being fetched in the background...',
|
||||
'data' => [
|
||||
'consent_request' => $consentResult,
|
||||
'consent_reconcile' => $reconcileResult,
|
||||
'data_fetch' => $fetchResult,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
$state = 'COMPLETED';
|
||||
$pollAgain = false;
|
||||
}
|
||||
|
||||
$message = match ($state) {
|
||||
|
|
@ -5608,9 +5528,15 @@ class Patient extends BaseController
|
|||
$containers = [
|
||||
is_array($rowReq) ? ($rowReq['consent'] ?? null) : null,
|
||||
is_array($rowReq) ? ($rowReq['consentDetail'] ?? null) : null,
|
||||
is_array($rowReq) ? ($rowReq['data']['consent'] ?? null) : null,
|
||||
is_array($rowReq) ? ($rowReq['data']['consentDetail'] ?? null) : null,
|
||||
is_array($rowReq) ? ($rowReq['data'] ?? null) : null,
|
||||
is_array($rowReq) ? $rowReq : null,
|
||||
is_array($rowResp) ? ($rowResp['consent'] ?? null) : null,
|
||||
is_array($rowResp) ? ($rowResp['consentDetail'] ?? null) : null,
|
||||
is_array($rowResp) ? ($rowResp['data']['consent'] ?? null) : null,
|
||||
is_array($rowResp) ? ($rowResp['data']['consentDetail'] ?? null) : null,
|
||||
is_array($rowResp) ? ($rowResp['data'] ?? null) : null,
|
||||
is_array($rowResp) ? $rowResp : null,
|
||||
];
|
||||
|
||||
|
|
@ -5624,6 +5550,10 @@ class Patient extends BaseController
|
|||
?? $c['permission']['date_range']['from']
|
||||
?? $c['date_range']['from']
|
||||
?? $c['dateRange']['from']
|
||||
?? $c['valid_from']
|
||||
?? $c['validFrom']
|
||||
?? $c['date_from']
|
||||
?? $c['dateFrom']
|
||||
?? ''
|
||||
));
|
||||
}
|
||||
|
|
@ -5633,6 +5563,10 @@ class Patient extends BaseController
|
|||
?? $c['permission']['date_range']['to']
|
||||
?? $c['date_range']['to']
|
||||
?? $c['dateRange']['to']
|
||||
?? $c['valid_to']
|
||||
?? $c['validTo']
|
||||
?? $c['date_to']
|
||||
?? $c['dateTo']
|
||||
?? ''
|
||||
));
|
||||
}
|
||||
|
|
@ -5641,6 +5575,8 @@ class Patient extends BaseController
|
|||
$c['permission']['dataEraseAt']
|
||||
?? $c['permission']['data_erase_at']
|
||||
?? $c['expiry']
|
||||
?? $c['erase_at']
|
||||
?? $c['eraseAt']
|
||||
?? $c['dataEraseAt']
|
||||
?? $c['data_erase_at']
|
||||
?? ''
|
||||
|
|
@ -6280,5 +6216,39 @@ class Patient extends BaseController
|
|||
'csrfHash' => csrf_hash(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function countAbdmDocumentsForPatient(int $pno, string $consentRequestRef = ''): int
|
||||
{
|
||||
if (! $this->db->tableExists('abdm_hiu_documents')) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$builder = $this->db->table('abdm_hiu_documents')->where('patient_id', $pno);
|
||||
if ($consentRequestRef !== '') {
|
||||
$builder->groupStart()
|
||||
->where('consent_request_id', $consentRequestRef)
|
||||
->orWhere('consent_ref', $consentRequestRef)
|
||||
->groupEnd();
|
||||
}
|
||||
|
||||
return (int) $builder->countAllResults();
|
||||
}
|
||||
|
||||
private function triggerBackgroundHiuPoll(): void
|
||||
{
|
||||
try {
|
||||
$sparkPath = ROOTPATH . 'spark';
|
||||
if (DIRECTORY_SEPARATOR === '\\') {
|
||||
$cmd = 'start /B php "' . $sparkPath . '" abdm:hiu-poll --limit=10 > NUL 2>&1';
|
||||
@pclose(@popen($cmd, 'r'));
|
||||
} else {
|
||||
$phpBin = is_executable('/usr/bin/php8.3') ? '/usr/bin/php8.3' : (is_executable('/usr/bin/php') ? '/usr/bin/php' : 'php');
|
||||
$cmd = $phpBin . ' "' . $sparkPath . '" abdm:hiu-poll --limit=10 > /dev/null 2>&1 &';
|
||||
@exec($cmd);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
log_message('warning', '[triggerBackgroundHiuPoll] ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1360,10 +1360,35 @@ class AbdmTaskBoardSyncService
|
|||
// 6. IPD Discharge: ipd_discharge
|
||||
if ($entityType === 'ipd_discharge' || $taskType === 'ipd_discharge_publish') {
|
||||
if ($dbConn->tableExists('ipd_discharge')) {
|
||||
$dRow = $dbConn->table('ipd_discharge')->select('discharge_date, created_at')->where('id', $entityId)->get(1)->getRowArray();
|
||||
$date = ! empty($dRow['discharge_date']) ? $dRow['discharge_date'] : ($dRow['created_at'] ?? null);
|
||||
if (! empty($date) && strtotime((string) $date) > 0) {
|
||||
return (string) $date;
|
||||
$dFields = $dbConn->getFieldNames('ipd_discharge') ?? [];
|
||||
$sel = in_array('ipd_id', $dFields, true) ? 'id, ipd_id' : 'id';
|
||||
if (in_array('created_datetime', $dFields, true)) {
|
||||
$sel .= ', created_datetime';
|
||||
}
|
||||
$dRow = $dbConn->table('ipd_discharge')->select($sel)->where('id', $entityId)->get(1)->getRowArray();
|
||||
$targetIpdId = ! empty($dRow['ipd_id']) ? (int) $dRow['ipd_id'] : (int) $entityId;
|
||||
if ($dbConn->tableExists('ipd_master')) {
|
||||
$mFields = $dbConn->getFieldNames('ipd_master') ?? [];
|
||||
$mSel = [];
|
||||
if (in_array('discharge_date', $mFields, true)) {
|
||||
$mSel[] = 'discharge_date';
|
||||
}
|
||||
if (in_array('discharge_time', $mFields, true)) {
|
||||
$mSel[] = 'discharge_time';
|
||||
}
|
||||
if (! empty($mSel)) {
|
||||
$mRow = $dbConn->table('ipd_master')->select(implode(', ', $mSel))->where('id', $targetIpdId)->get(1)->getRowArray();
|
||||
if (! empty($mRow['discharge_date']) && strtotime((string) $mRow['discharge_date']) > 0) {
|
||||
$dt = (string) $mRow['discharge_date'];
|
||||
if (! empty($mRow['discharge_time'])) {
|
||||
$dt .= ' ' . $mRow['discharge_time'];
|
||||
}
|
||||
return $dt;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (! empty($dRow['created_datetime']) && strtotime((string) $dRow['created_datetime']) > 0) {
|
||||
return (string) $dRow['created_datetime'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1061,15 +1061,15 @@ if ($patientPhotoPath === '') {
|
|||
<div class="row g-3 mb-3">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label fw-semibold" for="abdmCustomDateFrom">Date From</label>
|
||||
<input type="date" class="form-control" id="abdmCustomDateFrom">
|
||||
<input type="datetime-local" class="form-control" id="abdmCustomDateFrom" step="1">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label fw-semibold" for="abdmCustomDateTo">Date To</label>
|
||||
<input type="date" class="form-control" id="abdmCustomDateTo">
|
||||
<input type="datetime-local" class="form-control" id="abdmCustomDateTo" step="1">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label fw-semibold" for="abdmCustomEraseDate">Expiry Date</label>
|
||||
<input type="date" class="form-control" id="abdmCustomEraseDate">
|
||||
<input type="datetime-local" class="form-control" id="abdmCustomEraseDate" step="1">
|
||||
</div>
|
||||
</div>
|
||||
<div class="small text-danger d-none" id="abdmCustomConsentError"></div>
|
||||
|
|
@ -1383,35 +1383,20 @@ $(function() {
|
|||
html += '<div class="small text-muted">No structured sections available for this record.</div>';
|
||||
}
|
||||
|
||||
// Attachments (scanned images/PDFs) are stripped from the list payload
|
||||
// to keep it light -- lazily fetch the full per-document detail (which
|
||||
// includes them) after the modal is rendered, see below.
|
||||
// Attachments (scanned images/PDFs) are loaded on-demand per document
|
||||
// to keep the initial render fast and avoid flooding the server/browser.
|
||||
if (docId) {
|
||||
html += '<div class="mt-3"><div class="fw-semibold small mb-2">Attached Files</div>'
|
||||
+ '<div class="abdm-fetch-attachments small text-muted" data-doc-id="' + escHtml(docId) + '">Loading attached files...</div></div>';
|
||||
html += '<div class="mt-2 pt-2 border-top">'
|
||||
+ '<button type="button" class="btn btn-outline-secondary btn-sm abdm-load-attachments-btn" data-doc-id="' + escHtml(docId) + '">'
|
||||
+ '<i class="fa fa-paperclip me-1"></i> View Attached Files / Scans'
|
||||
+ '</button>'
|
||||
+ '<div class="abdm-fetch-attachments mt-2 d-none" data-doc-id="' + escHtml(docId) + '"></div>'
|
||||
+ '</div>';
|
||||
}
|
||||
|
||||
html += '</div></div>';
|
||||
});
|
||||
$('#abdmFetchResultModalBody').html(html);
|
||||
|
||||
docIds.forEach(function(docId) {
|
||||
fetch(abdmDocDetailBaseUrl + '/' + encodeURIComponent(docId), { credentials: 'same-origin' })
|
||||
.then(function(resp) { return resp.json(); })
|
||||
.then(function(data) {
|
||||
var item = (data && data.ok === 1) ? (data.item || {}) : null;
|
||||
var summary = (item && item.summary) || {};
|
||||
var $target = $('.abdm-fetch-attachments[data-doc-id="' + docId + '"]');
|
||||
if (!item) {
|
||||
$target.removeClass('text-muted').addClass('text-danger').text('Unable to load attached files.');
|
||||
return;
|
||||
}
|
||||
$target.removeClass('small text-muted').html(renderAttachmentsHtml(summary.attachments));
|
||||
})
|
||||
.catch(function() {
|
||||
$('.abdm-fetch-attachments[data-doc-id="' + docId + '"]').removeClass('text-muted').addClass('text-danger').text('Unable to load attached files.');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var zoom = 1;
|
||||
|
|
@ -1836,23 +1821,61 @@ $(function() {
|
|||
loadAbdmConsentRequests();
|
||||
});
|
||||
|
||||
function formatDateTimeLocal(d) {
|
||||
var pad = function(n) { return (n < 10 ? '0' : '') + n; };
|
||||
return d.getFullYear() + '-'
|
||||
+ pad(d.getMonth() + 1) + '-'
|
||||
+ pad(d.getDate()) + 'T'
|
||||
+ pad(d.getHours()) + ':'
|
||||
+ pad(d.getMinutes()) + ':'
|
||||
+ pad(d.getSeconds());
|
||||
}
|
||||
|
||||
$('#abdmCustomConsentModal').on('show.bs.modal', function() {
|
||||
$('#abdmCustomConsentError').addClass('d-none').text('');
|
||||
if (!$('#abdmCustomDateFrom').val()) {
|
||||
var from = new Date();
|
||||
from.setDate(from.getDate() - 365);
|
||||
$('#abdmCustomDateFrom').val(from.toISOString().slice(0, 10));
|
||||
from.setHours(0, 0, 0, 0);
|
||||
$('#abdmCustomDateFrom').val(formatDateTimeLocal(from));
|
||||
}
|
||||
if (!$('#abdmCustomDateTo').val()) {
|
||||
$('#abdmCustomDateTo').val(new Date().toISOString().slice(0, 10));
|
||||
var to = new Date();
|
||||
$('#abdmCustomDateTo').val(formatDateTimeLocal(to));
|
||||
}
|
||||
if (!$('#abdmCustomEraseDate').val()) {
|
||||
var erase = new Date();
|
||||
erase.setFullYear(erase.getFullYear() + 1);
|
||||
$('#abdmCustomEraseDate').val(erase.toISOString().slice(0, 10));
|
||||
erase.setHours(23, 59, 59, 0);
|
||||
$('#abdmCustomEraseDate').val(formatDateTimeLocal(erase));
|
||||
}
|
||||
});
|
||||
|
||||
$(document).off('click.abdmOpd', '.abdm-load-attachments-btn').on('click.abdmOpd', '.abdm-load-attachments-btn', function() {
|
||||
var $btn = $(this);
|
||||
var docId = $btn.data('doc-id');
|
||||
var $target = $('.abdm-fetch-attachments[data-doc-id="' + docId + '"]');
|
||||
if ($target.hasClass('abdm-loaded')) {
|
||||
$target.toggleClass('d-none');
|
||||
var isHidden = $target.hasClass('d-none');
|
||||
$btn.html('<i class="fa fa-paperclip me-1"></i> ' + (isHidden ? 'View Attached Files / Scans' : 'Hide Attached Files'));
|
||||
return;
|
||||
}
|
||||
$btn.prop('disabled', true).html('<span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span> Loading attached files...');
|
||||
fetch(abdmDocDetailBaseUrl + '/' + encodeURIComponent(docId), { credentials: 'same-origin' })
|
||||
.then(function(resp) { return resp.json(); })
|
||||
.then(function(data) {
|
||||
var item = (data && data.ok === 1) ? (data.item || {}) : null;
|
||||
var summary = (item && item.summary) || {};
|
||||
$target.removeClass('d-none').addClass('abdm-loaded').html(renderAttachmentsHtml(summary.attachments));
|
||||
$btn.prop('disabled', false).html('<i class="fa fa-paperclip me-1"></i> Hide Attached Files');
|
||||
})
|
||||
.catch(function() {
|
||||
$btn.prop('disabled', false).html('<i class="fa fa-paperclip me-1"></i> View Attached Files / Scans');
|
||||
$target.removeClass('d-none').html('<div class="small text-danger">Unable to load attached files.</div>');
|
||||
});
|
||||
});
|
||||
|
||||
$(document).off('click.abdmOpd', '#btnSendCustomConsent').on('click.abdmOpd', '#btnSendCustomConsent', function() {
|
||||
if (autoFlowRunning || fetchOnlyRunningIdx !== -1) {
|
||||
return;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user