feat(ipd-charges): group delete, sort order, Up/Down buttons, foldable billing panels
- Safe delete for empty IPD charge groups with CSRF-safe AJAX - Migration: add sort_order to ipd_item_type (backfilled) - Sort order UI on create/edit/list views; persist in controller/model - Up/Down swap buttons on group list (AJAX swap endpoint + CSRF refresh) - Fix 500 on edit screen: restore missing getItemTypeById() in ItemIpdModel - IpdBillingModel: propagate sort_order ordering to ipd-charges and bill-details tabs - Billing IPD Charges tab: Bedside Clinical/Nursing Charge and Doctor Visit Charge panels now collapsible, start collapsed
This commit is contained in:
parent
058debb593
commit
fbbecee3b4
|
|
@ -1071,6 +1071,8 @@ $routes->group('item-ipd', static function($routes) {
|
|||
$routes->post('update', 'ItemIpd::updateRecord');
|
||||
$routes->post('create-type', 'ItemIpd::createItemTypeRecord');
|
||||
$routes->post('update-type', 'ItemIpd::updateItemTypeRecord');
|
||||
$routes->post('delete-type', 'ItemIpd::deleteItemTypeRecord');
|
||||
$routes->post('change-sort-type', 'ItemIpd::changeItemTypeSort');
|
||||
$routes->post('insurance/add', 'ItemIpd::addInsuranceItemRecord');
|
||||
$routes->post('insurance/remove', 'ItemIpd::removeInsuranceItem');
|
||||
});
|
||||
|
|
@ -1143,6 +1145,7 @@ $routes->group('Item_IPD', static function($routes) {
|
|||
$routes->post('UpdateRecord', 'ItemIpd::updateRecord');
|
||||
$routes->post('CreateItemTypeRecord', 'ItemIpd::createItemTypeRecord');
|
||||
$routes->post('UpdateItemTypeRecord', 'ItemIpd::updateItemTypeRecord');
|
||||
$routes->post('change_sort_itemtype', 'ItemIpd::changeItemTypeSort');
|
||||
$routes->post('AddInsuranceItemRecord', 'ItemIpd::addInsuranceItemRecord');
|
||||
$routes->post('remove_record_item', 'ItemIpd::removeInsuranceItem');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,12 +7,20 @@ use App\Models\ItemIpdModel;
|
|||
|
||||
class ItemIpd extends BaseController
|
||||
{
|
||||
private function getPostedItemTypeSortOrder(): int
|
||||
{
|
||||
$sortOrder = $this->request->getPost('sort_order');
|
||||
|
||||
return max(0, (int) $sortOrder);
|
||||
}
|
||||
|
||||
public function searchItemType(): string
|
||||
{
|
||||
$model = new ItemIpdModel();
|
||||
|
||||
return view('Setting/Charges/IPD_Charges/item_type_search_v', [
|
||||
'data' => $model->getItemTypes(),
|
||||
'supportsSortOrder' => $model->supportsItemTypeSortOrder(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
@ -222,6 +230,25 @@ class ItemIpd extends BaseController
|
|||
]);
|
||||
}
|
||||
|
||||
public function deleteItemTypeRecord()
|
||||
{
|
||||
if (! $this->request->isAJAX()) {
|
||||
return $this->response->setJSON(['deleted' => 0, 'error_text' => 'Invalid request']);
|
||||
}
|
||||
|
||||
$model = new ItemIpdModel();
|
||||
$id = (int) $this->request->getPost('itype_id');
|
||||
|
||||
$deleted = $model->deleteItemType($id);
|
||||
|
||||
return $this->response->setJSON([
|
||||
'deleted' => $deleted ? 1 : 0,
|
||||
'error_text' => $deleted ? '' : 'Cannot delete: group has charges or does not exist.',
|
||||
'csrfName' => csrf_token(),
|
||||
'csrfHash' => csrf_hash(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function createItemTypeRecord()
|
||||
{
|
||||
if (! $this->request->isAJAX()) {
|
||||
|
|
@ -233,6 +260,10 @@ class ItemIpd extends BaseController
|
|||
'group_desc' => $this->request->getPost('input_Item_type'),
|
||||
];
|
||||
|
||||
if ($model->supportsItemTypeSortOrder()) {
|
||||
$data['sort_order'] = $this->getPostedItemTypeSortOrder();
|
||||
}
|
||||
|
||||
$insertId = $model->insertItemType($data);
|
||||
|
||||
return $this->response->setJSON([
|
||||
|
|
@ -256,6 +287,10 @@ class ItemIpd extends BaseController
|
|||
'group_desc' => $this->request->getPost('input_Item_type'),
|
||||
];
|
||||
|
||||
if ($model->supportsItemTypeSortOrder()) {
|
||||
$data['sort_order'] = $this->getPostedItemTypeSortOrder();
|
||||
}
|
||||
|
||||
$updated = $model->updateItemType($data, $itemTypeId);
|
||||
|
||||
return $this->response->setJSON([
|
||||
|
|
@ -267,4 +302,36 @@ class ItemIpd extends BaseController
|
|||
]);
|
||||
}
|
||||
|
||||
public function changeItemTypeSort()
|
||||
{
|
||||
if (! $this->request->isAJAX()) {
|
||||
return $this->response->setJSON(['moved' => 0, 'error_text' => 'Invalid request']);
|
||||
}
|
||||
|
||||
$model = new ItemIpdModel();
|
||||
|
||||
if (! $model->supportsItemTypeSortOrder()) {
|
||||
return $this->response->setJSON([
|
||||
'moved' => 0,
|
||||
'error_text' => 'Sort order is not available in this environment.',
|
||||
'csrfName' => csrf_token(),
|
||||
'csrfHash' => csrf_hash(),
|
||||
]);
|
||||
}
|
||||
|
||||
$currentId = (int) $this->request->getPost('current_id');
|
||||
$currentSort = max(0, (int) $this->request->getPost('current_sort'));
|
||||
$targetId = (int) $this->request->getPost('target_id');
|
||||
$targetSort = max(0, (int) $this->request->getPost('target_sort'));
|
||||
|
||||
$moved = $model->swapItemTypeSortOrder($currentId, $currentSort, $targetId, $targetSort);
|
||||
|
||||
return $this->response->setJSON([
|
||||
'moved' => $moved ? 1 : 0,
|
||||
'error_text' => $moved ? '' : 'Could not update sort order.',
|
||||
'csrfName' => csrf_token(),
|
||||
'csrfHash' => csrf_hash(),
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
namespace App\Database\Migrations;
|
||||
|
||||
use CodeIgniter\Database\Migration;
|
||||
|
||||
class AddSortOrderToIpdItemType extends Migration
|
||||
{
|
||||
private function tableExists(string $table): bool
|
||||
{
|
||||
return $this->db->query('SHOW TABLES LIKE ' . $this->db->escape($table))->getNumRows() > 0;
|
||||
}
|
||||
|
||||
private function fieldExists(string $table, string $field): bool
|
||||
{
|
||||
return $this->db->query('SHOW COLUMNS FROM `' . $table . '` LIKE ' . $this->db->escape($field))->getNumRows() > 0;
|
||||
}
|
||||
|
||||
public function up()
|
||||
{
|
||||
if (! $this->tableExists('ipd_item_type')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (! $this->fieldExists('ipd_item_type', 'sort_order')) {
|
||||
$this->forge->addColumn('ipd_item_type', [
|
||||
'sort_order' => [
|
||||
'type' => 'INT',
|
||||
'constraint' => 11,
|
||||
'default' => 0,
|
||||
'null' => false,
|
||||
'after' => 'group_desc',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
$rows = $this->db->table('ipd_item_type')
|
||||
->select('itype_id')
|
||||
->orderBy('group_desc', 'ASC')
|
||||
->get()
|
||||
->getResult();
|
||||
|
||||
$sortOrder = 1;
|
||||
foreach ($rows as $row) {
|
||||
$this->db->table('ipd_item_type')
|
||||
->where('itype_id', (int) ($row->itype_id ?? 0))
|
||||
->update(['sort_order' => $sortOrder]);
|
||||
$sortOrder++;
|
||||
}
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
if ($this->tableExists('ipd_item_type') && $this->fieldExists('ipd_item_type', 'sort_order')) {
|
||||
$this->forge->dropColumn('ipd_item_type', 'sort_order');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,17 @@ class IpdBillingModel extends Model
|
|||
{
|
||||
protected $table = 'v_ipd_list';
|
||||
|
||||
private ?bool $ipdItemTypeHasSortOrder = null;
|
||||
|
||||
private function ipdItemTypeHasSortOrder(): bool
|
||||
{
|
||||
if ($this->ipdItemTypeHasSortOrder === null) {
|
||||
$this->ipdItemTypeHasSortOrder = $this->db->fieldExists('sort_order', 'ipd_item_type');
|
||||
}
|
||||
|
||||
return $this->ipdItemTypeHasSortOrder;
|
||||
}
|
||||
|
||||
public function getCurrentAdmissions(): array
|
||||
{
|
||||
$builder = $this->baseIpdListQuery();
|
||||
|
|
@ -172,10 +183,16 @@ class IpdBillingModel extends Model
|
|||
|
||||
public function getIpdCharges(int $ipdId): array
|
||||
{
|
||||
return $this->db->table('ipd_invoice_item i')
|
||||
$builder = $this->db->table('ipd_invoice_item i')
|
||||
->select('i.*, t.group_desc')
|
||||
->join('ipd_item_type t', 'i.item_type = t.itype_id', 'left')
|
||||
->where('i.ipd_id', $ipdId)
|
||||
->where('i.ipd_id', $ipdId);
|
||||
|
||||
if ($this->ipdItemTypeHasSortOrder()) {
|
||||
$builder->orderBy('COALESCE(t.sort_order, 0)', 'ASC', false);
|
||||
}
|
||||
|
||||
return $builder
|
||||
->orderBy('i.id', 'DESC')
|
||||
->get()
|
||||
->getResult();
|
||||
|
|
@ -183,11 +200,17 @@ class IpdBillingModel extends Model
|
|||
|
||||
public function getIpdChargesGrouped(int $ipdId): array
|
||||
{
|
||||
return $this->db->table('ipd_invoice_item i')
|
||||
$builder = $this->db->table('ipd_invoice_item i')
|
||||
->select('t.group_desc, i.*, sum(i.item_amount) as xAmount')
|
||||
->join('ipd_item_type t', 'i.item_type = t.itype_id', 'left')
|
||||
->where('i.ipd_id', $ipdId)
|
||||
->groupBy('i.item_type,i.id')
|
||||
->groupBy('i.item_type,i.id');
|
||||
|
||||
if ($this->ipdItemTypeHasSortOrder()) {
|
||||
$builder->orderBy('COALESCE(t.sort_order, 0)', 'ASC', false);
|
||||
}
|
||||
|
||||
return $builder
|
||||
->orderBy('i.item_type', 'ASC')
|
||||
->orderBy('i.id', 'ASC')
|
||||
->get()
|
||||
|
|
@ -365,6 +388,10 @@ class IpdBillingModel extends Model
|
|||
$builder->where('i.package_id', 0);
|
||||
}
|
||||
|
||||
if ($this->ipdItemTypeHasSortOrder()) {
|
||||
$builder->orderBy('COALESCE(t.sort_order, 0)', 'ASC', false);
|
||||
}
|
||||
|
||||
return $builder
|
||||
->orderBy('t.group_desc', 'ASC')
|
||||
->orderBy('i.id', 'ASC')
|
||||
|
|
|
|||
|
|
@ -7,34 +7,89 @@ use Config\Database;
|
|||
|
||||
class ItemIpdModel extends Model
|
||||
{
|
||||
/**
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
private array $fieldExistsCache = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->db = Database::connect();
|
||||
}
|
||||
|
||||
public function supportsItemTypeSortOrder(): bool
|
||||
{
|
||||
return $this->fieldExists('ipd_item_type', 'sort_order');
|
||||
}
|
||||
|
||||
private function fieldExists(string $table, string $field): bool
|
||||
{
|
||||
$cacheKey = $table . '.' . $field;
|
||||
|
||||
if (! array_key_exists($cacheKey, $this->fieldExistsCache)) {
|
||||
$this->fieldExistsCache[$cacheKey] = $this->db->fieldExists($field, $table);
|
||||
}
|
||||
|
||||
return $this->fieldExistsCache[$cacheKey];
|
||||
}
|
||||
|
||||
private function applyItemTypeOrdering($builder, string $alias = '')
|
||||
{
|
||||
$prefix = $alias !== '' ? $alias . '.' : '';
|
||||
|
||||
if ($this->supportsItemTypeSortOrder()) {
|
||||
$builder->orderBy('COALESCE(' . $prefix . 'sort_order, 0)', 'ASC', false);
|
||||
}
|
||||
|
||||
return $builder->orderBy($prefix . 'group_desc', 'ASC');
|
||||
}
|
||||
|
||||
private function filterItemTypeData(array $data): array
|
||||
{
|
||||
if (! $this->supportsItemTypeSortOrder()) {
|
||||
unset($data['sort_order']);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getItemTypes(): array
|
||||
{
|
||||
return $this->db->table('ipd_item_type')
|
||||
->orderBy('group_desc', 'ASC')
|
||||
$select = ['t.itype_id', 't.group_desc', 'COUNT(m.id) AS item_count'];
|
||||
$groupBy = ['t.itype_id', 't.group_desc'];
|
||||
|
||||
if ($this->supportsItemTypeSortOrder()) {
|
||||
$select[] = 'COALESCE(t.sort_order, 0) AS sort_order';
|
||||
$groupBy[] = 't.sort_order';
|
||||
}
|
||||
|
||||
$builder = $this->db->table('ipd_item_type t')
|
||||
->select(implode(', ', $select), false)
|
||||
->join('ipd_items m', 'm.itype = t.itype_id', 'left')
|
||||
->groupBy(implode(', ', $groupBy), false);
|
||||
|
||||
return $this->applyItemTypeOrdering($builder, 't')
|
||||
->get()
|
||||
->getResult();
|
||||
}
|
||||
|
||||
public function getItemTypesList(): array
|
||||
{
|
||||
return $this->db->table('ipd_item_type')
|
||||
->orderBy('group_desc', 'ASC')
|
||||
$builder = $this->db->table('ipd_item_type');
|
||||
|
||||
return $this->applyItemTypeOrdering($builder)
|
||||
->get()
|
||||
->getResult();
|
||||
}
|
||||
|
||||
public function getItemTypeById(int $id): array
|
||||
{
|
||||
return $this->db->table('ipd_item_type')
|
||||
->where('itype_id', $id)
|
||||
->get()
|
||||
->getResult();
|
||||
$builder = $this->db->table('ipd_item_type');
|
||||
$builder->where('itype_id', $id);
|
||||
$this->applyItemTypeOrdering($builder);
|
||||
|
||||
return $builder->get()->getResult();
|
||||
}
|
||||
|
||||
public function getItemsByType(int $typeId): array
|
||||
|
|
@ -68,7 +123,7 @@ class ItemIpdModel extends Model
|
|||
't.group_desc',
|
||||
]);
|
||||
$builder->join('ipd_item_type t', 'm.itype = t.itype_id', 'inner');
|
||||
$builder->orderBy('t.group_desc', 'ASC');
|
||||
$this->applyItemTypeOrdering($builder, 't');
|
||||
|
||||
return $builder->get()->getResult();
|
||||
}
|
||||
|
|
@ -171,7 +226,7 @@ class ItemIpdModel extends Model
|
|||
|
||||
public function insertItemType(array $data): int
|
||||
{
|
||||
$this->db->table('ipd_item_type')->insert($data);
|
||||
$this->db->table('ipd_item_type')->insert($this->filterItemTypeData($data));
|
||||
|
||||
return (int) $this->db->insertID();
|
||||
}
|
||||
|
|
@ -180,7 +235,48 @@ class ItemIpdModel extends Model
|
|||
{
|
||||
return (bool) $this->db->table('ipd_item_type')
|
||||
->where('itype_id', $id)
|
||||
->update($data);
|
||||
->update($this->filterItemTypeData($data));
|
||||
}
|
||||
|
||||
public function swapItemTypeSortOrder(int $currentId, int $currentSort, int $targetId, int $targetSort): bool
|
||||
{
|
||||
if (! $this->supportsItemTypeSortOrder() || $currentId <= 0 || $targetId <= 0 || $currentId === $targetId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->db->transStart();
|
||||
|
||||
$this->db->table('ipd_item_type')
|
||||
->where('itype_id', $currentId)
|
||||
->update(['sort_order' => 0]);
|
||||
|
||||
$this->db->table('ipd_item_type')
|
||||
->where('itype_id', $targetId)
|
||||
->update(['sort_order' => $currentSort]);
|
||||
|
||||
$this->db->table('ipd_item_type')
|
||||
->where('itype_id', $currentId)
|
||||
->update(['sort_order' => $targetSort]);
|
||||
|
||||
$this->db->transComplete();
|
||||
|
||||
return $this->db->transStatus();
|
||||
}
|
||||
|
||||
public function deleteItemType(int $id): bool
|
||||
{
|
||||
// Only delete if no items are linked
|
||||
$count = (int) $this->db->table('ipd_items')
|
||||
->where('itype', $id)
|
||||
->countAllResults();
|
||||
|
||||
if ($count > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool) $this->db->table('ipd_item_type')
|
||||
->where('itype_id', $id)
|
||||
->delete();
|
||||
}
|
||||
|
||||
public function insertItemInsurance(array $data): int
|
||||
|
|
|
|||
|
|
@ -17,6 +17,10 @@
|
|||
<label class="form-label">Charges Group Name</label>
|
||||
<input class="form-control" name="input_Item_type" placeholder="Item Name" type="text" autocomplete="off">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Sort Order</label>
|
||||
<input class="form-control" name="sort_order" placeholder="0" value="0" type="number" min="0" step="1" autocomplete="off">
|
||||
</div>
|
||||
<div class="col-md-4 d-flex align-items-end">
|
||||
<button type="submit" class="btn btn-primary" id="btn_update">Add Record</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -19,6 +19,10 @@
|
|||
<label class="form-label">Charges Group Name</label>
|
||||
<input class="form-control" name="input_Item_type" placeholder="Item Name" value="<?= esc($data_item[0]->group_desc ?? '') ?>" type="text" autocomplete="off">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Sort Order</label>
|
||||
<input class="form-control" name="sort_order" placeholder="0" value="<?= esc((string) ((int) ($data_item[0]->sort_order ?? 0))) ?>" type="number" min="0" step="1" autocomplete="off">
|
||||
</div>
|
||||
<div class="col-md-4 d-flex align-items-end">
|
||||
<button type="button" class="btn btn-primary" id="btn_update">Update Record</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -7,19 +7,39 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<input type="hidden" id="csrf_name" value="<?= csrf_token() ?>">
|
||||
<input type="hidden" id="csrf_val" value="<?= csrf_hash() ?>">
|
||||
<table id="example1" class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Charges Group Name</th>
|
||||
<th></th>
|
||||
<th style="width: 120px;">Sort Order</th>
|
||||
<th style="width: 280px;">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($data as $row) : ?>
|
||||
<?php
|
||||
$rows = is_array($data) ? array_values($data) : [];
|
||||
foreach ($rows as $index => $row) :
|
||||
$currentId = (int) ($row->itype_id ?? 0);
|
||||
$currentSort = (int) ($row->sort_order ?? 0);
|
||||
$prevRow = $rows[$index - 1] ?? null;
|
||||
$nextRow = $rows[$index + 1] ?? null;
|
||||
?>
|
||||
<tr>
|
||||
<td><?= esc($row->group_desc ?? '') ?></td>
|
||||
<td><?= esc((string) ((int) ($row->sort_order ?? 0))) ?></td>
|
||||
<td>
|
||||
<button onclick="load_form_div('<?= base_url('item-ipd/itemtype-record') ?>/' + <?= (int) ($row->itype_id ?? 0) ?>, 'maindiv', 'IPD Charge Master');" type="button" class="btn btn-primary btn-sm">Edit</button>
|
||||
<?php if ((int)($row->item_count ?? 0) === 0): ?>
|
||||
<button onclick="deleteItemTypeGroup(<?= (int)($row->itype_id ?? 0) ?>, this);" type="button" class="btn btn-danger btn-sm ms-1">Delete</button>
|
||||
<?php endif ?>
|
||||
<?php if (($supportsSortOrder ?? false) && $nextRow !== null): ?>
|
||||
<button onclick="moveItemTypeGroup(<?= $currentId ?>, <?= $currentSort ?>, <?= (int) ($nextRow->itype_id ?? 0) ?>, <?= (int) ($nextRow->sort_order ?? 0) ?>, this);" type="button" class="btn btn-outline-primary btn-sm ms-1">Down</button>
|
||||
<?php endif ?>
|
||||
<?php if (($supportsSortOrder ?? false) && $prevRow !== null): ?>
|
||||
<button onclick="moveItemTypeGroup(<?= $currentId ?>, <?= $currentSort ?>, <?= (int) ($prevRow->itype_id ?? 0) ?>, <?= (int) ($prevRow->sort_order ?? 0) ?>, this);" type="button" class="btn btn-outline-primary btn-sm ms-1">Up</button>
|
||||
<?php endif ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
|
|
@ -27,7 +47,8 @@
|
|||
<tfoot>
|
||||
<tr>
|
||||
<th>Charges Group Name</th>
|
||||
<th></th>
|
||||
<th>Sort Order</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
|
@ -43,4 +64,68 @@
|
|||
|
||||
new simpleDatatables.DataTable(table);
|
||||
})();
|
||||
|
||||
function deleteItemTypeGroup(itypeId, btn) {
|
||||
if (!confirm('Delete this empty charges group? This cannot be undone.')) {
|
||||
return;
|
||||
}
|
||||
btn.disabled = true;
|
||||
var csrfName = $('#csrf_name').val();
|
||||
var csrfVal = $('#csrf_val').val();
|
||||
var payload = {itype_id: itypeId};
|
||||
payload[csrfName] = csrfVal;
|
||||
|
||||
$.post('<?= base_url('item-ipd/delete-type') ?>', payload, function(res) {
|
||||
if (res.deleted) {
|
||||
$(btn).closest('tr').remove();
|
||||
} else {
|
||||
alert(res.error_text || 'Could not delete group.');
|
||||
btn.disabled = false;
|
||||
}
|
||||
if (res.csrfName && res.csrfHash) {
|
||||
$('#csrf_name').val(res.csrfName);
|
||||
$('#csrf_val').val(res.csrfHash);
|
||||
}
|
||||
}, 'json').fail(function() {
|
||||
alert('Request failed.');
|
||||
btn.disabled = false;
|
||||
});
|
||||
}
|
||||
|
||||
function moveItemTypeGroup(currentId, currentSort, targetId, targetSort, btn) {
|
||||
btn.disabled = true;
|
||||
|
||||
var csrfName = $('#csrf_name').val();
|
||||
var csrfVal = $('#csrf_val').val();
|
||||
var payload = {
|
||||
current_id: currentId,
|
||||
current_sort: currentSort,
|
||||
target_id: targetId,
|
||||
target_sort: targetSort
|
||||
};
|
||||
|
||||
payload[csrfName] = csrfVal;
|
||||
|
||||
$.post('<?= base_url('item-ipd/change-sort-type') ?>', payload, function(res) {
|
||||
if (res.csrfName && res.csrfHash) {
|
||||
$('#csrf_name').val(res.csrfName);
|
||||
$('#csrf_val').val(res.csrfHash);
|
||||
}
|
||||
|
||||
if (!res.moved) {
|
||||
alert(res.error_text || 'Could not update sort order.');
|
||||
btn.disabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof load_form_div === 'function') {
|
||||
load_form_div('<?= base_url('item-ipd/search-itemtype') ?>', 'maindiv', 'IPD Charge Master');
|
||||
} else {
|
||||
window.location.reload();
|
||||
}
|
||||
}, 'json').fail(function() {
|
||||
alert('Request failed.');
|
||||
btn.disabled = false;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -32,6 +32,28 @@ $doctorVisitFeeMap = $doctor_visit_fee_map ?? [];
|
|||
.ipd-charge-accordion .accordion-body {
|
||||
background: #f8fafc;
|
||||
}
|
||||
.ipd-charge-section-toggle {
|
||||
width: 100%;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
color: inherit;
|
||||
}
|
||||
.ipd-charge-section-toggle:focus {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
.ipd-charge-section-toggle .bi {
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
.ipd-charge-section-toggle.collapsed .bi {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="row g-3">
|
||||
|
|
@ -158,128 +180,142 @@ $doctorVisitFeeMap = $doctor_visit_fee_map ?? [];
|
|||
</div>
|
||||
<div class="card-body">
|
||||
<div class="card mb-3 border-info">
|
||||
<div class="card-header py-2"><strong>Bedside Clinical / Nursing Charge</strong></div>
|
||||
<div class="card-body">
|
||||
<div class="row g-2">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Item Type</label>
|
||||
<select class="form-select form-select-sm" id="bedside_item_type">
|
||||
<option value="">All</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Category</label>
|
||||
<select class="form-select form-select-sm" id="bedside_category">
|
||||
<option value="">Select</option>
|
||||
<?php foreach (array_keys($bedsideItemsByCategory) as $categoryName) : ?>
|
||||
<option value="<?= esc($categoryName) ?>"><?= esc($categoryName) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<label class="form-label">Item</label>
|
||||
<select class="form-select form-select-sm" id="bedside_item_id">
|
||||
<option value="">Select</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Quick Search</label>
|
||||
<input type="text" class="form-control form-control-sm" id="bedside_item_search" placeholder="Search by code or name">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Rate</label>
|
||||
<input class="form-control form-control-sm" id="bedside_item_rate" value="0" type="number" $11" />
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Qty</label>
|
||||
<input class="form-control form-control-sm" id="bedside_item_qty" value="1" type="number" $11" />
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Date</label>
|
||||
<input class="form-control form-control-sm" id="bedside_item_date" value="<?= esc($today) ?>" type="date" />
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Doctor</label>
|
||||
<select class="form-select form-select-sm" id="bedside_doc_id">
|
||||
<option value="0">NONE</option>
|
||||
<?php foreach ($doc_list ?? [] as $doc) : ?>
|
||||
<option value="<?= esc($doc->id ?? '') ?>"><?= esc($doc->DocSpecName ?? '') ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Comment</label>
|
||||
<input class="form-control form-control-sm" id="bedside_comment" value="" type="text" />
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<button type="button" class="btn btn-outline-primary btn-sm" onclick="ipdBedsideChargeAdd()">Add Bedside Charge</button>
|
||||
<div class="card-header py-2">
|
||||
<button type="button" class="ipd-charge-section-toggle" data-bs-toggle="collapse" data-bs-target="#bedsideChargeCollapse" aria-expanded="false" aria-controls="bedsideChargeCollapse">
|
||||
<span>Bedside Clinical / Nursing Charge</span>
|
||||
<i class="bi bi-chevron-down" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div id="bedsideChargeCollapse" class="collapse">
|
||||
<div class="card-body">
|
||||
<div class="row g-2">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Item Type</label>
|
||||
<select class="form-select form-select-sm" id="bedside_item_type">
|
||||
<option value="">All</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Category</label>
|
||||
<select class="form-select form-select-sm" id="bedside_category">
|
||||
<option value="">Select</option>
|
||||
<?php foreach (array_keys($bedsideItemsByCategory) as $categoryName) : ?>
|
||||
<option value="<?= esc($categoryName) ?>"><?= esc($categoryName) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<label class="form-label">Item</label>
|
||||
<select class="form-select form-select-sm" id="bedside_item_id">
|
||||
<option value="">Select</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Quick Search</label>
|
||||
<input type="text" class="form-control form-control-sm" id="bedside_item_search" placeholder="Search by code or name">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Rate</label>
|
||||
<input class="form-control form-control-sm" id="bedside_item_rate" value="0" type="number" $11" />
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Qty</label>
|
||||
<input class="form-control form-control-sm" id="bedside_item_qty" value="1" type="number" $11" />
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Date</label>
|
||||
<input class="form-control form-control-sm" id="bedside_item_date" value="<?= esc($today) ?>" type="date" />
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Doctor</label>
|
||||
<select class="form-select form-select-sm" id="bedside_doc_id">
|
||||
<option value="0">NONE</option>
|
||||
<?php foreach ($doc_list ?? [] as $doc) : ?>
|
||||
<option value="<?= esc($doc->id ?? '') ?>"><?= esc($doc->DocSpecName ?? '') ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Comment</label>
|
||||
<input class="form-control form-control-sm" id="bedside_comment" value="" type="text" />
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<button type="button" class="btn btn-outline-primary btn-sm" onclick="ipdBedsideChargeAdd()">Add Bedside Charge</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-3 border-primary">
|
||||
<div class="card-header py-2"><strong>Doctor Visit Charge</strong></div>
|
||||
<div class="card-body">
|
||||
<div class="row g-2">
|
||||
<div class="col-md-5">
|
||||
<label class="form-label">Doctor</label>
|
||||
<select class="form-select form-select-sm" id="doctor_visit_doc_id">
|
||||
<option value="">Select</option>
|
||||
<?php foreach ($doc_list ?? [] as $doc) : ?>
|
||||
<option value="<?= esc($doc->id ?? '') ?>"><?= esc($doc->DocSpecName ?? '') ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Visit Type</label>
|
||||
<select class="form-select form-select-sm" id="doctor_visit_fee_type">
|
||||
<option value="">Select</option>
|
||||
<?php foreach ($doctorVisitFeeTypes as $row) : ?>
|
||||
<option value="<?= esc($row['id'] ?? 0) ?>"><?= esc($row['fee_type'] ?? '') ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Rate</label>
|
||||
<input class="form-control form-control-sm" id="doctor_visit_rate" value="0.00" type="number" $11" />
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<label class="form-label">Description</label>
|
||||
<input class="form-control form-control-sm" id="doctor_visit_fee_desc" value="" type="text" readonly />
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Qty</label>
|
||||
<input class="form-control form-control-sm" id="doctor_visit_qty" value="1" type="number" $11" />
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Date</label>
|
||||
<input class="form-control form-control-sm" id="doctor_visit_date" value="<?= esc($today) ?>" type="date" />
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Time</label>
|
||||
<input class="form-control form-control-sm" id="doctor_visit_time" type="time" />
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Duration (min)</label>
|
||||
<input class="form-control form-control-sm" id="doctor_visit_duration" value="0" type="number" min="0" step="1" />
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Hospital/Clinic</label>
|
||||
<input class="form-control form-control-sm" id="doctor_visit_hospital" value="" type="text" />
|
||||
</div>
|
||||
<div class="col-md-3 d-flex align-items-end">
|
||||
<div class="form-check mb-1">
|
||||
<input class="form-check-input" type="checkbox" id="doctor_visit_outside" value="1">
|
||||
<label class="form-check-label" for="doctor_visit_outside">Outside Doctor</label>
|
||||
<div class="card-header py-2">
|
||||
<button type="button" class="ipd-charge-section-toggle" data-bs-toggle="collapse" data-bs-target="#doctorVisitChargeCollapse" aria-expanded="false" aria-controls="doctorVisitChargeCollapse">
|
||||
<span>Doctor Visit Charge</span>
|
||||
<i class="bi bi-chevron-down" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div id="doctorVisitChargeCollapse" class="collapse">
|
||||
<div class="card-body">
|
||||
<div class="row g-2">
|
||||
<div class="col-md-5">
|
||||
<label class="form-label">Doctor</label>
|
||||
<select class="form-select form-select-sm" id="doctor_visit_doc_id">
|
||||
<option value="">Select</option>
|
||||
<?php foreach ($doc_list ?? [] as $doc) : ?>
|
||||
<option value="<?= esc($doc->id ?? '') ?>"><?= esc($doc->DocSpecName ?? '') ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Visit Type</label>
|
||||
<select class="form-select form-select-sm" id="doctor_visit_fee_type">
|
||||
<option value="">Select</option>
|
||||
<?php foreach ($doctorVisitFeeTypes as $row) : ?>
|
||||
<option value="<?= esc($row['id'] ?? 0) ?>"><?= esc($row['fee_type'] ?? '') ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Rate</label>
|
||||
<input class="form-control form-control-sm" id="doctor_visit_rate" value="0.00" type="number" $11" />
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<label class="form-label">Description</label>
|
||||
<input class="form-control form-control-sm" id="doctor_visit_fee_desc" value="" type="text" readonly />
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Qty</label>
|
||||
<input class="form-control form-control-sm" id="doctor_visit_qty" value="1" type="number" $11" />
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Date</label>
|
||||
<input class="form-control form-control-sm" id="doctor_visit_date" value="<?= esc($today) ?>" type="date" />
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Time</label>
|
||||
<input class="form-control form-control-sm" id="doctor_visit_time" type="time" />
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Duration (min)</label>
|
||||
<input class="form-control form-control-sm" id="doctor_visit_duration" value="0" type="number" min="0" step="1" />
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Hospital/Clinic</label>
|
||||
<input class="form-control form-control-sm" id="doctor_visit_hospital" value="" type="text" />
|
||||
</div>
|
||||
<div class="col-md-3 d-flex align-items-end">
|
||||
<div class="form-check mb-1">
|
||||
<input class="form-check-input" type="checkbox" id="doctor_visit_outside" value="1">
|
||||
<label class="form-check-label" for="doctor_visit_outside">Outside Doctor</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Comment</label>
|
||||
<input class="form-control form-control-sm" id="doctor_visit_comment" value="" type="text" />
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<button type="button" class="btn btn-outline-primary btn-sm" onclick="ipdDoctorVisitChargeAdd()">Add Doctor Visit</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Comment</label>
|
||||
<input class="form-control form-control-sm" id="doctor_visit_comment" value="" type="text" />
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<button type="button" class="btn btn-outline-primary btn-sm" onclick="ipdDoctorVisitChargeAdd()">Add Doctor Visit</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user