diff --git a/app/Config/Routes.php b/app/Config/Routes.php
index 93b9a4d..2f33878 100644
--- a/app/Config/Routes.php
+++ b/app/Config/Routes.php
@@ -322,6 +322,15 @@ $routes->get('Finance/scroll_items_table', 'Finance::scrollItemsTable');
$routes->get('Finance/bank_deposits', 'Finance::bankDeposits');
$routes->get('Finance/bank_deposits_table', 'Finance::bankDepositsTable');
$routes->get('Finance/bank_audit', 'Finance::bankAudit');
+$routes->get('Finance/medical_store_credit_account', 'Finance::medicalStoreCreditAccount');
+$routes->get('Finance/medical_store_requests_table', 'Finance::medicalStoreRequestsTable');
+$routes->get('Finance/medical_store_dashboard_card', 'Finance::medicalStoreDashboardCard');
+$routes->get('Finance/medical_store_request_detail/(:num)', 'Finance::medicalStoreRequestDetail/$1');
+$routes->get('Finance/medical_store_request_lines_table', 'Finance::medicalStoreRequestLinesTable');
+$routes->post('Finance/medical_store_request_review', 'Finance::medicalStoreRequestReview');
+$routes->post('Finance/medical_store_request_approve', 'Finance::medicalStoreRequestApprove');
+$routes->post('Finance/medical_store_request_reject', 'Finance::medicalStoreRequestReject');
+$routes->post('Finance/medical_store_payment_create', 'Finance::medicalStorePaymentCreate');
$routes->get('Finance/payout/opd-consult', 'Finance::payoutOpdConsult');
$routes->get('Finance/payout/opd-consult-summary', 'Finance::payoutOpdConsultSummary');
$routes->get('Finance/payout/opd-consult-drafts-table', 'Finance::payoutOpdConsultDraftsTable');
diff --git a/app/Controllers/Finance.php b/app/Controllers/Finance.php
index 3fdc0e8..966dc93 100644
--- a/app/Controllers/Finance.php
+++ b/app/Controllers/Finance.php
@@ -19,6 +19,11 @@ use App\Models\FinanceScrollSubmissionItemModel;
use App\Models\FinanceScrollSubmissionModel;
use App\Models\FinanceVendorInvoiceModel;
use App\Models\FinancePharmacyBillModel;
+use App\Models\FinancePayoutRequestAuditModel;
+use App\Models\FinancePayoutRequestLineModel;
+use App\Models\FinancePayoutRequestModel;
+use App\Models\FinanceOutgoingPaymentAllocationModel;
+use App\Models\FinanceOutgoingPaymentHistoryModel;
use App\Models\FinanceVendorModel;
class Finance extends BaseController
@@ -40,6 +45,11 @@ class Finance extends BaseController
private FinanceDoctorAgreementModel $doctorAgreementModel;
private FinanceDoctorPayoutModel $doctorPayoutModel;
private FinancePharmacyBillModel $pharmBillModel;
+ private FinancePayoutRequestModel $payoutRequestModel;
+ private FinancePayoutRequestLineModel $payoutRequestLineModel;
+ private FinanceOutgoingPaymentHistoryModel $outgoingPaymentHistoryModel;
+ private FinanceOutgoingPaymentAllocationModel $outgoingPaymentAllocationModel;
+ private FinancePayoutRequestAuditModel $payoutRequestAuditModel;
public function __construct()
{
@@ -60,6 +70,11 @@ class Finance extends BaseController
$this->doctorAgreementModel = new FinanceDoctorAgreementModel();
$this->doctorPayoutModel = new FinanceDoctorPayoutModel();
$this->pharmBillModel = new FinancePharmacyBillModel();
+ $this->payoutRequestModel = new FinancePayoutRequestModel();
+ $this->payoutRequestLineModel = new FinancePayoutRequestLineModel();
+ $this->outgoingPaymentHistoryModel = new FinanceOutgoingPaymentHistoryModel();
+ $this->outgoingPaymentAllocationModel = new FinanceOutgoingPaymentAllocationModel();
+ $this->payoutRequestAuditModel = new FinancePayoutRequestAuditModel();
}
public function bankDeposits()
@@ -3487,6 +3502,16 @@ class Finance extends BaseController
return trim((string) ($user->username ?? 'System'));
}
+ private function currentUserId(): ?int
+ {
+ $user = function_exists('auth') ? auth()->user() : null;
+ if (! $user || ! isset($user->id)) {
+ return null;
+ }
+
+ return (int) $user->id;
+ }
+
private function fetchCashTransactions(): array
{
return $this->cashTxnModel->orderBy('id', 'DESC')->findAll(15);
@@ -3587,6 +3612,399 @@ class Finance extends BaseController
];
}
+ public function medicalStoreCreditAccount()
+ {
+ if (! $this->canFinanceAny(['finance.workflow.view', 'finance.cash.accounts.accept', 'finance.cash.accounts.verify', 'finance.doctor_payout.manage'])) {
+ return $this->response->setStatusCode(403)->setBody('Access denied');
+ }
+
+ $summary = [
+ 'total_requested' => 0.0,
+ 'total_paid' => 0.0,
+ 'total_pending' => 0.0,
+ 'open_requests' => 0,
+ ];
+ $rows = [];
+
+ if ($this->db->tableExists('finance_payout_requests')) {
+ $summaryRow = $this->db->table('finance_payout_requests')
+ ->select('COALESCE(SUM(requested_amount),0) AS total_requested, COALESCE(SUM(paid_amount),0) AS total_paid, COALESCE(SUM(pending_amount),0) AS total_pending', false)
+ ->where('request_type', 'medical_store_credit')
+ ->get()->getRowArray();
+
+ $summary['total_requested'] = (float) ($summaryRow['total_requested'] ?? 0);
+ $summary['total_paid'] = (float) ($summaryRow['total_paid'] ?? 0);
+ $summary['total_pending'] = (float) ($summaryRow['total_pending'] ?? 0);
+ $summary['open_requests'] = $this->db->table('finance_payout_requests')
+ ->where('request_type', 'medical_store_credit')
+ ->whereIn('status', ['submitted', 'finance_review', 'approved', 'partially_paid'])
+ ->countAllResults();
+
+ $rows = $this->payoutRequestModel
+ ->where('request_type', 'medical_store_credit')
+ ->orderBy('id', 'DESC')
+ ->findAll(60);
+ }
+
+ return view('finance/medical_store_credit_account', [
+ 'summary' => $summary,
+ 'rows' => $rows,
+ ]);
+ }
+
+ public function medicalStoreRequestsTable()
+ {
+ if (! $this->canFinanceAny(['finance.workflow.view', 'finance.cash.accounts.accept', 'finance.cash.accounts.verify', 'finance.doctor_payout.manage'])) {
+ return $this->response->setStatusCode(403)->setBody('Access denied');
+ }
+
+ $status = trim((string) ($this->request->getGet('status') ?? ''));
+ $rows = [];
+ if ($this->db->tableExists('finance_payout_requests')) {
+ $builder = $this->payoutRequestModel
+ ->where('request_type', 'medical_store_credit')
+ ->orderBy('id', 'DESC');
+ if ($status !== '') {
+ $builder->where('status', $status);
+ }
+ $rows = $builder->findAll(200);
+ }
+
+ return view('finance/partials/medical_store_requests_table', ['rows' => $rows]);
+ }
+
+ public function medicalStoreRequestLinesTable()
+ {
+ if (! $this->canFinanceAny(['finance.workflow.view', 'finance.cash.accounts.accept', 'finance.cash.accounts.verify', 'finance.doctor_payout.manage'])) {
+ return $this->response->setStatusCode(403)->setBody('Access denied');
+ }
+
+ $requestId = (int) ($this->request->getGet('request_id') ?? 0);
+ if ($requestId <= 0) {
+ return view('finance/partials/medical_store_request_lines_table', [
+ 'request' => null,
+ 'lines' => [],
+ 'payments' => [],
+ ]);
+ }
+
+ $request = $this->payoutRequestModel->find($requestId);
+ $lines = $this->db->tableExists('finance_payout_request_lines')
+ ? $this->payoutRequestLineModel->where('request_id', $requestId)->orderBy('line_order', 'ASC')->orderBy('id', 'ASC')->findAll()
+ : [];
+ $payments = $this->db->tableExists('finance_outgoing_payment_history')
+ ? $this->outgoingPaymentHistoryModel->where('request_id', $requestId)->orderBy('id', 'DESC')->findAll()
+ : [];
+
+ return view('finance/partials/medical_store_request_lines_table', [
+ 'request' => $request,
+ 'lines' => $lines,
+ 'payments' => $payments,
+ ]);
+ }
+
+ public function medicalStoreRequestDetail($requestId = 0)
+ {
+ if (! $this->canFinanceAny(['finance.workflow.view', 'finance.cash.accounts.accept', 'finance.cash.accounts.verify', 'finance.doctor_payout.manage'])) {
+ return $this->response->setStatusCode(403)->setJSON(['status' => 0, 'message' => 'Access denied']);
+ }
+
+ $requestId = (int) $requestId;
+ if ($requestId <= 0) {
+ return $this->response->setStatusCode(422)->setJSON(['status' => 0, 'message' => 'Request ID required']);
+ }
+
+ $request = $this->payoutRequestModel->find($requestId);
+ if (! is_array($request)) {
+ return $this->response->setStatusCode(404)->setJSON(['status' => 0, 'message' => 'Request not found']);
+ }
+
+ $lines = $this->db->tableExists('finance_payout_request_lines')
+ ? $this->payoutRequestLineModel->where('request_id', $requestId)->orderBy('line_order', 'ASC')->orderBy('id', 'ASC')->findAll()
+ : [];
+ $payments = $this->db->tableExists('finance_outgoing_payment_history')
+ ? $this->outgoingPaymentHistoryModel->where('request_id', $requestId)->orderBy('id', 'DESC')->findAll()
+ : [];
+
+ return $this->response->setJSON([
+ 'status' => 1,
+ 'request' => $request,
+ 'lines' => $lines,
+ 'payments' => $payments,
+ ]);
+ }
+
+ public function medicalStoreRequestReview()
+ {
+ return $this->transitionMedicalStoreRequestStatus('finance_review', ['submitted']);
+ }
+
+ public function medicalStoreRequestApprove()
+ {
+ return $this->transitionMedicalStoreRequestStatus('approved', ['finance_review']);
+ }
+
+ public function medicalStoreRequestReject()
+ {
+ return $this->transitionMedicalStoreRequestStatus('rejected', ['finance_review', 'submitted']);
+ }
+
+ public function medicalStorePaymentCreate()
+ {
+ if (! $this->canFinanceAny(['finance.cash.accounts.accept', 'finance.cash.accounts.verify', 'finance.doctor_payout.manage'])) {
+ return $this->response->setStatusCode(403)->setJSON(['status' => 0, 'message' => 'Access denied']);
+ }
+
+ $requestId = (int) ($this->request->getPost('request_id') ?? 0);
+ $amount = round((float) ($this->request->getPost('amount') ?? 0), 2);
+ $paymentMode = (int) ($this->request->getPost('payment_mode') ?? 1);
+ $remark = trim((string) ($this->request->getPost('remark') ?? ''));
+
+ if ($requestId <= 0 || $amount <= 0) {
+ return $this->response->setStatusCode(422)->setJSON(['status' => 0, 'message' => 'Request and amount are required.']);
+ }
+
+ $request = $this->payoutRequestModel->find($requestId);
+ if (! is_array($request)) {
+ return $this->response->setStatusCode(404)->setJSON(['status' => 0, 'message' => 'Request not found.']);
+ }
+
+ $status = (string) ($request['status'] ?? '');
+ if (! in_array($status, ['approved', 'partially_paid'], true)) {
+ return $this->response->setStatusCode(422)->setJSON(['status' => 0, 'message' => 'Only approved or partially paid requests can be paid.']);
+ }
+
+ $pending = round((float) ($request['pending_amount'] ?? 0), 2);
+ if ($pending <= 0) {
+ return $this->response->setStatusCode(422)->setJSON(['status' => 0, 'message' => 'Request has no pending amount.']);
+ }
+ if ($amount > $pending) {
+ return $this->response->setStatusCode(422)->setJSON(['status' => 0, 'message' => 'Payment amount exceeds pending amount.']);
+ }
+
+ $userId = $this->currentUserId();
+ $userName = $this->currentUserName();
+ $now = date('Y-m-d H:i:s');
+ $paymentNo = 'MSP-' . date('YmdHis') . '-' . random_int(1000, 9999);
+
+ $this->db->transStart();
+ $this->outgoingPaymentHistoryModel->insert([
+ 'payment_no' => $paymentNo,
+ 'payout_type' => 'medical_store',
+ 'payee_label' => 'Medical Store Credit Settlement',
+ 'request_id' => $requestId,
+ 'payment_date' => $now,
+ 'amount' => $amount,
+ 'payment_mode' => $paymentMode,
+ 'pay_bank_id' => (int) ($this->request->getPost('pay_bank_id') ?? 0),
+ 'card_bank' => trim((string) ($this->request->getPost('card_bank') ?? '')),
+ 'card_remark' => trim((string) ($this->request->getPost('card_remark') ?? '')),
+ 'cust_card' => trim((string) ($this->request->getPost('cust_card') ?? '')),
+ 'card_tran_id' => trim((string) ($this->request->getPost('card_tran_id') ?? '')),
+ 'bankcard_machine' => trim((string) ($this->request->getPost('bankcard_machine') ?? '')),
+ 'insert_code' => trim((string) ($this->request->getPost('insert_code') ?? '')),
+ 'credit_debit' => 0,
+ 'remark' => $remark,
+ 'status' => 'paid',
+ 'created_by' => $userName,
+ 'created_by_id' => $userId,
+ 'created_at' => $now,
+ 'updated_at' => $now,
+ ]);
+
+ $paymentId = (int) $this->outgoingPaymentHistoryModel->getInsertID();
+ $openLines = $this->payoutRequestLineModel
+ ->where('request_id', $requestId)
+ ->where('pending_amount >', 0)
+ ->orderBy('line_order', 'ASC')
+ ->orderBy('id', 'ASC')
+ ->findAll();
+
+ $remaining = $amount;
+ $allocOrder = 1;
+ foreach ($openLines as $line) {
+ $lineId = (int) ($line['id'] ?? 0);
+ $linePending = round((float) ($line['pending_amount'] ?? 0), 2);
+ if ($lineId <= 0 || $linePending <= 0 || $remaining <= 0) {
+ continue;
+ }
+
+ $alloc = round(min($linePending, $remaining), 2);
+ $this->outgoingPaymentAllocationModel->insert([
+ 'payment_id' => $paymentId,
+ 'request_id' => $requestId,
+ 'request_line_id' => $lineId,
+ 'allocated_amount' => $alloc,
+ 'allocation_order' => $allocOrder,
+ 'allocation_note' => 'Auto FIFO allocation',
+ 'created_by' => $userName,
+ 'created_by_id' => $userId,
+ 'created_at' => $now,
+ ]);
+
+ $newAllocated = round((float) ($line['allocated_amount'] ?? 0) + $alloc, 2);
+ $newPending = round(max(0, (float) ($line['line_amount'] ?? 0) - $newAllocated), 2);
+ $newLineStatus = $newPending <= 0 ? 'closed' : ($newAllocated > 0 ? 'partial' : 'open');
+ $this->payoutRequestLineModel->update($lineId, [
+ 'allocated_amount' => $newAllocated,
+ 'pending_amount' => $newPending,
+ 'line_status' => $newLineStatus,
+ 'updated_at' => $now,
+ ]);
+
+ $remaining = round($remaining - $alloc, 2);
+ $allocOrder++;
+ }
+
+ if ($remaining > 0) {
+ $this->db->transRollback();
+ return $this->response->setStatusCode(422)->setJSON(['status' => 0, 'message' => 'Payment exceeds open line capacity.']);
+ }
+
+ $this->recalculateMedicalStoreRequestTotals($requestId, $userId, $userName);
+ $this->logPayoutRequestAudit($requestId, 'payment_create', $status, null, 'Payment #' . $paymentNo . ' amount: ' . number_format($amount, 2, '.', ''));
+ $this->db->transComplete();
+
+ if (! $this->db->transStatus()) {
+ return $this->response->setStatusCode(500)->setJSON(['status' => 0, 'message' => 'Failed to save payout payment.']);
+ }
+
+ return $this->response->setJSON([
+ 'status' => 1,
+ 'message' => 'Payment saved and allocated successfully.',
+ 'payment_id' => $paymentId,
+ 'payment_no' => $paymentNo,
+ ]);
+ }
+
+ public function medicalStoreDashboardCard()
+ {
+ if (! $this->canFinanceAny(['finance.workflow.view', 'finance.cash.accounts.accept', 'finance.cash.accounts.verify', 'finance.doctor_payout.manage'])) {
+ return $this->response->setStatusCode(403)->setJSON(['status' => 0, 'message' => 'Access denied']);
+ }
+
+ if (! $this->db->tableExists('finance_payout_requests')) {
+ return $this->response->setJSON([
+ 'status' => 1,
+ 'summary' => ['total_requested' => 0, 'total_paid' => 0, 'total_pending' => 0, 'open_requests' => 0],
+ ]);
+ }
+
+ $summaryRow = $this->db->table('finance_payout_requests')
+ ->select('COALESCE(SUM(requested_amount),0) AS total_requested, COALESCE(SUM(paid_amount),0) AS total_paid, COALESCE(SUM(pending_amount),0) AS total_pending', false)
+ ->where('request_type', 'medical_store_credit')
+ ->get()->getRowArray();
+ $openRequests = $this->db->table('finance_payout_requests')
+ ->where('request_type', 'medical_store_credit')
+ ->whereIn('status', ['submitted', 'finance_review', 'approved', 'partially_paid'])
+ ->countAllResults();
+
+ return $this->response->setJSON([
+ 'status' => 1,
+ 'summary' => [
+ 'total_requested' => (float) ($summaryRow['total_requested'] ?? 0),
+ 'total_paid' => (float) ($summaryRow['total_paid'] ?? 0),
+ 'total_pending' => (float) ($summaryRow['total_pending'] ?? 0),
+ 'open_requests' => $openRequests,
+ ],
+ ]);
+ }
+
+ private function transitionMedicalStoreRequestStatus(string $newStatus, array $allowedFrom)
+ {
+ if (! $this->canFinanceAny(['finance.workflow.view', 'finance.cash.accounts.accept', 'finance.cash.accounts.verify', 'finance.doctor_payout.manage'])) {
+ return $this->response->setStatusCode(403)->setJSON(['status' => 0, 'message' => 'Access denied']);
+ }
+
+ $requestId = (int) ($this->request->getPost('request_id') ?? 0);
+ $remark = trim((string) ($this->request->getPost('remark') ?? ''));
+ if ($requestId <= 0) {
+ return $this->response->setStatusCode(422)->setJSON(['status' => 0, 'message' => 'Request ID required.']);
+ }
+
+ $request = $this->payoutRequestModel->find($requestId);
+ if (! is_array($request)) {
+ return $this->response->setStatusCode(404)->setJSON(['status' => 0, 'message' => 'Request not found.']);
+ }
+
+ $oldStatus = (string) ($request['status'] ?? '');
+ if (! in_array($oldStatus, $allowedFrom, true)) {
+ return $this->response->setStatusCode(422)->setJSON(['status' => 0, 'message' => 'Invalid status transition from ' . $oldStatus . ' to ' . $newStatus . '.']);
+ }
+
+ $now = date('Y-m-d H:i:s');
+ $update = [
+ 'status' => $newStatus,
+ 'updated_at' => $now,
+ 'updated_by' => $this->currentUserName(),
+ 'updated_by_id' => $this->currentUserId(),
+ ];
+ if ($newStatus === 'finance_review') {
+ $update['finance_reviewed_at'] = $now;
+ }
+ if ($newStatus === 'approved') {
+ $update['approved_at'] = $now;
+ if ((float) ($request['approved_amount'] ?? 0) <= 0) {
+ $update['approved_amount'] = (float) ($request['requested_amount'] ?? 0);
+ }
+ }
+
+ $this->payoutRequestModel->update($requestId, $update);
+ $this->logPayoutRequestAudit($requestId, 'status_change', $oldStatus, $newStatus, $remark);
+
+ return $this->response->setJSON(['status' => 1, 'message' => 'Request status updated to ' . $newStatus . '.']);
+ }
+
+ private function recalculateMedicalStoreRequestTotals(int $requestId, ?int $userId = null, ?string $userName = null): void
+ {
+ $sumLines = $this->db->table('finance_payout_request_lines')
+ ->select('COALESCE(SUM(line_amount),0) AS line_total, COALESCE(SUM(allocated_amount),0) AS allocated_total, COALESCE(SUM(pending_amount),0) AS pending_total', false)
+ ->where('request_id', $requestId)
+ ->get()->getRowArray();
+
+ $requested = round((float) ($sumLines['line_total'] ?? 0), 2);
+ $paid = round((float) ($sumLines['allocated_total'] ?? 0), 2);
+ $pending = round((float) ($sumLines['pending_total'] ?? 0), 2);
+ $newStatus = $paid <= 0 ? 'approved' : ($pending > 0 ? 'partially_paid' : 'paid');
+ $update = [
+ 'requested_amount' => $requested,
+ 'paid_amount' => $paid,
+ 'pending_amount' => $pending,
+ 'status' => $newStatus,
+ 'updated_at' => date('Y-m-d H:i:s'),
+ ];
+
+ if ($userName !== null) {
+ $update['updated_by'] = $userName;
+ }
+ if ($userId !== null) {
+ $update['updated_by_id'] = $userId;
+ }
+ if ($newStatus === 'paid') {
+ $update['closed_at'] = date('Y-m-d H:i:s');
+ }
+
+ $this->payoutRequestModel->update($requestId, $update);
+ }
+
+ private function logPayoutRequestAudit(int $requestId, string $actionType, ?string $oldStatus, ?string $newStatus, string $note = ''): void
+ {
+ if (! $this->db->tableExists('finance_payout_request_audit')) {
+ return;
+ }
+
+ $this->payoutRequestAuditModel->insert([
+ 'request_id' => $requestId,
+ 'action_type' => $actionType,
+ 'old_status' => $oldStatus,
+ 'new_status' => $newStatus,
+ 'action_note' => $note !== '' ? $note : null,
+ 'action_by' => $this->currentUserName(),
+ 'action_by_id' => $this->currentUserId(),
+ 'action_at' => date('Y-m-d H:i:s'),
+ ]);
+ }
+
private function fetchCollectorOptions(): array
{
if (! method_exists($this->db, 'tableExists') || ! $this->db->tableExists('payment_history')) {
diff --git a/public/help.html b/public/help.html
index 82f2b97..a9e20aa 100644
--- a/public/help.html
+++ b/public/help.html
@@ -3,121 +3,264 @@
- HMS Software Help Guide
+
+ E-Atria HMS Help & Operations Guide
+
-
-
-
-
-
HMS Software Help Guide
-
Version 1.0 | Last updated: 2026-04-02
+
+
-
Contents
-
+
+ No guide section matches your search.
+
+
-
1. Getting Started
-
- Login with your assigned user ID.
- Use the left sidebar to open modules.
- Use Dashboard for daily status.
- Use My Profile to verify your name and login details.
-
+
+
+ 01 Recent Updates
+ These functions were added or strengthened after the previous help guide was published.
+ OT Management Phase 1 NEW 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.
+ Department-specific pre-operative forms 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.
+ IPD Nursing and discharge controls Dedicated nursing access, clinical workspace improvements, configurable discharge content, diagnosis masters, and more reliable HTML/PDF template rendering are available.
+ Controlled Payment Edit Authorized finance users can correct payment mode, amount, and recorded user. Zero-value corrections are supported; negative values remain blocked.
+ Pharmacy credit payout workflow Pharmacy can review the eligible medical-store credit pool, create payout requests, submit them to Finance, and monitor submitted, approved, partially paid, paid, or rejected status.
+ Stronger session and permission controls Single-active-session protection, clearer access-denied pages, streamlined permission management, and dedicated permissions for payment editing, nursing, discharge, and OT actions are active.
+ Clinical drill-down dashboards Management dashboards support navigation from summary indicators to underlying operational and clinical records.
+ Pharmacy usability fixes Credit payout routes, Medicine Return labels, and the Org. Credit Invoice empty-grid behavior have been corrected.
+ Deployment note: New database-backed features require their migrations and permissions to be applied by the system administrator.
+
-
2. User Roles and Access
-
Access is permission-based. Typical groups include Admin, Billing users, Diagnosis users, and Stock roles (Stock Manager, Department Head, Storekeeper). If you see "Access denied", contact administrator to assign correct permission.
+
+ 02 Getting Started
+
+
1. Sign in Use only your assigned account. Shared accounts weaken clinical and financial audit records.
+
2. Select a module Use the sidebar for Patient, OPD, IPD, Diagnosis, Pharmacy, Finance, Stock, Reports, or Settings.
+
3. Search first Search by patient code, name, mobile, ABHA, invoice, IPD, or case code before creating a duplicate.
+
4. Verify context Confirm patient, encounter, department, payer, and date before saving clinical or billing data.
+
5. Complete the workflow Draft, verify, approve, print, or submit as required. A draft is not always final.
+
6. Sign out Use the profile menu, especially on shared workstations.
+
+ Common screen behavior
+ Most pages open inside the main workspace without a full browser refresh. Filters affect the current list. Clear them before concluding a record is missing. PDF/print buttons may open a new tab; allow pop-ups for the HMS domain. Red actions may cancel, delete, refund, reject, or finalize and require extra care.
+ Before finalization: Recheck identifiers, dates, amounts, medicine batches, clinical findings, and print template. Final records may require permission and an edit reason to change.
+
-
3. Daily Operation Flow
-
- Patient registration and OPD/IPD workflow.
- Charge posting and invoice generation.
- Payment collection and receipt.
- Lab/radiology processing and report verification.
- Inventory issue/receipt for consumables and medicines.
-
+
+ 03 User Roles & Access
+ Menus and actions are permission-based. Two users may open the same patient but see different buttons.
+ Role area Typical responsibility Restricted actions
+ Registration Patient search, registration, appointment, token, admission support Clinical verification and financial correction
+ Doctor / Clinical Consultation, diagnosis, prescription, orders, discharge Finance approval and stock administration
+ Nursing / OT Nursing workspace, bedside care, safety checks, OT status, handover Scheduling or signed assessment unless assigned
+ Diagnosis Sample workflow, findings, imaging, verification, report printing Verified-report edits without reason/permission
+ Pharmacy Dispensing, returns, inventory, purchasing, supplier and credit workflows Finance payout approval
+ Billing / Finance Invoices, collections, refunds, reconciliation, payouts Payment edits without finance.payment.edit
+ Administrator Users, permissions, masters, templates, integrations, operations Infrastructure actions still require confirmation
+
+ For Access denied , note the module and action and ask an administrator to review the role. Never use another person's login.
+
-
4. Billing and Cash Flow
-
- Use Billing -> Patient List for patient-level work.
- Use OPD Invoice and Charges Invoice to post/collect payments.
- Use IPD Billing for admitted patients, package and discharge flow.
- Use Payment Request and Refund Request for controlled approvals.
- Use report screens for daily reconciliation.
-
+
+ 04 Patient Management
+ Search first. Use UHID/patient code, name, phone, Aadhaar reference, old UHID, or ABHA.Review matches. Compare DOB, gender, guardian, address, and contact number.Create or update. Enter demographics, identity documents, photo, guardian/family data, and attachments.Open the profile. Use it for OPD, IPD, billing, diagnosis, documents, and health-record history.Link ABHA. Verify that the ABHA profile belongs to the same patient first.
+ Duplicate prevention: Try alternate spelling, phone, old code, and family details before creating a new patient.
+
-
5. Diagnosis and Reports
-
- Diagnosis module supports pathology and imaging workflows.
- Draft findings before final verification.
- Use print templates for standardized report output.
- Use audit trails and verification states for NABH-style controls.
-
+
+ 05 OPD Workflow
+ Appointment and check-in
+ Select patient, doctor, date/slot, or walk-in. Confirm department, referral, consultation type, and organization/insurance case. Generate the visit/token. Scan & Share patients may arrive through the facility QR. Collect or confirm consultation payment according to policy.
+ Clinical consultation
+ Record vitals, complaints, history, examination, allergies/ADR, medications, diagnosis, advice, and follow-up. Add medicines with dosage, frequency, route, duration, and instructions. Order pathology or imaging investigations and review previous visits. Generate referral letters, prescription PDFs, and FHIR e-Prescription records when required.
+ AI assistance: AI can draft complaints, prescriptions, or extract report values where configured. A clinician must review all generated content.
+
-
6. Hospital Stock Module
-
- Item Masters: category, item, supplier setup.
- Indent & Issue: departments raise request, approver approves, storekeeper issues.
- Purchase: create PO and receive stock.
- Dashboard: expiry risk, short stock, new requests, daily-use status.
- Reports: consumption, monthly issue, near-expiry, low/reorder alerts.
-
-
Key item fields: purchase unit, issue unit, issue-per-purchase ratio, min/reorder levels, expiry date, and store location.
+
+ 06 IPD Admission, Nursing & Discharge
+ Admission and beds
+ Create admission with department, doctor, date/time, and reason. Assign an available Department → Ward → Category → Bed. Record HPI/admission history and treating doctors. Use bed transfer instead of overwriting the bed to preserve history.
+ Nursing
+ Document shift notes and observations against the correct admission. Post bedside consumables, nursing items, and doctor visits promptly. Scan paper forms and use Surgery/OT for operative patients. Check the patient/IPD banner before every entry.
+ Discharge
+ Complete diagnosis, course, procedures, condition, medication, advice, diet, and follow-up. Select and preview the approved discharge template. Reconcile pending charges, returns, package adjustments, and balance. Verify PDF page breaks, placeholders, dates, letterhead mode, and signatures. Generate/share the ABDM discharge summary when requirements are met.
+
-
7. Admin and Configuration
-
- User management, password reset, and permission mapping.
- Doctor/refer/master settings.
- Template management for reports and print formats.
- Bed management and hospital stock settings.
-
+
+ 07 OT Management
+ Available from IPD Nursing and OT Surgery Queue. Request, scheduling, examination, status, and post-operative actions have separate permissions.
+ Create request. Enter procedure, surgeon, priority, indication, and requested schedule.Complete pre-op examination. Use the department-specific form; revisions to completed forms require a reason.Schedule. Authorized OT staff assign date/time and coordination details.Call patient. Update the case when preparation begins.Complete safety gates. Confirm pre-op checks and WHO sign-in before starting.Record progress. Move through requested, scheduled, called, in progress, and completed; give reasons for cancellation or major changes.Sign post-op assessment. Record observations, outcome, instructions, and handover.
+ Clinical integrity: OT actions create event history and use version checks. Refresh if another user changed the case.
+
-
8. Security and Compliance
-
- Use personal login only; do not share credentials.
- Keep audit remarks clear for financial and clinical edits.
- Follow organization policy for health data privacy.
-
+
+ 08 Diagnosis, Laboratory & Imaging
+ Supported workflows include Pathology, Biopsy, X-Ray, Ultrasound, MRI, CT, and Echo.
+ Laboratory Receive the OPD, IPD, or walk-in request and confirm patient/tests. Record sample collection and lab number. Enter values; review units, reference ranges, flags, and comments. Save draft, then verify/sign according to policy. Print individual or compiled reports using the correct template.
+ Imaging Upload images, scans, or DICOM studies against the report. Use the viewer/gallery and draft findings/impression. Complete doctor verification; AI suggestions require radiologist review.
+ Verified reports: Authorized edits require a meaningful reason retained in the audit trail.
+
-
9. Backup and Maintenance
-
- Take scheduled database backups.
- Archive monthly reports.
- Apply migrations in controlled deployment steps.
- Validate writable folder permissions after server changes.
-
+
+ 09 Pharmacy / Medical Store
+ Dispensing Create counter, OPD, IPD, or organization-linked invoices in the correct context. Confirm medicine, batch, expiry, stock, rate, tax, discount, and quantity. Use Medicine Return or Medicine Return 2 for the applicable return flow. Use Org. Credit Invoice for corporate/insurance-linked medicine billing. Finalize an IPD medicine account only after invoices, returns, discounts, and payments reconcile.
+ Inventory and purchase Maintain drug, generic, formulation, category, company, supplier, batch, tax, and rate masters. Create purchase orders, receive invoices/batches, and process purchase returns. Monitor expiry, low stock, movement, and supplier ledger reports.
+ Credit payout request Open Pharmacy → Credit Payout Request. Review/filter the eligible pool. Select valid lines, confirm amounts, add remarks, and submit. Monitor status while Finance handles approval and payment.
+
-
10. Troubleshooting
-
- Access denied: verify group permissions.
- Form not loading: check session status and route URL.
- Wrong stock: verify conversion fields and ledger actions.
- Print mismatch: verify template and print setting configuration.
-
+
+ 10 Billing, Invoices & Payments
+ Bill OPD consultation, procedures, diagnostics, IPD charges, packages, and pharmacy links. Apply discounts only with authority and supporting remarks. Use payment/refund request workflows instead of informal adjustments. Confirm organization/insurance case before posting payer charges. Print/export daily collection and balance reports for reconciliation.
+ Payment Edit Authorized users can correct payment mode, amount, or recorded user. Amount may be 0.00 for a valid correction but cannot be negative.
+ Do not use Payment Edit to hide a refund or cancellation. Use the approved reversal workflow and retain the reason.
+
-
For legal terms, see
Software Use License .
+
+ 11 Finance & Accounting
+ Cash Book Daily department collections and opening/closing position.
Bank Deposits Deposit references, dates, amounts, and status.
Reconciliation Match statements to system transactions.
POS Settlement Track terminal settlements, charges, and differences.
Doctor Payouts Agreements, commission, approval, and disbursement.
Vendor Control PO, receipt, invoice, payment, and balance.
+ Review pharmacy payout line evidence, approve/reject according to policy, record payments, and retain remarks for each status change.
+
+
+
+ 12 Insurance & Organization Cases
+ Create/select the case and verify patient, payer, policy, approval, and validity. Link OPD, IPD, diagnosis, procedures, and pharmacy invoices. Apply approved rates/packages, including Ayushman mappings. Use contingent billing when liability depends on case outcome. Prepare claim sheets and supporting documents. Track submission, query, approval, deduction, payment, and balance.
+ Case selection: Select a valid system case. Empty or placeholder identifiers are not payer cases.
+
+
+
+ 13 ABDM, ABHA & Health Records
+ ABHA identity Create ABHA through supported Aadhaar/mobile OTP flows. Verify/fetch profile before linking to the HMS patient. Display/download ABHA card and use facility QR for Scan & Share.
+ Care context and sharing Confirm verified ABHA and correct visit/admission. Discover/link the care context. Select e-Prescription, Discharge Summary, Diagnostic Report, Immunization, Wellness, Health Document, or supported invoice. Preview FHIR R4 data: identity, dates, practitioner, coding, and attachments. Share and monitor gateway/bridge status.
+ Consent and HIU Request consent only for a legitimate purpose and date range. Track requested, granted, denied, expired, and revoked status. Fetch health information only after grant; data may contain FHIR documents and attachments.
+ Privacy: ABDM access is purpose-limited and consent-driven. Use data only for authorized care.
+
+
+
+ 14 Hospital Stock
+ Configure category, item, units, conversion, location, min/reorder, and expiry control. Departments raise indents with quantity and purpose. Authorized users approve, reject, or revise. Storekeepers issue approved quantities and batches. Create purchase orders and goods receipts. Review consumption, issues, expiry, low-stock, reorder, and daily-use reports.
+ Purchase and issue units may differ. Wrong conversion causes incorrect stock and reorder quantities.
+
+
+
+ 15 Reports & Dashboards
+ Area Examples Review focus Clinical OPD, diagnosis workload/TAT, discharge, OT queue Completeness and delays Billing Collections, balances, refunds, credit invoices Mode/user totals and exceptions Finance Cash book, bank audit, POS, doctor payout Unmatched/unapproved items Pharmacy Stock, expiry, movement, sales, returns, supplier ledger Negative stock and expiry risk Insurance Organization dues, claims, deductions Aging and documents Audit NABH edits, billing logs, user actions Who changed what, when, and why
+ Use drill-down links from totals to source records. Retain the same dates and filters while reconciling.
+
+
+
+ 16 Administration & System Operations
+ Application administration Manage users, groups, permissions, passwords, and active access. Maintain doctor, referral, insurance, bank/payment, charge, diagnosis, medicine, and stock masters. Configure OPD, diagnosis, and discharge print templates. Configure AI, HealthPlix, ABDM HFR/gateway/bridge, and report-doctor mappings.
+ System Operations Review OS/uptime, CPU, memory, disk, RAID, database, web, PHP-FPM, and SSH health. Use Update HMS only during an approved window and review its log. Restart services only when needed. Reboot/shutdown require confirmation and coordination.
+ Production safety: Confirm backup and rollback readiness before migrations, updates, restarts, or environment changes.
+
+
+
+ 17 Security, Audit & Compliance
+ Never disclose passwords, OTPs, API keys, or tokens. Single-active-session protection may end an older login. Grant the least permission required and review access after role changes. Clinical verification, payment edits, refunds, cancellations, OT changes, and completed-form revisions require audit evidence. Verify SNOMED CT/ICD-10 codes against documented clinical meaning. Follow NABH, ABDM/FHIR, NHCX, privacy, retention, and hospital policies.
+
+
+
+ 18 Backup & Maintenance
+ Schedule database and uploaded-document backups and test restoration. Retain an off-server/off-site copy according to policy. Apply code updates and migrations in a controlled sequence. After deployment test login, dashboard, patient search, billing, PDF, uploads, jobs, and integrations. Monitor storage, database growth, logs, failed queues, ABDM tasks, and large attachments. Keep production error display disabled; use protected logs.
+
+
+
+ 19 Troubleshooting
+ Problem Check Action
+ Access denied Role, permission, module Ask Admin; do not borrow credentials.
+ Page/form does not load Session, network, URL/status, console Refresh dashboard, reopen, report exact URL/status.
+ Save does nothing Required/invalid number or date fields Correct highlighted fields; report console/network details.
+ DataTables warning Empty table, screen URL, table ID Report URL and table ID.
+ Unexpected token < HTML server error returned instead of JSON Capture endpoint/status; Admin checks logs.
+ PDF mismatch Template, paper mode, placeholders, pasted HTML Preview intended template and report record/template.
+ Wrong stock Batch, conversion, return, issue, receipt, transfer Reconcile ledger before adjustment.
+ Payment mismatch Invoice, mode, user, amount, refund/void Use approved correction and document reason.
+ ABDM pending/failed ABHA, consent, HFR/token, bridge/task log Identify failure; preserve request ID.
+ OT changed elsewhere Stale version warning Refresh, review history, re-enter update.
+
+ Include in a support request Exact URL and module Date/time and user role, never password Patient/IPD/invoice/case code through approved channels Steps, expected result, and actual result Screenshot and console/network status
+
+
+
-
+
+
+
-
+