feat: add Day Care & Emergency admission module with IPD conversion and clean patient names
- Add Day Care (<24h) and Emergency (Casualty) admission pathways with prefixes DC- and ER- - Add dedicated physical wards, ESI 5-level triage, MLC tracking, and PADSS day care discharge - Implement audited 'Convert to IPD' workflow with SBAR handover and automated bed transfer - Support unified IPD billing with dedicated group headers for Day Care and Emergency charges - Redesign Hospital Admission Desk with executive card layout and modern triage controls - Add comprehensive user documentation and SVG flowchart in public/help.html - Completely remove legacy patient_master.p_lname column from database and application code
This commit is contained in:
parent
ab3ab0c17f
commit
6e78e6010e
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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] ?? ''));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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] = [];
|
||||
|
|
|
|||
|
|
@ -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'] = '<div style="text-align:center; border-bottom:2px solid #087f75; margin-bottom:12px; padding-bottom:6px;"><h2 style="margin:0; color:#123765; font-size:18px;">' . ($isDayCare ? 'DAY CARE SURGERY & OBSERVATION DISCHARGE SUMMARY' : ($isEmergency ? 'EMERGENCY & CASUALTY DISCHARGE CARD' : 'INPATIENT DISCHARGE SUMMARY')) . '</h2><small style="color:#555;">' . ($isDayCare ? 'Short Stay Ambulatory Episode (< 24 Hours) • NABH Compliant Discharge Document' : ($isEmergency ? 'Emergency Triage & Observation • Statutory Medical Record' : 'Clinical Summary • Hospital Record')) . '</small></div>';
|
||||
|
||||
$tokens['PADSS_SCORE'] = '<div class="discharge-section" style="margin-top:10px; margin-bottom:10px;"><h4 style="color:#123765; border-bottom:1px solid #ccc; padding-bottom:3px; margin-bottom:6px; font-size:14px;">Post-Anaesthesia Discharge Readiness Assessment (PADSS)</h4><table border="1" cellpadding="5" style="width:100%; border-collapse:collapse; font-size:12px;"><tr><td><b>Vital Signs</b>: Stable / Within 20% baseline</td><td><b>Ambulation</b>: Steady Gait / No Dizziness</td><td><b>Nausea / Emesis</b>: Minimal / Tolerating Orals</td></tr><tr><td><b>Pain Score</b>: Controlled (≤ 3/10)</td><td><b>Surgical Bleeding</b>: Dry / Minimal Dressing Soakage</td><td><strong style="color:#16a34a;">Total Score: ' . $dcScore . '/10 (Discharge Clearance Met ≥ 9)</strong></td></tr></table></div>';
|
||||
|
||||
$tokens['RED_FLAGS_WARNINGS'] = '<div class="alert alert-warning" style="background:#fff3cd; border:1px solid #ffeeba; color:#856404; padding:8px 12px; margin-top:10px; border-radius:4px; font-size:12px;"><strong><i class="fa fa-warning"></i> EMERGENCY RETURN PRECAUTIONS (RED FLAGS):</strong><br/>Seek immediate medical attention or return to the Emergency / Casualty department if you develop: <em>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.</em></div>';
|
||||
|
||||
// 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'] ?? ''));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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'] ?? ''));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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] ?? ''));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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])) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,320 @@
|
|||
<?php
|
||||
|
||||
namespace App\Database\Migrations;
|
||||
|
||||
use CodeIgniter\Database\Migration;
|
||||
|
||||
class CreateDaycareEmergencyModule extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
// 1. Extend ipd_master table with Day Care, Emergency, Triage, MLC, and IPD Escalation fields
|
||||
if ($this->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}}'
|
||||
. '<table class="discharge-info-table" border="1" cellpadding="6" style="width:100%; border-collapse:collapse; margin-bottom:12px;">'
|
||||
. '<tr>'
|
||||
. '<td><b>Patient Name</b>: {{PATIENT_NAME}}</td>'
|
||||
. '<td><b>UHID</b>: {{UHID}}</td>'
|
||||
. '<td><b>Day Care No.</b>: {{IPD_CODE}}</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
. '<td><b>Age / Gender</b>: {{AGE_GENDER}}</td>'
|
||||
. '<td><b>Guardian</b>: {{GUARDIAN_RELATION}}{{GUARDIAN_NAME}}</td>'
|
||||
. '<td><b>Phone</b>: {{PATIENT_MOBILE}}</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
. '<td><b>Admission</b>: {{ADMIT_DATE}}</td>'
|
||||
. '<td><b>Discharge</b>: {{DISCHARGE_DATE}}</td>'
|
||||
. '<td><b>Attending Doctor</b>: {{DOCTOR_NAMES}}</td>'
|
||||
. '</tr>'
|
||||
. '</table>'
|
||||
. '<div>{{PRESENTING_COMPLAINTS}}</div>'
|
||||
. '<div>{{FINAL_DIAGNOSIS}}</div>'
|
||||
. '<div>{{PROCEDURE}}</div>'
|
||||
. '<div>{{SURGERY}}</div>'
|
||||
. '<div>{{PADSS_SCORE}}</div>'
|
||||
. '<div>{{COURSE_IN_HOSPITAL}}</div>'
|
||||
. '<div>{{DISCHARGE_MEDICATIONS}}</div>'
|
||||
. '<div>{{RED_FLAGS_WARNINGS}}</div>'
|
||||
. '<div>{{DIETARY_ADVICE}}</div>'
|
||||
. '<div>{{REVIEW_AFTER}}</div>';
|
||||
|
||||
$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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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']);
|
||||
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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 === '') {
|
||||
|
|
|
|||
|
|
@ -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 .= ' <span class="badge bg-warning text-dark">Day Care</span>';
|
||||
} 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 .= ' <span class="badge bg-' . $badgeColor . '">ER' . ($triageLvl > 0 ? ' ESI-' . $triageLvl : '') . '</span>';
|
||||
} elseif (! empty($row['converted_from_type'])) {
|
||||
$codeBadge .= ' <span class="badge bg-secondary" title="Converted from ' . esc($row['converted_from_type']) . ' (' . esc($row['converted_from_code'] ?? '') . ')"><i class="bi bi-arrow-repeat"></i> IPD (from ' . ucfirst(esc($row['converted_from_type'])) . ')</span>';
|
||||
}
|
||||
if (! empty($row['is_mlc'])) {
|
||||
$codeBadge .= ' <span class="badge bg-danger">MLC</span>';
|
||||
}
|
||||
$row['ipd_code'] = $codeBadge;
|
||||
|
||||
foreach ($columns as $column) {
|
||||
$item[] = $row[$column] ?? '';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@
|
|||
<option value="ICCU">ICCU</option>
|
||||
<option value="CCU">CCU</option>
|
||||
<option value="Emergency">Emergency</option>
|
||||
<option value="Day Care">Day Care</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ if ($patientPhotoPath === '') {
|
|||
<div class="card-header d-flex justify-content-between align-items-center flex-wrap gap-2">
|
||||
<h3 class="card-title mb-0">OPD History</h3>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<button type="button" class="btn btn-outline-primary btn-sm fw-semibold" onclick="openAbdmHipLinkModal(<?= (int)$patient->id ?>, '<?= esc($patientAbhaAddress ?? '') ?>', { name: '<?= esc($patient->p_fname . ' ' . ($patient->p_lname ?? '')) ?>', gender: '<?= $patient->gender == 1 ? 'M' : ($patient->gender == 2 ? 'F' : 'O') ?>', yob: '<?= !empty($patient->dob) ? date('Y', strtotime($patient->dob)) : '' ?>', phone: '<?= esc($patient->mphone1 ?? '') ?>', abha_number: '<?= esc($patient->abha_id ?? '') ?>' })">
|
||||
<button type="button" class="btn btn-outline-primary btn-sm fw-semibold" onclick="openAbdmHipLinkModal(<?= (int)$patient->id ?>, '<?= esc($patientAbhaAddress ?? '') ?>', { name: '<?= esc($patient->p_fname ?? '') ?>', gender: '<?= $patient->gender == 1 ? 'M' : ($patient->gender == 2 ? 'F' : 'O') ?>', yob: '<?= !empty($patient->dob) ? date('Y', strtotime($patient->dob)) : '' ?>', phone: '<?= esc($patient->mphone1 ?? '') ?>', abha_number: '<?= esc($patient->abha_id ?? '') ?>' })">
|
||||
<i class="bi bi-link-45deg me-1"></i>Link to ABHA (HIP)
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm"
|
||||
|
|
|
|||
|
|
@ -62,7 +62,11 @@
|
|||
<?php if ($canChargeEdit) : ?>
|
||||
<button type="button" class="btn btn-danger btn-sm" id="btn_lab">Add Charge</button>
|
||||
<?php endif; ?>
|
||||
<button type="button" class="btn btn-warning btn-sm" id="btn_ipd">IPD</button>
|
||||
<div class="btn-group w-100" role="group">
|
||||
<button type="button" class="btn btn-danger btn-sm" id="btn_emergency" title="Emergency / Casualty Admission"><i class="bi bi-ambulance me-1"></i>Casualty</button>
|
||||
<button type="button" class="btn btn-warning btn-sm text-dark" id="btn_daycare" title="Day Care Admission (< 24 Hrs)"><i class="bi bi-clock-history me-1"></i>Day Care</button>
|
||||
<button type="button" class="btn btn-primary btn-sm" id="btn_ipd" title="Regular Inpatient Admission"><i class="bi bi-hospital me-1"></i>IPD</button>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="button" class="btn btn-outline-primary btn-sm flex-fill" id="btn_patient_profile_scan" title="Scan document using camera/scanner">
|
||||
<i class="bi bi-camera me-1"></i>Scan
|
||||
|
|
@ -71,7 +75,7 @@
|
|||
<i class="bi bi-upload me-1"></i>Upload
|
||||
</button>
|
||||
</div>
|
||||
<button type="button" class="btn btn-outline-primary btn-sm w-100 mt-2 fw-semibold" onclick="openAbdmHipLinkModal(<?= (int)$data[0]->id ?>, '<?= esc($abhaAddress) ?>', { name: '<?= esc($data[0]->p_fname . ' ' . ($data[0]->p_lname ?? '')) ?>', gender: '<?= $data[0]->gender == 1 ? 'M' : ($data[0]->gender == 2 ? 'F' : 'O') ?>', yob: '<?= !empty($data[0]->dob) ? date('Y', strtotime($data[0]->dob)) : '' ?>', phone: '<?= esc($data[0]->mphone1 ?? '') ?>', abha_number: '<?= esc($patientAbhaId) ?>' })">
|
||||
<button type="button" class="btn btn-outline-primary btn-sm w-100 mt-2 fw-semibold" onclick="openAbdmHipLinkModal(<?= (int)$data[0]->id ?>, '<?= esc($abhaAddress) ?>', { name: '<?= esc($data[0]->p_fname ?? '') ?>', gender: '<?= $data[0]->gender == 1 ? 'M' : ($data[0]->gender == 2 ? 'F' : 'O') ?>', yob: '<?= !empty($data[0]->dob) ? date('Y', strtotime($data[0]->dob)) : '' ?>', phone: '<?= esc($data[0]->mphone1 ?? '') ?>', abha_number: '<?= esc($patientAbhaId) ?>' })">
|
||||
<i class="bi bi-link-45deg me-1"></i>Link Records to ABHA (HIP)
|
||||
</button>
|
||||
<?php if (empty($abhaAddress) && !empty($data[0]->mphone1)) : ?>
|
||||
|
|
@ -514,7 +518,7 @@ $(document).ready(function() {
|
|||
candidates.unshift({
|
||||
id: currentPatientId,
|
||||
p_code: <?= json_encode((string) ($data[0]->p_code ?? '')) ?>,
|
||||
name: <?= json_encode(trim((string) ($data[0]->p_fname ?? '') . ' ' . (string) ($data[0]->p_lname ?? ''))) ?>,
|
||||
name: <?= json_encode(trim((string) ($data[0]->p_fname ?? ''))) ?>,
|
||||
gender: <?= (int) ($data[0]->gender ?? 0) ?>,
|
||||
gender_label: <?= json_encode((string) ($data[0]->xgender ?? '')) ?>,
|
||||
dob: <?= json_encode((string) ($data[0]->dob ?? '')) ?>,
|
||||
|
|
@ -600,10 +604,22 @@ $(document).ready(function() {
|
|||
load_form('<?= base_url('Opdcase/addopd') ?>/' + p_id);
|
||||
});
|
||||
|
||||
$('#btn_emergency').off('click.personProfile').on('click.personProfile', function() {
|
||||
var p_id = getPatientIdOrWarn();
|
||||
if (p_id === null) return;
|
||||
load_form('<?= base_url('IpdNew/addipd') ?>/' + p_id + '?type=emergency');
|
||||
});
|
||||
|
||||
$('#btn_daycare').off('click.personProfile').on('click.personProfile', function() {
|
||||
var p_id = getPatientIdOrWarn();
|
||||
if (p_id === null) return;
|
||||
load_form('<?= base_url('IpdNew/addipd') ?>/' + p_id + '?type=daycare');
|
||||
});
|
||||
|
||||
$('#btn_ipd').off('click.personProfile').on('click.personProfile', function() {
|
||||
var p_id = getPatientIdOrWarn();
|
||||
if (p_id === null) return;
|
||||
load_form('<?= base_url('IpdNew/addipd') ?>/' + p_id);
|
||||
load_form('<?= base_url('IpdNew/addipd') ?>/' + p_id + '?type=ipd');
|
||||
});
|
||||
|
||||
$('#btn_lab').off('click.personProfile').on('click.personProfile', function() {
|
||||
|
|
|
|||
272
app/Views/billing/ipd/admit_patient.php
Normal file
272
app/Views/billing/ipd/admit_patient.php
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
<?php
|
||||
$defaultType = $default_type ?? 'emergency';
|
||||
?>
|
||||
<div class="container-fluid px-0">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card shadow-sm border-0 mb-3" style="border-radius:12px;">
|
||||
<div class="card-header bg-white py-3 d-flex align-items-center justify-content-between flex-wrap gap-2" style="border-bottom:1px solid #e2e8f0;">
|
||||
<div>
|
||||
<h4 class="mb-0 fw-bold" style="color:#0f172a;">
|
||||
<i class="bi bi-person-plus-fill text-primary me-2"></i>Patient Admission Desk
|
||||
</h4>
|
||||
<small class="text-muted">Register patient admission for <strong>Emergency / Casualty</strong>, <strong>Day Care (< 24h)</strong>, or <strong>Inpatient (IPD)</strong></small>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="javascript:load_form_div('<?= base_url('billing/ipd/current-admission') ?>','maindiv','Current Admission');">
|
||||
<i class="bi bi-arrow-left me-1"></i> Current Admissions
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-success btn-sm" onclick="javascript:load_form('<?= base_url('billing/patient') ?>','Patient Registration');">
|
||||
<i class="bi bi-person-plus me-1"></i> New Patient Registration
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body p-4">
|
||||
<!-- Admission Category Switcher -->
|
||||
<div class="mb-4">
|
||||
<label class="form-label fw-bold text-dark small text-uppercase" style="letter-spacing:0.5px;">1. Select Primary Admission Type:</label>
|
||||
<div class="d-flex flex-wrap gap-3" id="admitTypePillGroup">
|
||||
<div class="form-check form-check-inline p-0 m-0">
|
||||
<input class="btn-check" type="radio" name="admit_target_type" id="type_emergency" value="emergency" <?= ($defaultType === 'emergency') ? 'checked' : '' ?>>
|
||||
<label class="btn btn-outline-danger px-3 py-2 fw-semibold" for="type_emergency">
|
||||
<i class="bi bi-ambulance me-2"></i>Emergency / Casualty
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline p-0 m-0">
|
||||
<input class="btn-check" type="radio" name="admit_target_type" id="type_daycare" value="daycare" <?= ($defaultType === 'daycare') ? 'checked' : '' ?>>
|
||||
<label class="btn btn-outline-warning text-dark px-3 py-2 fw-semibold" for="type_daycare">
|
||||
<i class="bi bi-clock-history me-2"></i>Day Care Unit (< 24 Hrs)
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline p-0 m-0">
|
||||
<input class="btn-check" type="radio" name="admit_target_type" id="type_ipd" value="ipd" <?= ($defaultType === 'ipd') ? 'checked' : '' ?>>
|
||||
<label class="btn btn-outline-primary px-3 py-2 fw-semibold" for="type_ipd">
|
||||
<i class="bi bi-hospital me-2"></i>Regular Inpatient (IPD)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Search Input Box -->
|
||||
<div class="mb-4">
|
||||
<label class="form-label fw-bold text-dark small text-uppercase" style="letter-spacing:0.5px;">2. Search Registered Patient:</label>
|
||||
<div class="input-group input-group-lg shadow-sm" style="border-radius:10px; overflow:hidden;">
|
||||
<span class="input-group-text bg-white border-end-0 text-muted"><i class="bi bi-search fs-5"></i></span>
|
||||
<input type="text" id="patientAdmitSearchInput" class="form-control border-start-0" placeholder="Type patient name, UHID / Patient Code (e.g. 24090001), or mobile number..." autofocus autocomplete="off">
|
||||
<button class="btn btn-primary px-4 fw-semibold" type="button" id="btnSearchAdmit">Search</button>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center mt-2 px-1">
|
||||
<span class="text-muted small"><i class="bi bi-info-circle me-1"></i>Start typing at least 2 characters to auto-search</span>
|
||||
<a href="javascript:load_form('<?= base_url('billing/patient') ?>','Patient Registration');" class="small text-decoration-none fw-semibold">
|
||||
+ Patient not registered yet? Register New Patient →
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Search Loading Indicator -->
|
||||
<div id="admitSearchLoading" class="text-center py-4" style="display:none;">
|
||||
<div class="spinner-border text-primary" role="status"></div>
|
||||
<p class="mt-2 text-muted small">Searching patient database...</p>
|
||||
</div>
|
||||
|
||||
<!-- Search Results Area -->
|
||||
<div id="admitSearchResultsWrapper">
|
||||
<div class="text-center py-5 text-muted border rounded-3 bg-light" id="admitEmptyNotice">
|
||||
<i class="bi bi-person-badge fs-1 text-secondary opacity-50 d-block mb-2"></i>
|
||||
<h6 class="fw-semibold text-secondary">Search for a patient to begin admission</h6>
|
||||
<p class="small mb-0">Enter a name, patient code, or phone number in the search bar above.</p>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive d-none" id="admitResultsTableWrapper">
|
||||
<table class="table table-hover align-middle border">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th style="width:130px;">UHID / Code</th>
|
||||
<th>Patient Name</th>
|
||||
<th>Age / Gender</th>
|
||||
<th>Mobile</th>
|
||||
<th>Current Status</th>
|
||||
<th class="text-end" style="min-width:240px;">Direct Admission Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="admitResultsTbody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
var searchTimer = null;
|
||||
var $input = $('#patientAdmitSearchInput');
|
||||
var $loading = $('#admitSearchLoading');
|
||||
var $emptyNotice = $('#admitEmptyNotice');
|
||||
var $tableWrapper = $('#admitResultsTableWrapper');
|
||||
var $tbody = $('#admitResultsTbody');
|
||||
|
||||
function getSelectedType() {
|
||||
return $('input[name="admit_target_type"]:checked').val() || 'emergency';
|
||||
}
|
||||
|
||||
function executePatientSearch() {
|
||||
var q = $.trim($input.val());
|
||||
if (q.length < 2) {
|
||||
$tableWrapper.addClass('d-none');
|
||||
$emptyNotice.removeClass('d-none').html(
|
||||
'<i class="bi bi-person-badge fs-1 text-secondary opacity-50 d-block mb-2"></i>' +
|
||||
'<h6 class="fw-semibold text-secondary">Search for a patient to begin admission</h6>' +
|
||||
'<p class="small mb-0">Enter at least 2 characters to find patient records.</p>'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$loading.show();
|
||||
$tableWrapper.addClass('d-none');
|
||||
$emptyNotice.addClass('d-none');
|
||||
|
||||
$.ajax({
|
||||
url: '<?= base_url('billing/ipd/search-patient-admit') ?>',
|
||||
method: 'GET',
|
||||
data: { q: q },
|
||||
dataType: 'json'
|
||||
}).done(function(data) {
|
||||
$loading.hide();
|
||||
if (!data || data.length === 0) {
|
||||
$tableWrapper.addClass('d-none');
|
||||
$emptyNotice.removeClass('d-none').html(
|
||||
'<i class="bi bi-exclamation-circle fs-1 text-warning d-block mb-2"></i>' +
|
||||
'<h6 class="fw-semibold text-dark">No patient found matching "' + $('<div>').text(q).html() + '"</h6>' +
|
||||
'<p class="small text-muted mb-3">Check the spelling or register this patient first.</p>' +
|
||||
'<button type="button" class="btn btn-sm btn-success fw-semibold" onclick="load_form(\'<?= base_url('billing/patient') ?>\',\'Patient Registration\');">' +
|
||||
'<i class="bi bi-person-plus me-1"></i> Register New Patient Now</button>'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
renderPatientsTable(data);
|
||||
}).fail(function() {
|
||||
$loading.hide();
|
||||
$emptyNotice.removeClass('d-none').html(
|
||||
'<i class="bi bi-x-circle fs-1 text-danger d-block mb-2"></i>' +
|
||||
'<h6 class="text-danger fw-semibold">Error communicating with server</h6>' +
|
||||
'<p class="small text-muted mb-0">Please try again or contact system support.</p>'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function renderPatientsTable(patients) {
|
||||
$tbody.empty();
|
||||
var curType = getSelectedType();
|
||||
|
||||
$.each(patients, function(idx, pt) {
|
||||
var activeIpd = pt.active_ipd;
|
||||
var isAdmitted = activeIpd && activeIpd.id;
|
||||
var tr = $('<tr>');
|
||||
|
||||
// UHID / Code
|
||||
var uhidHtml = '<span class="badge bg-light text-dark border font-monospace fs-6">' + (pt.p_code || ('#' + pt.id)) + '</span>';
|
||||
tr.append($('<td>').html(uhidHtml));
|
||||
|
||||
// Name
|
||||
var nameHtml = '<div class="fw-bold text-dark">' + $('<div>').text(pt.patient_name || 'Unnamed').html() + '</div>';
|
||||
tr.append($('<td>').html(nameHtml));
|
||||
|
||||
// Age / Gender
|
||||
var ageGender = (pt.age ? pt.age + ' Y' : '-') + ' / ' + (pt.gender_text || '-');
|
||||
tr.append($('<td>').text(ageGender));
|
||||
|
||||
// Phone
|
||||
var phone = pt.mphone1 || pt.mphone2 || '-';
|
||||
tr.append($('<td>').text(phone));
|
||||
|
||||
// Status
|
||||
if (isAdmitted) {
|
||||
var admTypeBadge = 'badge bg-primary';
|
||||
var admLabel = 'Inpatient';
|
||||
if (activeIpd.admission_type === 'emergency') {
|
||||
admTypeBadge = 'badge bg-danger';
|
||||
admLabel = 'Casualty';
|
||||
} else if (activeIpd.admission_type === 'daycare') {
|
||||
admTypeBadge = 'badge bg-warning text-dark';
|
||||
admLabel = 'Day Care';
|
||||
}
|
||||
var statusHtml = '<span class="' + admTypeBadge + ' me-1">' + admLabel + '</span>' +
|
||||
'<a href="javascript:load_form(\'<?= base_url('billing/ipd/panel') ?>/' + activeIpd.id + '\',\'IPD Panel\');" class="small fw-semibold text-decoration-none">[' + (activeIpd.ipd_code || ('#' + activeIpd.id)) + '] Panel →</a>';
|
||||
tr.append($('<td>').html(statusHtml));
|
||||
} else {
|
||||
tr.append($('<td>').html('<span class="badge bg-success-subtle text-success border border-success-subtle">Not Admitted</span>'));
|
||||
}
|
||||
|
||||
// Direct Admission Buttons
|
||||
var actionTd = $('<td class="text-end">');
|
||||
if (isAdmitted) {
|
||||
actionTd.html(
|
||||
'<button type="button" class="btn btn-outline-secondary btn-sm" onclick="load_form(\'<?= base_url('billing/ipd/panel') ?>/' + activeIpd.id + '\',\'IPD Panel\');">' +
|
||||
'<i class="bi bi-box-arrow-up-right me-1"></i> Open Active Panel</button>'
|
||||
);
|
||||
} else {
|
||||
var btnGroup = $('<div class="btn-group btn-group-sm">');
|
||||
|
||||
// Emergency Button
|
||||
var erBtn = $('<button type="button" class="btn btn-danger" title="Admit directly to Emergency / Casualty">')
|
||||
.html('<i class="bi bi-ambulance me-1"></i>Casualty')
|
||||
.on('click', function() {
|
||||
load_form('<?= base_url('IpdNew/addipd') ?>/' + pt.id + '?type=emergency', 'Emergency Admission');
|
||||
});
|
||||
|
||||
// Day Care Button
|
||||
var dcBtn = $('<button type="button" class="btn btn-warning text-dark" title="Admit directly to Day Care (< 24h)">')
|
||||
.html('<i class="bi bi-clock-history me-1"></i>Day Care')
|
||||
.on('click', function() {
|
||||
load_form('<?= base_url('IpdNew/addipd') ?>/' + pt.id + '?type=daycare', 'Day Care Admission');
|
||||
});
|
||||
|
||||
// Regular IPD Button
|
||||
var ipdBtn = $('<button type="button" class="btn btn-primary" title="Admit directly to Inpatient IPD">')
|
||||
.html('<i class="bi bi-hospital me-1"></i>IPD')
|
||||
.on('click', function() {
|
||||
load_form('<?= base_url('IpdNew/addipd') ?>/' + pt.id + '?type=ipd', 'IPD Admission');
|
||||
});
|
||||
|
||||
btnGroup.append(erBtn).append(dcBtn).append(ipdBtn);
|
||||
actionTd.append(btnGroup);
|
||||
}
|
||||
|
||||
tr.append(actionTd);
|
||||
$tbody.append(tr);
|
||||
});
|
||||
|
||||
$tableWrapper.removeClass('d-none');
|
||||
$emptyNotice.addClass('d-none');
|
||||
}
|
||||
|
||||
$input.on('keyup input', function(e) {
|
||||
if (e.key === 'Enter') {
|
||||
clearTimeout(searchTimer);
|
||||
executePatientSearch();
|
||||
return;
|
||||
}
|
||||
clearTimeout(searchTimer);
|
||||
searchTimer = setTimeout(executePatientSearch, 300);
|
||||
});
|
||||
|
||||
$('#btnSearchAdmit').on('click', executePatientSearch);
|
||||
|
||||
$('input[name="admit_target_type"]').on('change', function() {
|
||||
var curQ = $.trim($input.val());
|
||||
if (curQ.length >= 2) {
|
||||
executePatientSearch();
|
||||
}
|
||||
});
|
||||
|
||||
// Auto-focus search input
|
||||
setTimeout(function() {
|
||||
$input.trigger('focus');
|
||||
}, 150);
|
||||
});
|
||||
</script>
|
||||
|
|
@ -1,10 +1,41 @@
|
|||
|
||||
|
||||
<?php
|
||||
$allCount = count($records ?? []);
|
||||
$ipdCount = 0;
|
||||
$dcCount = 0;
|
||||
$erCount = 0;
|
||||
foreach ($records ?? [] as $r) {
|
||||
$t = $r->admission_type ?? 'ipd';
|
||||
if ($t === 'daycare') $dcCount++;
|
||||
elseif ($t === 'emergency') $erCount++;
|
||||
else $ipdCount++;
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="col-md-12">
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header d-flex align-items-center justify-content-between flex-wrap gap-2">
|
||||
<h3 class="card-title mb-0">Current Admission</h3>
|
||||
<h3 class="card-title mb-0">Current Admissions</h3>
|
||||
<div class="d-flex align-items-center gap-2 flex-wrap">
|
||||
<div class="btn-group btn-group-sm" role="group" id="admTypeFilterGroup">
|
||||
<button type="button" class="btn btn-outline-primary active" data-filter="all">All (<?= $allCount ?>)</button>
|
||||
<button type="button" class="btn btn-outline-primary" data-filter="ipd">Inpatient IPD (<?= $ipdCount ?>)</button>
|
||||
<button type="button" class="btn btn-outline-warning text-dark" data-filter="daycare"><i class="bi bi-clock-history"></i> Day Care (<?= $dcCount ?>)</button>
|
||||
<button type="button" class="btn btn-outline-danger" data-filter="emergency"><i class="bi bi-ambulance"></i> Emergency (<?= $erCount ?>)</button>
|
||||
</div>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-sm btn-success dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-plus-circle me-1"></i> New Admission
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end shadow-sm">
|
||||
<li><a class="dropdown-item text-danger fw-semibold" href="javascript:load_form_div('<?= base_url('billing/ipd/admit?type=emergency') ?>','maindiv','Emergency Admission')"><i class="bi bi-ambulance me-2"></i>Emergency (Casualty)</a></li>
|
||||
<li><a class="dropdown-item text-warning fw-semibold" href="javascript:load_form_div('<?= base_url('billing/ipd/admit?type=daycare') ?>','maindiv','Day Care Admission')"><i class="bi bi-clock-history me-2"></i>Day Care (< 24 Hrs)</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item text-primary fw-semibold" href="javascript:load_form_div('<?= base_url('billing/ipd/admit?type=ipd') ?>','maindiv','Inpatient Admission')"><i class="bi bi-hospital me-2"></i>Regular Inpatient (IPD)</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
|
|
@ -15,22 +46,47 @@
|
|||
<table id="ipd-current-table" class="table table-striped table-hover align-middle TableData">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>IPD Code</th>
|
||||
<th>Admission Code</th>
|
||||
<th>Name/Patient Code</th>
|
||||
<th>Bed No. [Type]</th>
|
||||
<th>Bed No. [Ward]</th>
|
||||
<th>Register Date</th>
|
||||
<th>No. of Days</th>
|
||||
<th>Dr. Name</th>
|
||||
<th>Doctor Name</th>
|
||||
<th>Admit Type</th>
|
||||
<th>Amount</th>
|
||||
<th></th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach (($records ?? []) as $row) : ?>
|
||||
<tr style="color:<?= esc($row->color ?? '') ?>;">
|
||||
<td><?= esc($row->ipd_code ?? '') ?></td>
|
||||
<td>[<?= esc($row->p_code ?? '') ?>] <?= esc($row->p_fname ?? '') ?><br /><?= esc($row->p_rname ?? '') ?></td>
|
||||
<?php foreach (($records ?? []) as $row) :
|
||||
$admType = $row->admission_type ?? 'ipd';
|
||||
?>
|
||||
<tr style="color:<?= esc($row->color ?? '') ?>;" data-adm-type="<?= esc($admType) ?>">
|
||||
<td>
|
||||
<strong><?= esc($row->ipd_code ?? '') ?></strong>
|
||||
<?php if ($admType === 'daycare'): ?>
|
||||
<br><span class="badge bg-warning text-dark" style="font-size:11px;"><i class="bi bi-clock-history"></i> Day Care</span>
|
||||
<?php elseif ($admType === 'emergency'): ?>
|
||||
<?php
|
||||
$tLvl = (int) ($row->triage_level ?? 3);
|
||||
$tColor = $tLvl === 1 ? 'danger' : ($tLvl === 2 ? 'warning text-dark' : ($tLvl === 3 ? 'warning text-dark' : 'info text-dark'));
|
||||
?>
|
||||
<br><span class="badge bg-<?= $tColor ?>" style="font-size:11px;"><i class="bi bi-ambulance"></i> ER ESI-<?= $tLvl ?></span>
|
||||
<?php endif; ?>
|
||||
<?php if (! empty($row->converted_from_type)): ?>
|
||||
<br><span class="badge bg-info text-dark" style="font-size:10px;"><i class="bi bi-arrow-repeat"></i> from <?= ucfirst(esc($row->converted_from_type)) ?></span>
|
||||
<?php endif; ?>
|
||||
<?php if (! empty($row->is_mlc)): ?>
|
||||
<br><span class="badge bg-danger" style="font-size:10px;">MLC</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<strong>[<?= esc($row->p_code ?? '') ?>]</strong> <?= esc($row->p_fname ?? '') ?><br />
|
||||
<small class="text-muted"><?= esc($row->p_rname ?? '') ?></small>
|
||||
<?php if (! empty($row->daycare_procedure_name)): ?>
|
||||
<br><small class="text-primary font-monospace"><?= esc($row->daycare_procedure_name) ?></small>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?= esc($row->Bed_Desc ?? '') ?></td>
|
||||
<td><?= esc($row->str_register_date ?? '') ?></td>
|
||||
<td><?= esc($row->no_days ?? '') ?></td>
|
||||
|
|
@ -39,9 +95,9 @@
|
|||
<td>C:<?= esc($row->charge_amount ?? '') ?>/M:<?= esc($row->med_amount ?? '') ?>/P:<?= esc($row->paid_amount ?? '') ?>/B:<?= esc($row->balance ?? '') ?></td>
|
||||
<td>
|
||||
<?php if (! empty($row->id)) : ?>
|
||||
<button type="button" class="btn btn-primary btn-sm" onclick="load_form_div('<?= base_url('billing/ipd/panel') ?>/<?= (int) ($row->id ?? 0) ?>','maindiv','IPD Panel');">View Invoice</button>
|
||||
<button type="button" class="btn btn-primary btn-sm" onclick="load_form_div('<?= base_url('billing/ipd/panel') ?>/<?= (int) ($row->id ?? 0) ?>','maindiv','IPD Panel');">View Panel</button>
|
||||
<?php else : ?>
|
||||
<button type="button" class="btn btn-secondary btn-sm" disabled>View Invoice</button>
|
||||
<button type="button" class="btn btn-secondary btn-sm" disabled>View Panel</button>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
@ -49,15 +105,15 @@
|
|||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th>IPD Code</th>
|
||||
<th>Admission Code</th>
|
||||
<th>Name/Patient Code</th>
|
||||
<th>Bed No. [Type]</th>
|
||||
<th>Bed No. [Ward]</th>
|
||||
<th>Register Date</th>
|
||||
<th>No. of Days</th>
|
||||
<th>Dr. Name</th>
|
||||
<th>Doctor Name</th>
|
||||
<th>Admit Type</th>
|
||||
<th>Amount</th>
|
||||
<th></th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
|
@ -73,6 +129,26 @@
|
|||
return;
|
||||
}
|
||||
|
||||
$('#ipd-current-table').DataTable();
|
||||
var table = $('#ipd-current-table').DataTable({
|
||||
pageLength: 25,
|
||||
order: [[0, 'desc']]
|
||||
});
|
||||
|
||||
$('#admTypeFilterGroup button').on('click', function() {
|
||||
$('#admTypeFilterGroup button').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
var filter = $(this).data('filter');
|
||||
|
||||
if (filter === 'all') {
|
||||
table.search('').columns().search('').draw();
|
||||
} else if (filter === 'daycare') {
|
||||
table.search('Day Care').draw();
|
||||
} else if (filter === 'emergency') {
|
||||
table.search('ER').draw();
|
||||
} else if (filter === 'ipd') {
|
||||
table.search('A2').draw();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,15 @@
|
|||
<div class="d-flex flex-wrap align-items-center justify-content-between gap-2">
|
||||
<h1 class="mb-0">IPD Billing</h1>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<button class="btn btn-sm btn-danger" onclick="javascript:load_form_div('<?= base_url('billing/ipd/admit?type=emergency') ?>','maindiv','Emergency Admission');">
|
||||
<i class="bi bi-ambulance"></i> Emergency Admission
|
||||
</button>
|
||||
<button class="btn btn-sm btn-warning text-dark" onclick="javascript:load_form_div('<?= base_url('billing/ipd/admit?type=daycare') ?>','maindiv','Day Care Admission');">
|
||||
<i class="bi bi-clock-history"></i> Day Care Admission
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-primary" onclick="javascript:load_form_div('<?= base_url('billing/ipd/admit?type=ipd') ?>','maindiv','Inpatient Admission');">
|
||||
<i class="bi bi-person-plus"></i> Inpatient Admission
|
||||
</button>
|
||||
<button class="btn btn-sm btn-primary" onclick="javascript:load_form_div('<?= base_url('billing/ipd/current-admission') ?>','maindiv','Current Admission');">
|
||||
<i class="bi bi-person-check"></i> Current Admission
|
||||
</button>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -75,8 +75,40 @@ if ($person) {
|
|||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="d-flex flex-wrap align-items-center justify-content-between gap-2">
|
||||
<h5 class="mb-0">IPD : <?= esc($ipd->ipd_code ?? '') ?></h5>
|
||||
<div class="d-flex flex-wrap align-items-center gap-2">
|
||||
<h5 class="mb-0">
|
||||
<?php if (($ipd->admission_type ?? 'ipd') === 'daycare'): ?>
|
||||
Day Care : <?= esc($ipd->ipd_code ?? '') ?>
|
||||
<?php elseif (($ipd->admission_type ?? 'ipd') === 'emergency'): ?>
|
||||
Emergency : <?= esc($ipd->ipd_code ?? '') ?>
|
||||
<?php else: ?>
|
||||
IPD : <?= esc($ipd->ipd_code ?? '') ?>
|
||||
<?php endif; ?>
|
||||
</h5>
|
||||
<?php if (($ipd->admission_type ?? 'ipd') === 'daycare'): ?>
|
||||
<span class="badge bg-warning text-dark"><i class="bi bi-clock-history"></i> Day Care (< 24 Hrs)</span>
|
||||
<?php elseif (($ipd->admission_type ?? 'ipd') === 'emergency'): ?>
|
||||
<?php
|
||||
$tLevel = (int) ($ipd->triage_level ?? 3);
|
||||
$tColor = $tLevel === 1 ? 'danger' : ($tLevel === 2 ? 'warning text-dark' : ($tLevel === 3 ? 'warning text-dark' : 'info text-dark'));
|
||||
?>
|
||||
<span class="badge bg-<?= $tColor ?>"><i class="bi bi-ambulance"></i> Emergency ESI-<?= $tLevel ?></span>
|
||||
<?php endif; ?>
|
||||
<?php if (! empty($ipd->converted_from_type)): ?>
|
||||
<span class="badge bg-info text-dark" title="Converted from <?= esc($ipd->converted_from_type) ?> (<?= esc($ipd->converted_from_code) ?>) on <?= esc($ipd->converted_at) ?>">
|
||||
<i class="bi bi-arrow-repeat"></i> Converted from <?= ucfirst(esc($ipd->converted_from_type)) ?> (<?= esc($ipd->converted_from_code) ?>)
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
<?php if (! empty($ipd->is_mlc)): ?>
|
||||
<span class="badge bg-danger"><i class="bi bi-shield-shaded"></i> MLC</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="ipd-actions">
|
||||
<?php if (in_array($ipd->admission_type ?? 'ipd', ['daycare', 'emergency'], true) && (int) ($ipd->ipd_status ?? 0) === 0): ?>
|
||||
<button type="button" class="btn btn-sm btn-danger" data-bs-toggle="modal" data-bs-target="#convertToIpdModal">
|
||||
<i class="bi bi-arrow-up-right-circle-fill"></i> Convert to IPD
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<button class="btn btn-sm btn-primary" onclick="javascript:load_form_div('<?= base_url('billing/patient/person_record') ?>/<?= esc($person->id ?? 0) ?>/0','maindiv','Patient Record');">
|
||||
<i class="bi bi-person-lines-fill"></i> Patient Record
|
||||
</button>
|
||||
|
|
@ -91,12 +123,37 @@ if ($person) {
|
|||
<strong>/ Age :</strong> <?= esc($age) ?>
|
||||
<strong>/ Gender :</strong> <?= esc($person->xgender ?? '') ?>
|
||||
<strong>/ UHID :</strong> <?= esc($person->p_code ?? '') ?>
|
||||
<strong>/ Bed :</strong> <span class="badge bg-light text-dark border"><?= esc($ipd->bed_no ?? 'None') ?></span>
|
||||
<strong>/ No of Days :</strong> <span id="ipd-panel-no-days"><?= esc($ipd->no_days ?? '') ?></span>
|
||||
</p>
|
||||
<p class="mb-0">
|
||||
<p class="mb-1">
|
||||
<strong>Admit Date :</strong> <span id="ipd-panel-admit-date"><?= esc($ipd->str_register_date ?? '') ?></span>
|
||||
<strong>/ Discharge Date :</strong> <span id="ipd-panel-discharge-date"><?= esc($ipd->str_discharge_date ?? '') ?></span>
|
||||
<?php if (! empty($ipd->daycare_procedure_name)): ?>
|
||||
<strong>/ Day Care Procedure :</strong> <span class="text-primary font-monospace"><?= esc($ipd->daycare_procedure_name) ?></span>
|
||||
<?php endif; ?>
|
||||
</p>
|
||||
<?php if (($ipd->admission_type ?? '') === 'emergency' || ! empty($ipd->is_mlc) || ! empty($ipd->converted_from_type)): ?>
|
||||
<div class="p-2 mt-1 rounded" style="background:#f8fafc; border:1px solid #e2e8f0; font-size:12.5px;">
|
||||
<?php if (($ipd->admission_type ?? '') === 'emergency'): ?>
|
||||
<span class="me-3"><strong>Triage Acuity:</strong> Level <?= esc($ipd->triage_level ?? '3') ?></span>
|
||||
<?php if (! empty($ipd->brought_by)): ?>
|
||||
<span class="me-3"><strong>Brought By:</strong> <?= esc($ipd->brought_by) ?></span>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
<?php if (! empty($ipd->is_mlc)): ?>
|
||||
<span class="me-3 text-danger"><strong>MLC No:</strong> <?= esc($ipd->mlc_number ?: 'Pending') ?></span>
|
||||
<?php if (! empty($ipd->police_station)): ?>
|
||||
<span class="me-3"><strong>Police Station:</strong> <?= esc($ipd->police_station) ?></span>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
<?php if (! empty($ipd->converted_from_type)): ?>
|
||||
<div class="text-muted mt-1">
|
||||
<i class="bi bi-info-circle"></i> Escalated from <strong><?= ucfirst(esc($ipd->converted_from_type)) ?></strong> (Original: <?= esc($ipd->converted_from_code) ?>) on <?= esc($ipd->converted_at) ?>. Reason: <em><?= esc($ipd->conversion_reason ?: 'Clinical escalation') ?></em>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
@ -358,5 +415,167 @@ if ($person) {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Convert to IPD Modal handling
|
||||
var convertModal = document.getElementById('convertToIpdModal');
|
||||
if (convertModal) {
|
||||
convertModal.addEventListener('shown.bs.modal', function() {
|
||||
var $bedSelect = $('#convert_target_bed_id');
|
||||
var $docSelect = $('#convert_doctor_id');
|
||||
|
||||
$bedSelect.html('<option value="">Loading available beds...</option>');
|
||||
|
||||
$.getJSON('<?= site_url('billing/ipd/available-ipd-beds') ?>', function(resp) {
|
||||
if (resp && resp.status) {
|
||||
var bedHtml = '<option value="">-- Select Target Inpatient Bed --</option>';
|
||||
var groupedBeds = {};
|
||||
(resp.beds || []).forEach(function(b) {
|
||||
var wName = b.ward_name ? (b.ward_name + ' (' + (b.ward_type || 'General') + ')') : 'General Wards';
|
||||
if (!groupedBeds[wName]) {
|
||||
groupedBeds[wName] = [];
|
||||
}
|
||||
groupedBeds[wName].push(b);
|
||||
});
|
||||
|
||||
for (var group in groupedBeds) {
|
||||
bedHtml += '<optgroup label="' + group + '">';
|
||||
groupedBeds[group].forEach(function(b) {
|
||||
bedHtml += '<option value="' + b.id + '">Bed ' + (b.bed_number || b.bed_code) + ' - ' + group + '</option>';
|
||||
});
|
||||
bedHtml += '</optgroup>';
|
||||
}
|
||||
$bedSelect.html(bedHtml);
|
||||
|
||||
var docHtml = '<option value="">Keep current attending doctor</option>';
|
||||
(resp.doctors || []).forEach(function(d) {
|
||||
docHtml += '<option value="' + d.id + '">' + d.doc_name + '</option>';
|
||||
});
|
||||
$docSelect.html(docHtml);
|
||||
} else {
|
||||
$bedSelect.html('<option value="">Failed to load beds</option>');
|
||||
}
|
||||
}).fail(function() {
|
||||
$bedSelect.html('<option value="">Unable to load beds</option>');
|
||||
});
|
||||
});
|
||||
|
||||
$('#convertIpdForm').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
var $btn = $('#btnConfirmConvertToIpd');
|
||||
var bedId = $('#convert_target_bed_id').val();
|
||||
var reason = $('#convert_reason').val();
|
||||
|
||||
if (!bedId) {
|
||||
alert('Please select a target Inpatient Bed.');
|
||||
return;
|
||||
}
|
||||
if (!reason || reason.trim() === '') {
|
||||
alert('Please specify the clinical reason for conversion to IPD.');
|
||||
return;
|
||||
}
|
||||
|
||||
$btn.prop('disabled', true).html('<span class="spinner-border spinner-border-sm"></span> Converting...');
|
||||
|
||||
$.post('<?= site_url('billing/ipd/convert-to-ipd') ?>', $(this).serialize(), function(res) {
|
||||
$btn.prop('disabled', false).html('<i class="bi bi-check-circle-fill"></i> Confirm & Transfer to IPD');
|
||||
if (res && res.status) {
|
||||
var modalInstance = bootstrap.Modal.getInstance(convertModal);
|
||||
if (modalInstance) {
|
||||
modalInstance.hide();
|
||||
}
|
||||
if (typeof window.notify === 'function') {
|
||||
window.notify('success', 'IPD Conversion', res.message || 'Converted to Inpatient successfully');
|
||||
} else {
|
||||
alert(res.message || 'Converted to Inpatient successfully');
|
||||
}
|
||||
load_form_div('<?= base_url('billing/ipd/panel/' . (int) ($ipd->id ?? 0)) ?>', 'maindiv', 'IPD Panel');
|
||||
} else {
|
||||
alert(res.message || 'Failed to convert to IPD');
|
||||
}
|
||||
}, 'json').fail(function(xhr) {
|
||||
$btn.prop('disabled', false).html('<i class="bi bi-check-circle-fill"></i> Confirm & Transfer to IPD');
|
||||
var msg = (xhr.responseJSON && xhr.responseJSON.message) ? xhr.responseJSON.message : 'Server error occurred during conversion';
|
||||
alert(msg);
|
||||
});
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<!-- Convert / Escalate to IPD Modal -->
|
||||
<div class="modal fade" id="convertToIpdModal" tabindex="-1" aria-labelledby="convertToIpdModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-danger text-white">
|
||||
<h5 class="modal-title" id="convertToIpdModalLabel">
|
||||
<i class="bi bi-arrow-up-right-circle-fill"></i> Escalate / Convert to Inpatient (IPD)
|
||||
</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-info py-2" style="font-size:13px;">
|
||||
<i class="bi bi-info-circle-fill"></i>
|
||||
Converting this patient to <strong>Inpatient (IPD)</strong> will transfer the patient to an IPD ward/bed and update their encounter status.
|
||||
<strong>All current Day Care / Emergency bills, orders, and medications will automatically remain unified under this patient's invoice.</strong>
|
||||
</div>
|
||||
|
||||
<form id="convertIpdForm">
|
||||
<input type="hidden" name="ipd_id" value="<?= (int) ($ipd->id ?? 0) ?>">
|
||||
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label font-weight-bold">Select Inpatient Ward & Bed <span class="text-danger">*</span></label>
|
||||
<select class="form-select" id="convert_target_bed_id" name="target_bed_id" required>
|
||||
<option value="">Loading available beds...</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label font-weight-bold">Admitting / Primary Consultant</label>
|
||||
<select class="form-select" id="convert_doctor_id" name="doctor_id">
|
||||
<option value="">Keep current doctor</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label font-weight-bold">Reason for Escalation / IPD Admission <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" name="conversion_reason" id="convert_reason" placeholder="e.g. Clinical deterioration, post-op complication, stay required > 24 hours, ICU monitoring needed" required>
|
||||
</div>
|
||||
|
||||
<div class="card border-light-subtle mb-3">
|
||||
<div class="card-header bg-light py-2">
|
||||
<strong class="small text-primary"><i class="bi bi-journal-medical"></i> SBAR Clinical Handover Protocol (Medical Standard)</strong>
|
||||
</div>
|
||||
<div class="card-body p-3">
|
||||
<div class="row g-2">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label small mb-1"><strong>S - Situation:</strong></label>
|
||||
<textarea class="form-control form-control-sm" name="sbar_situation" rows="2" placeholder="Current condition, acute change, vital signs..."></textarea>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label small mb-1"><strong>B - Background:</strong></label>
|
||||
<textarea class="form-control form-control-sm" name="sbar_background" rows="2" placeholder="Admission diagnosis, procedures done in ER/Day Care, past medical history..."></textarea>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label small mb-1"><strong>A - Assessment:</strong></label>
|
||||
<textarea class="form-control form-control-sm" name="sbar_assessment" rows="2" placeholder="Clinical findings, lab investigations, IV access, O2 requirements..."></textarea>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label small mb-1"><strong>R - Recommendation:</strong></label>
|
||||
<textarea class="form-control form-control-sm" name="sbar_recommendation" rows="2" placeholder="Target monitoring plan, stat medications, pending lab reviews..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-danger" id="btnConfirmConvertToIpd">
|
||||
<i class="bi bi-check-circle-fill"></i> Confirm & Transfer to IPD
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -296,7 +296,7 @@ $initialPatientId = (int) ($patient_id ?? 0);
|
|||
|
||||
function renderPatient(data) {
|
||||
var patient = data.patient || {};
|
||||
var name = $.trim([(patient.p_fname || ''), (patient.p_lname || '')].join(' ')) || ('Patient #' + currentPatientId);
|
||||
var name = $.trim(patient.p_fname || '') || ('Patient #' + currentPatientId);
|
||||
$('#patientName').text(name);
|
||||
$('#patientMeta').html([
|
||||
patient.p_code ? 'UHID: ' + esc(patient.p_code) : '',
|
||||
|
|
|
|||
|
|
@ -235,7 +235,19 @@ if (! $canChargesSettings && $user && method_exists($user, 'inGroup')) {
|
|||
<li class="nav-item">
|
||||
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('/billing/ipd') ?>','IPD List')">
|
||||
<i class="bi bi-hospital"></i>
|
||||
<span>IPD Billing </span>
|
||||
<span>IPD Billing</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('/billing/ipd/admit?type=emergency') ?>','Emergency / Casualty Admission')">
|
||||
<i class="bi bi-ambulance text-danger"></i>
|
||||
<span>Emergency / Casualty</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('/billing/ipd/admit?type=daycare') ?>','Day Care Admission')">
|
||||
<i class="bi bi-clock-history text-warning"></i>
|
||||
<span>Day Care Unit</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
|
|
@ -336,6 +348,12 @@ if (! $canChargesSettings && $user && method_exists($user, 'inGroup')) {
|
|||
<?php if ($canIpdNursing || $canIpdDischarge || $canIpdOt) { ?>
|
||||
<li class="nav-heading">In-Patient & Nursing Care</li>
|
||||
<?php if ($canIpdNursing) { ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('billing/ipd/admit') ?>','Admit Patient Desk')">
|
||||
<i class="bi bi-person-plus text-success"></i>
|
||||
<span>Admit Patient Desk</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('/ipd/patient') ?>','IPD Patient List')">
|
||||
<i class="bi bi-person-vcard"></i>
|
||||
|
|
|
|||
452
public/help.html
452
public/help.html
|
|
@ -52,7 +52,20 @@
|
|||
.search-hidden { display:none!important; }
|
||||
.no-results { display:none; background:var(--soft-amber); border:1px solid #eccb91; padding:18px; border-radius:6px; }
|
||||
.back-top { text-align:right; margin-top:18px; font-size:.85rem; }
|
||||
footer { color:var(--muted); }
|
||||
.flow-container { background:#f8fafc; border:1px solid var(--line); border-radius:8px; padding:18px; margin:20px 0; overflow-x:auto; }
|
||||
.flow-svg { max-width:100%; min-width:680px; height:auto; display:block; margin:0 auto; }
|
||||
.badge-esi { display:inline-block; padding:3px 8px; border-radius:4px; font-weight:700; font-size:.75rem; color:#fff; text-shadow:0 1px 1px rgba(0,0,0,0.25); }
|
||||
.badge-esi-1 { background:#dc3545; }
|
||||
.badge-esi-2 { background:#fd7e14; }
|
||||
.badge-esi-3 { background:#ffc107; color:#212529; text-shadow:none; }
|
||||
.badge-esi-4 { background:#0dcaf0; color:#212529; text-shadow:none; }
|
||||
.badge-esi-5 { background:#198754; }
|
||||
.sbar-card { border-left:4px solid var(--navy); background:var(--soft-blue); padding:12px 16px; margin-bottom:10px; border-radius:0 6px 6px 0; }
|
||||
.sbar-letter { display:inline-block; width:26px; height:26px; line-height:26px; text-align:center; background:var(--navy); color:#fff; font-weight:800; border-radius:50%; margin-right:8px; font-size:.85rem; }
|
||||
.prefix-badge { font-family:Consolas,monospace; font-weight:700; padding:2px 7px; border-radius:4px; font-size:.85rem; }
|
||||
.prefix-er { background:#ffe5e7; color:#b43c47; border:1px solid #f5c2c7; }
|
||||
.prefix-dc { background:#e0f7fa; color:#00796b; border:1px solid #b2dfdb; }
|
||||
.prefix-ipd { background:#e8f0fe; color:#1a73e8; border:1px solid #c2d7fa; }
|
||||
@media (max-width:991.98px) { .layout{grid-template-columns:1fr}.toc-panel{position:static;max-height:none}.toc-links{columns:2}.quick-grid{grid-template-columns:1fr 1fr} }
|
||||
@media (max-width:575.98px) { .toc-links{columns:1}.quick-grid{grid-template-columns:1fr}.guide-section{border-radius:0;margin-left:-12px;margin-right:-12px}.guide-title{font-size:1.45rem} }
|
||||
@media print { body{background:#fff}.guide-header,.toc-panel,.back-top,.no-print{display:none!important}.layout{display:block}.guide-section{border:0;break-inside:avoid;padding:12px 0} }
|
||||
|
|
@ -75,7 +88,7 @@
|
|||
<aside class="toc-panel no-print" aria-label="Guide contents">
|
||||
<div class="p-3 border-bottom"><h2 class="mb-0">Contents</h2></div>
|
||||
<nav class="toc-links py-2">
|
||||
<a href="#recent-updates">Recent Updates</a><a href="#getting-started">Getting Started</a><a href="#roles-access">Roles & Access</a><a href="#patient-management">Patient Management</a><a href="#opd">OPD Workflow</a><a href="#ipd">IPD & Nursing</a><a href="#ot-management">OT Management</a><a href="#diagnosis">Diagnosis & Imaging</a><a href="#pharmacy">Pharmacy</a><a href="#billing">Billing & Payments</a><a href="#finance">Finance</a><a href="#insurance">Insurance & ORG Cases</a><a href="#abdm">ABDM & ABHA</a><a href="#stock">Hospital Stock</a><a href="#reports">Reports & Dashboards</a><a href="#admin">Admin & System Operations</a><a href="#security">Security & Compliance</a><a href="#backup">Backup & Maintenance</a><a href="#troubleshooting">Troubleshooting</a>
|
||||
<a href="#recent-updates">Recent Updates</a><a href="#getting-started">Getting Started</a><a href="#roles-access">Roles & Access</a><a href="#patient-management">Patient Management</a><a href="#opd">OPD Workflow</a><a href="#ipd">IPD & Nursing</a><a href="#daycare-emergency">Day Care & Emergency</a><a href="#ot-management">OT Management</a><a href="#diagnosis">Diagnosis & Imaging</a><a href="#pharmacy">Pharmacy</a><a href="#billing">Billing & Payments</a><a href="#finance">Finance</a><a href="#insurance">Insurance & ORG Cases</a><a href="#abdm">ABDM & ABHA</a><a href="#stock">Hospital Stock</a><a href="#reports">Reports & Dashboards</a><a href="#admin">Admin & System Operations</a><a href="#security">Security & Compliance</a><a href="#backup">Backup & Maintenance</a><a href="#troubleshooting">Troubleshooting</a>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
|
|
@ -83,6 +96,7 @@
|
|||
<section id="recent-updates" class="guide-section searchable-section">
|
||||
<h2><span class="section-number">01</span>Recent Updates</h2>
|
||||
<p>These functions were added or strengthened after the previous help guide was published.</p>
|
||||
<div class="update-item"><strong>Day Care & Emergency (Casualty) Module <span class="status-chip ms-1">NEW</span></strong><div>Dedicated admission workflows for Day Care (<code>DC</code> prefix) and Emergency/Casualty (<code>ER</code> prefix), ESI Triage Levels 1–5, MLC registry, specialized wards (Day Care Unit & Casualty Ward), clinical <strong>Convert to IPD</strong> escalation with SBAR handover notes, unified IPD billing merging ER/Day Care charges under distinct group sub-headers, and medical standard Day Care discharge summaries with PADSS criteria (≥9).</div></div>
|
||||
<div class="update-item"><strong>OT Management Phase 1 <span class="status-chip ms-1">NEW</span></strong><div>Surgery requests, OT queue, scheduling, patient call status, pre-operative safety checks, WHO sign-in, operation progress, immutable event history, and signed post-operative assessment are now integrated with IPD Nursing.</div></div>
|
||||
<div class="update-item"><strong>Department-specific pre-operative forms</strong><div>Specialty forms can appear according to the admitted patient's department. Ophthalmology pre-operative examination is available with completed-record edit reasons and audit history.</div></div>
|
||||
<div class="update-item"><strong>IPD Nursing and discharge controls</strong><div>Dedicated nursing access, clinical workspace improvements, configurable discharge content, diagnosis masters, and more reliable HTML/PDF template rendering are available.</div></div>
|
||||
|
|
@ -149,15 +163,423 @@
|
|||
<ul><li>Complete diagnosis, course, procedures, condition, medication, advice, diet, and follow-up.</li><li>Select and preview the approved discharge template.</li><li>Reconcile pending charges, returns, package adjustments, and balance.</li><li>Verify PDF page breaks, placeholders, dates, letterhead mode, and signatures.</li><li>Generate/share the ABDM discharge summary when requirements are met.</li></ul>
|
||||
</section>
|
||||
|
||||
<section id="daycare-emergency" class="guide-section searchable-section">
|
||||
<h2><span class="section-number">07</span>Day Care & Emergency (Casualty) Module</h2>
|
||||
<p>Designed in compliance with <strong>NABH</strong>, <strong>HL7 FHIR R4</strong>, and <strong>ESI (Emergency Severity Index)</strong> triage standards. This module supports short-stay observation, planned day care surgical/medical procedures (<24 hours), acute casualty resuscitation, and seamless clinical escalation to Inpatient (IPD) with unified billing and SBAR handover.</p>
|
||||
|
||||
<!-- Quick Access / Where to find links guide box -->
|
||||
<div style="background:#f0f9ff; border:1px solid #bae6fd; border-left:5px solid #0284c7; border-radius:8px; padding:18px 22px; margin:20px 0;">
|
||||
<h4 style="margin:0 0 10px 0; color:#0369a1; font-size:16px; font-weight:700;">
|
||||
<i class="bi bi-compass" style="margin-right:6px;"></i>Quick Access: Where to Find Admission Links in the HMS
|
||||
</h4>
|
||||
<p style="margin:0 0 12px 0; font-size:13.5px; color:#334155;">
|
||||
You can register or admit patients for <strong>Emergency / Casualty</strong> or <strong>Day Care (< 24 Hrs)</strong> from <strong>4 primary places</strong> across the application:
|
||||
</p>
|
||||
<div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(280px, 1fr)); gap:14px; margin-top:10px;">
|
||||
<div style="background:#fff; border:1px solid #e2e8f0; border-radius:6px; padding:12px 14px;">
|
||||
<strong style="color:#0f172a; font-size:13px;"><span style="color:#dc2626;">▶</span> 1. Left Navigation Sidebar:</strong>
|
||||
<div style="font-size:12.5px; color:#475569; margin-top:4px;">
|
||||
Under <strong>Billing & Accounts</strong> in the left sidebar, click directly on:
|
||||
<ul style="margin:4px 0 0 18px; padding:0;">
|
||||
<li><span style="color:#dc2626; font-weight:600;">Emergency / Casualty</span> (with red ambulance icon)</li>
|
||||
<li><span style="color:#d97706; font-weight:600;">Day Care Unit</span> (with clock icon)</li>
|
||||
<li>Or under <strong>In-Patient & Nursing Care</strong>: click <strong>Admit Patient Desk</strong></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div style="background:#fff; border:1px solid #e2e8f0; border-radius:6px; padding:12px 14px;">
|
||||
<strong style="color:#0f172a; font-size:13px;"><span style="color:#0284c7;">▶</span> 2. Patient Profile Screen:</strong>
|
||||
<div style="font-size:12.5px; color:#475569; margin-top:4px;">
|
||||
Search for any patient and open their profile. Under the action buttons, click:
|
||||
<div style="margin-top:6px; display:flex; gap:6px;">
|
||||
<span style="background:#dc3545; color:#fff; border-radius:4px; font-size:11px; padding:3px 8px; font-weight:bold;">Casualty</span>
|
||||
<span style="background:#ffc107; color:#212529; border-radius:4px; font-size:11px; padding:3px 8px; font-weight:bold;">Day Care</span>
|
||||
<span style="background:#0d6efd; color:#fff; border-radius:4px; font-size:11px; padding:3px 8px; font-weight:bold;">IPD</span>
|
||||
</div>
|
||||
<span style="font-size:11.5px; color:#64748b; margin-top:4px; display:block;">Instantly pre-selects the admission type and displays triage or procedure fields.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="background:#fff; border:1px solid #e2e8f0; border-radius:6px; padding:12px 14px;">
|
||||
<strong style="color:#0f172a; font-size:13px;"><span style="color:#16a34a;">▶</span> 3. IPD Dashboard & Admissions:</strong>
|
||||
<div style="font-size:12.5px; color:#475569; margin-top:4px;">
|
||||
Open <strong>IPD Billing</strong> from sidebar:
|
||||
<ul style="margin:4px 0 0 18px; padding:0;">
|
||||
<li>Top right buttons: <strong>Emergency Admission</strong> & <strong>Day Care Admission</strong></li>
|
||||
<li>On <strong>Current Admissions</strong>: click the green <strong>+ New Admission</strong> dropdown</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div style="background:#fff; border:1px solid #e2e8f0; border-radius:6px; padding:12px 14px;">
|
||||
<strong style="color:#0f172a; font-size:13px;"><span style="color:#7c3aed;">▶</span> 4. Patient Admission Desk:</strong>
|
||||
<div style="font-size:12.5px; color:#475569; margin-top:4px;">
|
||||
Search any patient by Name, UHID, or Phone number. Then click <strong>Casualty</strong>, <strong>Day Care</strong>, or <strong>IPD</strong>.
|
||||
<br><small style="color:#64748b;">If patient is brand new, click <em>+ New Patient Registration</em> first.</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Responsive Clinical Flowchart -->
|
||||
<h3>Clinical & Operational Flowchart</h3>
|
||||
<p>The following flowchart illustrates the complete patient lifecycle: acute emergency triage or planned procedure admission, dedicated physical ward allocation, bedside service delivery, discharge disposition, and the audited escalation pathway to full Inpatient (IPD) admission.</p>
|
||||
|
||||
<div class="flow-container">
|
||||
<svg class="flow-svg" viewBox="0 0 960 550" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Day Care, Emergency, and IPD Conversion Clinical Flowchart">
|
||||
<defs>
|
||||
<linearGradient id="grad-er" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#b43c47"/>
|
||||
<stop offset="100%" stop-color="#dc3545"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="grad-dc" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#00796b"/>
|
||||
<stop offset="100%" stop-color="#087f75"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="grad-ipd" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#123765"/>
|
||||
<stop offset="100%" stop-color="#1e518f"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="grad-conv" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#b96710"/>
|
||||
<stop offset="100%" stop-color="#d97706"/>
|
||||
</linearGradient>
|
||||
<filter id="card-shadow" x="-5%" y="-5%" width="110%" height="115%">
|
||||
<feDropShadow dx="0" dy="2" stdDeviation="3" flood-opacity="0.08"/>
|
||||
</filter>
|
||||
<marker id="arrow-gray" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
|
||||
<path d="M 0 1 L 8 5 L 0 9 z" fill="#617087"/>
|
||||
</marker>
|
||||
<marker id="arrow-gold" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
|
||||
<path d="M 0 1 L 8 5 L 0 9 z" fill="#b96710"/>
|
||||
</marker>
|
||||
<marker id="arrow-red" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
|
||||
<path d="M 0 1 L 8 5 L 0 9 z" fill="#b43c47"/>
|
||||
</marker>
|
||||
<marker id="arrow-teal" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
|
||||
<path d="M 0 1 L 8 5 L 0 9 z" fill="#087f75"/>
|
||||
</marker>
|
||||
<marker id="arrow-navy" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
|
||||
<path d="M 0 1 L 8 5 L 0 9 z" fill="#123765"/>
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<!-- Column Background Tracks -->
|
||||
<rect x="20" y="20" width="280" height="510" rx="10" fill="#fff" stroke="#f2d6d8" stroke-width="1.5" filter="url(#card-shadow)"/>
|
||||
<rect x="340" y="20" width="280" height="510" rx="10" fill="#fff" stroke="#ccebe7" stroke-width="1.5" filter="url(#card-shadow)"/>
|
||||
<rect x="660" y="20" width="280" height="510" rx="10" fill="#fff" stroke="#d4e2f4" stroke-width="1.5" filter="url(#card-shadow)"/>
|
||||
|
||||
<!-- Column Headers -->
|
||||
<path d="M 20 30 Q 20 20 30 20 L 290 20 Q 300 20 300 30 L 300 56 L 20 56 Z" fill="url(#grad-er)"/>
|
||||
<text x="160" y="44" text-anchor="middle" fill="#fff" font-family="'Segoe UI', sans-serif" font-weight="700" font-size="13.5" letter-spacing="0.5">1. EMERGENCY / CASUALTY</text>
|
||||
|
||||
<path d="M 340 30 Q 340 20 350 20 L 610 20 Q 620 20 620 30 L 620 56 L 340 56 Z" fill="url(#grad-dc)"/>
|
||||
<text x="480" y="44" text-anchor="middle" fill="#fff" font-family="'Segoe UI', sans-serif" font-weight="700" font-size="13.5" letter-spacing="0.5">2. DAY CARE UNIT (<24H)</text>
|
||||
|
||||
<path d="M 660 30 Q 660 20 670 20 L 930 20 Q 940 20 940 30 L 940 56 L 660 56 Z" fill="url(#grad-ipd)"/>
|
||||
<text x="800" y="44" text-anchor="middle" fill="#fff" font-family="'Segoe UI', sans-serif" font-weight="700" font-size="13.5" letter-spacing="0.5">3. INPATIENT ADMISSION (IPD)</text>
|
||||
|
||||
<!-- STAGE 1: ADMISSION & TRIAGE -->
|
||||
<rect x="35" y="70" width="250" height="75" rx="6" fill="#fff5f5" stroke="#f5c2c7" stroke-width="1"/>
|
||||
<text x="45" y="90" fill="#b43c47" font-family="'Segoe UI', sans-serif" font-weight="700" font-size="12">Registration & Triage</text>
|
||||
<text x="45" y="107" fill="#172238" font-family="'Segoe UI', sans-serif" font-size="11">• Auto Prefix: ER-YYYY-XXXXX</text>
|
||||
<text x="45" y="122" fill="#172238" font-family="'Segoe UI', sans-serif" font-size="11">• ESI Triage Level 1 to 5</text>
|
||||
<text x="45" y="137" fill="#172238" font-family="'Segoe UI', sans-serif" font-size="11">• MLC Case Record & Police details</text>
|
||||
|
||||
<rect x="355" y="70" width="250" height="75" rx="6" fill="#f0faf9" stroke="#b2dfdb" stroke-width="1"/>
|
||||
<text x="365" y="90" fill="#00796b" font-family="'Segoe UI', sans-serif" font-weight="700" font-size="12">Day Care Registration</text>
|
||||
<text x="365" y="107" fill="#172238" font-family="'Segoe UI', sans-serif" font-size="11">• Auto Prefix: DC-YYYY-XXXXX</text>
|
||||
<text x="365" y="122" fill="#172238" font-family="'Segoe UI', sans-serif" font-size="11">• Procedure Name specified</text>
|
||||
<text x="365" y="137" fill="#172238" font-family="'Segoe UI', sans-serif" font-size="11">• Ward: Day Care Unit (DC-01)</text>
|
||||
|
||||
<rect x="675" y="70" width="250" height="75" rx="6" fill="#f4f7fc" stroke="#c2d7fa" stroke-width="1"/>
|
||||
<text x="685" y="90" fill="#123765" font-family="'Segoe UI', sans-serif" font-weight="700" font-size="12">Standard IPD Registration</text>
|
||||
<text x="685" y="107" fill="#172238" font-family="'Segoe UI', sans-serif" font-size="11">• Auto Prefix: A-YYYY-XXXXX</text>
|
||||
<text x="685" y="122" fill="#172238" font-family="'Segoe UI', sans-serif" font-size="11">• Planned Inpatient Admission</text>
|
||||
<text x="685" y="137" fill="#172238" font-family="'Segoe UI', sans-serif" font-size="11">• General / Private / ICU Wards</text>
|
||||
|
||||
<!-- Arrows Stage 1 to 2 -->
|
||||
<line x1="160" y1="145" x2="160" y2="168" stroke="#b43c47" stroke-width="1.5" marker-end="url(#arrow-red)"/>
|
||||
<line x1="480" y1="145" x2="480" y2="168" stroke="#087f75" stroke-width="1.5" marker-end="url(#arrow-teal)"/>
|
||||
<line x1="800" y1="145" x2="800" y2="168" stroke="#123765" stroke-width="1.5" marker-end="url(#arrow-navy)"/>
|
||||
|
||||
<!-- STAGE 2: CLINICAL CARE & BILLING -->
|
||||
<rect x="35" y="170" width="250" height="75" rx="6" fill="#fff" stroke="#f5c2c7" stroke-width="1"/>
|
||||
<text x="45" y="190" fill="#b43c47" font-family="'Segoe UI', sans-serif" font-weight="700" font-size="12">Casualty Care & Services</text>
|
||||
<text x="45" y="207" fill="#617087" font-family="'Segoe UI', sans-serif" font-size="11">• Rapid Resuscitation & Vitals</text>
|
||||
<text x="45" y="222" fill="#617087" font-family="'Segoe UI', sans-serif" font-size="11">• Stat Diagnostics & Bedside Rx</text>
|
||||
<text x="45" y="237" fill="#b43c47" font-family="'Segoe UI', sans-serif" font-size="10.5" font-weight="600">► Group: EMERGENCY CHARGES</text>
|
||||
|
||||
<rect x="355" y="170" width="250" height="75" rx="6" fill="#fff" stroke="#b2dfdb" stroke-width="1"/>
|
||||
<text x="365" y="190" fill="#00796b" font-family="'Segoe UI', sans-serif" font-weight="700" font-size="12">Procedure & Recovery</text>
|
||||
<text x="365" y="207" fill="#617087" font-family="'Segoe UI', sans-serif" font-size="11">• Short-Stay Intervention</text>
|
||||
<text x="365" y="222" fill="#617087" font-family="'Segoe UI', sans-serif" font-size="11">• Post-Anesthesia Recovery</text>
|
||||
<text x="365" y="237" fill="#00796b" font-family="'Segoe UI', sans-serif" font-size="10.5" font-weight="600">► Group: DAY CARE CHARGES</text>
|
||||
|
||||
<rect x="675" y="170" width="250" height="75" rx="6" fill="#fff" stroke="#c2d7fa" stroke-width="1"/>
|
||||
<text x="685" y="190" fill="#123765" font-family="'Segoe UI', sans-serif" font-weight="700" font-size="12">Inpatient Clinical Care</text>
|
||||
<text x="685" y="207" fill="#617087" font-family="'Segoe UI', sans-serif" font-size="11">• Bedside Nursing & Doctor Visits</text>
|
||||
<text x="685" y="222" fill="#617087" font-family="'Segoe UI', sans-serif" font-size="11">• OT Surgery / Daily Ward Rounds</text>
|
||||
<text x="685" y="237" fill="#123765" font-family="'Segoe UI', sans-serif" font-size="10.5" font-weight="600">► Multi-Group Unified Billing</text>
|
||||
|
||||
<!-- Arrows Stage 2 to Decision -->
|
||||
<line x1="160" y1="245" x2="160" y2="268" stroke="#617087" stroke-width="1.5" marker-end="url(#arrow-gray)"/>
|
||||
<line x1="480" y1="245" x2="480" y2="268" stroke="#617087" stroke-width="1.5" marker-end="url(#arrow-gray)"/>
|
||||
<line x1="800" y1="245" x2="800" y2="390" stroke="#123765" stroke-width="1.5" marker-end="url(#arrow-navy)"/>
|
||||
|
||||
<!-- STAGE 3: DECISION / EVALUATION -->
|
||||
<rect x="45" y="270" width="230" height="48" rx="6" fill="#fdfdfe" stroke="#617087" stroke-dasharray="3,3"/>
|
||||
<text x="160" y="289" text-anchor="middle" fill="#172238" font-family="'Segoe UI', sans-serif" font-weight="600" font-size="11.5">Stabilized or Needs Admission?</text>
|
||||
<text x="160" y="306" text-anchor="middle" fill="#617087" font-family="'Segoe UI', sans-serif" font-size="10.5">Clinical trajectory review</text>
|
||||
|
||||
<rect x="365" y="270" width="230" height="48" rx="6" fill="#fdfdfe" stroke="#617087" stroke-dasharray="3,3"/>
|
||||
<text x="480" y="289" text-anchor="middle" fill="#172238" font-family="'Segoe UI', sans-serif" font-weight="600" font-size="11.5">PADSS Score Evaluation</text>
|
||||
<text x="480" y="306" text-anchor="middle" fill="#617087" font-family="'Segoe UI', sans-serif" font-size="10.5">Score ≥ 9 required to discharge</text>
|
||||
|
||||
<!-- CONVERSION TO IPD BRIDGE -->
|
||||
<path d="M 230 318 L 230 350 L 290 350" fill="none" stroke="#b96710" stroke-width="1.5" stroke-dasharray="4,2"/>
|
||||
<path d="M 410 318 L 410 350 L 350 350" fill="none" stroke="#b96710" stroke-width="1.5" stroke-dasharray="4,2"/>
|
||||
|
||||
<rect x="180" y="335" width="460" height="66" rx="8" fill="#fff9ef" stroke="#b96710" stroke-width="1.5" filter="url(#card-shadow)"/>
|
||||
<text x="410" y="353" text-anchor="middle" fill="#b96710" font-family="'Segoe UI', sans-serif" font-weight="700" font-size="12">⚡ CLINICAL ESCALATION: CONVERT TO IPD</text>
|
||||
<text x="410" y="369" text-anchor="middle" fill="#172238" font-family="'Segoe UI', sans-serif" font-size="11">• SBAR Handover (Situation, Background, Assessment, Recommendation)</text>
|
||||
<text x="410" y="384" text-anchor="middle" fill="#172238" font-family="'Segoe UI', sans-serif" font-size="11">• Auto Bed Transfer (Free ER/DC bed → Assign Inpatient Bed)</text>
|
||||
<text x="410" y="396" text-anchor="middle" fill="#b96710" font-family="'Segoe UI', sans-serif" font-weight="600" font-size="10.5">→ Charges automatically merge into Unified IPD Bill with distinct group headers</text>
|
||||
|
||||
<!-- Arrow from Conversion to IPD Column -->
|
||||
<path d="M 640 368 L 675 368 L 750 368 L 750 245 L 790 245" fill="none" stroke="#b96710" stroke-width="2" marker-end="url(#arrow-gold)"/>
|
||||
|
||||
<!-- Downward arrows to Discharge -->
|
||||
<line x1="120" y1="318" x2="120" y2="423" stroke="#b43c47" stroke-width="1.5" marker-end="url(#arrow-red)"/>
|
||||
<text x="125" y="375" fill="#b43c47" font-family="'Segoe UI', sans-serif" font-weight="600" font-size="10">Direct Discharge</text>
|
||||
|
||||
<line x1="530" y1="318" x2="530" y2="423" stroke="#087f75" stroke-width="1.5" marker-end="url(#arrow-teal)"/>
|
||||
<text x="535" y="375" fill="#087f75" font-family="'Segoe UI', sans-serif" font-weight="600" font-size="10">PADSS ≥ 9</text>
|
||||
|
||||
<!-- STAGE 4: DISCHARGES -->
|
||||
<rect x="35" y="425" width="250" height="90" rx="6" fill="#fff" stroke="#f5c2c7" stroke-width="1"/>
|
||||
<text x="45" y="444" fill="#b43c47" font-family="'Segoe UI', sans-serif" font-weight="700" font-size="12">Emergency Discharge</text>
|
||||
<text x="45" y="461" fill="#172238" font-family="'Segoe UI', sans-serif" font-size="11">• Stabilized & Sent Home / Transfer</text>
|
||||
<text x="45" y="476" fill="#172238" font-family="'Segoe UI', sans-serif" font-size="11">• LAMA / DAMA / Referral Out</text>
|
||||
<text x="45" y="491" fill="#172238" font-family="'Segoe UI', sans-serif" font-size="11">• Casualty Bill Settlement</text>
|
||||
<text x="45" y="506" fill="#617087" font-family="'Segoe UI', sans-serif" font-size="10">• FHIR Class: EMER</text>
|
||||
|
||||
<rect x="355" y="425" width="250" height="90" rx="6" fill="#fff" stroke="#b2dfdb" stroke-width="1"/>
|
||||
<text x="365" y="444" fill="#00796b" font-family="'Segoe UI', sans-serif" font-weight="700" font-size="12">Day Care Discharge</text>
|
||||
<text x="365" y="461" fill="#172238" font-family="'Segoe UI', sans-serif" font-size="11">• Day Care Discharge Summary</text>
|
||||
<text x="365" y="476" fill="#172238" font-family="'Segoe UI', sans-serif" font-size="11">• PADSS Score recorded (≥9)</text>
|
||||
<text x="365" y="491" fill="#172238" font-family="'Segoe UI', sans-serif" font-size="11">• Home Meds & Red-Flag Warnings</text>
|
||||
<text x="365" y="506" fill="#617087" font-family="'Segoe UI', sans-serif" font-size="10">• FHIR Class: SS (Short Stay)</text>
|
||||
|
||||
<rect x="675" y="425" width="250" height="90" rx="6" fill="#fff" stroke="#c2d7fa" stroke-width="1"/>
|
||||
<text x="685" y="444" fill="#123765" font-family="'Segoe UI', sans-serif" font-weight="700" font-size="12">Inpatient Final Discharge</text>
|
||||
<text x="685" y="461" fill="#172238" font-family="'Segoe UI', sans-serif" font-size="11">• Complete Inpatient Discharge</text>
|
||||
<text x="685" y="476" fill="#123765" font-family="'Segoe UI', sans-serif" font-size="11" font-weight="600">• Unified IPD Bill (ER + DC + IPD)</text>
|
||||
<text x="685" y="491" fill="#172238" font-family="'Segoe UI', sans-serif" font-size="11">• Categorized Subtotal Headers</text>
|
||||
<text x="685" y="506" fill="#617087" font-family="'Segoe UI', sans-serif" font-size="10">• FHIR Class: IMP (Inpatient)</text>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h3>1. Key Identifiers & Dedicated Physical Wards</h3>
|
||||
<p>To prevent clinical confusion, enhance patient tracking, and ensure strict auditability, distinct admission prefixes and dedicated physical wards are maintained:</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-guide">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Admission Type</th>
|
||||
<th>Registration Prefix</th>
|
||||
<th>Default Physical Ward</th>
|
||||
<th>Billing Item Category</th>
|
||||
<th>HL7 FHIR Encounter</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>Casualty / Emergency</strong></td>
|
||||
<td><span class="prefix-badge prefix-er">ER-YYYY-XXXXX</span></td>
|
||||
<td>Casualty & Emergency Ward (<code>ER-01</code>)</td>
|
||||
<td><code>EMERGENCY / CASUALTY CHARGES</code></td>
|
||||
<td><code>EMER</code> (Emergency)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Day Care (<24 hrs)</strong></td>
|
||||
<td><span class="prefix-badge prefix-dc">DC-YYYY-XXXXX</span></td>
|
||||
<td>Day Care Unit (<code>DC-01</code>)</td>
|
||||
<td><code>DAY CARE CHARGES</code></td>
|
||||
<td><code>SS</code> (Short Stay)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Inpatient (IPD)</strong></td>
|
||||
<td><span class="prefix-badge prefix-ipd">A-YYYY-XXXXX</span></td>
|
||||
<td>General / Semi-Private / Deluxe / ICU / HDU</td>
|
||||
<td>Multi-Category IPD Billing</td>
|
||||
<td><code>IMP</code> (Inpatient)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h3>2. Emergency / Casualty Workflow</h3>
|
||||
<ol class="workflow">
|
||||
<li><strong>Rapid Check-in & ESI Triage:</strong> Immediate registration at the casualty reception. Clinical staff assign the Emergency Severity Index (ESI) Level based on clinical acuity:
|
||||
<div class="table-responsive my-2">
|
||||
<table class="table table-sm table-bordered">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>ESI Level</th>
|
||||
<th>Clinical Triage Category</th>
|
||||
<th>Target Door-to-Doctor Time</th>
|
||||
<th>Clinical Examples</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="badge-esi badge-esi-1">ESI 1</span></td>
|
||||
<td><strong>Resuscitation (Immediate Life-Threat)</strong></td>
|
||||
<td><strong>Immediate (0 min)</strong></td>
|
||||
<td>Cardiac arrest, respiratory failure, severe trauma with shock, anaphylaxis.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="badge-esi badge-esi-2">ESI 2</span></td>
|
||||
<td><strong>Emergent (High Risk / Severe Pain)</strong></td>
|
||||
<td><strong>≤ 10 minutes</strong></td>
|
||||
<td>Acute chest pain, severe dyspnea, suspected stroke, acute abdomen, danger vitals.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="badge-esi badge-esi-3">ESI 3</span></td>
|
||||
<td><strong>Urgent (≥2 Resources Required)</strong></td>
|
||||
<td><strong>≤ 30–60 minutes</strong></td>
|
||||
<td>Stable vitals requiring IV access, laboratory investigations, and X-ray/USG.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="badge-esi badge-esi-4">ESI 4</span></td>
|
||||
<td><strong>Less Urgent (1 Resource Required)</strong></td>
|
||||
<td><strong>≤ 60–120 minutes</strong></td>
|
||||
<td>Simple laceration needing suture, uncomplicated urinary tract infection, sprain.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="badge-esi badge-esi-5">ESI 5</span></td>
|
||||
<td><strong>Non-Urgent (0 Resources Required)</strong></td>
|
||||
<td><strong>≤ 120 minutes</strong></td>
|
||||
<td>Medication refill, minor rash, suture removal, chronic mild symptom review.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</li>
|
||||
<li><strong>Medico-Legal Case (MLC) Registry:</strong> If the casualty case involves road traffic accidents (RTA), assault, burns, poisoning, industrial trauma, or suspected foul play, toggle <strong>"Is Medico-Legal Case (MLC)"</strong>. Record:
|
||||
<ul>
|
||||
<li><strong>MLC Number:</strong> Formal hospital police register number.</li>
|
||||
<li><strong>Police Station:</strong> Jurisdiction police station notified.</li>
|
||||
<li><strong>Police Constable Details:</strong> Name and badge number of intimating/recording officer.</li>
|
||||
<li><strong>Brought By:</strong> Ambulance (e.g. 108), Police, Relative, or Good Samaritan with contact number.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>Casualty Bed & Orders:</strong> Patient is assigned a bed in the <em>Casualty & Emergency Ward</em>. Doctors post emergency orders (stat labs, ECG, imaging, IV fluids, emergency medicines). Consumables and nursing procedures automatically tag under <code>EMERGENCY / CASUALTY CHARGES</code>.</li>
|
||||
<li><strong>Emergency Disposition:</strong>
|
||||
<ul>
|
||||
<li><strong>Treated & Discharged:</strong> Once stabilized, settle the emergency bill and generate a casualty discharge slip.</li>
|
||||
<li><strong>Escalation / Admission Needed:</strong> If the patient requires surgical intervention, specialized ICU/HDU monitoring, or inpatient observation, initiate the <strong>Convert to IPD</strong> action.</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<h3>3. Day Care Workflow (<24 Hour Admissions)</h3>
|
||||
<p>Designed for planned surgical, therapeutic, or diagnostic procedures that do not require an overnight bed stay.</p>
|
||||
<ol class="workflow">
|
||||
<li><strong>Day Care Admission:</strong> In Registration, select <strong>Day Care</strong>. Enter the planned procedure name (e.g. <em>Cataract Extraction with Foldable IOL</em>, <em>Chemotherapy Session</em>, <em>Maintenance Hemodialysis</em>, <em>Upper GI Endoscopy / Colonoscopy</em>, or <em>Tympanoplasty</em>). System generates a <code>DC-YYYY-XXXXX</code> code and allocates a bed in the <strong>Day Care Unit (DC-01)</strong>.</li>
|
||||
<li><strong>Pre-Procedure & Surgical Care:</strong> Pre-op nursing assessment, surgical safety checklist, and procedure delivery. Services, procedure fees, and medications are billed under <code>DAY CARE CHARGES</code>.</li>
|
||||
<li><strong>Recovery & PADSS Scoring:</strong> Before clearing the patient for home discharge, the clinician or recovery nurse evaluates the patient against the <strong>Post-Anaesthetic Discharge Scoring System (PADSS)</strong>:
|
||||
<div class="table-responsive my-2">
|
||||
<table class="table table-sm table-bordered">
|
||||
<thead class="table-light"><tr><th>PADSS Criteria (2 pts each)</th><th>Score 2</th><th>Score 1</th><th>Score 0</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td><strong>Vital Signs (BP/Pulse)</strong></td><td>Within 20% of pre-op baseline</td><td>20%–40% of baseline</td><td>>40% of baseline</td></tr>
|
||||
<tr><td><strong>Activity & Ambulation</strong></td><td>Steady gait / no dizziness</td><td>Requires assistance</td><td>Unable to ambulate / vertigo</td></tr>
|
||||
<tr><td><strong>Nausea & Vomiting</strong></td><td>Minimal / none, drinks fluids</td><td>Moderate, managed with antiemetic</td><td>Severe / persistent vomiting</td></tr>
|
||||
<tr><td><strong>Pain Control</strong></td><td>Acceptable / mild, oral analgesics</td><td>Moderate, requires parenteral meds</td><td>Severe, unmanaged pain</td></tr>
|
||||
<tr><td><strong>Surgical Bleeding</strong></td><td>Minimal / dry dressing</td><td>Moderate, 1–2 dressing changes</td><td>Severe / active bleeding</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="note"><strong>Discharge Threshold:</strong> Patient must achieve a total score of <strong>≥ 9 out of 10</strong>, be accompanied by a responsible adult, and be oriented to time and place before home discharge.</div>
|
||||
</li>
|
||||
<li><strong>Day Care Discharge Summary:</strong> Select the <em>Day Care Discharge Summary</em> template. It automatically displays:
|
||||
<ul>
|
||||
<li>Procedure performed, anesthesia technique, and operating surgeon.</li>
|
||||
<li>Post-operative PADSS discharge score (≥9).</li>
|
||||
<li>Take-home medications with clear dosage, frequency, and duration.</li>
|
||||
<li>Dietary, wound dressing, and activity instructions.</li>
|
||||
<li><strong>Red-Flag Emergency Warnings:</strong> Explicit warnings regarding severe bleeding, sudden fever >101°F, persistent vomiting, wound discharge, or chest pain, with direct 24/7 hospital emergency telephone numbers.</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<h3>4. Clinical Escalation: Convert to IPD Workflow</h3>
|
||||
<p>When an Emergency or Day Care patient deteriorates, suffers surgical complications, requires prolonged observation (>24 hours), or must be shifted to an intensive care unit (ICU/HDU), the system provides an audited, one-click <strong>Convert to IPD</strong> escalation workflow.</p>
|
||||
|
||||
<div class="quick-grid mb-3">
|
||||
<div class="quick-item"><strong>1. Trigger Action</strong>Open patient panel (<code>billing/ipd/panel/{id}</code>) and click <strong>"Convert to IPD"</strong> in the top header.</div>
|
||||
<div class="quick-item"><strong>2. Bed & Doctor Transfer</strong>Select the target Inpatient Ward (General, Private, ICU) and confirming doctor. Previous ER/DC bed is released immediately.</div>
|
||||
<div class="quick-item"><strong>3. SBAR Handover</strong>Document Situation, Background, Assessment, and Recommendation for safe nursing & doctor handover.</div>
|
||||
</div>
|
||||
|
||||
<h4>The SBAR Clinical Handover Framework</h4>
|
||||
<p>NABH standards require structured clinical communication during patient transfers across care settings. The conversion modal enforces the SBAR format:</p>
|
||||
<div class="sbar-card"><span class="sbar-letter">S</span><strong>Situation:</strong> Reason for conversion (e.g. <em>"Post-operative bleeding following day-care laparoscopic hernia repair requiring exploratory laparotomy and overnight monitoring"</em>).</div>
|
||||
<div class="sbar-card"><span class="sbar-letter">B</span><strong>Background:</strong> Baseline clinical history, co-morbidities (e.g. hypertension, diabetes), and initial reason for presenting to ER or Day Care.</div>
|
||||
<div class="sbar-card"><span class="sbar-letter">A</span><strong>Assessment:</strong> Current vital signs, Glasgow Coma Scale (GCS), stat lab results, imaging impressions, and interventions administered in Casualty or Day Care Unit.</div>
|
||||
<div class="sbar-card"><span class="sbar-letter">R</span><strong>Recommendation:</strong> Inpatient monitoring orders, ICU/ward instructions, pending laboratory results, scheduled specialist consultations, and emergency escalation criteria.</div>
|
||||
|
||||
<div class="note"><strong>Preservation of Clinical History:</strong> The patient's record is updated with <code>converted_from_type</code> and <code>converted_from_code</code>. A permanent badge (e.g. <span class="badge bg-warning text-dark">Converted from ER (ER-2026-00012)</span>) is rendered across all clinical and billing screens for complete audit transparency.</div>
|
||||
|
||||
<h3>5. Unified IPD Billing with Distinct Group Headers</h3>
|
||||
<p>A core financial requirement of hospital operations is that when a patient converts from Day Care or Casualty to full IPD, <strong>no billing charges are lost or split into fragmented invoices</strong>. The entire financial account is unified under the Inpatient admission.</p>
|
||||
<ul>
|
||||
<li>All prior medications, dressings, doctor consultations, lab tests, and procedures delivered in the Emergency room remain intact under <code>EMERGENCY / CASUALTY CHARGES</code>.</li>
|
||||
<li>All surgical consumables, daycare room charges, and anesthesia charges delivered in the Day Care unit remain intact under <code>DAY CARE CHARGES</code>.</li>
|
||||
<li>Subsequent inpatient charges (Room Bed charges, ICU monitoring, Nursing care, Inpatient pharmacy) accumulate under standard IPD item types.</li>
|
||||
<li><strong>Final IPD Bill Print:</strong> The final discharge invoice organizes and prints these charges under clearly demarcated sub-headings with line-item rates, quantities, taxes, and category subtotals:
|
||||
<div class="table-responsive my-2">
|
||||
<table class="table table-sm table-bordered">
|
||||
<thead class="table-dark"><tr><th>Printed Section Header</th><th>Content & Scope</th><th>Typical Line Items</th></tr></thead>
|
||||
<tbody>
|
||||
<tr class="table-danger"><td><strong>EMERGENCY / CASUALTY CHARGES</strong></td><td>Services rendered during casualty stay</td><td>Casualty consultation, stat ECG, IV cannulation, oxygen, emergency trauma tray</td></tr>
|
||||
<tr class="table-info"><td><strong>DAY CARE CHARGES</strong></td><td>Interventions during daycare stay</td><td>Daycare bed rent, endoscopy tray, daycare nursing observation, dialyzer pack</td></tr>
|
||||
<tr class="table-primary"><td><strong>ROOM / BED CHARGES</strong></td><td>Inpatient ward/ICU charges post-conversion</td><td>General Ward Bed / Deluxe Room / ICU monitoring charges per diem</td></tr>
|
||||
<tr class="table-light"><td><strong>INVESTIGATIONS & LAB</strong></td><td>Pathology & imaging investigations</td><td>CBC, Serum Creatinine, Chest X-Ray, CT Scan, Ultrasound</td></tr>
|
||||
<tr class="table-light"><td><strong>SURGERY & PROCEDURES</strong></td><td>Inpatient operative interventions</td><td>Major surgery charges, surgeon fees, OT anesthesia, recovery charges</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</li>
|
||||
<li><strong>TPA & Mediclaim Compliance:</strong> Insurance third-party administrators (TPAs), Ayushman Bharat (PM-JAY), and corporate panels require clear demarcation of pre-admission casualty/daycare expenses from inpatient ward expenses. The unified bill provides 100% itemized transparency.</li>
|
||||
</ul>
|
||||
|
||||
<h3>6. Bed Management & Live Tracking</h3>
|
||||
<ul>
|
||||
<li><strong>Dedicated Casualty & Day Care Wards:</strong> Configured in <em>Settings → Bed Master</em>. Default wards <code>Casualty & Emergency Ward</code> (ER-01) and <code>Day Care Unit</code> (DC-01) are active. Additional day care wards (e.g. Dialysis Unit, Chemo Daycare) can be defined with ward type <code>Day Care</code>.</li>
|
||||
<li><strong>Current Admissions Filtering:</strong> In <em>Billing → IPD Admissions</em>, users can switch views using live pill buttons:
|
||||
<ul>
|
||||
<li><code>All</code> — Combined hospital bed census.</li>
|
||||
<li><code>Inpatient IPD</code> — Standard admitted inpatients.</li>
|
||||
<li><code>Day Care</code> — Patients occupying Day Care short-stay beds.</li>
|
||||
<li><code>Emergency</code> — Patients actively under Casualty / Triage observation.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>Automatic Bed Release:</strong> When <strong>Convert to IPD</strong> is executed, the patient's Casualty or Day Care bed is automatically flagged available (<code>VACANT</code>) for new arrivals, eliminating ghost bed blocks.</li>
|
||||
</ul>
|
||||
|
||||
<div class="safety"><strong>Clinical Safety Rule:</strong> Never overwrite a patient's admission code manually in the database. Always use the <strong>Convert to IPD</strong> modal so that SBAR handover notes, bed occupancy transfers, and HL7 FHIR encounter progressions are recorded in the audit trail.</div>
|
||||
</section>
|
||||
|
||||
<section id="ot-management" class="guide-section searchable-section">
|
||||
<h2><span class="section-number">07</span>OT Management</h2>
|
||||
<h2><span class="section-number">08</span>OT Management</h2>
|
||||
<p>Available from IPD Nursing and OT Surgery Queue. Request, scheduling, examination, status, and post-operative actions have separate permissions.</p>
|
||||
<ol class="workflow"><li><strong>Create request.</strong> Enter procedure, surgeon, priority, indication, and requested schedule.</li><li><strong>Complete pre-op examination.</strong> Use the department-specific form; revisions to completed forms require a reason.</li><li><strong>Schedule.</strong> Authorized OT staff assign date/time and coordination details.</li><li><strong>Call patient.</strong> Update the case when preparation begins.</li><li><strong>Complete safety gates.</strong> Confirm pre-op checks and WHO sign-in before starting.</li><li><strong>Record progress.</strong> Move through requested, scheduled, called, in progress, and completed; give reasons for cancellation or major changes.</li><li><strong>Sign post-op assessment.</strong> Record observations, outcome, instructions, and handover.</li></ol>
|
||||
<div class="safety"><strong>Clinical integrity:</strong> OT actions create event history and use version checks. Refresh if another user changed the case.</div>
|
||||
</section>
|
||||
|
||||
<section id="diagnosis" class="guide-section searchable-section">
|
||||
<h2><span class="section-number">08</span>Diagnosis, Laboratory & Imaging</h2>
|
||||
<h2><span class="section-number">09</span>Diagnosis, Laboratory & Imaging</h2>
|
||||
<p>Supported workflows include Pathology, Biopsy, X-Ray, Ultrasound, MRI, CT, and Echo.</p>
|
||||
<h3>Laboratory</h3><ol class="workflow"><li>Receive the OPD, IPD, or walk-in request and confirm patient/tests.</li><li>Record sample collection and lab number.</li><li>Enter values; review units, reference ranges, flags, and comments.</li><li>Save draft, then verify/sign according to policy.</li><li>Print individual or compiled reports using the correct template.</li></ol>
|
||||
<h3>Imaging</h3><ul><li>Upload images, scans, or DICOM studies against the report.</li><li>Use the viewer/gallery and draft findings/impression.</li><li>Complete doctor verification; AI suggestions require radiologist review.</li></ul>
|
||||
|
|
@ -165,33 +587,33 @@
|
|||
</section>
|
||||
|
||||
<section id="pharmacy" class="guide-section searchable-section">
|
||||
<h2><span class="section-number">09</span>Pharmacy / Medical Store</h2>
|
||||
<h2><span class="section-number">10</span>Pharmacy / Medical Store</h2>
|
||||
<h3>Dispensing</h3><ul><li>Create counter, OPD, IPD, or organization-linked invoices in the correct context.</li><li>Confirm medicine, batch, expiry, stock, rate, tax, discount, and quantity.</li><li>Use <strong>Medicine Return</strong> or <strong>Medicine Return 2</strong> for the applicable return flow.</li><li>Use Org. Credit Invoice for corporate/insurance-linked medicine billing.</li><li>Finalize an IPD medicine account only after invoices, returns, discounts, and payments reconcile.</li></ul>
|
||||
<h3>Inventory and purchase</h3><ul><li>Maintain drug, generic, formulation, category, company, supplier, batch, tax, and rate masters.</li><li>Create purchase orders, receive invoices/batches, and process purchase returns.</li><li>Monitor expiry, low stock, movement, and supplier ledger reports.</li></ul>
|
||||
<h3>Credit payout request</h3><ol class="workflow"><li>Open Pharmacy → Credit Payout Request.</li><li>Review/filter the eligible pool.</li><li>Select valid lines, confirm amounts, add remarks, and submit.</li><li>Monitor status while Finance handles approval and payment.</li></ol>
|
||||
</section>
|
||||
|
||||
<section id="billing" class="guide-section searchable-section">
|
||||
<h2><span class="section-number">10</span>Billing, Invoices & Payments</h2>
|
||||
<h2><span class="section-number">11</span>Billing, Invoices & Payments</h2>
|
||||
<ul><li>Bill OPD consultation, procedures, diagnostics, IPD charges, packages, and pharmacy links.</li><li>Apply discounts only with authority and supporting remarks.</li><li>Use payment/refund request workflows instead of informal adjustments.</li><li>Confirm organization/insurance case before posting payer charges.</li><li>Print/export daily collection and balance reports for reconciliation.</li></ul>
|
||||
<h3>Payment Edit</h3><p>Authorized users can correct payment mode, amount, or recorded user. Amount may be <strong>0.00</strong> for a valid correction but cannot be negative.</p>
|
||||
<div class="safety"><strong>Do not use Payment Edit to hide a refund or cancellation.</strong> Use the approved reversal workflow and retain the reason.</div>
|
||||
</section>
|
||||
|
||||
<section id="finance" class="guide-section searchable-section">
|
||||
<h2><span class="section-number">11</span>Finance & Accounting</h2>
|
||||
<h2><span class="section-number">12</span>Finance & Accounting</h2>
|
||||
<div class="quick-grid"><div class="quick-item"><strong>Cash Book</strong>Daily department collections and opening/closing position.</div><div class="quick-item"><strong>Bank Deposits</strong>Deposit references, dates, amounts, and status.</div><div class="quick-item"><strong>Reconciliation</strong>Match statements to system transactions.</div><div class="quick-item"><strong>POS Settlement</strong>Track terminal settlements, charges, and differences.</div><div class="quick-item"><strong>Doctor Payouts</strong>Agreements, commission, approval, and disbursement.</div><div class="quick-item"><strong>Vendor Control</strong>PO, receipt, invoice, payment, and balance.</div></div>
|
||||
<p class="mt-3">Review pharmacy payout line evidence, approve/reject according to policy, record payments, and retain remarks for each status change.</p>
|
||||
</section>
|
||||
|
||||
<section id="insurance" class="guide-section searchable-section">
|
||||
<h2><span class="section-number">12</span>Insurance & Organization Cases</h2>
|
||||
<h2><span class="section-number">13</span>Insurance & Organization Cases</h2>
|
||||
<ol class="workflow"><li>Create/select the case and verify patient, payer, policy, approval, and validity.</li><li>Link OPD, IPD, diagnosis, procedures, and pharmacy invoices.</li><li>Apply approved rates/packages, including Ayushman mappings.</li><li>Use contingent billing when liability depends on case outcome.</li><li>Prepare claim sheets and supporting documents.</li><li>Track submission, query, approval, deduction, payment, and balance.</li></ol>
|
||||
<div class="warning"><strong>Case selection:</strong> Select a valid system case. Empty or placeholder identifiers are not payer cases.</div>
|
||||
</section>
|
||||
|
||||
<section id="abdm" class="guide-section searchable-section">
|
||||
<h2><span class="section-number">13</span>ABDM, ABHA & Health Records</h2>
|
||||
<h2><span class="section-number">14</span>ABDM, ABHA & Health Records</h2>
|
||||
<h3>ABHA identity</h3><ul><li>Create ABHA through supported Aadhaar/mobile OTP flows.</li><li>Verify/fetch profile before linking to the HMS patient.</li><li>Display/download ABHA card and use facility QR for Scan & Share.</li></ul>
|
||||
<h3>Care context and sharing</h3><ol class="workflow"><li>Confirm verified ABHA and correct visit/admission.</li><li>Discover/link the care context.</li><li>Select e-Prescription, Discharge Summary, Diagnostic Report, Immunization, Wellness, Health Document, or supported invoice.</li><li>Preview FHIR R4 data: identity, dates, practitioner, coding, and attachments.</li><li>Share and monitor gateway/bridge status.</li></ol>
|
||||
<h3>Consent and HIU</h3><ul><li>Request consent only for a legitimate purpose and date range.</li><li>Track requested, granted, denied, expired, and revoked status.</li><li>Fetch health information only after grant; data may contain FHIR documents and attachments.</li></ul>
|
||||
|
|
@ -199,36 +621,36 @@
|
|||
</section>
|
||||
|
||||
<section id="stock" class="guide-section searchable-section">
|
||||
<h2><span class="section-number">14</span>Hospital Stock</h2>
|
||||
<h2><span class="section-number">15</span>Hospital Stock</h2>
|
||||
<ol class="workflow"><li>Configure category, item, units, conversion, location, min/reorder, and expiry control.</li><li>Departments raise indents with quantity and purpose.</li><li>Authorized users approve, reject, or revise.</li><li>Storekeepers issue approved quantities and batches.</li><li>Create purchase orders and goods receipts.</li><li>Review consumption, issues, expiry, low-stock, reorder, and daily-use reports.</li></ol>
|
||||
<div class="note">Purchase and issue units may differ. Wrong conversion causes incorrect stock and reorder quantities.</div>
|
||||
</section>
|
||||
|
||||
<section id="reports" class="guide-section searchable-section">
|
||||
<h2><span class="section-number">15</span>Reports & Dashboards</h2>
|
||||
<h2><span class="section-number">16</span>Reports & Dashboards</h2>
|
||||
<div class="table-responsive"><table class="table table-bordered table-guide"><thead><tr><th>Area</th><th>Examples</th><th>Review focus</th></tr></thead><tbody><tr><td>Clinical</td><td>OPD, diagnosis workload/TAT, discharge, OT queue</td><td>Completeness and delays</td></tr><tr><td>Billing</td><td>Collections, balances, refunds, credit invoices</td><td>Mode/user totals and exceptions</td></tr><tr><td>Finance</td><td>Cash book, bank audit, POS, doctor payout</td><td>Unmatched/unapproved items</td></tr><tr><td>Pharmacy</td><td>Stock, expiry, movement, sales, returns, supplier ledger</td><td>Negative stock and expiry risk</td></tr><tr><td>Insurance</td><td>Organization dues, claims, deductions</td><td>Aging and documents</td></tr><tr><td>Audit</td><td>NABH edits, billing logs, user actions</td><td>Who changed what, when, and why</td></tr></tbody></table></div>
|
||||
<p>Use drill-down links from totals to source records. Retain the same dates and filters while reconciling.</p>
|
||||
</section>
|
||||
|
||||
<section id="admin" class="guide-section searchable-section">
|
||||
<h2><span class="section-number">16</span>Administration & System Operations</h2>
|
||||
<h2><span class="section-number">17</span>Administration & System Operations</h2>
|
||||
<h3>Application administration</h3><ul><li>Manage users, groups, permissions, passwords, and active access.</li><li>Maintain doctor, referral, insurance, bank/payment, charge, diagnosis, medicine, and stock masters.</li><li>Configure OPD, diagnosis, and discharge print templates.</li><li>Configure AI, HealthPlix, ABDM HFR/gateway/bridge, and report-doctor mappings.</li></ul>
|
||||
<h3>System Operations</h3><ul><li>Review OS/uptime, CPU, memory, disk, RAID, database, web, PHP-FPM, and SSH health.</li><li>Use Update HMS only during an approved window and review its log.</li><li>Restart services only when needed. Reboot/shutdown require confirmation and coordination.</li></ul>
|
||||
<div class="safety"><strong>Production safety:</strong> Confirm backup and rollback readiness before migrations, updates, restarts, or environment changes.</div>
|
||||
</section>
|
||||
|
||||
<section id="security" class="guide-section searchable-section">
|
||||
<h2><span class="section-number">17</span>Security, Audit & Compliance</h2>
|
||||
<h2><span class="section-number">18</span>Security, Audit & Compliance</h2>
|
||||
<ul><li>Never disclose passwords, OTPs, API keys, or tokens.</li><li>Single-active-session protection may end an older login.</li><li>Grant the least permission required and review access after role changes.</li><li>Clinical verification, payment edits, refunds, cancellations, OT changes, and completed-form revisions require audit evidence.</li><li>Verify SNOMED CT/ICD-10 codes against documented clinical meaning.</li><li>Follow NABH, ABDM/FHIR, NHCX, privacy, retention, and hospital policies.</li></ul>
|
||||
</section>
|
||||
|
||||
<section id="backup" class="guide-section searchable-section">
|
||||
<h2><span class="section-number">18</span>Backup & Maintenance</h2>
|
||||
<h2><span class="section-number">19</span>Backup & Maintenance</h2>
|
||||
<ul><li>Schedule database and uploaded-document backups and test restoration.</li><li>Retain an off-server/off-site copy according to policy.</li><li>Apply code updates and migrations in a controlled sequence.</li><li>After deployment test login, dashboard, patient search, billing, PDF, uploads, jobs, and integrations.</li><li>Monitor storage, database growth, logs, failed queues, ABDM tasks, and large attachments.</li><li>Keep production error display disabled; use protected logs.</li></ul>
|
||||
</section>
|
||||
|
||||
<section id="troubleshooting" class="guide-section searchable-section">
|
||||
<h2><span class="section-number">19</span>Troubleshooting</h2>
|
||||
<h2><span class="section-number">20</span>Troubleshooting</h2>
|
||||
<div class="table-responsive"><table class="table table-bordered table-guide"><thead><tr><th>Problem</th><th>Check</th><th>Action</th></tr></thead><tbody>
|
||||
<tr><td>Access denied</td><td>Role, permission, module</td><td>Ask Admin; do not borrow credentials.</td></tr>
|
||||
<tr><td>Page/form does not load</td><td>Session, network, URL/status, console</td><td>Refresh dashboard, reopen, report exact URL/status.</td></tr>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user