Add Ayushman package import, billing claim sheet, and reporting dashboards

This commit is contained in:
DevSoftTech Server 2026-03-26 15:42:40 +05:30
parent 99ec71677a
commit 911e7b50a7
16 changed files with 1796 additions and 4 deletions

View File

@ -0,0 +1,60 @@
---
description: "Use when migrating CI3 modules to CI4, porting HMS controllers/models/views, implementing OPD/IPD/Pharmacy/Diagnosis/Finance/Lab features, fixing regressions after CI3→CI4 port, adding routes or auth permissions, creating migrations, implementing AJAX modal flows, print/Excel exports, or any Hospital Management System feature work in this CodeIgniter 4 project."
name: "HMS CI4 Migrator"
tools: [read, edit, search, execute, todo]
---
You are an expert CI3→CI4 migration engineer for a Hospital Management System (HMS). Your job is to port, implement, and debug full-stack features in this CodeIgniter 4 project.
## Project Context
- **Legacy source**: `old_code_ci3/` — always read this first before porting.
- **Worklog**: `WORKLOG_COPILOT.md` — check for module status, backlog, and known issues.
- **UI framework**: NiceAdmin (Bootstrap). Use consistent card/modal/button patterns from existing CI4 views.
- **AJAX pattern**: modals open via `$.ajax` → return HTML partials; list refresh via named `window.refresh*()` functions.
- **DataTables**: Simple-DataTables (not jQuery DataTables). Check existing views for column config pattern.
- **Routing**: `autoRoute = false`. Every endpoint must be explicitly registered in `app/Config/Routes.php`.
- **Auth**: Permission keys are `module.action` entries in `app/Config/AuthGroups.php`. Sidebar menu is in `app/Views/partials/sidebar.php`.
- **Audit trails**: Follow `*_update` table pattern when editing master records (e.g. `hc_items_update`, `package_update`).
## Core Workflow
1. **Read legacy code first.** Before writing any CI4 code, read the corresponding CI3 controller/model/view from `old_code_ci3/`.
2. **Map endpoints.** List legacy routes → CI4 equivalents. Add both legacy aliases and clean CI4 routes where needed.
3. **Port full stack per feature.** For each endpoint: Controller method → Model method → Route entry → View file.
4. **Follow existing CI4 patterns.** Read one or two existing CI4 controllers/models/views in the same module before writing new ones.
5. **Database**: Use Query Builder only — no raw SQL unless unavoidable. No DB views; use model joins. Always check if DB columns/tables exist before referencing them.
6. **Migrations**: Create numbered migration files (`app/Database/Migrations/`) for new tables; follow existing timestamp naming.
7. **Exports**: Excel via response headers (`Content-Type: application/vnd.ms-excel`); print via dedicated view with no debug toolbar. mPDF is available for PDF exports.
## HMS Domain Knowledge
| Module | Key Tables | CI4 Controller |
|--------|-----------|----------------|
| OPD Charges | hc_items, hc_item_type, hc_items_insurance | Item.php |
| IPD Charges | ipd_items, ipd_item_type, ipd_items_insurance | ItemIpd.php |
| Packages | package, package_group, package_insurance | Package.php |
| Diagnosis/Lab | lab_*, diagnosis_* | Diagnosis.php |
| Pharmacy | med_*, v_product_stock (view) | Medical.php |
| Finance | finance_*, bank_*, cashbook | Finance.php |
| OPD | hc_opd, hc_visit | Opd.php |
| IPD | ibd_admission, ibd_discharge | IpdPatient.php |
| Patient | hc_patient | Patient.php |
## Constraints
- **Never use autoRoute.** Always register routes explicitly.
- **AJAX responses**: Return JSON for data endpoints; return HTML for partial/modal endpoints. Never mix.
- **No debug toolbar in Excel/print responses.** Use `$this->response->setHeader(...)` and `return $this->response->setBody(...)`.
- **Namespace constants**: Use `constant('NAME')` after `defined('NAME')` check — never bare `NAME` in namespaced controllers.
- **File uploads**: Use `$file->move()` with try/catch; verify `public/assets/images` is writable; return JSON error on failure.
- **Legacy form keys**: Accept both old and new POST key names when porting forms that mix CI3 JS with CI4 backend.
- **Modals**: Insert modal `<div>` blocks outside loops to prevent duplicate IDs.
- **Report edits**: When updating a verified report, require an edit reason and persist it in the log/audit table.
## Output Expectations
- Produce working, runnable PHP/HTML/JS — not pseudocode.
- Include route entries whenever a new controller method is added.
- Include auth permission key entries whenever a new module action is added.
- Update `WORKLOG_COPILOT.md` with a dated summary of what was implemented after completing a significant feature.
- Keep changes minimal and focused — do not refactor unrelated code.

View File

@ -0,0 +1,133 @@
<?php
namespace App\Commands;
use Config\Database;
use CodeIgniter\CLI\BaseCommand;
use CodeIgniter\CLI\CLI;
class ImportAyushmanPackageMaster extends BaseCommand
{
protected $group = 'Import';
protected $name = 'ayushman:import-packages';
protected $description = 'Import Ayushman Bharat package master data from a CSV file into ayushman_package_master.';
protected $usage = 'ayushman:import-packages [csvPath] [--truncate]';
protected $arguments = [
'csvPath' => 'Optional CSV path. Defaults to writable/ayushman/105_PackageMasterReport.csv',
];
protected $options = [
'--truncate' => 'Clear ayushman_package_master before importing.',
];
public function run(array $params)
{
$csvPath = $params[0] ?? WRITEPATH . 'ayushman/105_PackageMasterReport.csv';
if (! is_file($csvPath)) {
CLI::error('CSV file not found: ' . $csvPath);
return;
}
$db = Database::connect();
if (! $db->tableExists('ayushman_package_master')) {
CLI::error('Table ayushman_package_master does not exist. Run php spark migrate first.');
return;
}
if (CLI::getOption('truncate')) {
$db->table('ayushman_package_master')->truncate();
CLI::write('Existing ayushman_package_master rows truncated.', 'yellow');
}
$handle = fopen($csvPath, 'rb');
if ($handle === false) {
CLI::error('Unable to open CSV file: ' . $csvPath);
return;
}
$rowNo = 0;
$inserted = 0;
$updated = 0;
$skipped = 0;
$now = date('Y-m-d H:i:s');
$table = $db->table('ayushman_package_master');
try {
while (($row = fgetcsv($handle)) !== false) {
$rowNo++;
if ($rowNo <= 3) {
continue;
}
$mapped = $this->mapCsvRow($row, basename($csvPath), $now);
if ($mapped === null) {
$skipped++;
continue;
}
$existing = $db->table('ayushman_package_master')
->select('id')
->where('procedure_code', $mapped['procedure_code'])
->get(1)
->getRowArray();
if (is_array($existing) && ! empty($existing['id'])) {
$id = (int) $existing['id'];
unset($mapped['created_at']);
$table->where('id', $id)->update($mapped);
$updated++;
continue;
}
$table->insert($mapped);
$inserted++;
}
} finally {
fclose($handle);
}
CLI::write('Ayushman package import complete', 'green');
CLI::write('CSV: ' . $csvPath);
CLI::write('Inserted: ' . $inserted, 'green');
CLI::write('Updated: ' . $updated, 'yellow');
CLI::write('Skipped: ' . $skipped);
}
private function mapCsvRow(array $row, string $sourceFile, string $timestamp): ?array
{
$row = array_pad($row, 10, '');
$specialityCode = trim((string) ($row[0] ?? ''));
$specialityName = trim((string) ($row[1] ?? ''));
$procedureCode = trim((string) ($row[2] ?? ''));
$procedureName = trim((string) ($row[3] ?? ''));
if ($procedureCode === '' && $procedureName === '') {
return null;
}
$amountText = trim((string) ($row[4] ?? '0'));
$amountText = str_replace(',', '', $amountText);
$packageAmount = is_numeric($amountText) ? (float) $amountText : 0.0;
return [
'speciality_code' => substr($specialityCode, 0, 50),
'speciality_name' => substr($specialityName, 0, 255),
'procedure_code' => substr($procedureCode, 0, 50),
'procedure_name' => $procedureName,
'package_amount' => number_format($packageAmount, 2, '.', ''),
'preauth_required' => strtolower(trim((string) ($row[5] ?? ''))) === 'yes' ? 1 : 0,
'procedure_type' => substr(trim((string) ($row[6] ?? '')), 0, 50),
'government_reserved' => strtolower(trim((string) ($row[7] ?? ''))) === 'yes' ? 1 : 0,
'pre_investigations' => trim((string) ($row[8] ?? '')),
'post_investigations' => trim((string) ($row[9] ?? '')),
'source_file' => substr($sourceFile, 0, 255),
'source_sheet' => 'Sheet1',
'created_at' => $timestamp,
'updated_at' => $timestamp,
];
}
}

View File

@ -345,6 +345,7 @@ $routes->group('billing', function($routes) {
$routes->get('ipd/panel/(:num)', 'Billing\\Ipd::panel/$1');
$routes->get('ipd/panel/(:num)/tab/(:segment)', 'Billing\\Ipd::panelTab/$1/$2');
$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');
$routes->post('ipd/panel/(:num)/discharge/discount/(:num)', 'Billing\\Ipd::updateDischargeDiscount/$1/$2');
$routes->post('ipd/panel/(:num)/discharge/charge/(:num)', 'Billing\\Ipd::updateDischargeCharge/$1/$2');
@ -361,6 +362,11 @@ $routes->group('billing', function($routes) {
$routes->post('ipd/nursing-charge/delete/(:num)/(:num)', 'Billing\\Ipd::deleteNursingCharge/$1/$2');
$routes->post('ipd/charge/package/(:num)', 'Billing\\Ipd::updateIpdChargePackage/$1');
$routes->post('ipd/package/add/(:num)', 'Billing\\Ipd::addIpdPackage/$1');
$routes->get('ipd/panel/(:num)/ayushman/search', 'Billing\Ipd::searchAyushmanPackages/$1');
$routes->post('ipd/panel/(:num)/ayushman/map/(:num)', 'Billing\Ipd::saveAyushmanPackageMapping/$1/$2');
$routes->post('ipd/panel/(:num)/ayushman/checklist', 'Billing\Ipd::saveAyushmanChecklist/$1');
$routes->get('ipd/panel/(:num)/ayushman/claim-sheet', 'Billing\Ipd::ayushmanClaimSheet/$1');
$routes->get('ipd/panel/(:num)/ayushman/claim-sheet/(:num)', 'Billing\Ipd::ayushmanClaimSheet/$1/$2');
$routes->post('ipd/package/update/(:num)', 'Billing\\Ipd::updateIpdPackage/$1');
$routes->post('ipd/package/delete/(:num)', 'Billing\\Ipd::deleteIpdPackage/$1');
$routes->get('ipd/payment/modal/(:num)', 'Billing\\Ipd::loadPaymentModal/$1');
@ -700,6 +706,12 @@ $routes->get('Report/insurance_ipd_report_data/(:segment)/(:segment)/(:segment)/
$routes->get('Report/insurance_combined_report', 'Report::insurance_combined_report');
$routes->get('Report/insurance_combined_report_data/(:segment)/(:segment)/(:segment)', 'Report::insurance_combined_report_data/$1/$2/$3');
$routes->get('Report/insurance_combined_report_data/(:segment)/(:segment)/(:segment)/(:num)', 'Report::insurance_combined_report_data/$1/$2/$3/$4');
$routes->get('Report/ayushman_unmapped_report', 'Report::ayushman_unmapped_report');
$routes->get('Report/ayushman_unmapped_report_data/(:segment)', 'Report::ayushman_unmapped_report_data/$1');
$routes->get('Report/ayushman_unmapped_report_data/(:segment)/(:num)', 'Report::ayushman_unmapped_report_data/$1/$2');
$routes->get('Report/ayushman_case_dashboard', 'Report::ayushman_case_dashboard');
$routes->get('Report/ayushman_case_dashboard_data/(:segment)/(:segment)', 'Report::ayushman_case_dashboard_data/$1/$2');
$routes->get('Report/ayushman_case_dashboard_data/(:segment)/(:segment)/(:num)', 'Report::ayushman_case_dashboard_data/$1/$2/$3');
// Legacy Report4 compatibility for Insurance Case reports
$routes->get('Report4/echs_ipd_list_main', 'Report::echs_ipd_list_main');

View File

@ -439,6 +439,9 @@ class Ipd extends BaseController
}
$panelData['ipd_insurance_id'] = $insCompId;
$panelData['package_list'] = $this->ipdModel->getPackageListForInsurance($insCompId);
$panelData['ayushman_specialities'] = $this->ipdModel->getAyushmanSpecialities();
$panelData['all_package_options'] = $this->ipdModel->getAllPackageOptions();
$panelData['ayushman_checklist_items'] = $this->ipdModel->getAyushmanChecklistForIpd($ipdId);
return view('billing/ipd/panel_package', $panelData);
}
@ -591,6 +594,37 @@ class Ipd extends BaseController
]);
}
public function updateDiagnosisChargeInclude(int $ipdId, int $invoiceId)
{
$permission = $this->requireAnyPermission([
'billing.access',
'billing.ipd.invoice',
]);
if ($permission) {
return $permission;
}
if (! $this->request->isAJAX()) {
return $this->response->setStatusCode(400)->setJSON(['ok' => false, 'error' => 'Invalid request']);
}
if ($ipdId <= 0 || $invoiceId <= 0) {
return $this->response->setStatusCode(422)->setJSON(['ok' => false, 'error' => 'Invalid invoice reference']);
}
$include = (int) ($this->request->getPost('ipd_include') ?? 0) === 1 ? 1 : 0;
$updated = $this->ipdModel->updateDiagnosisChargeInclude($ipdId, $invoiceId, $include);
if (! $updated) {
return $this->response->setStatusCode(500)->setJSON(['ok' => false, 'error' => 'Unable to update include status']);
}
return $this->response->setJSON([
'ok' => true,
'ipd_include' => $include,
]);
}
public function updateDischargeDiscount(int $ipdId, int $slot)
{
$permission = $this->requireAnyPermission([
@ -1415,6 +1449,7 @@ class Ipd extends BaseController
}
$packageId = (int) ($this->request->getPost('package_id') ?? 0);
$ayushmanPackageId = (int) ($this->request->getPost('ayushman_package_id') ?? 0);
$packageName = (string) ($this->request->getPost('package_name') ?? '');
$comment = (string) ($this->request->getPost('comment') ?? '');
$inputAmount = (float) ($this->request->getPost('input_amount') ?? 0);
@ -1429,14 +1464,62 @@ class Ipd extends BaseController
$amount = $inputAmount;
$orgCode = '';
if ($ayushmanPackageId > 0) {
$ayushmanPackage = $this->ipdModel->getAyushmanPackageById($ayushmanPackageId);
if ($ayushmanPackage === null) {
return $this->response->setStatusCode(404)->setJSON([
'ok' => false,
'error' => 'Ayushman package not found.',
]);
}
$packageId = (int) ($ayushmanPackage['linked_package_id'] ?? 0);
$packageName = (string) ($ayushmanPackage['procedure_name'] ?? $packageName);
$amount = (float) ($ayushmanPackage['package_amount'] ?? $amount);
$orgCode = (string) ($ayushmanPackage['procedure_code'] ?? '');
$descParts = [];
$specialityName = trim((string) ($ayushmanPackage['speciality_name'] ?? ''));
if ($specialityName !== '') {
$descParts[] = 'Speciality: ' . $specialityName;
}
$procedureType = trim((string) ($ayushmanPackage['procedure_type'] ?? ''));
if ($procedureType !== '') {
$descParts[] = 'Type: ' . $procedureType;
}
$preauthRequired = (int) ($ayushmanPackage['preauth_required'] ?? 0) === 1 ? 'Yes' : 'No';
$descParts[] = 'Preauth: ' . $preauthRequired;
array_unshift($descParts, 'Ayushman');
$packageDesc = substr(implode(' | ', $descParts), 0, 250);
$commentParts = [];
if ($orgCode !== '') {
$commentParts[] = 'Ayushman Code: ' . $orgCode;
}
$preInvestigations = trim((string) ($ayushmanPackage['pre_investigations'] ?? ''));
if ($preInvestigations !== '') {
$commentParts[] = 'Pre: ' . $preInvestigations;
}
$postInvestigations = trim((string) ($ayushmanPackage['post_investigations'] ?? ''));
if ($postInvestigations !== '') {
$commentParts[] = 'Post: ' . $postInvestigations;
}
if ($comment !== '') {
$commentParts[] = $comment;
}
$comment = substr(implode(' | ', $commentParts), 0, 250);
}
if ($packageId > 1) {
$package = $this->ipdModel->getPackageWithInsurance($packageId, $insCompId);
if ($package) {
if ($ayushmanPackageId <= 0) {
$packageName = (string) ($package->ipd_pakage_name ?? $packageName);
$packageDesc = (string) ($package->Pakage_description ?? $packageDesc);
$amount = (float) ($package->amount1 ?? $amount);
$orgCode = (string) ($package->code ?? '');
}
$amount = (float) ($package->amount1 ?? $amount);
}
}
if ($inputAmount > 0) {
@ -1474,6 +1557,181 @@ class Ipd extends BaseController
]);
}
public function searchAyushmanPackages(int $ipdId)
{
$permission = $this->requireAnyPermission([
'billing.access',
'billing.ipd.invoice',
]);
if ($permission) {
return $permission;
}
if (! $this->request->isAJAX()) {
return $this->response->setStatusCode(400)->setJSON(['ok' => false, 'error' => 'Invalid request']);
}
$panelData = $this->ipdModel->getIpdPanelInfo($ipdId);
if (empty($panelData)) {
return $this->response->setStatusCode(404)->setJSON(['ok' => false, 'error' => 'IPD not found']);
}
$query = trim((string) ($this->request->getGet('q') ?? ''));
$specialityCode = trim((string) ($this->request->getGet('speciality_code') ?? ''));
$items = $this->ipdModel->searchAyushmanPackages($query, $specialityCode, 30);
return $this->response->setJSON([
'ok' => true,
'items' => $items,
]);
}
public function saveAyushmanPackageMapping(int $ipdId, int $ayushmanId)
{
$permission = $this->requireAnyPermission([
'billing.access',
'billing.ipd.invoice',
]);
if ($permission) {
return $permission;
}
if (! $this->request->isAJAX()) {
return $this->response->setStatusCode(400)->setJSON(['ok' => false, 'error' => 'Invalid request']);
}
$panelData = $this->ipdModel->getIpdPanelInfo($ipdId);
if (empty($panelData)) {
return $this->response->setStatusCode(404)->setJSON(['ok' => false, 'error' => 'IPD not found']);
}
$linkedPackageId = (int) ($this->request->getPost('linked_package_id') ?? 0);
$updated = $this->ipdModel->updateAyushmanPackageLink($ayushmanId, $linkedPackageId);
if (! $updated) {
return $this->response->setStatusCode(404)->setJSON(['ok' => false, 'error' => 'Ayushman treatment not found']);
}
$mappedPackage = null;
if ($linkedPackageId > 0) {
foreach ($this->ipdModel->getAllPackageOptions() as $row) {
if ((int) ($row['id'] ?? 0) === $linkedPackageId) {
$mappedPackage = trim((string) ($row['ipd_pakage_name'] ?? ''));
break;
}
}
}
return $this->response->setJSON([
'ok' => true,
'linked_package_id' => $linkedPackageId,
'linked_package_name' => $mappedPackage,
]);
}
public function saveAyushmanChecklist(int $ipdId)
{
$permission = $this->requireAnyPermission([
'billing.access',
'billing.ipd.invoice',
]);
if ($permission) {
return $permission;
}
if (! $this->request->isAJAX()) {
return $this->response->setStatusCode(400)->setJSON(['ok' => false, 'error' => 'Invalid request']);
}
$panelData = $this->ipdModel->getIpdPanelInfo($ipdId);
if (empty($panelData)) {
return $this->response->setStatusCode(404)->setJSON(['ok' => false, 'error' => 'IPD not found']);
}
$ipdInfo = $panelData['ipd_info'] ?? null;
$caseId = (int) ($ipdInfo->case_id ?? 0);
if ($caseId <= 0) {
return $this->response->setStatusCode(422)->setJSON(['ok' => false, 'error' => 'No organization case linked to this IPD']);
}
$fieldMap = array_flip($this->db->getFieldNames('organization_case_master') ?? []);
$update = [];
if (isset($fieldMap['preauth_send'])) {
$update['preauth_send'] = (int) ($this->request->getPost('preauth_send') ?? 0) === 1 ? 1 : 0;
}
if (isset($fieldMap['doc_recd'])) {
$update['doc_recd'] = (int) ($this->request->getPost('doc_recd') ?? 0) === 1 ? 1 : 0;
}
if (isset($fieldMap['final_bill_send'])) {
$update['final_bill_send'] = (int) ($this->request->getPost('final_bill_send') ?? 0) === 1 ? 1 : 0;
}
if (isset($fieldMap['org_approved_status'])) {
$update['org_approved_status'] = trim((string) ($this->request->getPost('org_approved_status') ?? 'Under Process'));
}
if (isset($fieldMap['remark'])) {
$update['remark'] = trim((string) ($this->request->getPost('remark') ?? ''));
}
if (isset($fieldMap['org_approved_status_id']) && $this->db->tableExists('org_approved_status')) {
$statusText = (string) ($update['org_approved_status'] ?? '');
if ($statusText !== '') {
$statusRow = $this->db->table('org_approved_status')
->select('id')
->where('app_status', $statusText)
->get(1)
->getRowArray();
if (is_array($statusRow) && ! empty($statusRow['id'])) {
$update['org_approved_status_id'] = (int) $statusRow['id'];
}
}
}
if (empty($update)) {
return $this->response->setJSON(['ok' => true]);
}
$this->db->table('organization_case_master')
->where('id', $caseId)
->update($update);
return $this->response->setJSON([
'ok' => true,
'message' => 'Ayushman checklist updated.',
]);
}
public function ayushmanClaimSheet(int $ipdId, int $output = 0)
{
$permission = $this->requireAnyPermission([
'billing.access',
'billing.ipd.invoice',
]);
if ($permission) {
return $permission;
}
$panelData = $this->ipdModel->getIpdPanelInfo($ipdId);
if (empty($panelData)) {
return $this->response->setStatusCode(404)->setBody('IPD not found');
}
$checklistItems = $this->ipdModel->getAyushmanChecklistForIpd($ipdId);
$data = [
'ipd_info' => $panelData['ipd_info'] ?? null,
'person_info' => $panelData['person_info'] ?? null,
'checklist_items' => $checklistItems,
'generated_at' => date('d-m-Y H:i:s'),
];
$content = view('billing/ipd/ayushman_claim_sheet', $data);
if ($output === 1) {
ExportExcel($content, 'Ayushman_Claim_Sheet_' . $ipdId);
return $this->response->setBody('');
}
return $this->response->setBody($content);
}
public function updateIpdPackage(int $packageItemId)
{
$permission = $this->requireAnyPermission([

View File

@ -900,6 +900,149 @@ class Report extends BaseController
]);
}
public function ayushman_unmapped_report()
{
$specialities = [];
if ($this->db->tableExists('ayushman_package_master')) {
$specialities = $this->db->table('ayushman_package_master')
->select('speciality_code, speciality_name')
->groupBy('speciality_code, speciality_name')
->orderBy('speciality_name', 'ASC')
->get()
->getResult();
}
return view('report/ayushman_unmapped_report', [
'specialities' => $specialities,
]);
}
public function ayushman_unmapped_report_data(string $specialityCode = '0', int $output = 0)
{
if (! $this->db->tableExists('ayushman_package_master')) {
return $this->response->setBody('<div class="alert alert-warning">Ayushman package master table is not available.</div>');
}
$builder = $this->db->table('ayushman_package_master a')
->select('a.speciality_code, a.speciality_name, a.procedure_code, a.procedure_name, a.package_amount, a.preauth_required, a.pre_investigations, a.post_investigations')
->where('(a.linked_package_id IS NULL OR a.linked_package_id = 0)', null, false)
->orderBy('a.speciality_name', 'ASC')
->orderBy('a.procedure_name', 'ASC');
if ($specialityCode !== '0') {
$builder->where('a.speciality_code', $specialityCode);
}
$rows = $builder->get()->getResult();
$data = [
'rows' => $rows,
'speciality_code' => $specialityCode,
];
$content = view('report/ayushman_unmapped_report_table', $data);
if ($output === 1) {
ExportExcel($content, 'Ayushman_Unmapped_Procedures_' . date('YmdHis'));
return $this->response->setBody('');
}
return $this->response->setBody($content);
}
public function ayushman_case_dashboard()
{
return view('report/ayushman_case_dashboard');
}
public function ayushman_case_dashboard_data(string $dateRange, string $alertType = 'all', int $output = 0)
{
[$minRange, $maxRange] = $this->parseDateRange($dateRange);
$builder = $this->db->table('organization_case_master o')
->select('o.id as case_id, o.case_id_code, o.preauth_send, o.doc_recd, o.final_bill_send, o.org_approved_status, o.Org_insurance_comp')
->select('i.id as ipd_id, i.ipd_code')
->select("DATE_FORMAT(i.register_date, '%d-%m-%Y') as str_register_date", false)
->select('p.p_code, p.p_fname')
->select('ins.short_name as insurance_company')
->select('(SELECT COUNT(*) FROM ipd_package ip JOIN ayushman_package_master a ON a.procedure_code = ip.org_code WHERE ip.ipd_id = i.id) as ayushman_proc_count', false)
->select('(SELECT COUNT(*) FROM ipd_package ip JOIN ayushman_package_master a ON a.procedure_code = ip.org_code WHERE ip.ipd_id = i.id AND (a.linked_package_id IS NULL OR a.linked_package_id = 0)) as unmapped_count', false)
->select('(SELECT COUNT(*) FROM ipd_package ip JOIN ayushman_package_master a ON a.procedure_code = ip.org_code WHERE ip.ipd_id = i.id AND a.preauth_required = 1) as preauth_required_count', false)
->join('ipd_master i', 'o.id = i.case_id', 'inner')
->join('patient_master p', 'i.p_id = p.id', 'inner')
->join('hc_insurance ins', 'ins.id = o.insurance_id', 'left')
->where('o.case_type', 1)
->where('i.register_date >=', $minRange)
->where('i.register_date <=', $maxRange)
->groupStart()
->like('o.Org_insurance_comp', 'ayushman')
->orLike('o.Org_insurance_comp', 'pmjay')
->orLike('ins.short_name', 'ayushman')
->orLike('ins.ins_company_name', 'ayushman')
->groupEnd()
->orderBy('i.id', 'DESC');
$rows = $builder->get()->getResultArray();
$alertType = strtolower(trim($alertType));
if (! in_array($alertType, ['all', 'preauth-pending', 'docs-pending', 'mapping-gap'], true)) {
$alertType = 'all';
}
$rows = array_values(array_filter($rows, static function (array $row) use ($alertType): bool {
$preauthPending = (int) ($row['preauth_required_count'] ?? 0) > 0 && (int) ($row['preauth_send'] ?? 0) !== 1;
$docsPending = (int) ($row['doc_recd'] ?? 0) !== 1;
$mappingGap = (int) ($row['unmapped_count'] ?? 0) > 0;
if ($alertType === 'preauth-pending') {
return $preauthPending;
}
if ($alertType === 'docs-pending') {
return $docsPending;
}
if ($alertType === 'mapping-gap') {
return $mappingGap;
}
return true;
}));
$summary = [
'total_cases' => count($rows),
'preauth_pending' => 0,
'docs_pending' => 0,
'mapping_gaps' => 0,
];
foreach ($rows as $row) {
if ((int) ($row['preauth_required_count'] ?? 0) > 0 && (int) ($row['preauth_send'] ?? 0) !== 1) {
$summary['preauth_pending']++;
}
if ((int) ($row['doc_recd'] ?? 0) !== 1) {
$summary['docs_pending']++;
}
if ((int) ($row['unmapped_count'] ?? 0) > 0) {
$summary['mapping_gaps']++;
}
}
$data = [
'rows' => $rows,
'summary' => $summary,
'min_range' => $minRange,
'max_range' => $maxRange,
'alert_type' => $alertType,
];
$content = view('report/ayushman_case_dashboard_table', $data);
if ($output === 1) {
ExportExcel($content, 'Ayushman_Case_Dashboard_' . date('YmdHis'));
return $this->response->setBody('');
}
return $this->response->setBody($content);
}
public function insurance_opd_report_data(
string $dateRange,
string $insuranceId = '0',

View File

@ -0,0 +1,101 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateAyushmanPackageMasterTable extends Migration
{
public function up(): void
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'unsigned' => true,
'auto_increment' => true,
],
'speciality_code' => [
'type' => 'VARCHAR',
'constraint' => 50,
'default' => '',
],
'speciality_name' => [
'type' => 'VARCHAR',
'constraint' => 255,
'default' => '',
],
'procedure_code' => [
'type' => 'VARCHAR',
'constraint' => 50,
'default' => '',
],
'procedure_name' => [
'type' => 'TEXT',
'null' => true,
],
'package_amount' => [
'type' => 'DECIMAL',
'constraint' => '12,2',
'default' => 0,
],
'preauth_required' => [
'type' => 'TINYINT',
'constraint' => 1,
'default' => 0,
],
'procedure_type' => [
'type' => 'VARCHAR',
'constraint' => 50,
'default' => '',
],
'government_reserved' => [
'type' => 'TINYINT',
'constraint' => 1,
'default' => 0,
],
'pre_investigations' => [
'type' => 'TEXT',
'null' => true,
],
'post_investigations' => [
'type' => 'TEXT',
'null' => true,
],
'linked_package_id' => [
'type' => 'INT',
'unsigned' => true,
'null' => true,
],
'source_file' => [
'type' => 'VARCHAR',
'constraint' => 255,
'default' => '',
],
'source_sheet' => [
'type' => 'VARCHAR',
'constraint' => 100,
'default' => 'Sheet1',
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addUniqueKey('procedure_code');
$this->forge->addKey('speciality_code');
$this->forge->addKey('linked_package_id');
$this->forge->addKey('preauth_required');
$this->forge->createTable('ayushman_package_master', true);
}
public function down(): void
{
$this->forge->dropTable('ayushman_package_master', true);
}
}

View File

@ -140,9 +140,15 @@ class IpdBillingModel extends Model
->select("in_master.ins_company_name as ins_company_name", false)
->select("in_master.short_name as ins_short_name", false)
->select("o.case_id_code as case_id_code", false)
->select("o.Org_insurance_comp as org_insurance_comp", false)
->select("o.doc_recd as doc_recd", false)
->select("o.insurance_no as insurance_no", false)
->select("o.insurance_no_1 as insurance_no_1", false)
->select("o.insurance_no_2 as insurance_no_2", false)
->select("o.preauth_send as preauth_send", false)
->select("o.final_bill_send as final_bill_send", false)
->select("o.org_approved_status as org_approved_status", false)
->select("o.remark as remark", false)
->join('organization_case_master o', 'i.case_id = o.id', 'left')
->join('hc_insurance in_master', 'in_master.id = o.insurance_id', 'left')
->where('i.id', $ipdId)
@ -422,6 +428,123 @@ class IpdBillingModel extends Model
->getResult();
}
public function getAyushmanSpecialities(): array
{
if (! $this->db->tableExists('ayushman_package_master')) {
return [];
}
return $this->db->table('ayushman_package_master')
->select('speciality_code, speciality_name')
->groupBy('speciality_code, speciality_name')
->orderBy('speciality_name', 'ASC')
->get()
->getResultArray();
}
public function searchAyushmanPackages(string $query = '', string $specialityCode = '', int $limit = 25): array
{
if (! $this->db->tableExists('ayushman_package_master')) {
return [];
}
$builder = $this->db->table('ayushman_package_master a')
->select('a.id, a.speciality_code, a.speciality_name, a.procedure_code, a.procedure_name, a.package_amount, a.preauth_required, a.procedure_type, a.government_reserved, a.pre_investigations, a.post_investigations, a.linked_package_id')
->select('p.ipd_pakage_name as linked_package_name', false)
->join('package p', 'p.id = a.linked_package_id', 'left');
if ($specialityCode !== '') {
$builder->where('a.speciality_code', $specialityCode);
}
$query = trim($query);
if ($query !== '') {
$builder->groupStart()
->like('a.procedure_code', $query)
->orLike('a.procedure_name', $query)
->orLike('a.speciality_name', $query)
->groupEnd();
}
$limit = max(1, min(100, $limit));
return $builder
->orderBy('a.speciality_name', 'ASC')
->orderBy('a.procedure_name', 'ASC')
->limit($limit)
->get()
->getResultArray();
}
public function getAyushmanPackageById(int $id): ?array
{
if ($id <= 0 || ! $this->db->tableExists('ayushman_package_master')) {
return null;
}
$row = $this->db->table('ayushman_package_master')
->where('id', $id)
->get()
->getRowArray();
return is_array($row) && ! empty($row) ? $row : null;
}
public function getAllPackageOptions(): array
{
return $this->db->table('package p')
->select('p.id, p.ipd_pakage_name, p.Pakage_Min_Amount, g.pakage_group_name')
->join('package_group g', 'g.pak_id = p.pakage_group_id', 'left')
->orderBy('g.pakage_group_name', 'ASC')
->orderBy('p.ipd_pakage_name', 'ASC')
->get()
->getResultArray();
}
public function updateAyushmanPackageLink(int $ayushmanId, int $linkedPackageId): bool
{
if ($ayushmanId <= 0 || ! $this->db->tableExists('ayushman_package_master')) {
return false;
}
$exists = $this->db->table('ayushman_package_master')
->select('id')
->where('id', $ayushmanId)
->get(1)
->getRowArray();
if (! is_array($exists) || empty($exists)) {
return false;
}
$update = [
'linked_package_id' => $linkedPackageId > 0 ? $linkedPackageId : null,
'updated_at' => date('Y-m-d H:i:s'),
];
$this->db->table('ayushman_package_master')
->where('id', $ayushmanId)
->update($update);
return true;
}
public function getAyushmanChecklistForIpd(int $ipdId): array
{
if ($ipdId <= 0 || ! $this->db->tableExists('ayushman_package_master')) {
return [];
}
return $this->db->table('ipd_package p')
->select('p.id as ipd_package_id, p.package_name, p.package_desc, p.comment, p.org_code, p.package_Amount')
->select('a.id as ayushman_id, a.speciality_code, a.speciality_name, a.procedure_code, a.procedure_name, a.preauth_required, a.pre_investigations, a.post_investigations, a.linked_package_id')
->join('ayushman_package_master a', 'a.procedure_code = p.org_code', 'inner')
->where('p.ipd_id', $ipdId)
->orderBy('p.id', 'DESC')
->get()
->getResultArray();
}
public function getPackageWithInsurance(int $packageId, int $insuranceId): ?object
{
return $this->db->table('package p')
@ -453,6 +576,31 @@ class IpdBillingModel extends Model
->getResult();
}
public function updateDiagnosisChargeInclude(int $ipdId, int $invoiceId, int $include): bool
{
if ($ipdId <= 0 || $invoiceId <= 0) {
return false;
}
$exists = $this->db->table('invoice_master')
->select('id')
->where('id', $invoiceId)
->where('ipd_id', $ipdId)
->get(1)
->getRowArray();
if (! is_array($exists) || empty($exists)) {
return false;
}
$builder = $this->db->table('invoice_master');
$builder->where('id', $invoiceId)
->where('ipd_id', $ipdId)
->set('ipd_include', $include > 0 ? 1 : 0)
->update();
return true;
}
public function getPayments(int $ipdId): array
{
return $this->db->table('payment_history')

View File

@ -0,0 +1,70 @@
<?php
$ipd = $ipd_info ?? null;
$person = $person_info ?? null;
$items = $checklist_items ?? [];
$generatedAt = $generated_at ?? date('d-m-Y H:i:s');
?>
<section class="content">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">Ayushman Claim Sheet</h5>
<div class="d-flex gap-2">
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="window.print()">Print</button>
</div>
</div>
<div class="card-body">
<div class="row g-2 mb-3">
<div class="col-md-4"><strong>Patient:</strong> <?= esc($person->p_fname ?? '') ?></div>
<div class="col-md-2"><strong>UHID:</strong> <?= esc($person->p_code ?? '') ?></div>
<div class="col-md-2"><strong>IPD:</strong> <?= esc($ipd->ipd_code ?? '') ?></div>
<div class="col-md-4"><strong>Case:</strong> <?= esc($ipd->case_id_code ?? '') ?></div>
<div class="col-md-4"><strong>Insurance:</strong> <?= esc($ipd->ins_company_name ?? '') ?></div>
<div class="col-md-4"><strong>Scheme:</strong> <?= esc($ipd->org_insurance_comp ?? '') ?></div>
<div class="col-md-4"><strong>Generated:</strong> <?= esc($generatedAt) ?></div>
</div>
<div class="row g-2 mb-3">
<div class="col-md-3"><strong>Preauth Sent:</strong> <?= (int) ($ipd->preauth_send ?? 0) === 1 ? 'Yes' : 'No' ?></div>
<div class="col-md-3"><strong>Documents Received:</strong> <?= (int) ($ipd->doc_recd ?? 0) === 1 ? 'Yes' : 'No' ?></div>
<div class="col-md-3"><strong>Final Bill Sent:</strong> <?= (int) ($ipd->final_bill_send ?? 0) === 1 ? 'Yes' : 'No' ?></div>
<div class="col-md-3"><strong>Approval Status:</strong> <?= esc((string) ($ipd->org_approved_status ?? 'Under Process')) ?></div>
<div class="col-md-12"><strong>Remark:</strong> <?= esc((string) ($ipd->remark ?? '')) ?></div>
</div>
<?php if (empty($items)) : ?>
<div class="alert alert-warning">No Ayushman procedures linked to this IPD package list.</div>
<?php else : ?>
<table class="table table-bordered table-sm table-striped align-middle">
<thead class="table-light">
<tr>
<th style="width: 40px;">#</th>
<th style="width: 140px;">Procedure Code</th>
<th>Procedure Name</th>
<th style="width: 120px;" class="text-end">Amount</th>
<th style="width: 90px;">Preauth</th>
<th>Pre Investigations</th>
<th>Post Investigations</th>
</tr>
</thead>
<tbody>
<?php foreach ($items as $index => $item) : ?>
<tr>
<td><?= $index + 1 ?></td>
<td><?= esc((string) ($item['procedure_code'] ?? '')) ?></td>
<td>
<div class="fw-semibold"><?= esc((string) ($item['procedure_name'] ?? '')) ?></div>
<div class="small text-muted"><?= esc((string) (($item['speciality_name'] ?? '') . ' [' . ($item['speciality_code'] ?? '') . ']')) ?></div>
</td>
<td class="text-end"><?= number_format((float) ($item['package_Amount'] ?? 0), 2) ?></td>
<td><?= (int) ($item['preauth_required'] ?? 0) === 1 ? 'Required' : 'No' ?></td>
<td><?= esc((string) ($item['pre_investigations'] ?? '')) ?></td>
<td><?= esc((string) ($item['post_investigations'] ?? '')) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
</div>
</section>

View File

@ -1,3 +1,9 @@
<?php
$ipdId = (int) (($ipd_info->id ?? 0));
$csrfName = csrf_token();
$csrfHash = csrf_hash();
?>
<div class="table-responsive">
<table class="table table-striped table-hover align-middle">
<thead>
@ -20,7 +26,17 @@
<td><?= esc($row->str_date ?? '') ?></td>
<td><?= esc($row->charge_list ?? '') ?></td>
<td class="text-end"><?= esc($row->amount ?? '') ?></td>
<td><?= ((int) ($row->ipd_include ?? 0) > 0) ? 'Yes' : 'No' ?></td>
<td>
<div class="form-check m-0">
<input
class="form-check-input ipd-diagnosis-include"
type="checkbox"
value="1"
data-invoice-id="<?= (int) ($row->inv_id ?? 0) ?>"
<?= ((int) ($row->ipd_include ?? 0) > 0) ? 'checked' : '' ?>
>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php else : ?>
@ -31,3 +47,40 @@
</tbody>
</table>
</div>
<script>
(function () {
var diagnosisIncludeUrlBase = '<?= site_url('billing/ipd/panel/' . $ipdId . '/diagnosis/include') ?>';
var diagnosisCsrfName = '<?= esc($csrfName) ?>';
var diagnosisCsrfHash = '<?= esc($csrfHash) ?>';
$(document).off('change', '.ipd-diagnosis-include').on('change', '.ipd-diagnosis-include', function () {
var checkbox = $(this);
var invoiceId = parseInt(checkbox.data('invoice-id') || 0, 10);
if (invoiceId <= 0) {
checkbox.prop('checked', !checkbox.is(':checked'));
return;
}
var payload = {};
payload[diagnosisCsrfName] = diagnosisCsrfHash;
payload.ipd_include = checkbox.is(':checked') ? 1 : 0;
checkbox.prop('disabled', true);
$.post(diagnosisIncludeUrlBase + '/' + invoiceId, payload, function (resp) {
diagnosisCsrfHash = (resp && resp.csrfHash) ? resp.csrfHash : diagnosisCsrfHash;
if (!resp || !resp.ok) {
checkbox.prop('checked', !checkbox.is(':checked'));
alert((resp && resp.error) ? resp.error : 'Unable to update include status');
}
}, 'json').fail(function (xhr) {
checkbox.prop('checked', !checkbox.is(':checked'));
var msg = (xhr && xhr.responseJSON && xhr.responseJSON.error) ? xhr.responseJSON.error : 'Unable to update include status';
alert(msg);
}).always(function () {
checkbox.prop('disabled', false);
});
});
})();
</script>

View File

@ -3,6 +3,13 @@ $ipd = $ipd_info ?? null;
$ipdId = (int) ($ipd->id ?? 0);
$csrfName = csrf_token();
$csrfHash = csrf_hash();
$insuranceLabel = trim((string) ($ipd->ins_company_name ?? ''));
$schemeLabel = trim((string) ($ipd->org_insurance_comp ?? ''));
$caseLabel = strtolower(trim($insuranceLabel . ' ' . $schemeLabel));
$isAyushmanCase = strpos($caseLabel, 'ayushman') !== false || strpos($caseLabel, 'pmjay') !== false || strpos($caseLabel, 'bharat') !== false;
$ayushmanCollapseClass = $isAyushmanCase ? 'accordion-collapse collapse show' : 'accordion-collapse collapse';
$ayushmanButtonClass = $isAyushmanCase ? 'accordion-button' : 'accordion-button collapsed';
$ayushmanChecklistItems = $ayushman_checklist_items ?? [];
?>
<div class="card border-top border-3 border-danger">
@ -24,13 +31,26 @@ $csrfHash = csrf_hash();
<?php if (! empty($ipd_packages)) : ?>
<?php $srNo = 1; ?>
<?php foreach ($ipd_packages as $row) : ?>
<?php
$isAyushmanRow = stripos((string) ($row->package_desc ?? ''), 'Ayushman') !== false
|| stripos((string) ($row->comment ?? ''), 'Ayushman Code:') !== false;
?>
<tr>
<td><?= $srNo++ ?></td>
<td>
<?= esc($row->package_name ?? '') ?>
<?php if ($isAyushmanRow) : ?>
<span class="badge bg-success ms-1">Ayushman</span>
<?php endif; ?>
<?php if (! empty($row->org_code ?? '')) : ?>
<span class="badge bg-info text-dark ms-1"><?= esc($row->org_code ?? '') ?></span>
<?php endif; ?>
<?php if (! empty($row->package_desc ?? $row->comment ?? '')) : ?>
<br><em><?= esc($row->package_desc ?? $row->comment ?? '') ?></em>
<?php endif; ?>
<?php if (! empty($row->comment ?? '') && ($row->comment ?? '') !== ($row->package_desc ?? '')) : ?>
<br><small class="text-muted"><?= esc($row->comment ?? '') ?></small>
<?php endif; ?>
</td>
<td>
<input class="form-control form-control-sm" style="width: 120px" id="input_amt_<?= (int) ($row->id ?? 0) ?>" value="<?= esc($row->package_Amount ?? 0) ?>" type="number" step="0.01" />
@ -115,6 +135,188 @@ $csrfHash = csrf_hash();
</div>
</div>
</div>
<?php if ($isAyushmanCase) : ?>
<div class="accordion-item">
<h2 class="accordion-header" id="heading_package_checklist">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapse_package_checklist" aria-expanded="true">
Ayushman Preauth Checklist
</button>
</h2>
<div id="collapse_package_checklist" class="accordion-collapse collapse show" aria-labelledby="heading_package_checklist" data-bs-parent="#accordion_package">
<div class="accordion-body">
<div class="row g-3">
<div class="col-md-3">
<div class="form-check mt-4">
<input class="form-check-input" type="checkbox" id="ayushman_preauth_send" <?= (int) ($ipd->preauth_send ?? 0) === 1 ? 'checked' : '' ?>>
<label class="form-check-label" for="ayushman_preauth_send">Preauth Sent</label>
</div>
</div>
<div class="col-md-3">
<div class="form-check mt-4">
<input class="form-check-input" type="checkbox" id="ayushman_doc_recd" <?= (int) ($ipd->doc_recd ?? 0) === 1 ? 'checked' : '' ?>>
<label class="form-check-label" for="ayushman_doc_recd">Documents Received</label>
</div>
</div>
<div class="col-md-3">
<div class="form-check mt-4">
<input class="form-check-input" type="checkbox" id="ayushman_final_bill_send" <?= (int) ($ipd->final_bill_send ?? 0) === 1 ? 'checked' : '' ?>>
<label class="form-check-label" for="ayushman_final_bill_send">Final Bill Sent</label>
</div>
</div>
<div class="col-md-3">
<label class="form-label">Approval Status</label>
<select class="form-select form-select-sm" id="ayushman_org_status">
<?php $currentOrgStatus = trim((string) ($ipd->org_approved_status ?? 'Under Process')); ?>
<?php foreach (['Under Process', 'Query', 'Approved', 'Deny'] as $statusLabel) : ?>
<option value="<?= esc($statusLabel) ?>" <?= $currentOrgStatus === $statusLabel ? 'selected' : '' ?>><?= esc($statusLabel) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-12">
<label class="form-label">Case Remark</label>
<textarea class="form-control form-control-sm" rows="2" id="ayushman_case_remark"><?= esc((string) ($ipd->remark ?? '')) ?></textarea>
</div>
</div>
<div class="table-responsive mt-3">
<table class="table table-bordered table-sm align-middle">
<thead class="table-light">
<tr>
<th>Selected Procedure</th>
<th>Speciality</th>
<th>Preauth</th>
<th>Pre Investigations</th>
<th>Post Investigations</th>
</tr>
</thead>
<tbody>
<?php if (! empty($ayushmanChecklistItems)) : ?>
<?php foreach ($ayushmanChecklistItems as $item) : ?>
<tr>
<td>
<div class="fw-semibold"><?= esc(($item['procedure_name'] ?? '') . ' [' . ($item['procedure_code'] ?? '') . ']') ?></div>
<div class="small text-muted">Package: <?= esc($item['package_name'] ?? '') ?></div>
</td>
<td><?= esc(($item['speciality_name'] ?? '') . ' [' . ($item['speciality_code'] ?? '') . ']') ?></td>
<td><?= (int) ($item['preauth_required'] ?? 0) === 1 ? '<span class="badge bg-warning text-dark">Required</span>' : '<span class="badge bg-secondary">No</span>' ?></td>
<td><?= esc((string) ($item['pre_investigations'] ?? '-')) ?></td>
<td><?= esc((string) ($item['post_investigations'] ?? '-')) ?></td>
</tr>
<?php endforeach; ?>
<?php else : ?>
<tr>
<td colspan="5" class="text-center text-muted">No Ayushman procedures have been added to this IPD package list yet.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
<div class="mt-3 d-flex flex-wrap gap-2">
<button type="button" class="btn btn-primary btn-sm" onclick="ipdAyushmanSaveChecklist()">Save Checklist</button>
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="ipdAyushmanClaimSheetPreview()">Preview Claim Sheet</button>
<button type="button" class="btn btn-outline-success btn-sm" onclick="ipdAyushmanClaimSheetExport()">Export Claim Sheet</button>
</div>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header" id="heading_package_ayushman">
<button class="<?= $ayushmanButtonClass ?>" type="button" data-bs-toggle="collapse" data-bs-target="#collapse_package_ayushman" aria-expanded="<?= $isAyushmanCase ? 'true' : 'false' ?>">
Ayushman Bharat Treatment Search
</button>
</h2>
<div id="collapse_package_ayushman" class="<?= $ayushmanCollapseClass ?>" aria-labelledby="heading_package_ayushman" data-bs-parent="#accordion_package">
<div class="accordion-body">
<div class="alert <?= $isAyushmanCase ? 'alert-success' : 'alert-secondary' ?> py-2 mb-3">
<div><strong>Insurance:</strong> <?= esc($insuranceLabel !== '' ? $insuranceLabel : 'Not set') ?></div>
<div><strong>Scheme:</strong> <?= esc($schemeLabel !== '' ? $schemeLabel : 'Not set') ?></div>
<div><strong>Preauth Status:</strong> <?= esc((string) ($ipd->org_approved_status ?? 'Under Process')) ?></div>
</div>
<div class="row g-2 mb-3">
<div class="col-md-4">
<label class="form-label">Speciality</label>
<select class="form-select form-select-sm" id="ayushman_speciality_code">
<option value="">All</option>
<?php foreach ($ayushman_specialities ?? [] as $speciality) : ?>
<option value="<?= esc($speciality['speciality_code'] ?? '') ?>">
<?= esc(($speciality['speciality_name'] ?? '') . ' [' . ($speciality['speciality_code'] ?? '') . ']') ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-5">
<label class="form-label">Procedure Search</label>
<input class="form-control form-control-sm" id="ayushman_search_query" type="text" placeholder="Procedure name or code" />
</div>
<div class="col-md-3 d-flex align-items-end gap-2">
<button type="button" class="btn btn-success btn-sm" onclick="ipdAyushmanSearch()">Search</button>
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="ipdAyushmanClear()">Clear</button>
</div>
</div>
<div id="ayushman_search_summary" class="small text-muted mb-2">Search imported Ayushman Bharat treatment list and add the selected treatment as an IPD package entry.</div>
<div class="table-responsive">
<table class="table table-striped table-sm align-middle">
<thead>
<tr>
<th>Procedure</th>
<th>Speciality</th>
<th>Mapped Package</th>
<th>Amount</th>
<th>Preauth</th>
<th style="width: 170px"></th>
</tr>
</thead>
<tbody id="ayushman_search_results">
<tr>
<td colspan="6" class="text-center text-muted">Run a search to view Ayushman treatments.</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<?php else : ?>
<div class="alert alert-light border mt-3 mb-0">
Ayushman Bharat treatment search is shown automatically for cases where insurance or scheme contains Ayushman or PMJAY.
</div>
<?php endif; ?>
</div>
</div>
</div>
<div class="modal fade" id="ayushmanMappingModal" tabindex="-1" aria-labelledby="ayushmanMappingModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="ayushmanMappingModalLabel">Map Ayushman Procedure to Internal Package</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<input type="hidden" id="ayushman_mapping_item_id" value="0" />
<div class="mb-3">
<div class="fw-semibold" id="ayushman_mapping_title">Select a procedure</div>
<div class="small text-muted" id="ayushman_mapping_meta"></div>
</div>
<div class="row g-3">
<div class="col-md-12">
<label class="form-label">Internal Package</label>
<select class="form-select" id="ayushman_mapping_package_id">
<option value="0">Not linked</option>
<?php foreach ($all_package_options ?? [] as $row) : ?>
<option value="<?= (int) ($row['id'] ?? 0) ?>">
<?= esc(trim((string) ($row['pakage_group_name'] ?? '')) . ' | ' . trim((string) ($row['ipd_pakage_name'] ?? '')) . ' | Rs. ' . number_format((float) ($row['Pakage_Min_Amount'] ?? 0), 2)) ?>
</option>
<?php endforeach; ?>
</select>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" onclick="ipdAyushmanSaveMapping()">Save Mapping</button>
</div>
</div>
</div>
</div>
@ -124,6 +326,66 @@ $csrfHash = csrf_hash();
var ipdPackageCsrfHash = '<?= esc($csrfHash) ?>';
var ipdPackageBaseUrl = '<?= base_url() ?>';
var ipdPackageIpdId = <?= (int) $ipdId ?>;
var ipdAyushmanSearchUrl = '<?= site_url('billing/ipd/panel/' . $ipdId . '/ayushman/search') ?>';
var ipdAyushmanMapUrlBase = '<?= site_url('billing/ipd/panel/' . $ipdId . '/ayushman/map') ?>';
var ipdAyushmanChecklistUrl = '<?= site_url('billing/ipd/panel/' . $ipdId . '/ayushman/checklist') ?>';
var ipdAyushmanClaimSheetUrl = '<?= site_url('billing/ipd/panel/' . $ipdId . '/ayushman/claim-sheet') ?>';
var ipdAyushmanResults = {};
function normalizeText(value) {
return String(value || '')
.toLowerCase()
.replace(/[^a-z0-9\s]/g, ' ')
.replace(/\s+/g, ' ')
.trim();
}
function scorePackageSuggestion(procedureName, packageName) {
var p = normalizeText(procedureName);
var q = normalizeText(packageName);
if (!p || !q) {
return 0;
}
var score = 0;
if (q.indexOf(p) >= 0 || p.indexOf(q) >= 0) {
score += 8;
}
var pWords = p.split(' ');
var qWords = q.split(' ');
pWords.forEach(function(word) {
if (word.length >= 4 && qWords.indexOf(word) >= 0) {
score += 2;
}
});
return score;
}
function ipdAyushmanSuggestPackageId(procedureName) {
var select = document.getElementById('ayushman_mapping_package_id');
if (!select) {
return 0;
}
var bestId = 0;
var bestScore = 0;
for (var i = 0; i < select.options.length; i++) {
var option = select.options[i];
var id = parseInt(option.value || '0', 10);
if (id <= 0) {
continue;
}
var score = scorePackageSuggestion(procedureName, option.text || '');
if (score > bestScore) {
bestScore = score;
bestId = id;
}
}
return bestScore >= 4 ? bestId : 0;
}
function ipdPackageReload() {
load_form_div('<?= base_url('IpdNew/ipd_package/' . $ipdId) ?>', 'tab_package_content');
@ -171,6 +433,210 @@ $csrfHash = csrf_hash();
});
}
function ipdAyushmanRenderResults(items) {
var tbody = $('#ayushman_search_results');
tbody.empty();
ipdAyushmanResults = {};
if (!items || !items.length) {
tbody.append('<tr><td colspan="6" class="text-center text-muted">No Ayushman treatment matched the search.</td></tr>');
$('#ayushman_search_summary').text('No matching treatments found.');
return;
}
$('#ayushman_search_summary').text(items.length + ' treatment(s) found.');
items.forEach(function(item) {
ipdAyushmanResults[String(item.id)] = item;
var procedure = $('<div></div>');
procedure.append($('<div class="fw-semibold"></div>').text((item.procedure_name || '') + ' [' + (item.procedure_code || '') + ']'));
var meta = [];
if (item.procedure_type) {
meta.push('Type: ' + item.procedure_type);
}
if (Number(item.government_reserved || 0) === 1) {
meta.push('Government Reserved');
}
if (item.pre_investigations) {
meta.push('Pre: ' + item.pre_investigations);
}
if (item.post_investigations) {
meta.push('Post: ' + item.post_investigations);
}
if (meta.length > 0) {
procedure.append($('<div class="small text-muted"></div>').text(meta.join(' | ')));
}
var linkedText = item.linked_package_name ? item.linked_package_name : 'Not linked';
var linkedCell = $('<td></td>');
linkedCell.append($('<div></div>').text(linkedText));
linkedCell.append($('<button type="button" class="btn btn-outline-primary btn-sm mt-1">Map</button>').attr('onclick', 'ipdAyushmanOpenMapping(' + Number(item.id || 0) + ')'));
var row = $('<tr></tr>');
row.append($('<td></td>').append(procedure));
row.append($('<td></td>').text((item.speciality_name || '') + ' [' + (item.speciality_code || '') + ']'));
row.append(linkedCell);
row.append($('<td class="text-end"></td>').text(Number(item.package_amount || 0).toFixed(2)));
row.append($('<td></td>').html(Number(item.preauth_required || 0) === 1 ? '<span class="badge bg-warning text-dark">Yes</span>' : '<span class="badge bg-secondary">No</span>'));
row.append($('<td></td>').html('<button type="button" class="btn btn-success btn-sm">Add</button>').find('button').attr('onclick', 'ipdAyushmanAdd(' + Number(item.id || 0) + ')').end());
tbody.append(row);
});
}
function ipdAyushmanOpenMapping(itemId) {
var item = ipdAyushmanResults[String(itemId)] || null;
if (!item) {
alert('Ayushman treatment not found in current result list.');
return;
}
$('#ayushman_mapping_item_id').val(itemId);
$('#ayushman_mapping_title').text((item.procedure_name || '') + ' [' + (item.procedure_code || '') + ']');
var selectedPackageId = Number(item.linked_package_id || 0);
var suggestedPackageId = 0;
if (selectedPackageId <= 0) {
suggestedPackageId = ipdAyushmanSuggestPackageId(item.procedure_name || '');
if (suggestedPackageId > 0) {
selectedPackageId = suggestedPackageId;
}
}
$('#ayushman_mapping_package_id').val(String(selectedPackageId || 0));
var metaText = (item.speciality_name || '') + ' | Current link: ' + (item.linked_package_name || 'Not linked');
if (suggestedPackageId > 0) {
var suggestedText = $('#ayushman_mapping_package_id option:selected').text() || '';
metaText += ' | Suggested: ' + suggestedText;
}
$('#ayushman_mapping_meta').text(metaText);
var modalEl = document.getElementById('ayushmanMappingModal');
var modal = bootstrap.Modal.getOrCreateInstance(modalEl);
modal.show();
}
function ipdAyushmanSaveMapping() {
var itemId = parseInt($('#ayushman_mapping_item_id').val() || '0', 10);
if (itemId <= 0) {
alert('Select an Ayushman procedure first.');
return;
}
var payload = {};
payload[ipdPackageCsrfName] = ipdPackageCsrfHash;
payload.linked_package_id = $('#ayushman_mapping_package_id').val() || 0;
$.post(ipdAyushmanMapUrlBase + '/' + itemId, payload, function(resp) {
if (!resp || !resp.ok) {
alert((resp && resp.error) ? resp.error : 'Unable to save mapping');
return;
}
var item = ipdAyushmanResults[String(itemId)] || null;
if (item) {
item.linked_package_id = Number(resp.linked_package_id || 0);
item.linked_package_name = resp.linked_package_name || '';
}
var modalEl = document.getElementById('ayushmanMappingModal');
var modal = bootstrap.Modal.getInstance(modalEl);
if (modal) {
modal.hide();
}
ipdAyushmanSearch();
}, 'json').fail(function(xhr) {
var msg = (xhr && xhr.responseJSON && xhr.responseJSON.error) ? xhr.responseJSON.error : 'Unable to save mapping';
alert(msg);
});
}
function ipdAyushmanSaveChecklist() {
var payload = {};
payload[ipdPackageCsrfName] = ipdPackageCsrfHash;
payload.preauth_send = $('#ayushman_preauth_send').is(':checked') ? 1 : 0;
payload.doc_recd = $('#ayushman_doc_recd').is(':checked') ? 1 : 0;
payload.final_bill_send = $('#ayushman_final_bill_send').is(':checked') ? 1 : 0;
payload.org_approved_status = $('#ayushman_org_status').val() || 'Under Process';
payload.remark = $('#ayushman_case_remark').val() || '';
$.post(ipdAyushmanChecklistUrl, payload, function(resp) {
if (!resp || !resp.ok) {
alert((resp && resp.error) ? resp.error : 'Unable to save Ayushman checklist');
return;
}
alert(resp.message || 'Ayushman checklist updated.');
ipdPackageReload();
}, 'json').fail(function(xhr) {
var msg = (xhr && xhr.responseJSON && xhr.responseJSON.error) ? xhr.responseJSON.error : 'Unable to save Ayushman checklist';
alert(msg);
});
}
function ipdAyushmanClaimSheetPreview() {
window.open(ipdAyushmanClaimSheetUrl, '_blank');
}
function ipdAyushmanClaimSheetExport() {
window.open(ipdAyushmanClaimSheetUrl + '/1', '_blank');
}
function ipdAyushmanSearch() {
var specialityCode = $('#ayushman_speciality_code').val() || '';
var query = $('#ayushman_search_query').val() || '';
$('#ayushman_search_summary').text('Searching...');
$.getJSON(ipdAyushmanSearchUrl, {
speciality_code: specialityCode,
q: query
}, function(resp) {
if (!resp || !resp.ok) {
$('#ayushman_search_summary').text('Unable to load Ayushman treatments.');
return;
}
ipdAyushmanRenderResults(resp.items || []);
}).fail(function() {
$('#ayushman_search_summary').text('Unable to load Ayushman treatments.');
$('#ayushman_search_results').html('<tr><td colspan="6" class="text-center text-danger">Search request failed.</td></tr>');
});
}
function ipdAyushmanClear() {
$('#ayushman_speciality_code').val('');
$('#ayushman_search_query').val('');
$('#ayushman_search_summary').text('Search imported Ayushman Bharat treatment list and add the selected treatment as an IPD package entry.');
$('#ayushman_search_results').html('<tr><td colspan="6" class="text-center text-muted">Run a search to view Ayushman treatments.</td></tr>');
}
function ipdAyushmanAdd(itemId) {
var item = ipdAyushmanResults[String(itemId)] || null;
if (!item) {
alert('Ayushman treatment not found in current result list.');
return;
}
var payload = {};
payload[ipdPackageCsrfName] = ipdPackageCsrfHash;
payload.ayushman_package_id = itemId;
payload.package_id = item.linked_package_id || 0;
payload.package_name = item.procedure_name || '';
payload.input_amount = item.package_amount || 0;
payload.comment = '';
$.post(ipdPackageBaseUrl + 'billing/ipd/package/add/' + ipdPackageIpdId, payload, function(resp) {
if (resp && resp.ok) {
ipdPackageReload();
return;
}
alert((resp && resp.error) ? resp.error : 'Unable to add Ayushman package');
}, 'json').fail(function(xhr) {
var msg = (xhr && xhr.responseJSON && xhr.responseJSON.error) ? xhr.responseJSON.error : 'Unable to add Ayushman package';
alert(msg);
});
}
function ipdPackageUpdate(itemId) {
var payload = {};
payload[ipdPackageCsrfName] = ipdPackageCsrfHash;
@ -193,4 +659,15 @@ $csrfHash = csrf_hash();
ipdPackageReload();
});
}
$('#ayushman_search_query').on('keydown', function(event) {
if (event.key === 'Enter') {
event.preventDefault();
ipdAyushmanSearch();
}
});
<?php if ($isAyushmanCase) : ?>
ipdAyushmanSearch();
<?php endif; ?>
</script>

View File

@ -0,0 +1,75 @@
<section class="content">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">
<i class="bi bi-speedometer2 me-2"></i>Ayushman Case Dashboard
</h5>
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="load_form('<?= base_url('Report/insurance_credit_main') ?>', 'Insurance Credit')">
<i class="bi bi-arrow-left me-1"></i>Back to Main
</button>
</div>
<div class="card-body">
<div class="row g-3 align-items-end">
<div class="col-md-5">
<label class="form-label">Date Range (IPD Register Date)</label>
<div class="d-flex gap-2">
<input type="date" class="form-control" id="dashboard_start" value="<?= esc(date('Y-m-01')) ?>">
<input type="date" class="form-control" id="dashboard_end" value="<?= esc(date('Y-m-d')) ?>">
</div>
</div>
<div class="col-md-4">
<label class="form-label">Alert Type</label>
<select class="form-select" id="dashboard_alert_type">
<option value="all">All Cases</option>
<option value="preauth-pending">Preauth Pending</option>
<option value="docs-pending">Documents Pending</option>
<option value="mapping-gap">Mapping Gap</option>
</select>
</div>
<div class="col-md-3 d-flex gap-2">
<button type="button" class="btn btn-primary w-100" id="show_dashboard">
<i class="bi bi-search me-1"></i>Show
</button>
<button type="button" class="btn btn-outline-success w-100" id="export_dashboard">
<i class="bi bi-file-earmark-excel me-1"></i>Export
</button>
</div>
</div>
</div>
</div>
<div class="card mt-3">
<div class="card-body">
<div id="ayushman_dashboard_result" class="table-responsive">
<div class="text-center text-muted py-4">
<i class="bi bi-filter-circle" style="font-size: 2rem;"></i>
<p class="mt-2">Select date and alert filter, then click Show</p>
</div>
</div>
</div>
</div>
</section>
<script>
(function() {
function buildUrl(output) {
var startDate = document.getElementById('dashboard_start').value;
var endDate = document.getElementById('dashboard_end').value;
var dateRange = startDate + 'S' + endDate;
var alertType = document.getElementById('dashboard_alert_type').value || 'all';
var url = '<?= base_url('Report/ayushman_case_dashboard_data') ?>/' + encodeURIComponent(dateRange) + '/' + encodeURIComponent(alertType);
if (output === 1) {
url += '/1';
}
return url;
}
document.getElementById('show_dashboard').addEventListener('click', function() {
load_form_div(buildUrl(0), 'ayushman_dashboard_result');
});
document.getElementById('export_dashboard').addEventListener('click', function() {
window.open(buildUrl(1), '_blank');
});
})();
</script>

View File

@ -0,0 +1,105 @@
<?php
$rows = $rows ?? [];
$summary = $summary ?? ['total_cases' => 0, 'preauth_pending' => 0, 'docs_pending' => 0, 'mapping_gaps' => 0];
$minRange = $min_range ?? '';
$maxRange = $max_range ?? '';
$alertType = $alert_type ?? 'all';
?>
<div class="mb-3">
<div><strong>Date Range:</strong> <?= esc($minRange) ?> to <?= esc($maxRange) ?></div>
<div><strong>Alert Filter:</strong> <?= esc($alertType) ?></div>
</div>
<div class="row g-2 mb-3">
<div class="col-md-3">
<div class="card border-primary">
<div class="card-body py-2">
<div class="small text-muted">Total Cases</div>
<div class="fs-5 fw-semibold text-primary"><?= (int) ($summary['total_cases'] ?? 0) ?></div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card border-warning">
<div class="card-body py-2">
<div class="small text-muted">Preauth Pending</div>
<div class="fs-5 fw-semibold text-warning"><?= (int) ($summary['preauth_pending'] ?? 0) ?></div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card border-secondary">
<div class="card-body py-2">
<div class="small text-muted">Documents Pending</div>
<div class="fs-5 fw-semibold text-secondary"><?= (int) ($summary['docs_pending'] ?? 0) ?></div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card border-danger">
<div class="card-body py-2">
<div class="small text-muted">Mapping Gaps</div>
<div class="fs-5 fw-semibold text-danger"><?= (int) ($summary['mapping_gaps'] ?? 0) ?></div>
</div>
</div>
</div>
</div>
<?php if (empty($rows)) : ?>
<div class="alert alert-info">No Ayushman cases found for selected criteria.</div>
<?php else : ?>
<table class="table table-bordered table-sm table-striped align-middle">
<thead class="table-light">
<tr>
<th style="width:40px;">#</th>
<th style="width:120px;">Case</th>
<th style="width:110px;">IPD</th>
<th style="width:90px;">UHID</th>
<th>Patient</th>
<th style="width:140px;">Insurance</th>
<th style="width:95px;">Reg Date</th>
<th style="width:80px;" class="text-end">Ayushman Proc</th>
<th style="width:80px;" class="text-end">Unmapped</th>
<th style="width:80px;" class="text-end">Preauth Req</th>
<th style="width:100px;">Preauth Sent</th>
<th style="width:100px;">Docs</th>
<th style="width:130px;">Status</th>
</tr>
</thead>
<tbody>
<?php foreach ($rows as $index => $row) : ?>
<?php
$preauthPending = (int) ($row['preauth_required_count'] ?? 0) > 0 && (int) ($row['preauth_send'] ?? 0) !== 1;
$docsPending = (int) ($row['doc_recd'] ?? 0) !== 1;
$mappingGap = (int) ($row['unmapped_count'] ?? 0) > 0;
?>
<tr>
<td><?= $index + 1 ?></td>
<td><?= esc($row['case_id_code'] ?? '') ?></td>
<td><?= esc($row['ipd_code'] ?? '') ?></td>
<td><?= esc($row['p_code'] ?? '') ?></td>
<td><?= esc($row['p_fname'] ?? '') ?></td>
<td>
<?= esc($row['insurance_company'] ?? '') ?>
<?php if (! empty($row['Org_insurance_comp'] ?? '')) : ?>
<div class="small text-muted"><?= esc($row['Org_insurance_comp'] ?? '') ?></div>
<?php endif; ?>
</td>
<td><?= esc($row['str_register_date'] ?? '') ?></td>
<td class="text-end"><?= (int) ($row['ayushman_proc_count'] ?? 0) ?></td>
<td class="text-end"><?= (int) ($row['unmapped_count'] ?? 0) ?></td>
<td class="text-end"><?= (int) ($row['preauth_required_count'] ?? 0) ?></td>
<td><?= $preauthPending ? '<span class="badge bg-warning text-dark">Pending</span>' : '<span class="badge bg-success">Done</span>' ?></td>
<td><?= $docsPending ? '<span class="badge bg-secondary">Pending</span>' : '<span class="badge bg-success">Done</span>' ?></td>
<td>
<?= esc((string) ($row['org_approved_status'] ?? 'Under Process')) ?>
<?php if ($mappingGap) : ?>
<div><span class="badge bg-danger mt-1">Mapping Gap</span></div>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>

View File

@ -0,0 +1,72 @@
<?php
$specialities = $specialities ?? [];
?>
<section class="content">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">
<i class="bi bi-link-45deg me-2"></i>Ayushman Unmapped Procedures
</h5>
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="load_form('<?= base_url('Report/insurance_credit_main') ?>', 'Insurance Credit')">
<i class="bi bi-arrow-left me-1"></i>Back to Main
</button>
</div>
<div class="card-body">
<div class="row g-3 align-items-end">
<div class="col-md-6">
<label class="form-label">Speciality</label>
<select class="form-select" id="ayushman_speciality_filter">
<option value="0">All Specialities</option>
<?php foreach ($specialities as $row) : ?>
<option value="<?= esc($row->speciality_code ?? '') ?>">
<?= esc(($row->speciality_name ?? '') . ' [' . ($row->speciality_code ?? '') . ']') ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-3">
<button type="button" class="btn btn-primary w-100" id="show_ayushman_unmapped_report">
<i class="bi bi-search me-1"></i>Show
</button>
</div>
<div class="col-md-3">
<button type="button" class="btn btn-outline-success w-100" id="export_ayushman_unmapped_report">
<i class="bi bi-file-earmark-excel me-1"></i>Export Excel
</button>
</div>
</div>
</div>
</div>
<div class="card mt-3">
<div class="card-body">
<div id="ayushman_unmapped_result" class="table-responsive">
<div class="text-center text-muted py-4">
<i class="bi bi-filter-circle" style="font-size: 2rem;"></i>
<p class="mt-2">Select a speciality filter and click Show to generate report</p>
</div>
</div>
</div>
</div>
</section>
<script>
(function() {
function buildAyushmanReportUrl(output) {
var specialityCode = document.getElementById('ayushman_speciality_filter').value || '0';
var url = '<?= base_url('Report/ayushman_unmapped_report_data') ?>/' + encodeURIComponent(specialityCode);
if (output === 1) {
url += '/1';
}
return url;
}
document.getElementById('show_ayushman_unmapped_report').addEventListener('click', function() {
load_form_div(buildAyushmanReportUrl(0), 'ayushman_unmapped_result');
});
document.getElementById('export_ayushman_unmapped_report').addEventListener('click', function() {
window.open(buildAyushmanReportUrl(1), '_blank');
});
})();
</script>

View File

@ -0,0 +1,47 @@
<?php
$rows = $rows ?? [];
$specialityCode = $speciality_code ?? '0';
?>
<div class="mb-3 d-flex justify-content-between align-items-center flex-wrap gap-2">
<div>
<strong>Speciality Filter:</strong>
<?= esc($specialityCode === '0' ? 'All Specialities' : $specialityCode) ?>
</div>
<div>
<strong>Total Unmapped Procedures:</strong> <?= count($rows) ?>
</div>
</div>
<?php if (empty($rows)) : ?>
<div class="alert alert-info">No unmapped Ayushman procedures found for the selected filter.</div>
<?php else : ?>
<table class="table table-bordered table-sm table-striped align-middle">
<thead class="table-light">
<tr>
<th style="width: 40px;">#</th>
<th style="width: 140px;">Speciality</th>
<th style="width: 120px;">Procedure Code</th>
<th>Procedure Name</th>
<th style="width: 110px;" class="text-end">Amount</th>
<th style="width: 90px;">Preauth</th>
<th>Pre Investigations</th>
<th>Post Investigations</th>
</tr>
</thead>
<tbody>
<?php foreach ($rows as $index => $row) : ?>
<tr>
<td><?= $index + 1 ?></td>
<td><?= esc(($row->speciality_name ?? '') . ' [' . ($row->speciality_code ?? '') . ']') ?></td>
<td><?= esc($row->procedure_code ?? '') ?></td>
<td><?= esc($row->procedure_name ?? '') ?></td>
<td class="text-end"><?= number_format((float) ($row->package_amount ?? 0), 2) ?></td>
<td><?= (int) ($row->preauth_required ?? 0) === 1 ? 'Yes' : 'No' ?></td>
<td><?= esc((string) ($row->pre_investigations ?? '')) ?></td>
<td><?= esc((string) ($row->post_investigations ?? '')) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>

View File

@ -80,6 +80,8 @@
<ul class="report-links">
<li><a href="javascript:load_form('<?= base_url('Report/insurance_ipd_report') ?>','Insurance Case IPD');"><i class="bi bi-arrow-right-short"></i> Insurance Case IPD</a></li>
<li><a href="javascript:load_form('<?= base_url('Report/insurance_opd_report') ?>','Insurance Case OPD');"><i class="bi bi-arrow-right-short"></i> Insurance Case OPD</a></li>
<li><a href="javascript:load_form('<?= base_url('Report/ayushman_case_dashboard') ?>','Ayushman Case Dashboard');"><i class="bi bi-arrow-right-short"></i> Ayushman Case Dashboard</a></li>
<li><a href="javascript:load_form('<?= base_url('Report/ayushman_unmapped_report') ?>','Ayushman Unmapped Procedures');"><i class="bi bi-arrow-right-short"></i> Ayushman Unmapped Procedures</a></li>
<li><a href="javascript:load_form('<?= base_url('org-packing') ?>','Org. OPD Packing');"><i class="bi bi-arrow-right-short"></i> Or. OPD Packing</a></li>
</ul>
</div>

View File

@ -60,6 +60,40 @@
</div>
</div>
</div>
<div class="col-md-6 col-lg-4">
<div class="card h-100 border-warning shadow-sm hover-card">
<div class="card-body text-center">
<div class="mb-3">
<i class="bi bi-link-45deg text-warning" style="font-size: 3rem;"></i>
</div>
<h5 class="card-title">Ayushman Mapping Gaps</h5>
<p class="card-text text-muted">
Review Ayushman procedures that are still not linked to internal package masters
</p>
<button type="button" class="btn btn-warning" onclick="load_form('<?= base_url('Report/ayushman_unmapped_report') ?>', 'Ayushman Unmapped Procedures')">
<i class="bi bi-box-arrow-up-right me-1"></i> Open Report
</button>
</div>
</div>
</div>
<div class="col-md-6 col-lg-4">
<div class="card h-100 border-danger shadow-sm hover-card">
<div class="card-body text-center">
<div class="mb-3">
<i class="bi bi-speedometer2 text-danger" style="font-size: 3rem;"></i>
</div>
<h5 class="card-title">Ayushman Case Dashboard</h5>
<p class="card-text text-muted">
Track preauth pending, document pending, and mapping gap alerts for Ayushman IPD cases
</p>
<button type="button" class="btn btn-danger" onclick="load_form('<?= base_url('Report/ayushman_case_dashboard') ?>', 'Ayushman Case Dashboard')">
<i class="bi bi-box-arrow-up-right me-1"></i> Open Dashboard
</button>
</div>
</div>
</div>
</div>
<div class="row mt-4">
@ -73,6 +107,8 @@
<li><strong>OPD Organization Cases:</strong> Shows outpatient cases with insurance coverage (case_type = 0)</li>
<li><strong>IPD TPA Cases:</strong> Shows inpatient cases with TPA/insurance coverage (case_type = 1)</li>
<li><strong>Combined Summary:</strong> Consolidated view of all insurance cases with totals</li>
<li><strong>Ayushman Mapping Gaps:</strong> Procedures imported from Ayushman Bharat package master that still need internal package mapping</li>
<li><strong>Ayushman Case Dashboard:</strong> Action-oriented dashboard for pending preauth/documents and case-level mapping risk</li>
<li><strong>Status Types:</strong> Pending (0), Bill Complete (1), Submitted to Organization (2)</li>
</ul>
</div>