diff --git a/app/Config/Routes.php b/app/Config/Routes.php
index 588e3eb..4197f3a 100644
--- a/app/Config/Routes.php
+++ b/app/Config/Routes.php
@@ -425,6 +425,7 @@ $routes->post('setting/template/report_ultrasound_insert/(:num)', 'Setting\Templ
$routes->get('IpdNew/show_ipd_items/(:num)', 'Billing\\Ipd::panelTab/$1/ipd-charges');
$routes->get('IpdNew/show_ipd_items/(:num)/(:num)', 'Billing\\Ipd::panelTab/$1/ipd-charges');
// Legacy compatibility: patient profile still calls old new-IPD URL.
+$routes->get('IpdNew/admit', 'Billing\\Ipd::admit');
$routes->get('IpdNew/addipd/(:num)', 'Billing\\Ipd::legacyAddIpd/$1');
$routes->post('IpdNew/AddNew', 'Billing\\Ipd::legacyAddNew');
$routes->post('IpdNew/addnew', 'Billing\\Ipd::legacyAddNew');
@@ -498,11 +499,15 @@ $routes->post('ot/cases/(:num)/postop', 'IpdOt::savePostop/$1', ['filter' => 'pe
$routes->group('billing', function($routes) {
$routes->get('ipd', 'Billing\\Ipd::index');
+ $routes->get('ipd/admit', 'Billing\\Ipd::admit');
+ $routes->match(['GET', 'POST'], 'ipd/search-patient-admit', 'Billing\\Ipd::searchPatientForAdmit');
$routes->get('ipd/current-admission', 'Billing\\Ipd::currentAdmission');
$routes->get('ipd/invoices', 'Billing\\Ipd::ipdInvoices');
$routes->post('ipd/list', 'Billing\\Ipd::getIpdTable');
$routes->get('ipd/panel/(:num)', 'Billing\\Ipd::panel/$1');
$routes->get('ipd/panel/(:num)/tab/(:segment)', 'Billing\\Ipd::panelTab/$1/$2');
+ $routes->post('ipd/convert-to-ipd', 'Billing\\Ipd::convertToIpd');
+ $routes->get('ipd/available-ipd-beds', 'Billing\\Ipd::getAvailableIpdBeds');
$routes->post('ipd/panel/(:num)/admission', 'Billing\Ipd::updateAdmission/$1');
$routes->post('ipd/panel/(:num)/diagnosis/include/(:num)', 'Billing\\Ipd::updateDiagnosisChargeInclude/$1/$2');
$routes->post('ipd/panel/(:num)/discharge/update', 'Billing\\Ipd::updateDischargeProcess/$1');
diff --git a/app/Controllers/AbdmGateway.php b/app/Controllers/AbdmGateway.php
index 2eb7ec2..7c87dbe 100644
--- a/app/Controllers/AbdmGateway.php
+++ b/app/Controllers/AbdmGateway.php
@@ -148,11 +148,7 @@ class AbdmGateway extends BaseController
if ($match !== null) {
$patient = $match['patient'];
$patientId = (int) $patient['id'];
- $lName = trim((string) ($patient['p_lname'] ?? ''));
$patientDisplay = trim((string) ($patient['p_fname'] ?? ''));
- if ($lName !== '' && $lName !== '0') {
- $patientDisplay .= ' ' . $lName;
- }
if ($patientDisplay === '') {
$patientDisplay = 'Patient ' . $patientRef;
}
@@ -4627,11 +4623,7 @@ class AbdmGateway extends BaseController
$patient = $match['patient'];
$patientId = (int) $patient['id'];
$patientRef = (string) ($patient['p_code'] ?: ('P-' . $patientId));
- $lName = trim((string) ($patient['p_lname'] ?? ''));
$patientDisplay = trim((string) ($patient['p_fname'] ?? ''));
- if ($lName !== '' && $lName !== '0') {
- $patientDisplay .= ' ' . $lName;
- }
if ($patientDisplay === '') {
$patientDisplay = 'Patient ' . $patientRef;
}
@@ -4860,9 +4852,7 @@ class AbdmGateway extends BaseController
$patientName = '';
if (!empty($patient)) {
- $lName = trim((string) ($patient['p_lname'] ?? ''));
- $fName = trim((string) ($patient['p_fname'] ?? ''));
- $patientName = trim($fName . ($lName !== '' && $lName !== '0' ? ' ' . $lName : ''));
+ $patientName = trim((string) ($patient['p_fname'] ?? ''));
}
if ($patientName === '') {
$patientName = $patient['patient_name'] ?? 'Patient';
@@ -5285,11 +5275,7 @@ class AbdmGateway extends BaseController
$this->db->transComplete();
$patient = $this->db->table('patient_master')->where('id', $patientId)->get(1)->getRowArray() ?? [];
- $lName = trim((string) ($patient['p_lname'] ?? ''));
$patientDisplay = trim((string) ($patient['p_fname'] ?? ''));
- if ($lName !== '' && $lName !== '0') {
- $patientDisplay .= ' ' . $lName;
- }
$patientRef = (string) ($patient['p_code'] ?? $txn['patient_ref'] ?? $patientId);
$confirmedContexts = [];
@@ -5625,11 +5611,7 @@ class AbdmGateway extends BaseController
// 6. Name match
if ($name !== '') {
- $candLName = trim((string) ($cand['p_lname'] ?? ''));
$candName = trim((string) ($cand['p_fname'] ?? ''));
- if ($candLName !== '' && $candLName !== '0') {
- $candName .= ' ' . $candLName;
- }
$nameScore = $this->calculateNameMatchScore($name, $candName);
if ($nameScore > 0) {
$score += (int) ($nameScore * 25);
@@ -8462,14 +8444,7 @@ class AbdmGateway extends BaseController
private function patientDisplayName(array $patientRow): string
{
$first = trim((string) ($patientRow['p_fname'] ?? ''));
- $last = trim((string) ($patientRow['p_lname'] ?? ''));
-
- if (in_array(strtolower($last), ['0', '00', 'na', 'n/a', 'null', 'nil', '-'], true)) {
- $last = '';
- }
-
- $full = trim($first . ' ' . $last);
- return $full !== '' ? $full : 'Patient';
+ return $first !== '' ? $first : 'Patient';
}
/**
@@ -10339,11 +10314,7 @@ class AbdmGateway extends BaseController
$patientId = (int) $patient['id'];
$patientRef = (string) ($patient['p_code'] ?: ('P-' . $patientId));
- $lName = trim((string) ($patient['p_lname'] ?? ''));
$patientName = trim((string) ($patient['p_fname'] ?? ''));
- if ($lName !== '' && $lName !== '0') {
- $patientName .= ' ' . $lName;
- }
if ($patientName === '') {
$patientName = 'Patient ' . $patientRef;
}
diff --git a/app/Controllers/AbdmTaskBoard.php b/app/Controllers/AbdmTaskBoard.php
index 72cddc7..06bbb43 100644
--- a/app/Controllers/AbdmTaskBoard.php
+++ b/app/Controllers/AbdmTaskBoard.php
@@ -665,7 +665,7 @@ class AbdmTaskBoard extends BaseController
if ($this->db->tableExists('ipd_master')) {
foreach ($this->db->table('ipd_master i')
- ->select("i.id AS bill_id, i.ipd_code AS bill_code, i.p_id AS patient_id, COALESCE(NULLIF(NULLIF(TRIM(i.P_name), ''), '0'), TRIM(CONCAT_WS(' ', p.p_fname, p.p_lname))) AS patient_name, COALESCE(i.discharge_date, i.register_date) AS bill_date, i.net_amount AS amount", false)
+ ->select("i.id AS bill_id, i.ipd_code AS bill_code, i.p_id AS patient_id, COALESCE(NULLIF(NULLIF(TRIM(i.P_name), ''), '0'), NULLIF(TRIM(p.p_fname), '')) AS patient_name, COALESCE(i.discharge_date, i.register_date) AS bill_date, i.net_amount AS amount", false)
->join('patient_master p', 'p.id = i.p_id', 'left')
->orderBy('i.id', 'DESC')
->limit(100)
diff --git a/app/Controllers/Abha.php b/app/Controllers/Abha.php
index d162c7e..5f958d7 100644
--- a/app/Controllers/Abha.php
+++ b/app/Controllers/Abha.php
@@ -2034,7 +2034,7 @@ class Abha extends BaseController
$candidateAbhaCols = array_values(array_intersect(['abha_id', 'abha_no', 'abha', 'abha_address'], $fields));
$selectFields = array_values(array_intersect(
- ['id', 'p_code', 'p_fname', 'p_lname', 'gender', 'dob', 'age', 'mphone1', 'udai', 'add1', 'district', 'state', 'zip', 'profile_file_id', 'profile_picture', 'abha_profile_photo_base64'],
+ ['id', 'p_code', 'p_fname', 'gender', 'dob', 'age', 'mphone1', 'udai', 'add1', 'district', 'state', 'zip', 'profile_file_id', 'profile_picture', 'abha_profile_photo_base64'],
$fields
));
foreach ($candidateAbhaCols as $cCol) {
@@ -2106,13 +2106,9 @@ class Abha extends BaseController
});
if ($nameTokens !== []) {
- $hasLastName = in_array('p_lname', $fields, true);
- $collect(static function ($builder) use ($nameTokens, $hasLastName): void {
+ $collect(static function ($builder) use ($nameTokens): void {
foreach ($nameTokens as $token) {
$builder->orLike('p_fname', $token);
- if ($hasLastName) {
- $builder->orLike('p_lname', $token);
- }
}
});
}
@@ -2123,7 +2119,7 @@ class Abha extends BaseController
$candidates = [];
foreach ($rows as $row) {
- $rowName = strtoupper(trim((string) ($row['p_fname'] ?? '') . ' ' . (string) ($row['p_lname'] ?? '')));
+ $rowName = strtoupper(trim((string) ($row['p_fname'] ?? '')));
$rowTokens = array_values(array_filter(preg_split('/\s+/', $rowName) ?: [], fn ($t) => strlen($t) >= 3));
$nameOverlap = $nameTokens !== [] && $rowTokens !== [] && $nameTokens[0] === $rowTokens[0];
@@ -2179,7 +2175,7 @@ class Abha extends BaseController
$candidates[] = [
'id' => (int) ($row['id'] ?? 0),
'p_code' => (string) ($row['p_code'] ?? ''),
- 'name' => trim((string) ($row['p_fname'] ?? '') . ' ' . (string) ($row['p_lname'] ?? '')),
+ 'name' => trim((string) ($row['p_fname'] ?? '')),
'gender' => (int) ($row['gender'] ?? 0),
'gender_label' => $this->genderLabel((int) ($row['gender'] ?? 0)),
'dob' => $rowDob,
diff --git a/app/Controllers/Api/v1/MedicalStoreApi.php b/app/Controllers/Api/v1/MedicalStoreApi.php
index c68faaa..4de6c7e 100644
--- a/app/Controllers/Api/v1/MedicalStoreApi.php
+++ b/app/Controllers/Api/v1/MedicalStoreApi.php
@@ -130,13 +130,11 @@ class MedicalStoreApi extends BaseController
// Search patient_master by UHID, Mobile, Name, ABHA ID, ABHA Address
$builder = $this->db->table('patient_master p');
- $builder->select('p.id as patient_id, p.p_code as uhid, p.p_fname, p.p_lname, p.title, p.mphone1, p.gender, p.age, p.dob, p.add1, p.city, p.abha_id, p.abha_address, p.abha_verified_status, p.abha_kyc_verified');
+ $builder->select('p.id as patient_id, p.p_code as uhid, p.p_fname, p.title, p.mphone1, p.gender, p.age, p.dob, p.add1, p.city, p.abha_id, p.abha_address, p.abha_verified_status, p.abha_kyc_verified');
$builder->groupStart()
->like('p.p_code', $q)
->orLike('p.mphone1', $q)
->orLike('p.p_fname', $q)
- ->orLike('p.p_lname', $q)
- ->orLike("CONCAT(p.p_fname, ' ', p.p_lname)", $q)
->orLike('p.abha_id', $q)
->orLike('p.abha_address', $q)
->groupEnd();
@@ -146,7 +144,7 @@ class MedicalStoreApi extends BaseController
foreach ($patients as $pt) {
$pId = (int)$pt['patient_id'];
$genderStr = ((int)$pt['gender'] === 1) ? 'Male' : (((int)$pt['gender'] === 2) ? 'Female' : 'Other');
- $fullName = trim(($pt['title'] ? $pt['title'] . ' ' : '') . $pt['p_fname'] . ' ' . ($pt['p_lname'] ?? ''));
+ $fullName = trim(($pt['title'] ? $pt['title'] . ' ' : '') . $pt['p_fname']);
// Check Active OPD for today
$opdRow = $this->db->table('opd_master')
diff --git a/app/Controllers/Api/v1/NursingApi.php b/app/Controllers/Api/v1/NursingApi.php
index cb39241..c6b3d95 100644
--- a/app/Controllers/Api/v1/NursingApi.php
+++ b/app/Controllers/Api/v1/NursingApi.php
@@ -671,7 +671,7 @@ class NursingApi extends BaseController
$cleanAbha = str_replace('-', '', $q);
$builder = $db->table('patient_master p');
- $builder->select('p.id, p.p_code, p.p_fname, p.p_lname, p.title, p.gender, p.dob, p.age, p.mphone1, p.mphone2, p.email1, p.city, p.district, p.state, p.abha_id, p.abha_address, p.abha_verified_status, p.abha_kyc_verified, p.abha_mobile_verified, p.abha_profile_photo_base64');
+ $builder->select('p.id, p.p_code, p.p_fname, p.title, p.gender, p.dob, p.age, p.mphone1, p.mphone2, p.email1, p.city, p.district, p.state, p.abha_id, p.abha_address, p.abha_verified_status, p.abha_kyc_verified, p.abha_mobile_verified, p.abha_profile_photo_base64');
$builder->groupStart();
$builder->where('p.p_code', $q);
@@ -696,8 +696,6 @@ class NursingApi extends BaseController
if (strlen($q) >= 2) {
$builder->orLike('p.p_fname', $q);
- $builder->orLike('p.p_lname', $q);
- $builder->orLike("CONCAT(p.p_fname, ' ', p.p_lname)", $q);
$builder->orLike('p.mphone1', $q);
$builder->orLike('p.p_code', $q);
}
@@ -709,7 +707,7 @@ class NursingApi extends BaseController
$today = date('Y-m-d');
foreach ($patients as &$pat) {
$pId = (int) $pat['id'];
- $pat['fullName'] = trim(($pat['title'] ? $pat['title'] . ' ' : '') . $pat['p_fname'] . ($pat['p_lname'] && $pat['p_lname'] !== '0' ? ' ' . $pat['p_lname'] : ''));
+ $pat['fullName'] = trim(($pat['title'] ? $pat['title'] . ' ' : '') . $pat['p_fname']);
// Check IPD admission
$ipdRow = $db->table('ipd_master i')
diff --git a/app/Controllers/Billing/Ipd.php b/app/Controllers/Billing/Ipd.php
index 72032fe..f72114d 100644
--- a/app/Controllers/Billing/Ipd.php
+++ b/app/Controllers/Billing/Ipd.php
@@ -184,6 +184,85 @@ class Ipd extends BaseController
return view('billing/ipd/panel', $panelData);
}
+ public function admit()
+ {
+ $permission = $this->requireAnyPermission([
+ 'billing.access',
+ 'billing.ipd.access',
+ 'billing.ipd.current-admission',
+ 'ipd_nursing.view',
+ ]);
+ if ($permission) {
+ return $permission;
+ }
+
+ $defaultType = strtolower(trim((string) ($this->request->getGet('type') ?? '')));
+ if (! in_array($defaultType, ['ipd', 'daycare', 'emergency'], true)) {
+ $defaultType = 'emergency';
+ }
+
+ return view('billing/ipd/admit_patient', [
+ 'default_type' => $defaultType,
+ ]);
+ }
+
+ public function searchPatientForAdmit()
+ {
+ $permission = $this->requireAnyPermission([
+ 'billing.access',
+ 'billing.ipd.access',
+ 'billing.ipd.current-admission',
+ 'ipd_nursing.view',
+ ]);
+ if ($permission) {
+ return $this->response->setStatusCode(403)->setJSON(['error' => 'Permission denied']);
+ }
+
+ $term = trim((string) ($this->request->getGet('q') ?? $this->request->getPost('q') ?? ''));
+ if ($term === '') {
+ return $this->response->setJSON([]);
+ }
+
+ $termClean = preg_replace('/[^A-Za-z0-9 _.@\/-]/', '', $term);
+ if ($termClean === '') {
+ return $this->response->setJSON([]);
+ }
+
+ $builder = $this->db->table('patient_master p');
+ $builder->select('p.id, p.p_code, p.p_fname as patient_name, p.gender, p.age, p.dob, p.mphone1, p.mphone2');
+ $builder->groupStart()
+ ->like('p.p_fname', $termClean)
+ ->orLike('p.p_code', $termClean)
+ ->orLike('p.mphone1', $termClean)
+ ->orLike('p.mphone2', $termClean)
+ ->groupEnd();
+ $builder->limit(20);
+ $results = $builder->get()->getResultArray();
+
+ $patientIds = array_column($results, 'id');
+ $activeIpds = [];
+ if (! empty($patientIds)) {
+ $rows = $this->db->table('ipd_master')
+ ->select('id, p_id, coalesce(ipd_code, pid) as ipd_code, admission_type, ipd_status')
+ ->whereIn('p_id', $patientIds)
+ ->where('ipd_status', 0)
+ ->get()
+ ->getResultArray();
+ foreach ($rows as $r) {
+ $activeIpds[(int) $r['p_id']] = $r;
+ }
+ }
+
+ foreach ($results as &$pt) {
+ $genderVal = (int) ($pt['gender'] ?? 0);
+ $pt['gender_text'] = ($genderVal === 1) ? 'Male' : (($genderVal === 2) ? 'Female' : 'Other');
+ $pt['active_ipd'] = $activeIpds[(int) $pt['id']] ?? null;
+ }
+ unset($pt);
+
+ return $this->response->setJSON($results);
+ }
+
public function legacyAddIpd(int $patientId)
{
$permission = $this->requireAnyPermission([
@@ -217,6 +296,12 @@ class Ipd extends BaseController
return $this->response->setStatusCode(404)->setBody('Patient not found');
}
+ $defaultType = strtolower(trim((string) ($this->request->getGet('type') ?? 'ipd')));
+ if (! in_array($defaultType, ['ipd', 'daycare', 'emergency'], true)) {
+ $defaultType = 'ipd';
+ }
+ $data['default_admission_type'] = $defaultType;
+
return view('billing/ipd/legacy_registration', $data);
}
@@ -252,6 +337,19 @@ class Ipd extends BaseController
$registerDateInput = (string) ($this->request->getPost('res_date') ?? '');
$registerDate = $registerDateInput !== '' ? str_to_MysqlDate($registerDateInput) : date('Y-m-d');
+ $admissionType = strtolower(trim((string) ($this->request->getPost('admission_type') ?? 'ipd')));
+ if (! in_array($admissionType, ['ipd', 'daycare', 'emergency'], true)) {
+ $admissionType = 'ipd';
+ }
+
+ $triageLevel = (int) ($this->request->getPost('triage_level') ?? 0);
+ $isMlc = (int) ($this->request->getPost('optionsRadios_mlc') ?? 0);
+ $mlcNumber = trim((string) ($this->request->getPost('mlc_number') ?? ''));
+ $policeStation = trim((string) ($this->request->getPost('police_station') ?? ''));
+ $policeConstable = trim((string) ($this->request->getPost('police_constable_details') ?? ''));
+ $broughtBy = trim((string) ($this->request->getPost('brought_by') ?? ''));
+ $daycareProcedure = trim((string) ($this->request->getPost('daycare_procedure_name') ?? ''));
+
$master = [
'p_id' => $patientId,
'P_name' => (string) ($this->request->getPost('pname') ?? ''),
@@ -264,11 +362,20 @@ class Ipd extends BaseController
'remark' => (string) ($this->request->getPost('remark') ?? ''),
'P_mobile1' => (string) ($this->request->getPost('phone1') ?? ''),
'P_mobile2' => (string) ($this->request->getPost('phone2') ?? ''),
- 'case_type' => (int) ($this->request->getPost('optionsRadios_mlc') ?? 0),
+ 'case_type' => $isMlc,
'case_id' => (int) ($this->request->getPost('optionsRadios_org') ?? 0),
'reg_time' => (string) ($this->request->getPost('res_time') ?? date('H:i')),
'refer_by' => (int) ($this->request->getPost('refer_by_list') ?? 0),
'dept_id' => (int) ($this->request->getPost('dept_id') ?? 0),
+ 'admission_type' => $admissionType,
+ 'triage_level' => $triageLevel > 0 ? $triageLevel : null,
+ 'triage_time' => $triageLevel > 0 ? date('Y-m-d H:i:s') : null,
+ 'is_mlc' => $isMlc,
+ 'mlc_number' => $mlcNumber !== '' ? $mlcNumber : null,
+ 'police_station' => $policeStation !== '' ? $policeStation : null,
+ 'police_constable_details' => $policeConstable !== '' ? $policeConstable : null,
+ 'brought_by' => $broughtBy !== '' ? $broughtBy : null,
+ 'daycare_procedure_name' => $daycareProcedure !== '' ? $daycareProcedure : null,
];
$insertId = $this->ipdEditModel->insertIpd([
@@ -302,6 +409,83 @@ class Ipd extends BaseController
]);
}
+ public function convertToIpd()
+ {
+ $permission = $this->requireAnyPermission([
+ 'billing.access',
+ 'billing.ipd.invoice',
+ 'ipd_nursing.view',
+ ]);
+ if ($permission) {
+ return $permission;
+ }
+
+ $ipdId = (int) ($this->request->getPost('ipd_id') ?? 0);
+ $targetBedId = (int) ($this->request->getPost('target_bed_id') ?? 0);
+ $doctorId = (int) ($this->request->getPost('doctor_id') ?? 0);
+ $reason = trim((string) ($this->request->getPost('conversion_reason') ?? ''));
+
+ $sbarSituation = trim((string) ($this->request->getPost('sbar_situation') ?? ''));
+ $sbarBackground = trim((string) ($this->request->getPost('sbar_background') ?? ''));
+ $sbarAssessment = trim((string) ($this->request->getPost('sbar_assessment') ?? ''));
+ $sbarRecommendation = trim((string) ($this->request->getPost('sbar_recommendation') ?? ''));
+
+ $sbarNote = "S (Situation): " . $sbarSituation . "\n"
+ . "B (Background): " . $sbarBackground . "\n"
+ . "A (Assessment): " . $sbarAssessment . "\n"
+ . "R (Recommendation): " . $sbarRecommendation;
+
+ $result = $this->ipdEditModel->convertToIpd($ipdId, [
+ 'target_bed_id' => $targetBedId,
+ 'doctor_id' => $doctorId,
+ 'conversion_reason' => $reason,
+ 'sbar_handover' => trim($sbarNote),
+ ]);
+
+ if (! empty($result['status'])) {
+ $this->enqueueIpdAdmissionSync($ipdId, 'ipd.admission.escalated');
+ }
+
+ return $this->response->setJSON($result);
+ }
+
+ public function getAvailableIpdBeds()
+ {
+ $permission = $this->requireAnyPermission([
+ 'billing.access',
+ 'billing.ipd.invoice',
+ 'ipd_nursing.view',
+ ]);
+ if ($permission) {
+ return $permission;
+ }
+
+ $beds = $this->db->table('bed_master b')
+ ->select('b.id, b.bed_number, b.bed_code, w.ward_name, w.ward_type, w.floor_number')
+ ->join('ward_master w', 'w.id = b.ward_id', 'left')
+ ->where('b.bed_status', 'available')
+ ->where('b.status', 'active')
+ ->where('b.current_ipd_id IS NULL')
+ ->orderBy('w.ward_type', 'ASC')
+ ->orderBy('w.ward_name', 'ASC')
+ ->orderBy('b.bed_number', 'ASC')
+ ->get()
+ ->getResultArray();
+
+ $doctors = $this->db->table('doctor_master')
+ ->select("id, concat_ws(' ', 'Dr.', p_fname, p_mname, p_lname) as doc_name")
+ ->where('status', '1')
+ ->orderBy('p_fname', 'ASC')
+ ->get()
+ ->getResultArray();
+
+ return $this->response->setJSON([
+ 'status' => true,
+ 'beds' => $beds,
+ 'doctors' => $doctors,
+ ]);
+ }
+
private function buildLegacyIpdRegistrationData(int $patientId): array
{
$person = $this->db->table('patient_master')
@@ -333,11 +517,12 @@ class Ipd extends BaseController
if ($this->db->tableExists('bed_master')) {
$ipdBedList = $this->db->query(
"select b.id,
- concat('Bed No:', coalesce(b.bed_number, b.bed_code), '/', '[', coalesce(w.ward_name, ''), ']') as Bed_Desc
+ w.ward_type,
+ concat('Bed No:', coalesce(b.bed_number, b.bed_code), '/', '[', coalesce(w.ward_name, ''), ']', if(w.ward_type is not null and w.ward_type != '', concat(' (', w.ward_type, ')'), '')) as Bed_Desc
from bed_master b
left join ward_master w on w.id = b.ward_id
where b.bed_status = 'available'
- order by b.bed_number, b.id"
+ order by w.ward_type, b.bed_number, b.id"
)->getResult();
} elseif ($this->db->tableExists('hc_bed_master')) {
$ipdBedList = $this->db->query(
diff --git a/app/Controllers/DoctorDocument.php b/app/Controllers/DoctorDocument.php
index 24c8d74..b755219 100644
--- a/app/Controllers/DoctorDocument.php
+++ b/app/Controllers/DoctorDocument.php
@@ -401,7 +401,7 @@ class DoctorDocument extends BaseController
}
$builder = $this->db->table('ipd_master')
- ->select('ipd_master.*, patient_master.p_fname, patient_master.p_lname, patient_master.p_code, patient_master.age, patient_master.gender, patient_master.dob, patient_master.abha_id, patient_master.abha_address, patient_master.mphone1, doctor_master.p_fname as dr_fname, doctor_master.p_lname as dr_lname')
+ ->select('ipd_master.*, patient_master.p_fname, patient_master.p_code, patient_master.age, patient_master.gender, patient_master.dob, patient_master.abha_id, patient_master.abha_address, patient_master.mphone1, doctor_master.p_fname as dr_fname, doctor_master.p_lname as dr_lname')
->join('patient_master', 'patient_master.id = ipd_master.p_id', 'left')
->join('doctor_master', 'doctor_master.id = ipd_master.r_doc_id', 'left');
@@ -418,7 +418,6 @@ class DoctorDocument extends BaseController
->orLike('ipd_master.P_name', $query)
->orLike('patient_master.p_code', $query)
->orLike('patient_master.p_fname', $query)
- ->orLike('patient_master.p_lname', $query)
->groupEnd();
}
@@ -434,7 +433,7 @@ class DoctorDocument extends BaseController
foreach ($ipdRecords as $row) {
$ipdId = (int) $row['id'];
$patientId = (int) $row['p_id'];
- $patientName = trim(trim((string) ($row['p_fname'] ?? '')) . ' ' . trim((string) ($row['p_lname'] ?? '')));
+ $patientName = trim((string) ($row['p_fname'] ?? ''));
if ($patientName === '') {
$patientName = (string) ($row['P_name'] ?? ('Patient #' . $patientId));
}
@@ -777,7 +776,7 @@ class DoctorDocument extends BaseController
$lifestyle[] = 'Diet & Lifestyle Advice: ' . $adviceStr;
}
- $patientName = trim(trim((string) ($patientRow['p_fname'] ?? '')) . ' ' . trim((string) ($patientRow['p_lname'] ?? '')));
+ $patientName = trim((string) ($patientRow['p_fname'] ?? ''));
$abhaIdRaw = '';
foreach (['abha_id', 'abha_no', 'abha'] as $field) {
$candidate = trim((string) ($patientRow[$field] ?? ''));
@@ -2157,7 +2156,7 @@ class DoctorDocument extends BaseController
$docTitle = 'Medical Certificate';
}
- $patientName = trim(trim((string) ($patientRow['p_fname'] ?? '')) . ' ' . trim((string) ($patientRow['p_lname'] ?? '')));
+ $patientName = trim((string) ($patientRow['p_fname'] ?? ''));
$abhaIdRaw = '';
foreach (['abha_id', 'abha_no', 'abha'] as $field) {
$candidate = trim((string) ($patientRow[$field] ?? ''));
@@ -2437,7 +2436,7 @@ class DoctorDocument extends BaseController
$mimeType = 'text/html';
}
- $patientName = trim(trim((string) ($patientRow['p_fname'] ?? '')) . ' ' . trim((string) ($patientRow['p_lname'] ?? '')));
+ $patientName = trim((string) ($patientRow['p_fname'] ?? ''));
$abhaIdRaw = '';
foreach (['abha_id', 'abha_no', 'abha'] as $field) {
$candidate = trim((string) ($patientRow[$field] ?? ''));
diff --git a/app/Controllers/Immunization.php b/app/Controllers/Immunization.php
index 610e98d..3da6f09 100644
--- a/app/Controllers/Immunization.php
+++ b/app/Controllers/Immunization.php
@@ -250,7 +250,7 @@ class Immunization extends BaseController
{
$fields = $this->db->getFieldNames('patient_master') ?? [];
$select = ['id'];
- foreach (['p_code', 'p_fname', 'p_lname', 'gender', 'dob', 'age', 'age_in_month', 'mphone1', 'abha_id', 'abha_no', 'abha_address', 'abha'] as $field) {
+ foreach (['p_code', 'p_fname', 'gender', 'dob', 'age', 'age_in_month', 'mphone1', 'abha_id', 'abha_no', 'abha_address', 'abha'] as $field) {
if (in_array($field, $fields, true)) {
$select[] = $field;
}
diff --git a/app/Controllers/IpdPatient.php b/app/Controllers/IpdPatient.php
index fba723d..c41d9c8 100644
--- a/app/Controllers/IpdPatient.php
+++ b/app/Controllers/IpdPatient.php
@@ -88,7 +88,7 @@ class IpdPatient extends BaseController
if ($this->db->tableExists('patient_master')) {
$builder
- ->select('p.p_code, p.p_fname, p.p_lname')
+ ->select('p.p_code, p.p_fname')
->join('patient_master p', 'p.id = i.p_id', 'left');
}
}
@@ -117,7 +117,7 @@ class IpdPatient extends BaseController
}
}
- $patientName = trim(((string) ($row->p_fname ?? '')) . ' ' . ((string) ($row->p_lname ?? '')));
+ $patientName = trim((string) ($row->p_fname ?? ''));
if (! isset($floors[$floorNo])) {
$floors[$floorNo] = [];
diff --git a/app/Controllers/Ipd_discharge.php b/app/Controllers/Ipd_discharge.php
index ec47489..86fef95 100644
--- a/app/Controllers/Ipd_discharge.php
+++ b/app/Controllers/Ipd_discharge.php
@@ -477,6 +477,16 @@ class Ipd_discharge extends BaseController
'hospital_email' => esc($hEmail),
];
+ $isDayCare = ($ipd->admission_type ?? '') === 'daycare';
+ $isEmergency = ($ipd->admission_type ?? '') === 'emergency';
+ $dcScore = (int) ($ipd->discharge_score ?? 10);
+
+ $tokens['DAYCARE_HEADER'] = '
' . ($isDayCare ? 'DAY CARE SURGERY & OBSERVATION DISCHARGE SUMMARY' : ($isEmergency ? 'EMERGENCY & CASUALTY DISCHARGE CARD' : 'INPATIENT DISCHARGE SUMMARY')) . ' ' . ($isDayCare ? 'Short Stay Ambulatory Episode (< 24 Hours) • NABH Compliant Discharge Document' : ($isEmergency ? 'Emergency Triage & Observation • Statutory Medical Record' : 'Clinical Summary • Hospital Record')) . ' ';
+
+ $tokens['PADSS_SCORE'] = 'Post-Anaesthesia Discharge Readiness Assessment (PADSS) Vital Signs : Stable / Within 20% baselineAmbulation : Steady Gait / No DizzinessNausea / Emesis : Minimal / Tolerating OralsPain Score : Controlled (≤ 3/10)Surgical Bleeding : Dry / Minimal Dressing SoakageTotal Score: ' . $dcScore . '/10 (Discharge Clearance Met ≥ 9)
';
+
+ $tokens['RED_FLAGS_WARNINGS'] = ' EMERGENCY RETURN PRECAUTIONS (RED FLAGS): Seek immediate medical attention or return to the Emergency / Casualty department if you develop: High fever (>101°F) with chills, persistent nausea/vomiting, severe uncontrolled wound pain, sudden bleeding or soakage through dressing, shortness of breath, or chest pain.
';
+
// Add patient info table as a token (not included in CONTENT by default)
$tokens['PATIENT_INFO_TABLE'] = $this->buildAutoDischargeSummaryTable($panelData);
@@ -9009,11 +9019,7 @@ class Ipd_discharge extends BaseController
return;
}
- $lastName = trim((string) ($patientRow['p_lname'] ?? ''));
- if (! $this->isMeaningfulDischargeValue($lastName)) {
- $lastName = '';
- }
- $patientName = trim(trim((string) ($patientRow['p_fname'] ?? '')) . ' ' . $lastName);
+ $patientName = trim((string) ($patientRow['p_fname'] ?? ''));
if ($patientName === '') {
$patientName = trim((string) ($ipdRow['P_name'] ?? ''));
}
diff --git a/app/Controllers/Opd_prescription.php b/app/Controllers/Opd_prescription.php
index 6f8abfb..a03e5da 100644
--- a/app/Controllers/Opd_prescription.php
+++ b/app/Controllers/Opd_prescription.php
@@ -4879,8 +4879,7 @@ class Opd_prescription extends BaseController
$abhaAddress = $abhaField !== null ? trim((string) ($patientRow[$abhaField] ?? '')) : '';
$patFirstName = $this->sanitizePersonNamePart((string) ($patientRow['p_fname'] ?? ''));
- $patLastName = $this->sanitizePersonNamePart((string) ($patientRow['p_lname'] ?? ''));
- $patFullName = trim($patFirstName . ($patLastName !== '' ? ' ' . $patLastName : ''));
+ $patFullName = $patFirstName;
if ($patFullName === '') {
$patFullName = trim((string) ($opdRow['P_name'] ?? ''));
}
@@ -5108,8 +5107,7 @@ class Opd_prescription extends BaseController
$pRow = $this->db->table('patient_master')->where('id', $patientId)->get(1)->getRowArray();
if (! empty($pRow)) {
$fname = $this->sanitizePersonNamePart((string) ($pRow['p_fname'] ?? ''));
- $lname = $this->sanitizePersonNamePart((string) ($pRow['p_lname'] ?? ''));
- $patientName = trim($fname . ' ' . $lname);
+ $patientName = $fname;
foreach (['abha_number', 'abha_id', 'abha_address'] as $abhaField) {
if (in_array($abhaField, $pFields, true)) {
$val = trim((string) ($pRow[$abhaField] ?? ''));
@@ -5514,8 +5512,7 @@ class Opd_prescription extends BaseController
$pRow = $this->db->table('patient_master')->where('id', $patientId)->get(1)->getRowArray();
if (! empty($pRow)) {
$fname = $this->sanitizePersonNamePart((string) ($pRow['p_fname'] ?? ''));
- $lname = $this->sanitizePersonNamePart((string) ($pRow['p_lname'] ?? ''));
- $patientName = trim($fname . ' ' . $lname);
+ $patientName = $fname;
foreach (['abha_number', 'abha_id', 'abha_address'] as $abhaField) {
if (in_array($abhaField, $pFields, true)) {
$val = trim((string) ($pRow[$abhaField] ?? ''));
@@ -11554,7 +11551,7 @@ class Opd_prescription extends BaseController
{
try {
$escape = static fn (string $value): string => htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
- $patientName = trim($this->sanitizePersonNamePart((string) ($patientRow['p_fname'] ?? '')) . ' ' . $this->sanitizePersonNamePart((string) ($patientRow['p_lname'] ?? '')));
+ $patientName = trim($this->sanitizePersonNamePart((string) ($patientRow['p_fname'] ?? '')));
$patientName = $patientName !== '' ? $patientName : 'Patient';
$hospital = $this->getHospitalProfileForFhir();
$hospitalName = trim((string) ($hospital['name'] ?? '')) ?: 'Healthcare Facility';
@@ -11697,8 +11694,7 @@ class Opd_prescription extends BaseController
$abhaAddress = $abhaField !== null ? trim((string) ($patientRow[$abhaField] ?? '')) : '';
$patFirstName = $this->sanitizePersonNamePart((string) ($patientRow['p_fname'] ?? ''));
- $patLastName = $this->sanitizePersonNamePart((string) ($patientRow['p_lname'] ?? ''));
- $patFullName = trim($patFirstName . ($patLastName !== '' ? ' ' . $patLastName : ''));
+ $patFullName = $patFirstName;
if ($patFullName === '') {
$patFullName = trim((string) ($opdRow['P_name'] ?? ''));
}
diff --git a/app/Controllers/Patient.php b/app/Controllers/Patient.php
index 7260f30..f59fc62 100644
--- a/app/Controllers/Patient.php
+++ b/app/Controllers/Patient.php
@@ -5914,7 +5914,7 @@ class Patient extends BaseController
$docTitle = 'Patient Scanned Document';
}
- $patientName = trim(trim((string) ($patientRow['p_fname'] ?? '')) . ' ' . trim((string) ($patientRow['p_lname'] ?? '')));
+ $patientName = trim((string) ($patientRow['p_fname'] ?? ''));
$abhaIdRaw = '';
foreach (['abha_id', 'abha_no', 'abha'] as $field) {
$candidate = trim((string) ($patientRow[$field] ?? ''));
diff --git a/app/Controllers/Payment.php b/app/Controllers/Payment.php
index e2e3b5a..7588806 100644
--- a/app/Controllers/Payment.php
+++ b/app/Controllers/Payment.php
@@ -194,7 +194,7 @@ class Payment extends BaseController
}
$patient = $this->db->table($patientTable)->where('id', $patientId)->get(1)->getRowArray();
if ($patient !== null) {
- $context['patient'] = trim((string) ($patient['p_fname'] ?? '') . ' ' . (string) ($patient['p_lname'] ?? ''));
+ $context['patient'] = trim((string) ($patient['p_fname'] ?? ''));
break;
}
}
diff --git a/app/Controllers/Report.php b/app/Controllers/Report.php
index 744f2b3..2b42d9e 100644
--- a/app/Controllers/Report.php
+++ b/app/Controllers/Report.php
@@ -762,7 +762,7 @@ class Report extends BaseController
$builder = $this->db->table('payment_history p');
$builder->select('p.id as payment_id, p.payment_date, inv.inv_date, inv.invoice_code, inv.net_amount')
->select('DATEDIFF(DATE(p.payment_date), DATE(inv.inv_date)) as delay_days', false)
- ->select("CONCAT(IFNULL(pat.p_fname,''), ' ', IFNULL(pat.p_lname,'')) as patient_name", false)
+ ->select("IFNULL(pat.p_fname, '') as patient_name", false)
->select('pat.p_code as patient_code')
->select('p.amount as paid_amount, p.update_by')
->select("CASE p.payment_mode WHEN 1 THEN 'Cash' WHEN 2 THEN 'Bank' ELSE 'Other' END as pay_mode", false)
diff --git a/app/Controllers/Setting/BedManagement.php b/app/Controllers/Setting/BedManagement.php
index 95a05bf..81718b9 100644
--- a/app/Controllers/Setting/BedManagement.php
+++ b/app/Controllers/Setting/BedManagement.php
@@ -362,7 +362,7 @@ class BedManagement extends BaseController
if ($this->db->tableExists('patient_master')) {
$builder
- ->select('p.p_code, p.p_fname, p.p_lname')
+ ->select('p.p_code, p.p_fname')
->join('patient_master p', 'p.id = i.p_id', 'left');
}
}
@@ -392,7 +392,7 @@ class BedManagement extends BaseController
}
}
- $patientName = trim(((string) ($row->p_fname ?? '')) . ' ' . ((string) ($row->p_lname ?? '')));
+ $patientName = trim((string) ($row->p_fname ?? ''));
// Initialise floor / ward buckets
if (! isset($floors[$floorNo])) {
diff --git a/app/Database/Migrations/2026-09-22-000001_CreateDaycareEmergencyModule.php b/app/Database/Migrations/2026-09-22-000001_CreateDaycareEmergencyModule.php
new file mode 100644
index 0000000..a5d95a1
--- /dev/null
+++ b/app/Database/Migrations/2026-09-22-000001_CreateDaycareEmergencyModule.php
@@ -0,0 +1,320 @@
+db->tableExists('ipd_master')) {
+ $fields = [];
+
+ if (! $this->db->fieldExists('admission_type', 'ipd_master')) {
+ $fields['admission_type'] = [
+ 'type' => 'ENUM',
+ 'constraint' => ['ipd', 'daycare', 'emergency'],
+ 'default' => 'ipd',
+ 'null' => false,
+ 'after' => 'ipd_code',
+ ];
+ }
+
+ if (! $this->db->fieldExists('triage_level', 'ipd_master')) {
+ $fields['triage_level'] = [
+ 'type' => 'TINYINT',
+ 'constraint' => 1,
+ 'null' => true,
+ 'comment' => 'ESI Triage 1: Red, 2: Orange, 3: Yellow, 4: Green, 5: Blue',
+ 'after' => 'admission_type',
+ ];
+ }
+
+ if (! $this->db->fieldExists('triage_time', 'ipd_master')) {
+ $fields['triage_time'] = [
+ 'type' => 'DATETIME',
+ 'null' => true,
+ 'after' => 'triage_level',
+ ];
+ }
+
+ if (! $this->db->fieldExists('is_mlc', 'ipd_master')) {
+ $fields['is_mlc'] = [
+ 'type' => 'TINYINT',
+ 'constraint' => 1,
+ 'default' => 0,
+ 'null' => false,
+ 'after' => 'triage_time',
+ ];
+ }
+
+ if (! $this->db->fieldExists('mlc_number', 'ipd_master')) {
+ $fields['mlc_number'] = [
+ 'type' => 'VARCHAR',
+ 'constraint' => 60,
+ 'null' => true,
+ 'after' => 'is_mlc',
+ ];
+ }
+
+ if (! $this->db->fieldExists('police_station', 'ipd_master')) {
+ $fields['police_station'] = [
+ 'type' => 'VARCHAR',
+ 'constraint' => 120,
+ 'null' => true,
+ 'after' => 'mlc_number',
+ ];
+ }
+
+ if (! $this->db->fieldExists('police_constable_details', 'ipd_master')) {
+ $fields['police_constable_details'] = [
+ 'type' => 'VARCHAR',
+ 'constraint' => 150,
+ 'null' => true,
+ 'after' => 'police_station',
+ ];
+ }
+
+ if (! $this->db->fieldExists('brought_by', 'ipd_master')) {
+ $fields['brought_by'] = [
+ 'type' => 'VARCHAR',
+ 'constraint' => 120,
+ 'null' => true,
+ 'after' => 'police_constable_details',
+ ];
+ }
+
+ if (! $this->db->fieldExists('daycare_procedure_name', 'ipd_master')) {
+ $fields['daycare_procedure_name'] = [
+ 'type' => 'VARCHAR',
+ 'constraint' => 255,
+ 'null' => true,
+ 'after' => 'brought_by',
+ ];
+ }
+
+ if (! $this->db->fieldExists('converted_from_type', 'ipd_master')) {
+ $fields['converted_from_type'] = [
+ 'type' => 'VARCHAR',
+ 'constraint' => 20,
+ 'null' => true,
+ 'after' => 'daycare_procedure_name',
+ ];
+ }
+
+ if (! $this->db->fieldExists('converted_from_code', 'ipd_master')) {
+ $fields['converted_from_code'] = [
+ 'type' => 'VARCHAR',
+ 'constraint' => 50,
+ 'null' => true,
+ 'after' => 'converted_from_type',
+ ];
+ }
+
+ if (! $this->db->fieldExists('converted_at', 'ipd_master')) {
+ $fields['converted_at'] = [
+ 'type' => 'DATETIME',
+ 'null' => true,
+ 'after' => 'converted_from_code',
+ ];
+ }
+
+ if (! $this->db->fieldExists('conversion_reason', 'ipd_master')) {
+ $fields['conversion_reason'] = [
+ 'type' => 'TEXT',
+ 'null' => true,
+ 'after' => 'converted_at',
+ ];
+ }
+
+ if (! $this->db->fieldExists('converted_by', 'ipd_master')) {
+ $fields['converted_by'] = [
+ 'type' => 'INT',
+ 'constraint' => 11,
+ 'null' => true,
+ 'after' => 'conversion_reason',
+ ];
+ }
+
+ if (! $this->db->fieldExists('sbar_handover', 'ipd_master')) {
+ $fields['sbar_handover'] = [
+ 'type' => 'TEXT',
+ 'null' => true,
+ 'after' => 'converted_by',
+ ];
+ }
+
+ if (! $this->db->fieldExists('discharge_score', 'ipd_master')) {
+ $fields['discharge_score'] = [
+ 'type' => 'INT',
+ 'constraint' => 11,
+ 'null' => true,
+ 'comment' => 'PADSS or Modified Aldrete Score',
+ 'after' => 'sbar_handover',
+ ];
+ }
+
+ if (! empty($fields)) {
+ $this->forge->addColumn('ipd_master', $fields);
+ }
+ }
+
+ // 2. Modify ward_master.ward_type to support 'Day Care'
+ if ($this->db->tableExists('ward_master')) {
+ $this->db->query("
+ ALTER TABLE `ward_master`
+ MODIFY COLUMN `ward_type` ENUM(
+ 'General', 'Private', 'Semi-Private', 'Deluxe',
+ 'ICU', 'NICU', 'PICU', 'HDU', 'ICCU', 'CCU',
+ 'Emergency', 'Day Care'
+ ) NOT NULL DEFAULT 'General'
+ ");
+
+ // Seed default Emergency and Day Care wards if none exist
+ $erWardCount = $this->db->table('ward_master')->where('ward_type', 'Emergency')->countAllResults();
+ if ($erWardCount === 0) {
+ $this->db->table('ward_master')->insert([
+ 'ward_code' => 'ER-01',
+ 'ward_name' => 'Casualty & Emergency Ward',
+ 'ward_type' => 'Emergency',
+ 'gender_type' => 'unisex',
+ 'ward_category' => 'adult',
+ 'total_capacity' => 6,
+ 'floor_number' => 0,
+ 'has_oxygen' => 1,
+ 'has_suction' => 1,
+ 'has_monitor' => 1,
+ 'status' => 'active',
+ 'remarks' => 'Default Casualty / Emergency triage and observation unit',
+ 'created_at' => date('Y-m-d H:i:s'),
+ ]);
+ }
+
+ $dcWardCount = $this->db->table('ward_master')->where('ward_type', 'Day Care')->countAllResults();
+ if ($dcWardCount === 0) {
+ $this->db->table('ward_master')->insert([
+ 'ward_code' => 'DC-01',
+ 'ward_name' => 'Day Care Unit',
+ 'ward_type' => 'Day Care',
+ 'gender_type' => 'unisex',
+ 'ward_category' => 'adult',
+ 'total_capacity' => 6,
+ 'floor_number' => 1,
+ 'has_oxygen' => 1,
+ 'has_suction' => 1,
+ 'has_monitor' => 1,
+ 'status' => 'active',
+ 'remarks' => 'Day Care short stay unit for minor surgeries, dialysis, and chemotherapy',
+ 'created_at' => date('Y-m-d H:i:s'),
+ ]);
+ }
+ }
+
+ // 3. Ensure dedicated item types in ipd_item_type for separate billing headers
+ if ($this->db->tableExists('ipd_item_type')) {
+ $existingEr = $this->db->table('ipd_item_type')
+ ->groupStart()
+ ->like('desc', 'EMERGENCY')
+ ->orLike('group_desc', 'Emergency')
+ ->groupEnd()
+ ->countAllResults();
+
+ if ($existingEr === 0) {
+ $this->db->table('ipd_item_type')->insert([
+ 'desc' => 'EMERGENCY / CASUALTY CHARGES',
+ 'group_desc' => 'Emergency / Casualty Charges',
+ ]);
+ }
+
+ $existingDc = $this->db->table('ipd_item_type')
+ ->groupStart()
+ ->like('desc', 'DAY CARE')
+ ->orLike('group_desc', 'Day Care')
+ ->groupEnd()
+ ->countAllResults();
+
+ if ($existingDc === 0) {
+ $this->db->table('ipd_item_type')->insert([
+ 'desc' => 'DAY CARE CHARGES',
+ 'group_desc' => 'Day Care Charges',
+ ]);
+ }
+ }
+
+ // 4. Seed default Day Care Discharge Summary Template
+ if ($this->db->tableExists('ipd_discharge_templates')) {
+ $dcTemplateExists = $this->db->table('ipd_discharge_templates')
+ ->where('template_name', 'Day Care Discharge Summary')
+ ->countAllResults();
+
+ if ($dcTemplateExists === 0) {
+ $dcHtml = '{{DAYCARE_HEADER}}'
+ . ''
+ . ''
+ . 'Patient Name : {{PATIENT_NAME}} '
+ . 'UHID : {{UHID}} '
+ . 'Day Care No. : {{IPD_CODE}} '
+ . ' '
+ . ''
+ . 'Age / Gender : {{AGE_GENDER}} '
+ . 'Guardian : {{GUARDIAN_RELATION}}{{GUARDIAN_NAME}} '
+ . 'Phone : {{PATIENT_MOBILE}} '
+ . ' '
+ . ''
+ . 'Admission : {{ADMIT_DATE}} '
+ . 'Discharge : {{DISCHARGE_DATE}} '
+ . 'Attending Doctor : {{DOCTOR_NAMES}} '
+ . ' '
+ . '
'
+ . '{{PRESENTING_COMPLAINTS}}
'
+ . '{{FINAL_DIAGNOSIS}}
'
+ . '{{PROCEDURE}}
'
+ . '{{SURGERY}}
'
+ . '{{PADSS_SCORE}}
'
+ . '{{COURSE_IN_HOSPITAL}}
'
+ . '{{DISCHARGE_MEDICATIONS}}
'
+ . '{{RED_FLAGS_WARNINGS}}
'
+ . '{{DIETARY_ADVICE}}
'
+ . '{{REVIEW_AFTER}}
';
+
+ $this->db->table('ipd_discharge_templates')->insert([
+ 'template_name' => 'Day Care Discharge Summary',
+ 'page_size' => 'A4',
+ 'custom_width_mm' => 210,
+ 'custom_height_mm' => 297,
+ 'page_margin_top_cm' => 0.80,
+ 'page_margin_bottom_cm' => 0.80,
+ 'page_margin_left_cm' => 0.80,
+ 'page_margin_right_cm' => 0.80,
+ 'margin_header_cm' => 0.50,
+ 'margin_footer_cm' => 0.50,
+ 'template_html' => $dcHtml,
+ 'is_default' => 0,
+ 'status' => 1,
+ 'created_at' => date('Y-m-d H:i:s'),
+ ]);
+ }
+ }
+ }
+
+ public function down()
+ {
+ if ($this->db->tableExists('ipd_master')) {
+ $cols = [
+ 'discharge_score', 'sbar_handover', 'converted_by',
+ 'conversion_reason', 'converted_at', 'converted_from_code',
+ 'converted_from_type', 'daycare_procedure_name',
+ 'brought_by', 'police_constable_details', 'police_station',
+ 'mlc_number', 'is_mlc', 'triage_time', 'triage_level',
+ 'admission_type'
+ ];
+ foreach ($cols as $col) {
+ if ($this->db->fieldExists($col, 'ipd_master')) {
+ $this->forge->dropColumn('ipd_master', $col);
+ }
+ }
+ }
+ }
+}
diff --git a/app/Libraries/Abdm/ConsentSessionListService.php b/app/Libraries/Abdm/ConsentSessionListService.php
index f186b37..376e502 100644
--- a/app/Libraries/Abdm/ConsentSessionListService.php
+++ b/app/Libraries/Abdm/ConsentSessionListService.php
@@ -177,7 +177,7 @@ class ConsentSessionListService
$idCol = $this->resolveExistingColumn($fields, ['id']);
$uhidCol = $this->resolveExistingColumn($fields, ['p_code', 'uhid', 'uhid_no', 'patient_code', 'patient_id']);
$nameCol = $this->resolveExistingColumn($fields, ['p_fname', 'patient_name', 'name']);
- $lastNameCol = $this->resolveExistingColumn($fields, ['p_lname', 'last_name']);
+ $lastNameCol = $this->resolveExistingColumn($fields, ['last_name']);
$mobileCol = $this->resolveExistingColumn($fields, ['mphone1', 'mphone2', 'p_mobile', 'mobile', 'phone', 'contact_no', 'phone1']);
$abhaAddressCol = $this->resolveExistingColumn($fields, ['abha_address', 'abha_addr']);
diff --git a/app/Libraries/Abdm/Fhir/Generators/AbstractModuleFhirGenerator.php b/app/Libraries/Abdm/Fhir/Generators/AbstractModuleFhirGenerator.php
index ba7e140..93d2a37 100644
--- a/app/Libraries/Abdm/Fhir/Generators/AbstractModuleFhirGenerator.php
+++ b/app/Libraries/Abdm/Fhir/Generators/AbstractModuleFhirGenerator.php
@@ -286,9 +286,30 @@ abstract class AbstractModuleFhirGenerator
|| (isset($source['admission_date']))
|| (isset($enc['class_code']) && strtoupper((string) $enc['class_code']) === 'IMP');
- $defaultClass = $isIpd ? 'IMP' : 'AMB';
+ $admissionType = strtolower(trim((string) ($enc['admission_type'] ?? $source['admission_type'] ?? '')));
+ if ($admissionType === 'emergency') {
+ $defaultClass = 'EMER';
+ } elseif ($admissionType === 'daycare') {
+ $defaultClass = 'SS';
+ } else {
+ $defaultClass = $isIpd ? 'IMP' : 'AMB';
+ }
+
$classCode = strtoupper(trim((string) ($enc['class_code'] ?? $defaultClass)));
- $classDisplay = $classCode === 'IMP' ? 'inpatient encounter' : 'ambulatory';
+ switch ($classCode) {
+ case 'EMER':
+ $classDisplay = 'emergency';
+ break;
+ case 'SS':
+ $classDisplay = 'short stay';
+ break;
+ case 'IMP':
+ $classDisplay = 'inpatient encounter';
+ break;
+ default:
+ $classDisplay = 'ambulatory';
+ break;
+ }
$identifiers = [[
'system' => 'https://hms.local/encounter-id',
diff --git a/app/Libraries/Abdm/Sync/AbdmSyncWorkerService.php b/app/Libraries/Abdm/Sync/AbdmSyncWorkerService.php
index 1d287a3..64883d4 100644
--- a/app/Libraries/Abdm/Sync/AbdmSyncWorkerService.php
+++ b/app/Libraries/Abdm/Sync/AbdmSyncWorkerService.php
@@ -305,12 +305,7 @@ class AbdmSyncWorkerService
$name = trim((string) ($payload['patient_name'] ?? ''));
if ($name === '') {
- $first = trim((string) ($row['p_fname'] ?? ''));
- $last = trim((string) ($row['p_lname'] ?? ''));
- if (in_array(strtolower($last), ['0', '00', 'na', 'n/a', 'null', 'nil', '-'], true)) {
- $last = '';
- }
- $name = trim($first . ' ' . $last);
+ $name = trim((string) ($row['p_fname'] ?? ''));
}
$name = preg_replace('/\s0$/', '', $name) ?? $name;
if ($name === '') {
diff --git a/app/Models/IpdBillingModel.php b/app/Models/IpdBillingModel.php
index 03e685a..4c1a08b 100644
--- a/app/Models/IpdBillingModel.php
+++ b/app/Models/IpdBillingModel.php
@@ -14,6 +14,8 @@ class IpdBillingModel extends Model
{
return "CASE "
. "WHEN UPPER(" . $column . ") LIKE '%REGISTRATION%' THEN 10 "
+ . "WHEN UPPER(" . $column . ") LIKE '%EMERGENCY%' OR UPPER(" . $column . ") LIKE '%CASUALTY%' THEN 15 "
+ . "WHEN UPPER(" . $column . ") LIKE '%DAY CARE%' OR UPPER(" . $column . ") LIKE '%DAYCARE%' THEN 18 "
. "WHEN UPPER(" . $column . ") LIKE '%ACCOMMOD%' OR UPPER(" . $column . ") LIKE '%ACCOMOD%' OR UPPER(" . $column . ") LIKE '%BEDSIDE%' THEN 20 "
. "WHEN UPPER(" . $column . ") LIKE '%SURGERY%' OR UPPER(" . $column . ") LIKE '%OPERATION%' THEN 30 "
. "WHEN UPPER(" . $column . ") LIKE '%PROCEDURE%' THEN 40 "
@@ -35,6 +37,7 @@ class IpdBillingModel extends Model
{
$builder = $this->baseIpdListQuery();
$builder->select("i.id,i.ipd_code,p.p_code,p.p_fname,p.p_rname,")
+ ->select("i.admission_type,i.triage_level,i.is_mlc,i.converted_from_type,i.converted_from_code,i.daycare_procedure_name,")
->select("concat('Bed No :',b.bed_number,'[',w.ward_name,']') as Bed_Desc,")
->select("date_format(i.register_date,'%d-%m-%Y') as str_register_date,")
->select("if(i.ipd_status = 0,(to_days(sysdate()) - to_days(i.register_date)),(to_days(i.discharge_date) - to_days(i.register_date))) as no_days,")
@@ -90,6 +93,7 @@ class IpdBillingModel extends Model
$rows = $dataBuilder
->select(
"i.ipd_code,p.p_code,p.p_fname,
+ i.admission_type, i.triage_level, i.is_mlc, i.converted_from_type, i.converted_from_code,
if((o.id is not null),in_master.short_name,'Direct') as admit_type,
o.insurance_no_1 as insurance_no_1,
ipd_doc_list.doc_name as doc_name,
@@ -116,6 +120,23 @@ class IpdBillingModel extends Model
}
$row['action'] = $actionHtml;
+ $rawCode = (string) ($row['ipd_code'] ?? '');
+ $admType = (string) ($row['admission_type'] ?? 'ipd');
+ $codeBadge = esc($rawCode);
+ if ($admType === 'daycare') {
+ $codeBadge .= ' Day Care ';
+ } elseif ($admType === 'emergency') {
+ $triageLvl = (int) ($row['triage_level'] ?? 0);
+ $badgeColor = $triageLvl === 1 ? 'danger' : ($triageLvl === 2 ? 'warning' : ($triageLvl === 3 ? 'warning text-dark' : 'info text-dark'));
+ $codeBadge .= ' ER' . ($triageLvl > 0 ? ' ESI-' . $triageLvl : '') . ' ';
+ } elseif (! empty($row['converted_from_type'])) {
+ $codeBadge .= ' IPD (from ' . ucfirst(esc($row['converted_from_type'])) . ') ';
+ }
+ if (! empty($row['is_mlc'])) {
+ $codeBadge .= ' MLC ';
+ }
+ $row['ipd_code'] = $codeBadge;
+
foreach ($columns as $column) {
$item[] = $row[$column] ?? '';
}
diff --git a/app/Models/IpdModel.php b/app/Models/IpdModel.php
index 564cc86..cc86512 100644
--- a/app/Models/IpdModel.php
+++ b/app/Models/IpdModel.php
@@ -43,14 +43,25 @@ class IpdModel extends Model
$times = (int) ($countRow->xtimes ?? 0);
+ $admissionType = strtolower(trim((string) ($master['admission_type'] ?? 'ipd')));
+ if ($admissionType === 'daycare') {
+ $prefix = 'DC';
+ } elseif ($admissionType === 'emergency') {
+ $prefix = 'ER';
+ } else {
+ $prefix = 'A';
+ $admissionType = 'ipd';
+ }
+
$pid = str_pad(substr((string) $insertId, -7, 7), 7, '0', STR_PAD_LEFT);
- $pid = 'A' . date('ym') . $pid;
+ $pid = $prefix . date('ym') . $pid;
$this->db->table('ipd_master')
->where('id', $insertId)
->update([
- 'ipd_code' => $pid,
- 'ipd_times' => $times,
+ 'ipd_code' => $pid,
+ 'ipd_times' => $times,
+ 'admission_type' => $admissionType,
]);
$docList = $data['doc_list'] ?? [];
@@ -637,6 +648,103 @@ class IpdModel extends Model
return 1;
}
+ /**
+ * Converts a Day Care or Emergency admission to full Inpatient (IPD) status.
+ * Merges clinical notes, updates admission type, and transfers bed while keeping billing ledger unified.
+ *
+ * @param int $ipdId
+ * @param array $data
+ * @return array
+ */
+ public function convertToIpd(int $ipdId, array $data): array
+ {
+ $ipdId = (int) $ipdId;
+ if ($ipdId <= 0) {
+ return ['status' => false, 'message' => 'Invalid Admission ID'];
+ }
+
+ $ipd = $this->db->table('ipd_master')->where('id', $ipdId)->get()->getRowArray();
+ if (empty($ipd)) {
+ return ['status' => false, 'message' => 'Admission record not found'];
+ }
+
+ if ((int) ($ipd['ipd_status'] ?? 0) !== 0) {
+ return ['status' => false, 'message' => 'Cannot convert discharged admission'];
+ }
+
+ $currentType = (string) ($ipd['admission_type'] ?? 'ipd');
+ if ($currentType === 'ipd') {
+ return ['status' => false, 'message' => 'Patient is already admitted as Inpatient (IPD)'];
+ }
+
+ $targetBedId = (int) ($data['target_bed_id'] ?? 0);
+ $doctorId = (int) ($data['doctor_id'] ?? 0);
+ $reason = trim((string) ($data['conversion_reason'] ?? ''));
+ $sbar = trim((string) ($data['sbar_handover'] ?? ''));
+ $userId = (int) ($this->getUserIdentity()['id'] ?? 0);
+ $userSignature = $this->getUserSignature();
+
+ // 1. Update ipd_master to IPD
+ $updateMaster = [
+ 'admission_type' => 'ipd',
+ 'converted_from_type' => $currentType,
+ 'converted_from_code' => (string) ($ipd['ipd_code'] ?? ''),
+ 'converted_at' => date('Y-m-d H:i:s'),
+ 'conversion_reason' => $reason,
+ 'converted_by' => $userId > 0 ? $userId : null,
+ 'sbar_handover' => $sbar,
+ ];
+
+ if ($doctorId > 0) {
+ $updateMaster['r_doc_id'] = $doctorId;
+ }
+
+ $this->db->table('ipd_master')->where('id', $ipdId)->update($updateMaster);
+
+ // 2. Transfer Bed if target bed selected
+ if ($targetBedId > 0) {
+ $currBed = $this->db->table('bed_master')
+ ->select('id')
+ ->where('current_ipd_id', $ipdId)
+ ->get()
+ ->getRowArray();
+ $fromBedId = $currBed ? (int) $currBed['id'] : null;
+
+ $this->bedAssign([
+ 'ipd_id' => $ipdId,
+ 'bed_id' => $targetBedId,
+ 'assignment_type' => 'transfer',
+ 'Fdate' => date('Y-m-d H:i:s'),
+ 'TDate' => date('Y-m-d H:i:s'),
+ 'transfer_reason' => 'Escalation/Conversion from ' . ucfirst($currentType) . ' to IPD: ' . $reason,
+ 'transfer_from_bed_id' => $fromBedId,
+ 'remarks' => $sbar,
+ ]);
+ }
+
+ // 3. Update doctor list if doctorId provided
+ if ($doctorId > 0) {
+ $docExists = $this->db->table('ipd_master_doc_list')
+ ->where('ipd_id', $ipdId)
+ ->where('doc_id', $doctorId)
+ ->countAllResults();
+
+ if ($docExists === 0) {
+ $this->addIpdDoc([
+ 'ipd_id' => $ipdId,
+ 'doc_id' => $doctorId,
+ 'log' => 'Assigned on IPD Conversion by ' . $userSignature,
+ ]);
+ }
+ }
+
+ return [
+ 'status' => true,
+ 'message' => 'Successfully converted patient from ' . ucfirst($currentType) . ' to Inpatient (IPD)',
+ 'ipd_id' => $ipdId,
+ ];
+ }
+
private function getUserIdentity(): array
{
$userId = 0;
diff --git a/app/Views/Setting/Bed/ward_list.php b/app/Views/Setting/Bed/ward_list.php
index efbe6c2..738a3c7 100644
--- a/app/Views/Setting/Bed/ward_list.php
+++ b/app/Views/Setting/Bed/ward_list.php
@@ -62,6 +62,7 @@
ICCU
CCU
Emergency
+ Day Care
diff --git a/app/Views/billing/Patient_Profile_Opd_V.php b/app/Views/billing/Patient_Profile_Opd_V.php
index 1f200af..239a44e 100644
--- a/app/Views/billing/Patient_Profile_Opd_V.php
+++ b/app/Views/billing/Patient_Profile_Opd_V.php
@@ -70,7 +70,7 @@ if ($patientPhotoPath === '') {