chore: checkpoint before discharge history panel refactor

This commit is contained in:
DevSoftTech Server 2026-06-22 02:05:24 +05:30
parent 8fc60e893e
commit 96c030b72d
11 changed files with 1507 additions and 78 deletions

View File

@ -83,20 +83,35 @@ class Abha extends BaseController
$payload = $result['data'] ?? $result;
$newTxnId = $payload['txnId'] ?? $payload['txn_id'] ?? $txnId;
// aadhaar/verify-otp returns ABHAProfile when ABHA is created/found
$profile = $payload['ABHAProfile'] ?? [];
if (empty($profile) && ! empty($payload['accounts'][0])) {
$profile = $payload['accounts'][0]; // legacy fallback
}
$profile = $this->pickGatewayAbhaProfile($payload);
$abhaNum = (string) ($profile['ABHANumber'] ?? $payload['ABHANumber'] ?? '');
$name = (string) ($profile['name'] ?? $profile['fullName'] ?? '');
$photo = (string) ($profile['profilePhoto'] ?? '');
$mobile = (string) ($profile['mobile'] ?? $payload['mobile'] ?? $payload['mobileNumber'] ?? '');
$profileGender = (string) ($profile['gender'] ?? '');
$profileDob = (string) ($profile['dob'] ?? '');
$abhaNum = (string) ($profile['ABHANumber'] ?? $profile['abha_id'] ?? $payload['ABHANumber'] ?? $payload['abha_id'] ?? '');
$abhaAddress = (string) ($profile['preferredAbhaAddress'] ?? $profile['abha_address'] ?? $payload['preferredAbhaAddress'] ?? $payload['abha_address'] ?? '');
$name = (string) ($profile['name'] ?? $profile['fullName'] ?? $profile['full_name'] ?? $payload['name'] ?? $payload['full_name'] ?? '');
$photo = (string) ($profile['profilePhoto'] ?? $profile['profile_photo'] ?? $payload['profilePhoto'] ?? $payload['profile_photo'] ?? '');
$mobile = (string) ($profile['mobile'] ?? $payload['mobile'] ?? $payload['mobileNumber'] ?? '');
$profileGender = (string) ($profile['gender'] ?? $payload['gender'] ?? '');
$profileDob = (string) ($profile['dob'] ?? $profile['date_of_birth'] ?? $payload['dob'] ?? $payload['date_of_birth'] ?? '');
$verifiedStatus = (string) ($profile['verifiedStatus'] ?? $payload['verifiedStatus'] ?? '');
$verificationType = (string) ($profile['verificationType'] ?? $payload['verificationType'] ?? '');
$kycVerified = $profile['kycVerified'] ?? $payload['kycVerified'] ?? null;
$mobileVerified = $profile['mobileVerified'] ?? $payload['mobileVerified'] ?? null;
$patientInfo = $this->autoCreateOrFindPatient($abhaNum, $name, $mobile, $profileGender, $profileDob);
$patientInfo = $this->autoCreateOrFindPatient(
$abhaNum,
$name,
$mobile,
$profileGender,
$profileDob,
[
'abha_address' => $abhaAddress,
'profile_photo' => $photo,
'verified_status' => $verifiedStatus,
'verification_type' => $verificationType,
'kyc_verified' => $kycVerified,
'mobile_verified' => $mobileVerified,
]
);
return $this->response->setJSON([
'ok' => 1,
@ -180,20 +195,35 @@ class Abha extends BaseController
$payload = $result['data'] ?? $result;
// mobile/verify-otp returns data.profile (new format) or data.ABHAProfile / accounts[0] (legacy)
$profile = $payload['profile'] ?? $payload['ABHAProfile'] ?? [];
if (empty($profile) && ! empty($payload['accounts'][0])) {
$profile = $payload['accounts'][0];
}
$profile = $this->pickGatewayAbhaProfile($payload);
$abhaNum = (string) ($profile['ABHANumber'] ?? $payload['ABHANumber'] ?? '');
$name = (string) ($profile['name'] ?? $profile['fullName'] ?? $payload['name'] ?? '');
$photo = (string) ($profile['profilePhoto'] ?? $payload['profilePhoto'] ?? '');
$gender = (string) ($profile['gender'] ?? $payload['gender'] ?? '');
$dob = (string) ($profile['dob'] ?? $payload['dob'] ?? '');
$mobile = (string) ($profile['mobile'] ?? $payload['mobile'] ?? '');
$abhaNum = (string) ($profile['ABHANumber'] ?? $profile['abha_id'] ?? $payload['ABHANumber'] ?? $payload['abha_id'] ?? '');
$abhaAddress = (string) ($profile['preferredAbhaAddress'] ?? $profile['abha_address'] ?? $payload['preferredAbhaAddress'] ?? $payload['abha_address'] ?? '');
$name = (string) ($profile['name'] ?? $profile['fullName'] ?? $profile['full_name'] ?? $payload['name'] ?? $payload['full_name'] ?? '');
$photo = (string) ($profile['profilePhoto'] ?? $profile['profile_photo'] ?? $payload['profilePhoto'] ?? $payload['profile_photo'] ?? '');
$gender = (string) ($profile['gender'] ?? $payload['gender'] ?? '');
$dob = (string) ($profile['dob'] ?? $profile['date_of_birth'] ?? $payload['dob'] ?? $payload['date_of_birth'] ?? '');
$mobile = (string) ($profile['mobile'] ?? $payload['mobile'] ?? $payload['mobileNumber'] ?? '');
$verifiedStatus = (string) ($profile['verifiedStatus'] ?? $payload['verifiedStatus'] ?? '');
$verificationType = (string) ($profile['verificationType'] ?? $payload['verificationType'] ?? '');
$kycVerified = $profile['kycVerified'] ?? $payload['kycVerified'] ?? null;
$mobileVerified = $profile['mobileVerified'] ?? $payload['mobileVerified'] ?? null;
$patientInfo = $this->autoCreateOrFindPatient($abhaNum, $name, $mobile, $gender, $dob);
$patientInfo = $this->autoCreateOrFindPatient(
$abhaNum,
$name,
$mobile,
$gender,
$dob,
[
'abha_address' => $abhaAddress,
'profile_photo' => $photo,
'verified_status' => $verifiedStatus,
'verification_type' => $verificationType,
'kyc_verified' => $kycVerified,
'mobile_verified' => $mobileVerified,
]
);
return $this->response->setJSON([
'ok' => 1,
@ -287,7 +317,8 @@ class Abha extends BaseController
string $name,
string $mobile,
string $gender,
string $dob
string $dob,
array $abhaMeta = []
): array {
$db = \Config\Database::connect();
$fields = $db->getFieldNames('patient_master') ?? [];
@ -301,7 +332,8 @@ class Abha extends BaseController
$abhaField = 'abha_address';
}
$abhaNumClean = preg_replace('/\D/', '', $abhaNum); // strip dashes → 14 digits
$abhaNumClean = preg_replace('/\D/', '', $abhaNum); // strip dashes -> 14 digits
$abhaAddress = trim((string) ($abhaMeta['abha_address'] ?? ''));
// 1. Search by ABHA number
$existing = null;
@ -329,21 +361,58 @@ class Abha extends BaseController
->where('id', $patientId)
->update([$abhaField => $abhaNumClean]);
}
$this->syncAbhaMetaToPatient($patientId, $abhaMeta, $abhaNumClean, $abhaAddress, $fields);
return ['patient_id' => $patientId, 'p_code' => $pCode, 'is_new' => false];
}
// 2b. Safe repair path for orphan placeholder rows created during prior failed mapping.
// Reuse only when there is exactly one unambiguous candidate with no clinical linkage.
$placeholder = $this->findRepairablePlaceholderPatient($db, $fields);
if ($placeholder !== null) {
$patientId = (int) ($placeholder['id'] ?? 0);
if ($patientId > 0) {
$repairData = [
'p_fname' => strtoupper($name !== '' ? $name : 'ABHA PATIENT'),
'mphone1' => $mobile,
'gender' => $this->toPatientGenderValue($gender),
'estimate_dob' => 1,
];
$dobDb = $this->normalizeDobToDb($dob);
if ($dobDb !== '') {
$repairData['dob'] = $dobDb;
$repairData['estimate_dob'] = 0;
}
if ($abhaField && $abhaNumClean !== '') {
$repairData[$abhaField] = $abhaNumClean;
}
if ($abhaAddress !== '' && in_array('abha_address', $fields, true)) {
$repairData['abha_address'] = $abhaAddress;
}
$this->applyAbhaMetaColumns($repairData, $abhaMeta, $fields);
if (($abhaNumClean !== '' || $abhaAddress !== '') && in_array('abdm_linked_at', $fields, true)) {
$repairData['abdm_linked_at'] = date('Y-m-d H:i:s');
}
$db->table('patient_master')->where('id', $patientId)->update($repairData);
$row = $db->table('patient_master')->select('p_code')->where('id', $patientId)->get()->getRowArray();
$pCode = (string) ($row['p_code'] ?? '');
$this->syncAbhaMetaToPatient($patientId, $abhaMeta, $abhaNumClean, $abhaAddress, $fields);
return ['patient_id' => $patientId, 'p_code' => $pCode, 'is_new' => false];
}
}
// 3. Create new patient row from ABHA profile data
$genderDb = 1; // default Male
$genderAbs = strtoupper($gender);
if ($genderAbs === 'F' || $genderAbs === '2') { $genderDb = 2; }
$genderDb = $this->toPatientGenderValue($gender);
// Convert DOB to MySQL YYYY-MM-DD
$dobDb = '';
if (preg_match('/^(\d{2})-(\d{2})-(\d{4})$/', $dob, $m)) {
$dobDb = $m[3] . '-' . $m[2] . '-' . $m[1]; // DD-MM-YYYY → YYYY-MM-DD
} elseif (preg_match('/^\d{4}-\d{2}-\d{2}$/', $dob)) {
$dobDb = $dob;
}
$dobDb = $this->normalizeDobToDb($dob);
$insertData = [
'p_fname' => strtoupper($name !== '' ? $name : 'ABHA PATIENT'),
@ -361,6 +430,14 @@ class Abha extends BaseController
if ($abhaField && $abhaNumClean !== '') {
$insertData[$abhaField] = $abhaNumClean;
}
if ($abhaAddress !== '' && in_array('abha_address', $fields, true)) {
$insertData['abha_address'] = $abhaAddress;
}
$this->applyAbhaMetaColumns($insertData, $abhaMeta, $fields);
if (($abhaNumClean !== '' || $abhaAddress !== '') && in_array('abdm_linked_at', $fields, true)) {
$insertData['abdm_linked_at'] = date('Y-m-d H:i:s');
}
$patientModel = new \App\Models\PatientModel();
$patientId = $patientModel->insertPatient($insertData);
@ -370,9 +447,198 @@ class Abha extends BaseController
$pCode = (string) ($row['p_code'] ?? '');
}
if ($patientId > 0) {
$this->syncAbhaMetaToPatient($patientId, $abhaMeta, $abhaNumClean, $abhaAddress, $fields);
}
return ['patient_id' => $patientId, 'p_code' => $pCode, 'is_new' => true];
}
private function syncAbhaMetaToPatient(int $patientId, array $abhaMeta, string $abhaNumClean, string $abhaAddress, array $fields): void
{
if ($patientId <= 0) {
return;
}
$db = \Config\Database::connect();
$updates = [];
if ($abhaAddress !== '' && in_array('abha_address', $fields, true)) {
$updates['abha_address'] = $abhaAddress;
}
$this->applyAbhaMetaColumns($updates, $abhaMeta, $fields);
if (($abhaNumClean !== '' || $abhaAddress !== '') && in_array('abdm_linked_at', $fields, true)) {
$updates['abdm_linked_at'] = date('Y-m-d H:i:s');
}
if ($updates !== []) {
$db->table('patient_master')->where('id', $patientId)->update($updates);
}
}
private function applyAbhaMetaColumns(array &$target, array $abhaMeta, array $fields): void
{
$verifiedStatus = trim((string) ($abhaMeta['verified_status'] ?? ''));
if ($verifiedStatus !== '' && in_array('abha_verified_status', $fields, true)) {
$target['abha_verified_status'] = strtoupper($verifiedStatus);
}
$verificationType = trim((string) ($abhaMeta['verification_type'] ?? ''));
if ($verificationType !== '' && in_array('abha_verification_type', $fields, true)) {
$target['abha_verification_type'] = strtoupper($verificationType);
}
if (array_key_exists('kyc_verified', $abhaMeta) && in_array('abha_kyc_verified', $fields, true)) {
$target['abha_kyc_verified'] = $this->toDbBool($abhaMeta['kyc_verified']);
}
if (array_key_exists('mobile_verified', $abhaMeta) && in_array('abha_mobile_verified', $fields, true)) {
$target['abha_mobile_verified'] = $this->toDbBool($abhaMeta['mobile_verified']);
}
$profilePhoto = trim((string) ($abhaMeta['profile_photo'] ?? ''));
if ($profilePhoto !== '' && in_array('abha_profile_photo_base64', $fields, true)) {
$target['abha_profile_photo_base64'] = $profilePhoto;
}
}
private function toDbBool($value): int
{
if (is_bool($value)) {
return $value ? 1 : 0;
}
$v = strtolower(trim((string) $value));
if ($v === '1' || $v === 'true' || $v === 'yes' || $v === 'y') {
return 1;
}
return 0;
}
private function toPatientGenderValue(string $gender): int
{
$genderAbs = strtoupper(trim($gender));
if ($genderAbs === 'F' || $genderAbs === '2' || $genderAbs === 'FEMALE') {
return 2;
}
if ($genderAbs === 'O' || $genderAbs === '3' || $genderAbs === 'OTHER') {
return 3;
}
return 1;
}
private function normalizeDobToDb(string $dob): string
{
$dob = trim($dob);
if ($dob === '') {
return '';
}
if (preg_match('/^(\d{2})-(\d{2})-(\d{4})$/', $dob, $m)) {
return $m[3] . '-' . $m[2] . '-' . $m[1];
}
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $dob)) {
return $dob;
}
return '';
}
/**
* Find one safe placeholder candidate to repair.
* Conditions:
* - name is ABHA PATIENT
* - ABHA id and address are empty
* - no OPD/invoice linkage
* - inserted recently (last 24h)
* - exactly one candidate (to avoid accidental overwrite)
*
* @param array<int, string> $fields
* @return array<string, mixed>|null
*/
private function findRepairablePlaceholderPatient($db, array $fields): ?array
{
if (! $db->tableExists('patient_master')) {
return null;
}
$abhaField = null;
foreach (['abha_id', 'abha_no', 'abha'] as $f) {
if (in_array($f, $fields, true)) {
$abhaField = $f;
break;
}
}
$builder = $db->table('patient_master p')
->select('p.id,p.p_code,p.insert_date')
->where('UPPER(TRIM(COALESCE(p.p_fname, "")))', 'ABHA PATIENT')
->where('(p.mphone1 IS NULL OR TRIM(p.mphone1) = "")', null, false)
->where('p.insert_date >= DATE_SUB(NOW(), INTERVAL 24 HOUR)', null, false);
if ($abhaField !== null) {
$builder->where('(p.' . $abhaField . ' IS NULL OR TRIM(p.' . $abhaField . ') = "")', null, false);
}
if (in_array('abha_address', $fields, true)) {
$builder->where('(p.abha_address IS NULL OR TRIM(p.abha_address) = "")', null, false);
}
if ($db->tableExists('opd_master')) {
$builder->where('NOT EXISTS (SELECT 1 FROM opd_master o WHERE o.p_id = p.id)', null, false);
}
if ($db->tableExists('invoice_master')) {
$builder->where('NOT EXISTS (SELECT 1 FROM invoice_master i WHERE i.attach_type = 0 AND i.attach_id = p.id)', null, false);
}
$rows = $builder->orderBy('p.id', 'DESC')->limit(2)->get()->getResultArray();
if (count($rows) !== 1) {
return null;
}
return $rows[0];
}
/**
* @param array<string, mixed> $payload
* @return array<string, mixed>
*/
private function pickGatewayAbhaProfile(array $payload): array
{
$candidates = [
$payload['gateway_patient'] ?? null,
$payload['ABHAProfile'] ?? null,
$payload['accounts'][0] ?? null,
$payload['profile'] ?? null,
$payload,
];
foreach ($candidates as $candidate) {
if (is_array($candidate) && $this->looksLikeGatewayAbhaProfile($candidate)) {
return $candidate;
}
}
return [];
}
/**
* @param array<string, mixed> $profile
*/
private function looksLikeGatewayAbhaProfile(array $profile): bool
{
foreach (['ABHANumber', 'abha_id', 'preferredAbhaAddress', 'abha_address', 'name', 'fullName', 'full_name', 'gender', 'dob', 'date_of_birth', 'profilePhoto', 'profile_photo'] as $key) {
if (array_key_exists($key, $profile) && trim((string) $profile[$key]) !== '') {
return true;
}
}
return false;
}
// -------------------------------------------------------------------------
// Validate ABHA number or address
// POST abha/register/validate

View File

@ -529,6 +529,7 @@ class Ipd_discharge extends BaseController
// Main clinical discharge summary: use explicit DB field first (instruction_remark).
// Fallback to section extraction for older cached content.
$instructionRow = $ipdId > 0 ? $this->firstRowByIpd('ipd_discharge_instructions', $ipdId) : [];
$instructionMeta = $this->parseInstructionMetaPayload((string) ($instructionRow['comp_report'] ?? ''));
$clinicalSummaryRaw = $this->normalizeRichText((string) ($instructionRow['comp_remark'] ?? ''));
$clinicalSummary = $clinicalSummaryRaw !== ''
? '<div class="discharge-summary-content">' . $this->renderRichText($clinicalSummaryRaw) . '</div>'
@ -599,6 +600,28 @@ class Ipd_discharge extends BaseController
$otherAdvice = $section(['Other Advice:']);
$followUpInstructions = $section(['Discharge Summary:']);
// Heading-only fragments are treated as empty and should fallback to DB-backed meta values.
$otherAdviceHeadingOnly = trim(strip_tags((string) $otherAdvice));
if (preg_match('/^Other\s+Advice\s*:?$/i', $otherAdviceHeadingOnly) === 1) {
$otherAdvice = '';
}
if ($otherAdvice === '') {
$otherAdviceRaw = trim((string) ($instructionMeta['other_text'] ?? ''));
if ($otherAdviceRaw === ''
&& $this->tableHasColumns('ipd_discharge_drug_food_interaction', ['ipd_id'])
&& $this->db->fieldExists('food_text', 'ipd_discharge_drug_food_interaction')) {
$legacyFoodRow = $this->firstRowByIpd('ipd_discharge_drug_food_interaction', $ipdId);
$otherAdviceRaw = trim((string) ($legacyFoodRow['food_text'] ?? ''));
}
if ($otherAdviceRaw !== '') {
$otherAdvice = '<div class="discharge-field"><strong>Other Advice:</strong> '
. $this->renderStoredHtmlFragment($otherAdviceRaw)
. '</div>';
}
}
// Ignore heading-only extraction (no real follow-up narrative text).
$followUpHeadingOnly = trim(strip_tags((string) $followUpInstructions));
if (preg_match('/^Discharge\s+Summary\s*:?$/i', $followUpHeadingOnly) === 1) {

View File

@ -117,11 +117,13 @@ class Patient extends BaseController
];
$this->applyPatientAbhaFieldValues($data, $abhaId, $abhaAddress);
if ($chk_age === 'on') {
$data['age'] = $age_year;
$data['age_in_month'] = $age_month;
} else {
$data['dob'] = $this->parseDate($this->request->getPost('datepicker_dob'));
if (!$isAbhaVerifiedLocked) {
if ($chk_age === 'on') {
$data['age'] = $age_year;
$data['age_in_month'] = $age_month;
} else {
$data['dob'] = $this->parseDate($this->request->getPost('datepicker_dob'));
}
}
$patientModel = new PatientModel();
@ -610,10 +612,14 @@ class Patient extends BaseController
$profile_file_path = '/assets/images/no_image.svg';
$profile_picture_path = '';
$abha_profile_photo_base64 = '';
if ($this->db->fieldExists('profile_picture', 'patient_master')) {
$profile_picture_path = (string) ($data['data'][0]->profile_picture ?? '');
}
if ($this->db->fieldExists('abha_profile_photo_base64', 'patient_master')) {
$abha_profile_photo_base64 = trim((string) ($data['data'][0]->abha_profile_photo_base64 ?? ''));
}
$sql = "SELECT * from blood_group order by id";
$query = $this->db->query($sql);
@ -643,6 +649,10 @@ class Patient extends BaseController
} else {
$profile_file_path = $profile_picture_path;
}
} elseif ($abha_profile_photo_base64 !== '') {
$profile_file_path = str_starts_with($abha_profile_photo_base64, 'data:image')
? $abha_profile_photo_base64
: 'data:image/jpeg;base64,' . $abha_profile_photo_base64;
}
$data['profile_file_path'] = $profile_file_path;
@ -663,11 +673,17 @@ class Patient extends BaseController
}
$chk_age = $this->request->getPost('chk_age');
$pid = (int) $this->request->getPost('p_id');
$abhaIdInput = trim((string) $this->request->getPost('input_abha_id'));
$abhaAddressInput = trim((string) $this->request->getPost('input_abha_address'));
[$abhaId, $abhaAddress, $abhaError] = $this->normalizeAbhaInputs($abhaIdInput, $abhaAddressInput);
$age_month = (string) $this->request->getPost('input_age_month');
$age_year = (string) $this->request->getPost('input_age_year');
$patientModel = new PatientModel();
$existingPatient = $pid > 0 ? $patientModel->find($pid) : null;
$isAbhaVerifiedLocked = is_array($existingPatient)
&& trim((string) ($existingPatient['abha_id'] ?? $existingPatient['abha_no'] ?? $existingPatient['abha'] ?? $existingPatient['abha_address'] ?? '')) !== ''
&& strtoupper(trim((string) ($existingPatient['abha_verified_status'] ?? ''))) === 'VERIFIED';
$rules = [
'input_name' => 'required|min_length[1]|max_length[30]',
@ -717,6 +733,23 @@ class Patient extends BaseController
'estimate_dob' => $estimate_dob,
'blood_group' => $this->request->getPost('input_blood_group'),
];
if ($isAbhaVerifiedLocked) {
$data['p_fname'] = (string) ($existingPatient['p_fname'] ?? $data['p_fname']);
$data['gender'] = (string) ($existingPatient['gender'] ?? $data['gender']);
$data['dob'] = $existingPatient['dob'] ?? ($data['dob'] ?? null);
if (array_key_exists('age', $existingPatient)) {
$data['age'] = $existingPatient['age'];
}
if (array_key_exists('age_in_month', $existingPatient)) {
$data['age_in_month'] = $existingPatient['age_in_month'];
}
if (array_key_exists('estimate_dob', $existingPatient)) {
$data['estimate_dob'] = $existingPatient['estimate_dob'];
}
$abhaId = trim((string) ($existingPatient['abha_id'] ?? $existingPatient['abha_no'] ?? $existingPatient['abha'] ?? $existingPatient['abha_address'] ?? $abhaId));
}
$this->applyPatientAbhaFieldValues($data, $abhaId, $abhaAddress);
if ($chk_age === 'on') {
@ -726,8 +759,6 @@ class Patient extends BaseController
$data['dob'] = $this->parseDate($this->request->getPost('datepicker_dob'));
}
$pid = (int) $this->request->getPost('p_id');
$patientModel = new PatientModel();
$patientModel->updatePatient($data, $pid);
$this->saveNamesToNameList([
(string) $this->request->getPost('input_name'),
@ -1356,14 +1387,14 @@ class Patient extends BaseController
}
$profile = $this->pickGatewayAbhaProfile($payload);
$abhaNumberRaw = trim((string) ($profile['ABHANumber'] ?? $payload['ABHANumber'] ?? ''));
$abhaNumberRaw = trim((string) ($profile['ABHANumber'] ?? $profile['abha_id'] ?? $payload['ABHANumber'] ?? $payload['abha_id'] ?? ''));
$abhaDigits = preg_replace('/\D/', '', $abhaNumberRaw);
$abhaAddress = trim((string) ($profile['preferredAbhaAddress'] ?? $payload['abha_address'] ?? ''));
$gatewayName = trim((string) ($profile['name'] ?? $payload['name'] ?? ''));
$abhaAddress = trim((string) ($profile['preferredAbhaAddress'] ?? $profile['abha_address'] ?? $payload['abha_address'] ?? $payload['preferredAbhaAddress'] ?? ''));
$gatewayName = trim((string) ($profile['name'] ?? $profile['fullName'] ?? $profile['full_name'] ?? $payload['name'] ?? $payload['full_name'] ?? ''));
$gatewayMobile = trim((string) ($profile['mobile'] ?? $payload['mobile'] ?? $payload['mobileNumber'] ?? ''));
$gatewayDob = trim((string) ($profile['dob'] ?? $payload['dob'] ?? ''));
$gatewayDob = trim((string) ($profile['dob'] ?? $profile['date_of_birth'] ?? $payload['dob'] ?? $payload['date_of_birth'] ?? ''));
$gatewayGender = trim((string) ($profile['gender'] ?? $payload['gender'] ?? ''));
$gatewayPhoto = trim((string) ($profile['profilePhoto'] ?? $payload['profilePhoto'] ?? ''));
$gatewayPhoto = trim((string) ($profile['profilePhoto'] ?? $profile['profile_photo'] ?? $payload['profilePhoto'] ?? $payload['profile_photo'] ?? ''));
$patient = $this->findPatientForAbhaProfile($requestedPid, $abhaDigits, $gatewayMobile);
$patientId = (int) ($patient['id'] ?? 0);
@ -1467,22 +1498,37 @@ class Patient extends BaseController
*/
private function pickGatewayAbhaProfile(array $payload): array
{
if (isset($payload['gateway_patient']) && is_array($payload['gateway_patient'])) {
return $payload['gateway_patient'];
}
if (isset($payload['ABHAProfile']) && is_array($payload['ABHAProfile'])) {
return $payload['ABHAProfile'];
}
if (isset($payload['profile']) && is_array($payload['profile'])) {
return $payload['profile'];
}
if (isset($payload['accounts'][0]) && is_array($payload['accounts'][0])) {
return $payload['accounts'][0];
$candidates = [
$payload['gateway_patient'] ?? null,
$payload['ABHAProfile'] ?? null,
$payload['accounts'][0] ?? null,
$payload['profile'] ?? null,
$payload,
];
foreach ($candidates as $candidate) {
if (is_array($candidate) && $this->looksLikeGatewayAbhaProfile($candidate)) {
return $candidate;
}
}
return [];
}
/**
* @param array<string, mixed> $profile
*/
private function looksLikeGatewayAbhaProfile(array $profile): bool
{
foreach (['ABHANumber', 'abha_id', 'preferredAbhaAddress', 'abha_address', 'name', 'fullName', 'full_name', 'gender', 'dob', 'date_of_birth', 'profilePhoto', 'profile_photo'] as $key) {
if (array_key_exists($key, $profile) && trim((string) $profile[$key]) !== '') {
return true;
}
}
return false;
}
/**
* @return array<string, mixed>|null
*/

View File

@ -14,6 +14,9 @@ class AbdmGatewaySettings extends BaseController
$token = $this->readSettingValue('EATRIA_BRIDGE_TOKEN');
$hfrId = $this->readSettingValue('ABDM_HFR_ID');
if ($hfrId === '') {
$hfrId = $this->readSettingValue('H_HFR_ID');
}
$hmsName = $this->readSettingValue('ABDM_HMS_NAME');
$gwUrl = $this->readSettingValue('EATRIA_BRIDGE_URL') ?: 'https://abdm-bridge.e-atria.in/api';
$bridgeHospitalId = $this->readSettingValue('ABDM_BRIDGE_HOSPITAL_ID');
@ -129,6 +132,9 @@ class AbdmGatewaySettings extends BaseController
}
if ($hfrId === '') {
$hfrId = $this->readSettingValue('ABDM_HFR_ID');
if ($hfrId === '') {
$hfrId = $this->readSettingValue('H_HFR_ID');
}
}
$gwUrl = rtrim($gwUrl, '/');
@ -210,7 +216,7 @@ class AbdmGatewaySettings extends BaseController
]);
}
$statusUrl = $gwUrl . '/v3/gateway/status';
$statusUrl = $gwUrl . '/v3/gateway/status' . ($hfrId !== '' ? '?hfr_id=' . urlencode($hfrId) : '');
$ch2 = curl_init();
curl_setopt_array($ch2, [
CURLOPT_URL => $statusUrl,

View File

@ -175,7 +175,7 @@ HTML;
if ($this->db->tableExists('hospital_setting')) {
$hsRows = $this->db->table('hospital_setting')
->select('s_name, s_value')
->whereIn('s_name', ['EATRIA_BRIDGE_URL', 'EATRIA_BRIDGE_TOKEN', 'ABDM_HFR_ID'])
->whereIn('s_name', ['EATRIA_BRIDGE_URL', 'EATRIA_BRIDGE_TOKEN', 'ABDM_HFR_ID', 'H_HFR_ID'])
->get()
->getResultArray();
@ -192,6 +192,9 @@ HTML;
if ($sName === 'ABDM_HFR_ID') {
$hfrId = $sValue;
}
if ($sName === 'H_HFR_ID' && $hfrId === '') {
$hfrId = $sValue;
}
}
}

View File

@ -0,0 +1,108 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddAbhaProfileMetaToPatientMaster extends Migration
{
public function up(): void
{
if (! $this->tableExists('patient_master')) {
return;
}
$fields = $this->getColumns('patient_master');
$add = [];
if (! in_array('abha_profile_photo_base64', $fields, true)) {
$add['abha_profile_photo_base64'] = [
'type' => 'LONGTEXT',
'null' => true,
];
}
if (! in_array('abha_verified_status', $fields, true)) {
$add['abha_verified_status'] = [
'type' => 'VARCHAR',
'constraint' => 30,
'null' => true,
];
}
if (! in_array('abha_verification_type', $fields, true)) {
$add['abha_verification_type'] = [
'type' => 'VARCHAR',
'constraint' => 30,
'null' => true,
];
}
if (! in_array('abha_kyc_verified', $fields, true)) {
$add['abha_kyc_verified'] = [
'type' => 'TINYINT',
'constraint' => 1,
'null' => true,
'default' => 0,
];
}
if (! in_array('abha_mobile_verified', $fields, true)) {
$add['abha_mobile_verified'] = [
'type' => 'TINYINT',
'constraint' => 1,
'null' => true,
'default' => 0,
];
}
if ($add !== []) {
$this->forge->addColumn('patient_master', $add);
}
}
public function down(): void
{
if (! $this->tableExists('patient_master')) {
return;
}
$fields = $this->getColumns('patient_master');
foreach ([
'abha_profile_photo_base64',
'abha_verified_status',
'abha_verification_type',
'abha_kyc_verified',
'abha_mobile_verified',
] as $column) {
if (in_array($column, $fields, true)) {
$this->forge->dropColumn('patient_master', $column);
}
}
}
/**
* @return string[]
*/
private function getColumns(string $table): array
{
if (! preg_match('/^[a-zA-Z0-9_]+$/', $table)) {
return [];
}
$result = $this->db->query('SHOW COLUMNS FROM `' . $table . '`')->getResultArray();
return array_map(static fn (array $row): string => (string) ($row['Field'] ?? ''), $result);
}
private function tableExists(string $table): bool
{
if (! preg_match('/^[a-zA-Z0-9_]+$/', $table)) {
return false;
}
$row = $this->db->query("SHOW TABLES LIKE '" . $table . "'")->getRowArray();
return ! empty($row);
}
}

View File

@ -41,7 +41,7 @@ class EAtriaBridgeConnector implements AbdmConnectorInterface
if ($db->tableExists('hospital_setting')) {
$rows = $db->table('hospital_setting')
->select('s_name, s_value')
->whereIn('s_name', ['EATRIA_BRIDGE_TOKEN', 'ABDM_BRIDGE_TOKEN', 'EATRIA_BRIDGE_URL', 'ABDM_HFR_ID'])
->whereIn('s_name', ['EATRIA_BRIDGE_TOKEN', 'ABDM_BRIDGE_TOKEN', 'EATRIA_BRIDGE_URL', 'ABDM_HFR_ID', 'H_HFR_ID'])
->get()
->getResultArray();
@ -58,8 +58,12 @@ class EAtriaBridgeConnector implements AbdmConnectorInterface
if (! empty($dbSettings['EATRIA_BRIDGE_URL'])) {
$this->baseUrl = rtrim(trim($dbSettings['EATRIA_BRIDGE_URL']), '/');
}
if (! empty($dbSettings['ABDM_HFR_ID'])) {
$this->hfrId = trim($dbSettings['ABDM_HFR_ID']);
$dbHfrId = trim((string) ($dbSettings['ABDM_HFR_ID'] ?? ''));
if ($dbHfrId === '') {
$dbHfrId = trim((string) ($dbSettings['H_HFR_ID'] ?? ''));
}
if ($dbHfrId !== '') {
$this->hfrId = $dbHfrId;
}
}
} catch (\Throwable $e) {
@ -87,6 +91,22 @@ class EAtriaBridgeConnector implements AbdmConnectorInterface
return trim($token, " \t\n\r\0\x0B\"'");
}
private function normalizeBridgeGender($value): string
{
$v = strtoupper(trim((string) $value));
if ($v === '1' || $v === 'M' || $v === 'MALE') {
return 'M';
}
if ($v === '2' || $v === 'F' || $v === 'FEMALE') {
return 'F';
}
if ($v === '3' || $v === 'O' || $v === 'OTHER' || $v === 'OTHERS') {
return 'O';
}
return '';
}
// -------------------------------------------------------------------------
// Internal HTTP helper
// -------------------------------------------------------------------------
@ -111,6 +131,10 @@ class EAtriaBridgeConnector implements AbdmConnectorInterface
*/
private function get(string $path, array $query = []): array
{
// e-Atria bridge expects hfr_id alongside Bearer auth for GET endpoints too.
if ($this->hfrId !== '' && empty($query['hfr_id'])) {
$query['hfr_id'] = $this->hfrId;
}
return $this->httpCall('GET', $path, [], $query);
}
@ -476,9 +500,19 @@ class EAtriaBridgeConnector implements AbdmConnectorInterface
}
foreach (['abha_id', 'doctor_name', 'department', 'notes', 'gender', 'year_of_birth'] as $optional) {
if (! empty($data[$optional])) {
$body[$optional] = (string) $data[$optional];
if (empty($data[$optional])) {
continue;
}
if ($optional === 'gender') {
$gender = $this->normalizeBridgeGender($data[$optional]);
if ($gender !== '') {
$body['gender'] = $gender;
}
continue;
}
$body[$optional] = (string) $data[$optional];
}
return $this->post('/v3/records/push', $body);
@ -582,6 +616,15 @@ class EAtriaBridgeConnector implements AbdmConnectorInterface
public function opdTokenCreate(array $payload): array
{
if (array_key_exists('gender', $payload)) {
$gender = $this->normalizeBridgeGender($payload['gender']);
if ($gender !== '') {
$payload['gender'] = $gender;
} else {
unset($payload['gender']);
}
}
return $this->post('/v3/opd/token', $payload);
}
@ -619,6 +662,15 @@ class EAtriaBridgeConnector implements AbdmConnectorInterface
public function hipLinkToken(array $payload): array
{
if (array_key_exists('gender', $payload)) {
$gender = $this->normalizeBridgeGender($payload['gender']);
if ($gender !== '') {
$payload['gender'] = $gender;
} else {
unset($payload['gender']);
}
}
if ($this->hfrId !== '' && empty($payload['hfr_id'])) {
$payload['hfr_id'] = $this->hfrId;
}

View File

@ -684,9 +684,9 @@
</div>
</div>
<div class="mt-3 d-flex gap-2 flex-wrap">
<a id="abhareg_result_profile_btn" href="#" class="btn btn-success btn-sm d-none">
<button type="button" id="abhareg_result_profile_btn" class="btn btn-success btn-sm d-none" data-url="">
<i class="bi bi-person-lines-fill me-1"></i>View Patient Profile
</a>
</button>
<a id="abhareg_result_card_btn" href="#" target="_blank" class="btn btn-outline-primary btn-sm d-none">
<i class="bi bi-card-image me-1"></i>Print ABHA Card
</a>
@ -1099,7 +1099,7 @@
$('#abhareg_result_photo_ph').removeClass('d-none');
}
if (patId > 0) {
$('#abhareg_result_profile_btn').attr('href', '<?= base_url('billing/patient/person_record') ?>/' + patId).removeClass('d-none');
$('#abhareg_result_profile_btn').attr('data-url', '<?= base_url('billing/patient/person_record') ?>/' + patId).removeClass('d-none');
}
if (abhaRaw.length === 14) {
$('#abhareg_result_card_btn').attr('href', '<?= base_url('abha/card/') ?>' + abhaRaw).removeClass('d-none');
@ -1281,6 +1281,14 @@
$('#abhareg_mob_otp_input').on('keypress', function(e) {
if (e.which === 13) { $('#abhareg_mob_verify_btn').trigger('click'); }
});
$('#abhareg_result_profile_btn').on('click', function() {
var url = ($(this).attr('data-url') || '').toString().trim();
if (url === '') {
return;
}
load_form(url, 'Patient Profile');
});
})();
/* ===== END ABHA REGISTRATION WIZARD ===== */

View File

@ -11,8 +11,17 @@
<?php
$user = auth()->user();
$patientAbhaId = (string) ($data[0]->abha_id ?? $data[0]->abha_no ?? $data[0]->abha ?? $data[0]->abha_address ?? '');
$abhaAddress = trim((string) ($data[0]->abha_address ?? ''));
$abhaVerifiedStatus = trim((string) ($data[0]->abha_verified_status ?? ''));
$abhaVerificationType = trim((string) ($data[0]->abha_verification_type ?? ''));
$abhaKycVerified = (int) ($data[0]->abha_kyc_verified ?? 0) === 1;
$abhaMobileVerified = (int) ($data[0]->abha_mobile_verified ?? 0) === 1;
$abhaLinkedAt = trim((string) ($data[0]->abdm_linked_at ?? ''));
$abhaPhotoAvailable = trim((string) ($data[0]->abha_profile_photo_base64 ?? '')) !== '';
$abhaVerifiedLocked = $patientAbhaId !== '' && strtoupper($abhaVerifiedStatus) === 'VERIFIED';
$canEditNameAnytime = is_object($user) && method_exists($user, 'can') ? $user->can('billing.patient.edit-name-anytime') : false;
$isAdmin = is_object($user) && method_exists($user, 'inGroup') ? $user->inGroup('admin') : false;
$abhaLockNotice = $abhaVerifiedLocked ? 'ABHA verified: name, gender, ABHA ID, and date of birth are locked.' : '';
// Allow edit if within 24 hours OR user has edit-name-anytime permission OR is admin
if ($data[0]->p_edit == 1 || $canEditNameAnytime || $isAdmin) {
@ -60,7 +69,7 @@
<div class="form-group">
<label>Full Name</label>
<input class="form-control input-sm" name="input_name" placeholder="Full Name"
value="<?=$data[0]->p_fname ?>" type="text" autocomplete="off" <?=$readonlyName?>>
value="<?=$data[0]->p_fname ?>" type="text" autocomplete="off" <?=($readonlyName !== '' || $abhaVerifiedLocked) ? 'readonly' : ''?>>
</div>
</div>
<div class="col-md-3">
@ -69,14 +78,17 @@
<div class="radio">
<label>
<input name="optionsRadios_gender" id="options_gender1" value="1"
<?=radio_checked("1",$data[0]->gender)?> type="radio">
<?=radio_checked("1",$data[0]->gender)?> type="radio" <?= $abhaVerifiedLocked ? 'disabled' : '' ?>>
Male
</label>
<label>
<input name="optionsRadios_gender" id="options_gender2" value="2"
<?=radio_checked("2",$data[0]->gender)?> type="radio">
<?=radio_checked("2",$data[0]->gender)?> type="radio" <?= $abhaVerifiedLocked ? 'disabled' : '' ?>>
Female
</label>
<?php if ($abhaVerifiedLocked) : ?>
<input type="hidden" name="optionsRadios_gender" value="<?= esc((string) ($data[0]->gender ?? '')) ?>">
<?php endif; ?>
</div>
</div>
</div>
@ -93,7 +105,7 @@
<label>ABHA ID</label>
<input class="form-control input-sm" name="input_abha_id" id="input_abha_id"
value="<?= esc($patientAbhaId) ?>" placeholder="14-digit ABHA ID" type="text"
autocomplete="off" maxlength="14">
autocomplete="off" maxlength="14" <?= $abhaVerifiedLocked ? 'readonly' : '' ?>>
</div>
</div>
<div class="col-md-2">
@ -104,6 +116,26 @@
data-inputmask='"mask": "9999999999"' data-mask>
</div>
</div>
<div class="col-md-12">
<div class="alert alert-light border mt-2 mb-1">
<div class="row">
<div class="col-md-3"><strong>ABHA Address:</strong> <?= $abhaAddress !== '' ? esc($abhaAddress) : '-' ?></div>
<div class="col-md-3"><strong>Status:</strong> <?= $abhaVerifiedStatus !== '' ? esc($abhaVerifiedStatus) : 'UNAVAILABLE' ?></div>
<div class="col-md-3"><strong>Type:</strong> <?= $abhaVerificationType !== '' ? esc($abhaVerificationType) : '-' ?></div>
<div class="col-md-3"><strong>Linked At:</strong> <?= $abhaLinkedAt !== '' ? esc($abhaLinkedAt) : '-' ?></div>
</div>
<div class="row" style="margin-top: 6px;">
<div class="col-md-3"><strong>KYC Verified:</strong> <?= $abhaKycVerified ? 'YES' : 'NO' ?></div>
<div class="col-md-3"><strong>Mobile Verified:</strong> <?= $abhaMobileVerified ? 'YES' : 'NO' ?></div>
<div class="col-md-6"><strong>ABHA Photo:</strong> <?= $abhaPhotoAvailable ? 'AVAILABLE (BASE64 STORED)' : 'NOT AVAILABLE' ?></div>
</div>
<?php if ($abhaLockNotice !== '') : ?>
<div class="row" style="margin-top: 6px;">
<div class="col-md-12"><span class="badge bg-success-subtle text-success border border-success-subtle"><?= esc($abhaLockNotice) ?></span></div>
</div>
<?php endif; ?>
</div>
</div>
</div>
<div class="row well well-sm">
<?php
@ -127,8 +159,11 @@
?>
<div class="col-md-3">
<div class="form-group">
<label><input id="chk_age" name="chk_age" type="checkbox" <?=$checkbox_checked?>>
<label><input id="chk_age" name="chk_age" type="checkbox" <?=$checkbox_checked?> <?= $abhaVerifiedLocked ? 'disabled' : '' ?>>
Estimate Age</label>
<?php if ($abhaVerifiedLocked && $checkbox_checked === 'checked') : ?>
<input type="hidden" name="chk_age" value="on">
<?php endif; ?>
</div>
</div>
<div id="age_input_1" <?=$age_input_1?>>
@ -140,14 +175,14 @@
<td>Year :</td>
<td><input class="form-control number input-sm" name="input_age_year"
id="input_age_year" placeholder="Year" type="text"
autocomplete="off" style=" width:100px;" value="<?=$data[0]->age?>">
autocomplete="off" style=" width:100px;" value="<?=$data[0]->age?>" <?= $abhaVerifiedLocked ? 'readonly' : '' ?>>
</td>
<td style=" width:50px;"></td>
<td>Month : </td>
<td><input class="form-control number input-sm" name="input_age_month"
id="input_age_month" placeholder="Month" type="text"
autocomplete="off" style=" width:100px;"
value="<?=$data[0]->age_in_month?>"></td>
value="<?=$data[0]->age_in_month?>" <?= $abhaVerifiedLocked ? 'readonly' : '' ?>></td>
</tr>
</table>
</div>
@ -157,7 +192,7 @@
<div class="col-md-4">
<label> Date of Birth</label>
<input class="form-control input-sm" name="datepicker_dob"
id="datepicker_dob" type="date" value="<?=$DateofBirth ?>" />
id="datepicker_dob" type="date" value="<?=$DateofBirth ?>" <?= $abhaVerifiedLocked ? 'readonly' : '' ?> />
</div>
</div>
</div>

View File

@ -15,6 +15,14 @@
<?= csrf_field() ?>
<?php
$patientAbhaId = (string) ($data[0]->abha_id ?? $data[0]->abha_no ?? $data[0]->abha ?? $data[0]->abha_address ?? '');
$abhaAddress = trim((string) ($data[0]->abha_address ?? ''));
$abhaVerifiedStatus = trim((string) ($data[0]->abha_verified_status ?? ''));
$abhaVerificationType = trim((string) ($data[0]->abha_verification_type ?? ''));
$abhaKycVerified = (int) ($data[0]->abha_kyc_verified ?? 0) === 1;
$abhaMobileVerified = (int) ($data[0]->abha_mobile_verified ?? 0) === 1;
$abhaLinkedAt = trim((string) ($data[0]->abdm_linked_at ?? ''));
$abhaPhotoAvailable = trim((string) ($data[0]->abha_profile_photo_base64 ?? '')) !== '';
$isAbhaLinkedAndVerified = $patientAbhaId !== '' && strtoupper($abhaVerifiedStatus) === 'VERIFIED';
?>
<?php
$user = auth()->user();
@ -77,11 +85,13 @@
<li class="nav-item" role="presentation">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#profile-insurance" type="button" role="tab">Insurance</button>
</li>
<?php if (!$isAbhaLinkedAndVerified) : ?>
<li class="nav-item" role="presentation">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#profile-abha" type="button" role="tab">
<i class="bi bi-person-check me-1"></i>ABHA Create/Verify
</button>
</li>
<?php endif; ?>
<?php if(count($opd_List)>0) { ?>
<li class="nav-item" role="presentation">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#profile-opd" type="button" role="tab">OPD</button>
@ -107,7 +117,13 @@
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Relation</div>
<div class="col-lg-9 col-md-8"><?=$data[0]->p_relative?> <?=ucwords($data[0]->p_rname)?></div>
<div class="col-lg-9 col-md-8">
<?php if (trim((string) ($data[0]->p_relative ?? '')) !== '' || trim((string) ($data[0]->p_rname ?? '')) !== '') : ?>
<?=$data[0]->p_relative?> <?=ucwords($data[0]->p_rname)?>
<?php else : ?>
<span class="badge bg-warning-subtle text-warning border border-warning-subtle">Relation not filled</span>
<?php endif; ?>
</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Aadhar No.</div>
@ -123,6 +139,7 @@
<span class="text-muted small">Not linked</span>
<?php endif; ?>
</span>
<?php if (!$isAbhaLinkedAndVerified) : ?>
<button type="button" class="btn btn-sm btn-outline-primary py-0" style="font-size:12px"
onclick="openAbhaOtpModal(
<?= (int)$data[0]->id ?>,
@ -131,6 +148,7 @@
)">
<i class="bi bi-person-check me-1"></i><?= $patientAbhaId !== '' ? 'Re-link ABHA' : 'Link ABHA via OTP' ?>
</button>
<?php endif; ?>
<?php if ($patientAbhaId !== '') :
$abhaCardNum = preg_replace('/\D/', '', $patientAbhaId);
?>
@ -144,6 +162,47 @@
<?php endif; ?>
</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">ABHA Address</div>
<div class="col-lg-9 col-md-8">
<?php if ($abhaAddress !== '') : ?>
<span class="badge bg-primary-subtle text-primary border border-primary-subtle"><?= esc($abhaAddress) ?></span>
<?php else : ?>
<span class="text-muted small">Not available</span>
<?php endif; ?>
</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">ABHA Verification</div>
<div class="col-lg-9 col-md-8 d-flex align-items-center gap-2 flex-wrap">
<?php if ($abhaVerifiedStatus !== '') : ?>
<span class="badge bg-success-subtle text-success border border-success-subtle"><?= esc($abhaVerifiedStatus) ?></span>
<?php else : ?>
<span class="badge bg-secondary-subtle text-secondary border border-secondary-subtle">UNAVAILABLE</span>
<?php endif; ?>
<?php if ($abhaVerificationType !== '') : ?>
<span class="badge bg-info-subtle text-info border border-info-subtle"><?= esc($abhaVerificationType) ?></span>
<?php endif; ?>
<span class="badge <?= $abhaKycVerified ? 'bg-success-subtle text-success border border-success-subtle' : 'bg-warning-subtle text-warning border border-warning-subtle' ?>">KYC <?= $abhaKycVerified ? 'YES' : 'NO' ?></span>
<span class="badge <?= $abhaMobileVerified ? 'bg-success-subtle text-success border border-success-subtle' : 'bg-warning-subtle text-warning border border-warning-subtle' ?>">MOBILE <?= $abhaMobileVerified ? 'YES' : 'NO' ?></span>
</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">ABHA Linked At</div>
<div class="col-lg-9 col-md-8">
<?= $abhaLinkedAt !== '' ? esc($abhaLinkedAt) : '<span class="text-muted small">Not linked via OTP</span>' ?>
</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">ABHA Photo</div>
<div class="col-lg-9 col-md-8">
<span class="badge <?= $abhaPhotoAvailable ? 'bg-success-subtle text-success border border-success-subtle' : 'bg-secondary-subtle text-secondary border border-secondary-subtle' ?>">
<?= $abhaPhotoAvailable ? 'AVAILABLE (BASE64 STORED)' : 'NOT AVAILABLE' ?>
</span>
</div>
</div>
<?php $hpPatientSyncId = trim((string) ($data[0]->healthplix_sync_id ?? '')); ?>
<?php if ($hpPatientSyncId !== '') : ?>
<div class="row">
@ -155,7 +214,13 @@
<?php endif; ?>
<div class="row">
<div class="col-lg-3 col-md-4 label">Phone</div>
<div class="col-lg-9 col-md-8"><?=$data[0]->mphone1?></div>
<div class="col-lg-9 col-md-8">
<?php if (trim((string) ($data[0]->mphone1 ?? '')) !== '') : ?>
<?=$data[0]->mphone1?>
<?php else : ?>
<span class="badge bg-warning-subtle text-warning border border-warning-subtle">Phone not filled</span>
<?php endif; ?>
</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Email</div>
@ -179,6 +244,7 @@
<button type="button" id="btn_update_aadhar" class="btn btn-info">Update</button>
</div>
</div>
<?php if (!$isAbhaLinkedAndVerified) : ?>
<div class="col-lg-6">
<label class="form-label">ABHA ID</label>
<div class="input-group input-group-sm">
@ -193,6 +259,7 @@
</div>
<div class="form-text">Type to set manually, or use <i class="bi bi-person-check"></i> for OTP flow.</div>
</div>
<?php endif; ?>
<div class="col-lg-6">
<button type="button" class="btn btn-warning btn-sm mt-4" onclick="load_form('<?= base_url('billing/patient/show_cards') ?>/<?=$data[0]->id?>/1');">Insurance Update</button>
</div>
@ -312,9 +379,11 @@
</div>
<?php } ?>
<?php if (!$isAbhaLinkedAndVerified) : ?>
<div class="tab-pane fade pt-3" id="profile-abha" role="tabpanel">
<?= view('partials/abha_create_panel', ['data' => $data, 'patientAbhaId' => $patientAbhaId]) ?>
</div>
<?php endif; ?>
</div>
</div>

813
har_1.txt Normal file

File diff suppressed because one or more lines are too long