db = \Config\Database::connect(); } private function ensurePharmacyAccess() { $user = service('auth')->user(); $allowed = false; if ($user && method_exists($user, 'can')) { $allowed = $user->can('pharmacy.access') || $user->can('billing.access'); } if (! $allowed && $user && method_exists($user, 'inGroup')) { $allowed = $user->inGroup('superadmin', 'admin', 'developer'); } if ($allowed) { return null; } return $this->response ->setStatusCode(403) ->setBody('
Access denied for Pharmacy module.
'); } private function firstColumn(array $fields, array $candidates): ?string { foreach ($candidates as $candidate) { if (in_array($candidate, $fields, true)) { return $candidate; } } return null; } private function rowValue(array $row, array $keys, $default = null) { foreach ($keys as $key) { if (array_key_exists($key, $row)) { return $row[$key]; } } return $default; } private function buildBankSourceLabel(array $row): string { $sourceText = trim((string) $this->rowValue($row, [ 'pay_type', 'source_name', 'payment_source', 'source', 'name', 'title', ], '')); $bankText = trim((string) $this->rowValue($row, [ 'bank_name', 'bank', 'bank_account_name', 'account_name', ], '')); if ($sourceText !== '' && $bankText !== '') { return $sourceText . ' [' . $bankText . ']'; } if ($sourceText !== '') { return $sourceText; } if ($bankText !== '') { return $bankText; } $id = (int) ($row['id'] ?? 0); return $id > 0 ? ('Source #' . $id) : 'Default Bank Source'; } private function getActiveUsers(): array { if (! $this->db->tableExists('users')) { return []; } $fields = $this->db->getFieldNames('users') ?? []; $builder = $this->db->table('users'); $select = ['id']; foreach (['username', 'first_name', 'last_name', 'active'] as $f) { if (in_array($f, $fields, true)) { $select[] = $f; } } $builder->select(implode(',', array_unique($select))); if (in_array('active', $fields, true)) { $builder->where('active', 1); } return $builder->orderBy('id', 'DESC')->get()->getResultArray(); } private function getMedicalBankSources(): array { $sourceTable = null; if ($this->db->tableExists('medical_bank_source')) { $sourceTable = 'medical_bank_source'; } elseif ($this->db->tableExists('medical_bank_payment_source')) { $sourceTable = 'medical_bank_payment_source'; } if ($sourceTable === null) { return []; } $sourceFields = $this->db->getFieldNames($sourceTable) ?? []; $bankFk = $this->firstColumn($sourceFields, ['bank_id', 'medical_bank_id', 'bankid']); $builder = $this->db->table($sourceTable . ' s')->select('s.*'); if ($this->db->tableExists('medical_bank') && $bankFk !== null) { $bankFields = $this->db->getFieldNames('medical_bank') ?? []; $bankPk = $this->firstColumn($bankFields, ['id', 'bank_id']); $bankNameCol = $this->firstColumn($bankFields, ['bank_name', 'name', 'bank']); if ($bankPk !== null && $bankNameCol !== null) { $builder ->select('ifnull(m.' . $bankNameCol . ', "") as bank_name', false) ->join('medical_bank m', 'm.' . $bankPk . '=s.' . $bankFk, 'left'); } else { $builder->select('"" as bank_name', false); } } else { $builder->select('"" as bank_name', false); } $list = $builder->orderBy('s.id', 'ASC')->get()->getResultArray(); foreach ($list as &$row) { $row['_label'] = $this->buildBankSourceLabel($row); } unset($row); return $list; } private function getActorName(): string { $user = service('auth')->user(); if (! $user) { return 'system'; } $parts = []; if (isset($user->first_name) && (string) $user->first_name !== '') { $parts[] = (string) $user->first_name; } if (isset($user->last_name) && (string) $user->last_name !== '') { $parts[] = (string) $user->last_name; } $name = trim(implode(' ', $parts)); if ($name === '' && isset($user->username) && (string) $user->username !== '') { $name = (string) $user->username; } if ($name === '' && isset($user->email) && (string) $user->email !== '') { $name = (string) $user->email; } $id = isset($user->id) ? (string) $user->id : ''; return $id !== '' ? ($name . '[' . $id . ']') : $name; } private function appendMedicalInvoiceLog(int $invoiceId, string $message): void { if ($invoiceId <= 0 || trim($message) === '') { return; } if (! $this->db->tableExists('invoice_med_master') || ! $this->db->fieldExists('log', 'invoice_med_master')) { return; } $invoice = $this->db->table('invoice_med_master')->select('id,log')->where('id', $invoiceId)->get()->getRowArray(); if (! $invoice) { return; } $oldLog = trim((string) ($invoice['log'] ?? '')); $newLine = trim($message) . ' | By ' . $this->getActorName() . '[' . date('Y-m-d H:i:s') . ']'; $merged = $oldLog === '' ? $newLine : ($oldLog . PHP_EOL . $newLine); $this->db->table('invoice_med_master')->where('id', $invoiceId)->update([ 'log' => $merged, ]); } private function getMedicalInvoiceIdByPayId(int $payId): int { if ($payId <= 0 || ! $this->db->tableExists('payment_history_medical')) { return 0; } $fields = $this->db->getFieldNames('payment_history_medical') ?? []; $invoiceCol = null; foreach (['Medical_invoice_id', 'medical_invoice_id'] as $candidate) { if (in_array($candidate, $fields, true)) { $invoiceCol = $candidate; break; } } if ($invoiceCol === null) { return 0; } $row = $this->db->table('payment_history_medical')->select($invoiceCol)->where('id', $payId)->get()->getRowArray(); return (int) ($row[$invoiceCol] ?? 0); } private function resolvePaymentLogTable(): ?string { if ($this->db->tableExists('paymentmedical_history_log')) { return 'paymentmedical_history_log'; } if ($this->db->tableExists('payment_history_log')) { return 'payment_history_log'; } return null; } private function insertPaymentLog(int $payId, string $updateType, string $updateBy, string $updateLog = '', string $updateRemark = ''): void { $logTable = $this->resolvePaymentLogTable(); if ($logTable === null) { return; } $fields = $this->db->getFieldNames($logTable) ?? []; $insert = []; if (in_array('pay_id', $fields, true)) { $insert['pay_id'] = $payId; } if (in_array('update_type', $fields, true)) { $insert['update_type'] = $updateType; } if (in_array('update_by', $fields, true)) { $insert['update_by'] = $updateBy; } if (in_array('update_log', $fields, true) && $updateLog !== '') { $insert['update_log'] = $updateLog; } if (in_array('update_remark', $fields, true)) { if ($updateRemark !== '') { $insert['update_remark'] = $updateRemark; } elseif (! in_array('update_log', $fields, true) && $updateLog !== '') { $insert['update_remark'] = $updateLog; } } if (in_array('insert_datetime', $fields, true)) { $insert['insert_datetime'] = date('Y-m-d H:i:s'); } if ($insert !== []) { $this->db->table($logTable)->insert($insert); } } private function renderPaymentRecord(int $recNo): string { if (! $this->db->tableExists('payment_history_medical')) { return 'No Record Found'; } $payFields = $this->db->getFieldNames('payment_history_medical') ?? []; $idCol = $this->firstColumn($payFields, ['id']); if (! $idCol) { return 'No Record Found'; } $payment = $this->db->table('payment_history_medical') ->where($idCol, $recNo) ->get() ->getRowArray(); if (! $payment) { return 'No Record Found'; } $customerType = (int) ($this->rowValue($payment, ['Customerof_type', 'customerof_type'], 0)); $medicalInvoiceId = (int) ($this->rowValue($payment, ['Medical_invoice_id', 'medical_invoice_id'], 0)); $ipdId = (int) ($this->rowValue($payment, ['ipd_id', 'IPD_ID'], 0)); $paymentMode = (int) ($this->rowValue($payment, ['payment_mode'], 1)); $payBankId = (int) ($this->rowValue($payment, ['pay_bank_id', 'bank_id'], 0)); $amount = (float) ($this->rowValue($payment, ['amount'], 0)); $creditDebit = (int) ($this->rowValue($payment, ['credit_debit'], 0)); $invoiceNo = ''; $invoiceToName = ''; $invType = ''; if (($customerType === 1 || $customerType === 3) && $this->db->tableExists('invoice_med_master')) { $inv = $this->db->table('invoice_med_master')->where('id', $medicalInvoiceId)->get()->getRowArray(); if ($inv) { $invoiceToName = (string) ($inv['inv_name'] ?? ''); $invoiceNo = (string) ($inv['inv_med_code'] ?? ''); $invType = $customerType === 1 ? 'OPD' : 'CASH'; } } elseif ($customerType === 2 && $this->db->tableExists('ipd_master')) { $ipd = $this->db->table('ipd_master')->where('id', $ipdId)->get()->getRowArray(); if ($ipd) { $invoiceToName = (string) ($ipd['P_name'] ?? ($ipd['p_name'] ?? '')); $invoiceNo = (string) ($ipd['ipd_code'] ?? ''); $invType = 'IPD Medical Bill'; } } $amountStr = $creditDebit === 0 ? $amount : ($amount * -1); return view('medical/payment_edit', [ 'payment_history' => $payment, 'all_user_list' => $this->getActiveUsers(), 'invoice_no' => $invoiceNo, 'invoice_to_name' => $invoiceToName, 'inv_Type' => $invType, 'rec_no' => $recNo, 'amount_str' => $amountStr, 'payment_mode' => $paymentMode, 'pay_bank_id' => $payBankId, 'bank_sources' => $this->getMedicalBankSources(), ]); } public function index() { if ($deny = $this->ensurePharmacyAccess()) { return $deny; } return view('medical/payment_search'); } public function payment_record() { if ($deny = $this->ensurePharmacyAccess()) { return $deny; } $raw = (string) ($this->request->getPost('txtsearch') ?? ''); $recNo = (int) preg_replace('/\D+/', '', trim($raw)); if ($recNo <= 0) { return $this->response->setBody('No Record Found'); } return $this->response->setBody($this->renderPaymentRecord($recNo)); } public function change_to_bank() { if ($deny = $this->ensurePharmacyAccess()) { return $deny; } $payId = (int) ($this->request->getPost('pay_id') ?? 0); if ($payId <= 0 || ! $this->db->tableExists('payment_history_medical')) { return $this->response->setBody('No Record Found'); } $oldRow = $this->db->table('payment_history_medical')->where('id', $payId)->get()->getRowArray(); if (! $oldRow) { return $this->response->setBody('No Record Found'); } $fields = $this->db->getFieldNames('payment_history_medical') ?? []; $payTypeId = (int) ($this->request->getPost('cbo_pay_type') ?? 0); $cardTranId = trim((string) ($this->request->getPost('input_card_tran') ?? '')); $sourceLabel = ''; if ($payTypeId > 0) { foreach ($this->getMedicalBankSources() as $source) { if ((int) ($source['id'] ?? 0) === $payTypeId) { $sourceLabel = (string) ($source['_label'] ?? $this->buildBankSourceLabel($source)); break; } } } $update = []; if (in_array('payment_mode', $fields, true)) { $update['payment_mode'] = 2; } if (in_array('payment_mode_desc', $fields, true)) { $update['payment_mode_desc'] = $sourceLabel !== '' ? $sourceLabel : 'Bank/Online'; } if (in_array('pay_bank_id', $fields, true)) { $update['pay_bank_id'] = $payTypeId; } if (in_array('bank_id', $fields, true) && ! in_array('pay_bank_id', $fields, true)) { $update['bank_id'] = $payTypeId; } if (in_array('card_bank', $fields, true)) { $update['card_bank'] = $sourceLabel; } if (in_array('cust_card', $fields, true)) { $update['cust_card'] = ''; } if (in_array('card_remark', $fields, true)) { $update['card_remark'] = ''; } if (in_array('card_tran_id', $fields, true)) { $update['card_tran_id'] = $cardTranId; } if ($update !== []) { $this->db->table('payment_history_medical')->where('id', $payId)->update($update); $oldMode = ((int) ($oldRow['payment_mode'] ?? 1) === 1) ? 'CASH' : 'BANK'; $newMode = 'BANK'; $logText = 'Payment mode Change ' . $oldMode . ' to ' . $newMode; if ($sourceLabel !== '') { $logText .= ' [' . $sourceLabel . ']'; } if ($cardTranId !== '') { $logText .= ' TranID:' . $cardTranId; } $this->insertPaymentLog($payId, '2', $this->getActorName(), $logText); $this->appendMedicalInvoiceLog($this->getMedicalInvoiceIdByPayId($payId), 'Payment Update: ' . $logText); } return $this->response->setBody($this->renderPaymentRecord($payId)); } public function change_to_cash() { if ($deny = $this->ensurePharmacyAccess()) { return $deny; } $payId = (int) ($this->request->getPost('pay_id') ?? 0); if ($payId <= 0 || ! $this->db->tableExists('payment_history_medical')) { return $this->response->setBody('No Record Found'); } $oldRow = $this->db->table('payment_history_medical')->where('id', $payId)->get()->getRowArray(); if (! $oldRow) { return $this->response->setBody('No Record Found'); } $fields = $this->db->getFieldNames('payment_history_medical') ?? []; $update = []; if (in_array('payment_mode', $fields, true)) { $update['payment_mode'] = 1; } if (in_array('payment_mode_desc', $fields, true)) { $update['payment_mode_desc'] = 'Cash'; } if (in_array('pay_bank_id', $fields, true)) { $update['pay_bank_id'] = 0; } if (in_array('bank_id', $fields, true) && ! in_array('pay_bank_id', $fields, true)) { $update['bank_id'] = 0; } if (in_array('card_bank', $fields, true)) { $update['card_bank'] = ''; } if (in_array('cust_card', $fields, true)) { $update['cust_card'] = ''; } if (in_array('card_remark', $fields, true)) { $update['card_remark'] = ''; } if (in_array('card_tran_id', $fields, true)) { $update['card_tran_id'] = ''; } if ($update !== []) { $this->db->table('payment_history_medical')->where('id', $payId)->update($update); $oldMode = ((int) ($oldRow['payment_mode'] ?? 1) === 1) ? 'CASH' : 'BANK'; $logText = 'Payment mode Change ' . $oldMode . ' to CASH'; $this->insertPaymentLog($payId, '1', $this->getActorName(), $logText); $this->appendMedicalInvoiceLog($this->getMedicalInvoiceIdByPayId($payId), 'Payment Update: ' . $logText); } return $this->response->setBody($this->renderPaymentRecord($payId)); } public function update_amount() { if ($deny = $this->ensurePharmacyAccess()) { return $deny; } $payId = (int) ($this->request->getPost('pay_id') ?? 0); $changeValue = (float) ($this->request->getPost('change_value') ?? 0); if ($payId <= 0 || $changeValue <= 0 || ! $this->db->tableExists('payment_history_medical')) { return $this->response->setBody('No Record Found'); } $oldRow = $this->db->table('payment_history_medical')->where('id', $payId)->get()->getRowArray(); if (! $oldRow) { return $this->response->setBody('No Record Found'); } $oldAmt = (float) ($oldRow['amount'] ?? 0); $this->db->table('payment_history_medical')->where('id', $payId)->update([ 'amount' => $changeValue, ]); $logText = 'Amount value Change ' . number_format($oldAmt, 2, '.', '') . ' to ' . number_format($changeValue, 2, '.', ''); $this->insertPaymentLog($payId, '3', $this->getActorName(), $logText); $this->appendMedicalInvoiceLog($this->getMedicalInvoiceIdByPayId($payId), 'Payment Update: ' . $logText); return $this->response->setBody($this->renderPaymentRecord($payId)); } public function change_user() { if ($deny = $this->ensurePharmacyAccess()) { return $deny; } $payId = (int) ($this->request->getPost('pay_id') ?? 0); $userId = (int) ($this->request->getPost('user_list') ?? 0); if ($payId <= 0 || $userId <= 0 || ! $this->db->tableExists('payment_history_medical') || ! $this->db->tableExists('users')) { return $this->response->setBody('No Record Found'); } $payment = $this->db->table('payment_history_medical')->where('id', $payId)->get()->getRowArray(); $user = $this->db->table('users')->where('id', $userId)->get()->getRowArray(); if (! $payment || ! $user) { return $this->response->setBody('No Record Found'); } $userLabel = trim((string) (($user['first_name'] ?? '') . ' ' . ($user['last_name'] ?? ''))); if ($userLabel === '') { $userLabel = (string) ($user['username'] ?? ('User ' . $userId)); } $fields = $this->db->getFieldNames('payment_history_medical') ?? []; $update = []; if (in_array('update_by_id', $fields, true)) { $update['update_by_id'] = $userId; } if (in_array('update_by', $fields, true)) { $insertTime = (string) ($payment['insert_time'] ?? ($payment['insert_datetime'] ?? '')); $update['update_by'] = $userLabel . '[' . $insertTime . '][' . $userId . ']'; } if ($update !== []) { $this->db->table('payment_history_medical')->where('id', $payId)->update($update); $newUserText = $userLabel . '[' . $userId . ']'; $oldUserText = (string) ($payment['update_by'] ?? ''); if ($oldUserText === '') { $oldUserText = '-'; } $logText = 'Update User Change ' . $oldUserText . ' to ' . $newUserText; $this->insertPaymentLog( $payId, '3', $this->getActorName(), $logText, $logText ); $this->appendMedicalInvoiceLog($this->getMedicalInvoiceIdByPayId($payId), 'Payment Update: ' . $logText); } return $this->response->setBody($this->renderPaymentRecord($payId)); } private function parseLegacyDateRange(string $dateRange): array { $dateRange = trim($dateRange); if ($dateRange === '') { $today = date('Y-m-d'); return [$today, $today]; } $parts = explode('S', $dateRange); $fromRaw = str_replace('T', ' ', trim((string) ($parts[0] ?? ''))); $toRaw = str_replace('T', ' ', trim((string) ($parts[1] ?? ($parts[0] ?? '')))); $from = date('Y-m-d', strtotime($fromRaw ?: 'today')); $to = date('Y-m-d', strtotime($toRaw ?: $from)); if ($from > $to) { [$from, $to] = [$to, $from]; } return [$from, $to]; } public function payment_log() { if ($deny = $this->ensurePharmacyAccess()) { return $deny; } return view('medical/payment_log', [ 'today' => date('Y-m-d'), ]); } public function payment_log_data() { if ($deny = $this->ensurePharmacyAccess()) { return $deny; } $dateRange = trim((string) ($this->request->getPost('opd_date_range') ?? $this->request->getGet('opd_date_range') ?? '')); [$dateFrom, $dateTo] = $this->parseLegacyDateRange($dateRange); $logTable = $this->resolvePaymentLogTable(); if (! $this->db->tableExists('payment_history_medical') || $logTable === null) { return view('medical/payment_log_data', [ 'rows' => [], 'dateFrom' => $dateFrom, 'dateTo' => $dateTo, ]); } $pFields = $this->db->getFieldNames('payment_history_medical') ?? []; $lFields = $this->db->getFieldNames($logTable) ?? []; $resolveField = static function (array $fields, array $candidates): ?string { $fieldMap = []; foreach ($fields as $field) { $fieldMap[strtolower((string) $field)] = (string) $field; } foreach ($candidates as $candidate) { $key = strtolower((string) $candidate); if (isset($fieldMap[$key])) { return $fieldMap[$key]; } } return null; }; $customerTypeField = $resolveField($pFields, ['Customerof_type', 'customerof_type']); $invoiceCodeField = $resolveField($pFields, ['Medical_invoice_code', 'medical_invoice_code']); $ipdIdField = $resolveField($pFields, ['ipd_id', 'IPD_ID']); $insertTimeField = $resolveField($pFields, ['insert_time', 'insert_datetime', 'created_at']); $logInsertField = $resolveField($lFields, ['insert_datetime', 'insert_time', 'created_at']); $updateByField = $resolveField($lFields, ['update_by']); $updateTypeField = $resolveField($lFields, ['update_type']); $updateLogField = $resolveField($lFields, ['update_log']); $updateRemarkField = $resolveField($lFields, ['update_remark']); $logIdField = $resolveField($lFields, ['id']); $customerTypeCol = $customerTypeField !== null ? ('p.' . $customerTypeField) : '0'; $invoiceCodeCol = $invoiceCodeField !== null ? ('p.' . $invoiceCodeField) : "'-'"; $ipdIdCol = $ipdIdField !== null ? ('p.' . $ipdIdField) : "'-'"; $insertTimeCol = $insertTimeField !== null ? ('p.' . $insertTimeField) : 'null'; $logInsertCol = $logInsertField !== null ? ('l.' . $logInsertField) : 'null'; $updateByCol = $updateByField !== null ? ('l.' . $updateByField) : "'-'"; $updateTypeCol = $updateTypeField !== null ? ('l.' . $updateTypeField) : '0'; if ($updateLogField !== null && $updateRemarkField !== null) { $updateLogCol = 'COALESCE(NULLIF(l.' . $updateLogField . ',\'\'), l.' . $updateRemarkField . ', \'\')'; } elseif ($updateLogField !== null) { $updateLogCol = 'l.' . $updateLogField; } elseif ($updateRemarkField !== null) { $updateLogCol = 'l.' . $updateRemarkField; } else { $updateLogCol = "''"; } $select = [ 'p.id as id', 'ifnull(' . $invoiceCodeCol . ", '-') as Inv_code", 'ifnull(' . $insertTimeCol . ', null) as insert_time', 'ifnull(' . $logInsertCol . ', null) as log_insert', 'ifnull(' . $updateByCol . ", '-') as update_by", 'ifnull(' . $updateTypeCol . ', 0) as update_type', 'ifnull(' . $updateLogCol . ", '') as update_log", 'ifnull(p.amount,0) as amount', "(case {$customerTypeCol} when 1 then ifnull({$invoiceCodeCol}, '-') when 2 then ifnull({$ipdIdCol}, '-') when 3 then ifnull({$invoiceCodeCol}, '-') else 'Unknown' end) as invoice_code", "(case {$customerTypeCol} when 1 then 'UHID' when 2 then 'IPD' when 3 then 'CASH' else 'Unknown' end) as bill_type", "(case {$updateTypeCol} when 3 then 'Change Amount' when 2 then 'Cash to Bank' when 1 then 'Bank to Cash' else 'Unknown' end) as log_type", ]; $builder = $this->db->table('payment_history_medical p') ->select(implode(',', $select), false) ->join($logTable . ' l', 'p.id=l.pay_id', 'inner'); if ($logInsertField !== null) { $fromEsc = $this->db->escape($dateFrom); $toEsc = $this->db->escape($dateTo); $builder->where("DATE(l.{$logInsertField}) BETWEEN {$fromEsc} AND {$toEsc}", null, false); } if ($logIdField !== null) { $builder->orderBy('l.' . $logIdField, 'DESC'); } elseif ($logInsertField !== null) { $builder->orderBy('l.' . $logInsertField, 'DESC'); } else { $builder->orderBy('p.id', 'DESC'); } $rows = $builder->get()->getResultArray(); if ($rows === []) { $fallbackBuilder = $this->db->table('payment_history_medical p') ->select(implode(',', $select), false) ->join($logTable . ' l', 'p.id=l.pay_id', 'inner'); if ($logIdField !== null) { $fallbackBuilder->orderBy('l.' . $logIdField, 'DESC'); } elseif ($logInsertField !== null) { $fallbackBuilder->orderBy('l.' . $logInsertField, 'DESC'); } else { $fallbackBuilder->orderBy('p.id', 'DESC'); } $rows = $fallbackBuilder->limit(500)->get()->getResultArray(); } return view('medical/payment_log_data', [ 'rows' => $rows, 'dateFrom' => $dateFrom, 'dateTo' => $dateTo, ]); } }