diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 81f8d15..a7d5d34 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -601,6 +601,12 @@ $routes->get('Opd_prescription/opd_invest_master_get/(:num)', 'Opd_prescription: $routes->post('Opd_prescription/opd_invest_master_save', 'Opd_prescription::opd_invest_master_save'); $routes->post('Opd_prescription/opd_invest_master_remove/(:num)', 'Opd_prescription::opd_invest_master_remove/$1'); $routes->post('Opd_prescription/opd_invest_master_toggle_fav/(:num)', 'Opd_prescription::opd_invest_master_toggle_fav/$1'); +$routes->get('Opd_prescription/opd_invest_profile_master', 'Opd_prescription::opd_invest_profile_master', ['filter' => 'permission:doctor_work.medicine.manage,doctor_work.access']); +$routes->get('Opd_prescription/opd_invest_profile_master_list', 'Opd_prescription::opd_invest_profile_master_list'); +$routes->get('Opd_prescription/opd_invest_profile_master_data', 'Opd_prescription::opd_invest_profile_master_data'); +$routes->get('Opd_prescription/opd_invest_profile_master_get/(:num)', 'Opd_prescription::opd_invest_profile_master_get/$1'); +$routes->post('Opd_prescription/opd_invest_profile_master_save', 'Opd_prescription::opd_invest_profile_master_save'); +$routes->post('Opd_prescription/opd_invest_profile_master_remove/(:num)', 'Opd_prescription::opd_invest_profile_master_remove/$1'); $routes->get('Opd_prescription/medicine_search', 'Opd_prescription::medicine_search'); $routes->get('Opd_prescription/medicine_substitutes', 'Opd_prescription::medicine_substitutes'); diff --git a/app/Controllers/Opd_prescription.php b/app/Controllers/Opd_prescription.php index c36ff4c..8d9b2a0 100644 --- a/app/Controllers/Opd_prescription.php +++ b/app/Controllers/Opd_prescription.php @@ -3286,6 +3286,218 @@ class Opd_prescription extends BaseController ]); } + public function opd_invest_profile_master() + { + return view('billing/opd_invest_profile_master_workspace'); + } + + public function opd_invest_profile_master_list() + { + if (! $this->db->tableExists('invprofiles') || ! $this->db->tableExists('invtprofiles') || ! $this->db->tableExists('investigation')) { + return $this->response->setJSON(['rows' => []]); + } + + $pFields = $this->db->getFieldNames('invprofiles'); + $jFields = $this->db->getFieldNames('invtprofiles'); + $iFields = $this->db->getFieldNames('investigation'); + + $pCode = $this->resolveFirstField($pFields, ['Code', 'code', 'id']); + $pName = $this->resolveFirstField($pFields, ['Name', 'name']); + $jProfileCode = $this->resolveFirstField($jFields, ['ProfileCode', 'profile_code']); + $jInvestCode = $this->resolveFirstField($jFields, ['InvestigationCode', 'investigation_code']); + $jPrintOrder = $this->resolveFirstField($jFields, ['printOrder', 'print_order', 'id']); + $iCode = $this->resolveFirstField($iFields, ['Code', 'code']); + $iName = $this->resolveFirstField($iFields, ['Name', 'name']); + + if ($pCode === null || $pName === null || $jProfileCode === null || $jInvestCode === null || $iCode === null || $iName === null) { + return $this->response->setJSON(['rows' => []]); + } + + $filter = trim((string) $this->request->getGet('filter')); + + $sql = 'SELECT ' + . 'p.`' . $pCode . '` AS profile_code, ' + . 'p.`' . $pName . '` AS profile_name, ' + . 'i.`' . $iCode . '` AS investigation_code, ' + . 'i.`' . $iName . '` AS investigation_name ' + . 'FROM `invprofiles` p ' + . 'LEFT JOIN `invtprofiles` j ON p.`' . $pCode . '` = j.`' . $jProfileCode . '` ' + . 'LEFT JOIN `investigation` i ON i.`' . $iCode . '` = j.`' . $jInvestCode . '` '; + + $params = []; + if ($filter !== '') { + $sql .= 'WHERE p.`' . $pName . '` LIKE ? OR i.`' . $iName . '` LIKE ? '; + $params[] = '%' . $filter . '%'; + $params[] = '%' . $filter . '%'; + } + $sql .= 'ORDER BY p.`' . $pName . '` ASC'; + if ($jPrintOrder !== null) { + $sql .= ', j.`' . $jPrintOrder . '` ASC'; + } + + $rows = $this->db->query($sql, $params)->getResultArray(); + $map = []; + foreach ($rows as $row) { + $profileCode = (int) ($row['profile_code'] ?? 0); + $profileName = trim((string) ($row['profile_name'] ?? '')); + if ($profileCode <= 0 || $profileName === '') { + continue; + } + if (! isset($map[$profileCode])) { + $map[$profileCode] = [ + 'profile_code' => $profileCode, + 'profile_name' => $profileName, + 'tests' => [], + ]; + } + $testCode = trim((string) ($row['investigation_code'] ?? '')); + $testName = trim((string) ($row['investigation_name'] ?? '')); + if ($testCode !== '' && $testName !== '') { + $map[$profileCode]['tests'][] = [ + 'code' => $testCode, + 'name' => $testName, + ]; + } + } + + return $this->response->setJSON(['rows' => array_values($map)]); + } + + public function opd_invest_profile_master_data() + { + $rows = $this->opd_invest_profile_master_list()->getJSON(true); + $list = $rows['rows'] ?? []; + $draw = (int) $this->request->getGet('draw'); + $start = max(0, (int) $this->request->getGet('start')); + $length = (int) $this->request->getGet('length'); + if ($length <= 0) { + $length = 25; + } + + $paged = array_slice($list, $start, $length); + foreach ($paged as &$row) { + $row['test_count'] = count($row['tests'] ?? []); + $row['test_names'] = implode(', ', array_map(static function ($item) { + return (string) ($item['name'] ?? ''); + }, $row['tests'] ?? [])); + } + unset($row); + + return $this->response->setJSON([ + 'draw' => $draw, + 'recordsTotal' => count($list), + 'recordsFiltered' => count($list), + 'data' => $paged, + ]); + } + + public function opd_invest_profile_master_get(int $profileCode) + { + $response = $this->opd_invest_profile_master_list()->getJSON(true); + $rows = $response['rows'] ?? []; + foreach ($rows as $row) { + if ((int) ($row['profile_code'] ?? 0) === $profileCode) { + return $this->response->setJSON([ + 'update' => 1, + 'row' => $row, + 'csrfName' => csrf_token(), + 'csrfHash' => csrf_hash(), + ]); + } + } + + return $this->response->setJSON(['update' => 0, 'error_text' => 'Profile not found']); + } + + public function opd_invest_profile_master_save() + { + if (! $this->request->isAJAX()) { + return $this->response->setStatusCode(400)->setJSON(['update' => 0]); + } + if (! $this->db->tableExists('invprofiles') || ! $this->db->tableExists('invtprofiles') || ! $this->db->tableExists('investigation')) { + return $this->response->setJSON(['update' => 0, 'error_text' => 'Profile tables not found']); + } + + $profileCode = (int) $this->request->getPost('profile_code'); + $profileName = trim((string) $this->request->getPost('profile_name')); + $investigationCodes = array_values(array_filter(array_map('intval', explode(',', (string) $this->request->getPost('investigation_codes'))))); + + if ($profileName === '') { + return $this->response->setJSON(['update' => 0, 'error_text' => 'Profile name is required']); + } + if (empty($investigationCodes)) { + return $this->response->setJSON(['update' => 0, 'error_text' => 'Select at least one investigation']); + } + + $pFields = $this->db->getFieldNames('invprofiles'); + $jFields = $this->db->getFieldNames('invtprofiles'); + $pCode = $this->resolveFirstField($pFields, ['Code', 'code', 'id']); + $pName = $this->resolveFirstField($pFields, ['Name', 'name']); + $jProfileCode = $this->resolveFirstField($jFields, ['ProfileCode', 'profile_code']); + $jInvestCode = $this->resolveFirstField($jFields, ['InvestigationCode', 'investigation_code']); + $jPrintOrder = $this->resolveFirstField($jFields, ['printOrder', 'print_order', 'id']); + if ($pCode === null || $pName === null || $jProfileCode === null || $jInvestCode === null) { + return $this->response->setJSON(['update' => 0, 'error_text' => 'Invalid profile table structure']); + } + + if ($profileCode > 0) { + $this->db->table('invprofiles')->where($pCode, $profileCode)->update([$pName => $profileName]); + $savedCode = $profileCode; + $this->db->table('invtprofiles')->where($jProfileCode, $savedCode)->delete(); + } else { + $this->db->table('invprofiles')->insert([$pName => $profileName]); + $savedCode = (int) $this->db->insertID(); + } + + $order = 1; + foreach ($investigationCodes as $code) { + $joinRow = [ + $jProfileCode => $savedCode, + $jInvestCode => $code, + ]; + if ($jPrintOrder !== null) { + $joinRow[$jPrintOrder] = $order; + } + $this->db->table('invtprofiles')->insert($joinRow); + $order++; + } + + return $this->response->setJSON([ + 'update' => 1, + 'insertid' => $savedCode, + 'error_text' => $profileCode > 0 ? 'Profile updated' : 'Profile added', + 'csrfName' => csrf_token(), + 'csrfHash' => csrf_hash(), + ]); + } + + public function opd_invest_profile_master_remove(int $profileCode) + { + if (! $this->request->isAJAX()) { + return $this->response->setStatusCode(400)->setJSON(['update' => 0]); + } + if (! $this->db->tableExists('invprofiles') || ! $this->db->tableExists('invtprofiles')) { + return $this->response->setJSON(['update' => 0, 'error_text' => 'Profile tables not found']); + } + + $pFields = $this->db->getFieldNames('invprofiles'); + $jFields = $this->db->getFieldNames('invtprofiles'); + $pCode = $this->resolveFirstField($pFields, ['Code', 'code', 'id']); + $jProfileCode = $this->resolveFirstField($jFields, ['ProfileCode', 'profile_code']); + if ($pCode === null || $jProfileCode === null) { + return $this->response->setJSON(['update' => 0, 'error_text' => 'Invalid profile table structure']); + } + + $this->db->table('invtprofiles')->where($jProfileCode, $profileCode)->delete(); + $this->db->table('invprofiles')->where($pCode, $profileCode)->delete(); + return $this->response->setJSON([ + 'update' => 1, + 'error_text' => 'Profile deleted', + 'csrfName' => csrf_token(), + 'csrfHash' => csrf_hash(), + ]); + } + // ───────────────────────────────────────────────────────────────────────── public function opd_medicince_duplicate_report() diff --git a/app/Database/Migrations/2026-04-01-000032_CreateInvestigationProfileTables.php b/app/Database/Migrations/2026-04-01-000032_CreateInvestigationProfileTables.php new file mode 100644 index 0000000..ddf4c6f --- /dev/null +++ b/app/Database/Migrations/2026-04-01-000032_CreateInvestigationProfileTables.php @@ -0,0 +1,68 @@ +db->tableExists('invprofiles')) { + $this->forge->addField([ + 'Code' => [ + 'type' => 'INT', + 'constraint' => 11, + 'unsigned' => false, + 'auto_increment' => true, + ], + 'Name' => [ + 'type' => 'VARCHAR', + 'constraint' => 150, + 'null' => false, + ], + ]); + $this->forge->addKey('Code', true); + $this->forge->addUniqueKey('Name'); + $this->forge->createTable('invprofiles', true); + } + + if (! $this->db->tableExists('invtprofiles')) { + $this->forge->addField([ + 'id' => [ + 'type' => 'INT', + 'constraint' => 11, + 'auto_increment' => true, + ], + 'ProfileCode' => [ + 'type' => 'INT', + 'constraint' => 11, + 'null' => false, + ], + 'InvestigationCode' => [ + 'type' => 'INT', + 'constraint' => 11, + 'null' => false, + ], + 'printOrder' => [ + 'type' => 'INT', + 'constraint' => 11, + 'default' => 0, + ], + ]); + $this->forge->addKey('id', true); + $this->forge->addKey(['ProfileCode', 'InvestigationCode']); + $this->forge->createTable('invtprofiles', true); + } + } + + public function down() + { + if ($this->db->tableExists('invtprofiles')) { + $this->forge->dropTable('invtprofiles', true); + } + if ($this->db->tableExists('invprofiles')) { + $this->forge->dropTable('invprofiles', true); + } + } +} diff --git a/app/Views/billing/opd_invest_profile_master_workspace.php b/app/Views/billing/opd_invest_profile_master_workspace.php new file mode 100644 index 0000000..83429ae --- /dev/null +++ b/app/Views/billing/opd_invest_profile_master_workspace.php @@ -0,0 +1,299 @@ +
+
+
Investigation Profile Master
+ — +
+ +
+
+
+
+ +
+
+
+ + + + + + + + + + + +
ProfileCodeTestsInvestigation NamesAction
Loading...
+
+
+
+
+ +
+
+
New Profile
+
+ + + +
+ + +
+ +
+ + +
Search and select multiple investigations.
+
+ +
+ + +
+ +
Ready.
+
+
+
+
+
+ + diff --git a/app/Views/billing/opd_prescription_basic.php b/app/Views/billing/opd_prescription_basic.php index 2f11872..7ddc494 100644 --- a/app/Views/billing/opd_prescription_basic.php +++ b/app/Views/billing/opd_prescription_basic.php @@ -711,9 +711,9 @@
- +
@@ -1876,37 +1876,25 @@ } function refreshCustomProfileSelect() { - var rows = getCustomInvestigationProfiles(); var $sel = $('#inv_custom_profile_select'); if (!$sel.length) { return; } $sel.html(''); - if (legacyInvestigationProfileList.length) { - $sel.append(''); - var $legacyGroup = $sel.find('optgroup[label="Old HMS Profiles"]'); - legacyInvestigationProfileList.forEach(function(row) { - var key = (row && row.key) ? row.key : ''; - var name = (row && row.name) ? row.name : ''; - if (!key || !name) { - return; - } - $legacyGroup.append('