CHNAGE_CD_INLINE_EDIT_AND_ACM_TPA_DASHBOARD_INPAM
This commit is contained in:
parent
6359106685
commit
0848cb5eeb
@ -44,7 +44,7 @@ class Acl
|
||||
'#^/sendDataToTPA#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID, MANAGER_ROLE_ID]],
|
||||
'#^/swagger#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID, MANAGER_ROLE_ID]],
|
||||
'#^/viewClaimFile#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID, MANAGER_ROLE_ID, STAFF_ROLE_ID]],
|
||||
|
||||
'#^/employee/tpaReportsDashboard#' => ['roles' => [ACCOUNT_MANAGER_ROLE_ID]],
|
||||
|
||||
|
||||
|
||||
|
||||
@ -137,6 +137,7 @@ $routes->group("/client", ["filter" => "authMVC"], function ($routes) {
|
||||
$routes->get('view_deposit/(:num)', 'ClientController::view_Deposit/$1');
|
||||
$routes->get('createtransaction', 'ClientController::createtransaction');
|
||||
$routes->post('save_deposit', 'ClientController::saveDeposit');
|
||||
$routes->post('update_deposit_amount', 'ClientController::updateDepositAmount');
|
||||
$routes->post("saveApiData", "ClientController::saveApiData");
|
||||
$routes->get("generateToken","ClientController::sendToken");
|
||||
// $routes->get('view_Deposit/(:num)/(:num)','ClientController/view_Deposit/$1/$2');
|
||||
@ -240,7 +241,7 @@ $routes->group("/employee", ["filter" => "authMVC"], function ($routes) {
|
||||
$routes->get("bulkGenerateEcardAndStoreinS3", "EmployeeController::bulkGenerateEcardAndStoreinS3");
|
||||
$routes->get('clearCdSession', 'EmployeeController::clearCdSession');
|
||||
$routes->get('checkSessionStatus', 'EmployeeController::checkSessionStatus');
|
||||
|
||||
$routes->get("tpaReportsDashboard", "EmployeeController::tpaReportsDashboard");
|
||||
});
|
||||
|
||||
|
||||
|
||||
@ -902,6 +902,109 @@ class ClientController extends AdminController
|
||||
return $this->response->setJSON(['success' => $response['success']]);
|
||||
}
|
||||
|
||||
public function updateDepositAmount()
|
||||
{
|
||||
$rules = [
|
||||
'deposit_id' => 'required|is_natural_no_zero',
|
||||
'amount' => 'required|numeric|greater_than_equal_to[0]',
|
||||
'transaction_type' => 'required|in_list[Credit,Debit]',
|
||||
'client_id' => 'required',
|
||||
'insurer_id' => 'required|is_natural_no_zero',
|
||||
'cd_ac_pk' => 'required|is_natural_no_zero',
|
||||
];
|
||||
|
||||
if (! $this->validate($rules)) {
|
||||
return $this->response
|
||||
->setStatusCode(400)
|
||||
->setJSON([
|
||||
'status' => 'error',
|
||||
'message' => 'Input validation failed',
|
||||
'errors' => $this->validator->getErrors(),
|
||||
]);
|
||||
}
|
||||
|
||||
$postData = sanitizeInputArrayAdvanced($this->request->getPost());
|
||||
$depositId = (int) ($postData['deposit_id'] ?? 0);
|
||||
$clientId = $postData['client_id'] ?? null;
|
||||
$insurerId = (int) ($postData['insurer_id'] ?? 0);
|
||||
$cdAcPk = (int) ($postData['cd_ac_pk'] ?? 0);
|
||||
$amount = (float) ($postData['amount'] ?? 0);
|
||||
$transactionType = $postData['transaction_type'] ?? 'Credit';
|
||||
$loggedInUserID = get_session_userid();
|
||||
|
||||
$depositModel = new ClientDepositModel();
|
||||
|
||||
$targetRow = $depositModel
|
||||
->where('id', $depositId)
|
||||
->where('client_id', $clientId)
|
||||
->where('insurer_id', $insurerId)
|
||||
->where('cd_ac_pk', $cdAcPk)
|
||||
->where('is_active', 1)
|
||||
->first();
|
||||
|
||||
if (empty($targetRow)) {
|
||||
return $this->response
|
||||
->setStatusCode(404)
|
||||
->setJSON([
|
||||
'status' => 'error',
|
||||
'message' => 'Transaction not found for this account.',
|
||||
]);
|
||||
}
|
||||
|
||||
$existingType = $targetRow['transaction_type'] ?? '';
|
||||
if ($existingType !== $transactionType) {
|
||||
return $this->response
|
||||
->setStatusCode(400)
|
||||
->setJSON([
|
||||
'status' => 'error',
|
||||
'message' => 'Only ' . $existingType . ' value can be edited for this transaction.',
|
||||
]);
|
||||
}
|
||||
|
||||
$db = \Config\Database::connect();
|
||||
$db->transBegin();
|
||||
|
||||
$depositModel->update($depositId, [
|
||||
'amount' => $amount,
|
||||
'transaction_type' => $transactionType,
|
||||
'updated_by' => $loggedInUserID,
|
||||
]);
|
||||
|
||||
$rows = $depositModel
|
||||
->where('client_id', $clientId)
|
||||
->where('insurer_id', $insurerId)
|
||||
->where('cd_ac_pk', $cdAcPk)
|
||||
->where('is_active', 1)
|
||||
->orderBy('id', 'ASC')
|
||||
->findAll();
|
||||
|
||||
$runningBalance = 0.0;
|
||||
foreach ($rows as $row) {
|
||||
$rowAmount = (float) ($row['amount'] ?? 0);
|
||||
$runningBalance += (($row['transaction_type'] ?? '') === 'Credit') ? $rowAmount : -$rowAmount;
|
||||
$depositModel->update((int) $row['id'], [
|
||||
'balance' => $runningBalance,
|
||||
'updated_by' => $loggedInUserID,
|
||||
]);
|
||||
}
|
||||
|
||||
if ($db->transStatus() === false) {
|
||||
$db->transRollback();
|
||||
return $this->response
|
||||
->setStatusCode(500)
|
||||
->setJSON([
|
||||
'status' => 'error',
|
||||
'message' => 'Failed to update transaction.',
|
||||
]);
|
||||
}
|
||||
|
||||
$db->transCommit();
|
||||
return $this->response->setJSON([
|
||||
'status' => 'success',
|
||||
'message' => 'Transaction updated successfully.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function editClientOnboarding($id = null)
|
||||
{
|
||||
|
||||
|
||||
@ -55,6 +55,8 @@ use Dompdf\Dompdf;
|
||||
use Dompdf\Options;
|
||||
use Kint;
|
||||
|
||||
use Firebase\JWT\JWT;
|
||||
|
||||
class EmployeeController extends AdminController
|
||||
{
|
||||
|
||||
@ -7339,4 +7341,75 @@ class EmployeeController extends AdminController
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function tpaReportsDashboard()
|
||||
{
|
||||
$METABASE_SECRET_KEY = getenv('METABASE_SECRET_KEY');
|
||||
$policy_id = $this->request->getGet('client_policy') ?? null;
|
||||
|
||||
if ($this->request->getGet('api') != 1) {
|
||||
$data = [
|
||||
'tab_name' => 'TPA Reports',
|
||||
'page_name' => 'TPA Reports',
|
||||
'metabaseUrl' => 'https://nsights.nhanceindia.in',
|
||||
'dashboardApiUrl' => base_url('/employee/tpaReportsDashboard'),
|
||||
];
|
||||
|
||||
return $this->loadLayout('meta_dashboard_demo_one', $data);
|
||||
}
|
||||
|
||||
if (empty($policy_id)) {
|
||||
return $this->respond([
|
||||
'status' => 'failed',
|
||||
'message' => 'Client policy is required.',
|
||||
'data' => [],
|
||||
]);
|
||||
}
|
||||
|
||||
$client_policy_data = $this->clientPolicyModel
|
||||
->select('tpa.dashboard_id')
|
||||
->join('tpa', 'client_policy.tpa_id = tpa.id')
|
||||
->where('client_policy.id', $policy_id)
|
||||
->first();
|
||||
|
||||
$database_id = isset($client_policy_data['dashboard_id']) ? (int) $client_policy_data['dashboard_id'] : null;
|
||||
|
||||
if (empty($database_id)) {
|
||||
|
||||
if ($this->request->getGet('api') == 1) {
|
||||
return $this->respond([
|
||||
'status' => 'failed',
|
||||
'message' => 'There is no dashboard for this TPA.',
|
||||
'data' => []
|
||||
]);
|
||||
}
|
||||
|
||||
return view('errors/404', [
|
||||
'message' => 'There is no dashboard for this TPA.'
|
||||
]);
|
||||
}
|
||||
|
||||
$payload = [
|
||||
'resource' => [
|
||||
'dashboard' => $database_id
|
||||
],
|
||||
'exp' => time() + (10 * 60), // 10 minutes
|
||||
'params' => (object) ['client_policy' => $policy_id ], // MUST be object for Metabase
|
||||
|
||||
];
|
||||
|
||||
$token = JWT::encode($payload, $METABASE_SECRET_KEY, 'HS256');
|
||||
// dd($token);
|
||||
|
||||
return $this->respond([
|
||||
'status' => 'success',
|
||||
'message' => 'Form data received successfully!',
|
||||
'data' => [
|
||||
'metabaseToken' => $token,
|
||||
'metabaseUrl' => 'https://nsights.nhanceindia.in',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -365,7 +365,7 @@ class TestingController extends BaseController
|
||||
$insurerBranchModel = new InsurerBranchModel();
|
||||
$insurer_branch = $insurerBranchModel->getInsurerBranchesWithInsurerNames();
|
||||
|
||||
$dataArray = $data['pt_co_share_details']; // Example
|
||||
$dataArray = $data['pt_co_share_details'] ?? []; // Example
|
||||
$cd_ac_pk = 'CD12345';
|
||||
$role_id = 1;
|
||||
$team_id = ['6'];
|
||||
@ -1101,10 +1101,8 @@ class TestingController extends BaseController
|
||||
|
||||
// 🔐 Move this to .env in real projects
|
||||
$METABASE_SECRET_KEY = getenv('METABASE_SECRET_KEY');
|
||||
$database_id = (int)$this->request->getGet('database_id') ?? 2;
|
||||
$database_id = (int) ($this->request->getGet('database_id') ?? 2);
|
||||
$policy_id = $this->request->getGet('client_policy') ?? null;
|
||||
$tpa_url = 'https://nsights.nhanceindia.in/public/dashboard/4babf324-6c1e-4c5a-adbb-1c80a0f545b1';
|
||||
$policy_id = $policy_id ? $policy_id : 4687;
|
||||
$payload = [
|
||||
'resource' => [
|
||||
// 'dashboard' => 1
|
||||
@ -1127,21 +1125,33 @@ class TestingController extends BaseController
|
||||
// 'token' => $token,
|
||||
// 'iframe_url' => "https://your-metabase-domain/embed/dashboard/{$token}#bordered=true&titled=true"
|
||||
// ]);
|
||||
if($this->request->getGet('api') == 1)
|
||||
{
|
||||
return $this->respond([
|
||||
'status' => 'success',
|
||||
'message' => 'Form data received successfully!',
|
||||
'data' => [
|
||||
'metabaseToken' => $token,
|
||||
'metabaseUrl' => 'https://nsights.nhanceindia.in']
|
||||
if ($this->request->getGet('api') == 1) {
|
||||
if (empty($policy_id)) {
|
||||
return $this->respond([
|
||||
'status' => 'failed',
|
||||
'message' => 'Client policy is required.',
|
||||
'data' => [],
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->respond([
|
||||
'status' => 'success',
|
||||
'message' => 'Dashboard token generated.',
|
||||
'data' => [
|
||||
'metabaseToken' => $token,
|
||||
'metabaseUrl' => 'https://nsights.nhanceindia.in',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
return view('meta_dashboard_demo_one', [
|
||||
'metabaseToken' => $token,
|
||||
'metabaseUrl' => 'https://nsights.nhanceindia.in',
|
||||
]);
|
||||
$data = [
|
||||
'tab_name' => 'Metabase Dashboard',
|
||||
'page_name' => 'Metabase Dashboard',
|
||||
'metabaseUrl' => 'https://nsights.nhanceindia.in',
|
||||
'dashboardApiUrl' => base_url('/metaDashboardDemo'),
|
||||
];
|
||||
|
||||
return $this->loadLayout('meta_dashboard_demo_one', $data);
|
||||
}
|
||||
|
||||
public function testingquerys1(){
|
||||
@ -1175,7 +1185,26 @@ class TestingController extends BaseController
|
||||
public function metaTpaDashboardDemo()
|
||||
{
|
||||
$METABASE_SECRET_KEY = getenv('METABASE_SECRET_KEY');
|
||||
$policy_id = $this->request->getGet('client_policy') ?? 4687;
|
||||
$policy_id = $this->request->getGet('client_policy') ?? null;
|
||||
|
||||
if ($this->request->getGet('api') != 1) {
|
||||
$data = [
|
||||
'tab_name' => 'TPA Reports',
|
||||
'page_name' => 'TPA Reports',
|
||||
'metabaseUrl' => 'https://nsights.nhanceindia.in',
|
||||
'dashboardApiUrl' => base_url('/metaTpaDashboardDemo'),
|
||||
];
|
||||
|
||||
return $this->loadLayout('meta_dashboard_demo_one', $data);
|
||||
}
|
||||
|
||||
if (empty($policy_id)) {
|
||||
return $this->respond([
|
||||
'status' => 'failed',
|
||||
'message' => 'Client policy is required.',
|
||||
'data' => [],
|
||||
]);
|
||||
}
|
||||
|
||||
$client_policy_data = $this->ClientPolicyModel
|
||||
->select('tpa.dashboard_id')
|
||||
@ -1212,20 +1241,13 @@ class TestingController extends BaseController
|
||||
$token = JWT::encode($payload, $METABASE_SECRET_KEY, 'HS256');
|
||||
// dd($token);
|
||||
|
||||
if ($this->request->getGet('api') == 1) {
|
||||
return $this->respond([
|
||||
'status' => 'success',
|
||||
'message' => 'Form data received successfully!',
|
||||
'data' => [
|
||||
'metabaseToken' => $token,
|
||||
'metabaseUrl' => 'https://nsights.nhanceindia.in'
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
return view('meta_dashboard_demo_one', [
|
||||
'metabaseToken' => $token,
|
||||
'metabaseUrl' => 'https://nsights.nhanceindia.in',
|
||||
return $this->respond([
|
||||
'status' => 'success',
|
||||
'message' => 'Form data received successfully!',
|
||||
'data' => [
|
||||
'metabaseToken' => $token,
|
||||
'metabaseUrl' => 'https://nsights.nhanceindia.in',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@ -311,6 +311,24 @@ class ClientPolicyModel extends Model
|
||||
// Fetch the deposit data based on client and insurer IDs
|
||||
$query = $this->db->table('cash_deposit')
|
||||
->select('cash_deposit.*')
|
||||
->select("
|
||||
(select old_value from auditing_history where pk = cash_deposit.id and field_name = 'amount' and table_name = 'cash_deposit' order by id desc limit 1) as old_amt,
|
||||
(select new_value from auditing_history where pk = cash_deposit.id and field_name = 'amount' and table_name = 'cash_deposit' order by id desc limit 1) as new_amt,
|
||||
(select created_by from auditing_history where pk = cash_deposit.id and field_name = 'amount' and table_name = 'cash_deposit' order by id desc limit 1) as amt_updated_by,
|
||||
(select created_at from auditing_history where pk = cash_deposit.id and field_name = 'amount' and table_name = 'cash_deposit' order by id desc limit 1) as amt_updated_at,
|
||||
(
|
||||
select up.first_name
|
||||
from user_profiles up
|
||||
where up.id = (
|
||||
select created_by
|
||||
from auditing_history
|
||||
where pk = cash_deposit.id and field_name = 'amount' and table_name = 'cash_deposit'
|
||||
order by id desc
|
||||
limit 1
|
||||
)
|
||||
limit 1
|
||||
) as amt_updated_by_name
|
||||
")
|
||||
->select('insurers.name as insurer_name, insurers.short_name as insurer_short')
|
||||
->select('clients.client_name as clientname, clients.short_name as clientshort, client_policy.policy_no')
|
||||
->select('cd_master.cd_ac_no as cd_master_account_no')
|
||||
|
||||
@ -236,7 +236,7 @@ input:checked + .slider:before {
|
||||
class="text-danger">*</span></label>
|
||||
<select class="form-control" id="entity_type" name="entity_type_id" required>
|
||||
<option value="">Select Entity</option>
|
||||
<?php foreach($entity as $value) { ?>
|
||||
<?php foreach($entity ?? [] as $value) { ?>
|
||||
<?php if(isset($client['entity_type_id'])){ ?>
|
||||
<?php if($value['id'] == $client['entity_type_id']) { ?>
|
||||
<option value="<?= $value['id'] ?>" selected><?= $value['name'] ?></option>
|
||||
@ -285,7 +285,7 @@ input:checked + .slider:before {
|
||||
<label for="parent_client_id">Parent Group<span class="text-danger"></span></label>
|
||||
<select class="form-control" id="parent_client_id" name="parent_client_id">
|
||||
<option value="" >Select Parent</option>
|
||||
<?php foreach($clients as $value) { ?>
|
||||
<?php foreach($clients ?? [] as $value) { ?>
|
||||
<option value="<?= $value['id'] ?>" <?= isset($client['parent_client_id']) && $client['parent_client_id'] == $value['id'] ? "selected" : '' ?>><?= $value['client_name'] ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
@ -293,7 +293,7 @@ input:checked + .slider:before {
|
||||
|
||||
<div class="form-group col-md-4">
|
||||
|
||||
<label>HR File Processed By <span class="text-danger">*</span></label>
|
||||
<label>Enrollment file processed by <span class="text-danger">*</span></label>
|
||||
<div class="hr-processed-options">
|
||||
<div class="hr-processed-option">
|
||||
<input class="hr-radio-input"
|
||||
|
||||
@ -138,7 +138,7 @@ $cd_col_width_px = nhance_dt_column_widths_px($cd_header_labels, $cd_col_max_len
|
||||
<td>
|
||||
<a class="view-deposit-link"
|
||||
href="<?= base_url("client/view_deposit/{$value->insurer_id}?client_id={$value->client_id}&cd_ac_pk={$value->cd_ac_pk}"); ?>">
|
||||
<i class="mdi mdi-eye text-muted font-18 vertical-middle"></i><span>View Deposit</span>
|
||||
<i class="mdi mdi-eye text-muted font-18 vertical-middle"></i><span>View Details</span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@ -2017,6 +2017,11 @@ body[data-sidebar-size="condensed"] .footer {
|
||||
<li>
|
||||
<a href="<?= base_url('/employee/retail-endorsement-list') ?>">Retail Endorsement</a>
|
||||
</li>
|
||||
<?php if(in_array(get_role_id(), [ACCOUNT_MANAGER_ROLE_ID])) { ?>
|
||||
<li>
|
||||
<a href="<?= base_url('/employee/tpaReportsDashboard') ?>">TPA Reports</a>
|
||||
</li>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
@ -1,33 +1,345 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Metabase Dashboard</title>
|
||||
<style>
|
||||
.meta-dashboard-demo .filter-bar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px 20px;
|
||||
align-items: flex-end;
|
||||
background: #eef5fa;
|
||||
border-radius: 10px;
|
||||
padding: 10px 14px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.meta-dashboard-demo .filter-field {
|
||||
flex: 1 1 140px;
|
||||
min-width: 120px;
|
||||
max-width: 220px;
|
||||
}
|
||||
.meta-dashboard-demo .filter-field label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 4px;
|
||||
color: #111;
|
||||
}
|
||||
.meta-dashboard-demo .filter-field .select2-container {
|
||||
width: 100% !important;
|
||||
}
|
||||
.meta-dashboard-demo .filter-field .select2-selection--single {
|
||||
height: 32px !important;
|
||||
min-height: 32px;
|
||||
border: 1px solid #d8e2ea !important;
|
||||
border-radius: 8px !important;
|
||||
font-size: 12px;
|
||||
}
|
||||
.meta-dashboard-demo .filter-field .select2-selection__rendered {
|
||||
line-height: 30px !important;
|
||||
padding-left: 10px !important;
|
||||
color: #333;
|
||||
}
|
||||
.meta-dashboard-demo .filter-field .select2-selection__arrow {
|
||||
height: 30px !important;
|
||||
}
|
||||
.meta-dashboard-demo .filter-field .select2-container--disabled .select2-selection--single {
|
||||
background-color: #f5f7f9 !important;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.85;
|
||||
}
|
||||
.meta-dashboard-demo .dashboard-status {
|
||||
font-size: 12px;
|
||||
color: #5c6b7a;
|
||||
padding: 4px 0 10px;
|
||||
min-height: 20px;
|
||||
}
|
||||
.meta-dashboard-demo .dashboard-status.is-error { color: #c0392b; }
|
||||
.meta-dashboard-demo .dashboard-status.is-loading { color: #0a8794; }
|
||||
.meta-dashboard-demo #dashboard-container {
|
||||
min-height: 480px;
|
||||
}
|
||||
.meta-dashboard-demo #dashboard-container metabase-dashboard {
|
||||
display: block;
|
||||
width: 100%;
|
||||
min-height: 480px;
|
||||
}
|
||||
.meta-dashboard-demo .dashboard-placeholder {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 200px;
|
||||
color: #7a8a99;
|
||||
font-size: 13px;
|
||||
background: #fff;
|
||||
border: 1px dashed #d0dce6;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
<!-- Metabase embed script -->
|
||||
<script defer src="<?= esc($metabaseUrl) ?>/app/embed.js"></script>
|
||||
.emotion-65tole {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
function defineMetabaseConfig(config) {
|
||||
window.metabaseConfig = config;
|
||||
<div class="row meta-dashboard-demo">
|
||||
<div class="col-xl-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<!-- <h4 class="mb-3"><?= esc($page_name ?? 'Metabase Dashboard') ?></h4> -->
|
||||
|
||||
<div class="filter-bar">
|
||||
<div class="filter-field">
|
||||
<label for="filter-client">Client</label>
|
||||
<select id="filter-client" class="form-control form-control-sm meta-filter-select2">
|
||||
<option value="">Select</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-field">
|
||||
<label for="filter-branch">Branch</label>
|
||||
<select id="filter-branch" class="form-control form-control-sm meta-filter-select2" disabled>
|
||||
<option value="">Select</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-field">
|
||||
<label for="filter-policy">Policy</label>
|
||||
<select id="filter-policy" class="form-control form-control-sm meta-filter-select2" disabled>
|
||||
<option value="">Select</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="dashboard-status" class="dashboard-status"></div>
|
||||
|
||||
<div id="dashboard-container">
|
||||
<div class="dashboard-placeholder" id="dashboard-placeholder">
|
||||
Select client, branch, and policy to load the dashboard.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function defineMetabaseConfig(config) {
|
||||
window.metabaseConfig = config;
|
||||
}
|
||||
|
||||
defineMetabaseConfig({
|
||||
theme: { preset: "light" },
|
||||
isGuest: true,
|
||||
instanceUrl: "<?= esc($metabaseUrl ?? '') ?>"
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
$(function () {
|
||||
const CLIENTS_API = <?= json_encode(base_url('/util/getClientAndBranchAndPolicy')) ?>;
|
||||
const DASHBOARD_API = <?= json_encode($dashboardApiUrl ?? base_url('/metaTpaDashboardDemo')) ?>;
|
||||
const METABASE_URL = <?= json_encode($metabaseUrl ?? '') ?>;
|
||||
|
||||
const select2Options = {
|
||||
placeholder: 'Select',
|
||||
allowClear: true,
|
||||
width: '100%',
|
||||
minimumResultsForSearch: 0,
|
||||
dropdownParent: $(document.body)
|
||||
};
|
||||
|
||||
let branchByClient = {};
|
||||
let policyByClient = {};
|
||||
let embedScriptPromise = null;
|
||||
|
||||
const $elClient = $('#filter-client');
|
||||
const $elBranch = $('#filter-branch');
|
||||
const $elPolicy = $('#filter-policy');
|
||||
const elStatus = document.getElementById('dashboard-status');
|
||||
const elContainer = document.getElementById('dashboard-container');
|
||||
const elPlaceholder = document.getElementById('dashboard-placeholder');
|
||||
|
||||
function setStatus(message, type) {
|
||||
elStatus.textContent = message || '';
|
||||
elStatus.className = 'dashboard-status' + (type ? ' is-' + type : '');
|
||||
}
|
||||
|
||||
defineMetabaseConfig({
|
||||
theme: {
|
||||
preset: "light"
|
||||
},
|
||||
isGuest: true,
|
||||
instanceUrl: "<?= esc($metabaseUrl) ?>"
|
||||
function initSelect2($select, disabled) {
|
||||
if ($select.hasClass('select2-hidden-accessible')) {
|
||||
$select.select2('destroy');
|
||||
}
|
||||
$select.prop('disabled', false);
|
||||
$select.select2(select2Options);
|
||||
if (disabled) {
|
||||
$select.prop('disabled', true);
|
||||
}
|
||||
}
|
||||
|
||||
function rebuildSelect($select, placeholder, disabled, items, getValue, getLabel) {
|
||||
if ($select.hasClass('select2-hidden-accessible')) {
|
||||
$select.select2('destroy');
|
||||
}
|
||||
$select.empty().append($('<option>', { value: '', text: placeholder }));
|
||||
(items || []).forEach(function (item) {
|
||||
$select.append($('<option>', {
|
||||
value: String(getValue(item)),
|
||||
text: getLabel(item)
|
||||
}));
|
||||
});
|
||||
$select.val('');
|
||||
initSelect2($select, disabled);
|
||||
}
|
||||
|
||||
function filtersComplete() {
|
||||
return !!($elClient.val() && $elBranch.val() && $elPolicy.val());
|
||||
}
|
||||
|
||||
function loadMetabaseEmbedScript() {
|
||||
if (window.customElements && window.customElements.get('metabase-dashboard')) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (embedScriptPromise) {
|
||||
return embedScriptPromise;
|
||||
}
|
||||
embedScriptPromise = new Promise(function (resolve, reject) {
|
||||
const existing = document.querySelector('script[data-metabase-embed]');
|
||||
if (existing) {
|
||||
existing.addEventListener('load', function () { resolve(); }, { once: true });
|
||||
existing.addEventListener('error', reject, { once: true });
|
||||
return;
|
||||
}
|
||||
const script = document.createElement('script');
|
||||
script.src = METABASE_URL + '/app/embed.js';
|
||||
script.defer = true;
|
||||
script.dataset.metabaseEmbed = '1';
|
||||
script.onload = function () { resolve(); };
|
||||
script.onerror = reject;
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
return embedScriptPromise;
|
||||
}
|
||||
|
||||
function clearDashboard() {
|
||||
const existing = elContainer.querySelector('metabase-dashboard');
|
||||
if (existing) {
|
||||
existing.remove();
|
||||
}
|
||||
if (elPlaceholder) {
|
||||
elPlaceholder.style.display = '';
|
||||
}
|
||||
}
|
||||
|
||||
async function renderDashboard(token) {
|
||||
clearDashboard();
|
||||
if (!token) {
|
||||
return;
|
||||
}
|
||||
await loadMetabaseEmbedScript();
|
||||
if (elPlaceholder) {
|
||||
elPlaceholder.style.display = 'none';
|
||||
}
|
||||
const dash = document.createElement('metabase-dashboard');
|
||||
dash.setAttribute('token', token);
|
||||
dash.setAttribute('with-title', 'true');
|
||||
dash.setAttribute('with-downloads', 'true');
|
||||
elContainer.appendChild(dash);
|
||||
}
|
||||
|
||||
async function loadFilterData() {
|
||||
setStatus('Loading clients…', 'loading');
|
||||
try {
|
||||
const res = await fetch(CLIENTS_API, { credentials: 'same-origin' });
|
||||
const json = await res.json();
|
||||
if (!json.status) {
|
||||
setStatus('Could not load client list.', 'error');
|
||||
return;
|
||||
}
|
||||
branchByClient = json.branch_data || {};
|
||||
policyByClient = json.policyListByClient || {};
|
||||
|
||||
rebuildSelect($elClient, 'Select', false, json.client_data || [], function (c) { return c.id; }, function (c) {
|
||||
return c.client_name || c.client_short_name || ('Client ' + c.id);
|
||||
});
|
||||
rebuildSelect($elBranch, 'Select', true);
|
||||
rebuildSelect($elPolicy, 'Select', true);
|
||||
setStatus('');
|
||||
} catch (e) {
|
||||
setStatus('Failed to load filter options.', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchDashboard(policyId) {
|
||||
if (!filtersComplete()) {
|
||||
clearDashboard();
|
||||
setStatus('Select client, branch, and policy to load the dashboard.');
|
||||
return;
|
||||
}
|
||||
if (!policyId) {
|
||||
clearDashboard();
|
||||
setStatus('Select client, branch, and policy to load the dashboard.');
|
||||
return;
|
||||
}
|
||||
setStatus('Loading dashboard…', 'loading');
|
||||
try {
|
||||
const url = DASHBOARD_API + '?api=1&client_policy=' + encodeURIComponent(policyId);
|
||||
const res = await fetch(url, { credentials: 'same-origin' });
|
||||
const json = await res.json();
|
||||
if (json.status !== 'success' || !json.data || !json.data.metabaseToken) {
|
||||
clearDashboard();
|
||||
setStatus(json.message || 'No dashboard available for this policy.', 'error');
|
||||
return;
|
||||
}
|
||||
renderDashboard(json.data.metabaseToken);
|
||||
setStatus('');
|
||||
} catch (e) {
|
||||
clearDashboard();
|
||||
setStatus('Failed to load dashboard.', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
$elClient.on('change', function () {
|
||||
const clientId = $(this).val();
|
||||
rebuildSelect($elBranch, 'Select', !clientId);
|
||||
rebuildSelect($elPolicy, 'Select', true);
|
||||
clearDashboard();
|
||||
setStatus(clientId ? '' : 'Select client, branch, and policy to load the dashboard.');
|
||||
|
||||
if (!clientId) {
|
||||
return;
|
||||
}
|
||||
const branches = branchByClient[clientId] || [];
|
||||
rebuildSelect($elBranch, 'Select', branches.length === 0, branches, function (b) { return b.id; }, function (b) {
|
||||
return b.branch_name || ('Branch ' + b.id);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<metabase-dashboard
|
||||
token="<?= esc($metabaseToken) ?>"
|
||||
with-title="true"
|
||||
with-downloads="true">
|
||||
</metabase-dashboard>
|
||||
$elBranch.on('change', function () {
|
||||
const branchId = $(this).val();
|
||||
const clientId = $elClient.val();
|
||||
rebuildSelect($elPolicy, 'Select', !branchId);
|
||||
clearDashboard();
|
||||
setStatus(branchId ? '' : 'Select branch and policy to load the dashboard.');
|
||||
|
||||
</body>
|
||||
</html>
|
||||
if (!branchId || !clientId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const policiesForClient = policyByClient[clientId] || [];
|
||||
const policies = policiesForClient.filter(function (p) {
|
||||
return String(p.client_branch_id) === String(branchId);
|
||||
});
|
||||
|
||||
rebuildSelect($elPolicy, 'Select', policies.length === 0, policies, function (p) { return p.id; }, function (p) {
|
||||
const type = p.policy_type || '';
|
||||
const no = p.policy_no || '';
|
||||
return (type && no) ? (type + ' - ' + no) : (no || type || ('Policy ' + p.id));
|
||||
});
|
||||
if (policies.length === 0) {
|
||||
setStatus('No policies found for this branch.', 'error');
|
||||
}
|
||||
});
|
||||
|
||||
$elPolicy.on('change', function () {
|
||||
fetchDashboard($(this).val());
|
||||
});
|
||||
|
||||
initSelect2($elClient, false);
|
||||
initSelect2($elBranch, true);
|
||||
initSelect2($elPolicy, true);
|
||||
loadFilterData();
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -31,8 +31,8 @@ foreach ($depositdata as $row) {
|
||||
$vd_col_max_len[9] = max($vd_col_max_len[9], mb_strlen((string) ($row->description ?? '')));
|
||||
$vd_col_max_len[10] = max($vd_col_max_len[10], mb_strlen((string) ($row->username ?? '')));
|
||||
}
|
||||
$vd_col_min_px = [120, 100, 72, 160, 110, 88, 88, 88, 100, 140, 100];
|
||||
$vd_col_max_px = [200, 120, 120, 360, 160, 140, 120, 120, 140, 400, 200];
|
||||
$vd_col_min_px = [120, 100, 72, 160, 110, 88, 150, 150, 170, 140, 100];
|
||||
$vd_col_max_px = [200, 120, 120, 360, 160, 140, 180, 180, 220, 400, 200];
|
||||
$vd_col_width_px = nhance_dt_column_widths_px($vd_header_labels, $vd_col_max_len, $vd_col_min_px, $vd_col_max_px);
|
||||
?>
|
||||
<style>
|
||||
@ -98,6 +98,165 @@ $vd_col_width_px = nhance_dt_column_widths_px($vd_header_labels, $vd_col_max_len
|
||||
}
|
||||
.dataTables_length label {height: 21px !important;}
|
||||
#scroll-horizontal-datatable tbody tr:hover { background-color: #e0e0e0; }
|
||||
.deposit-inline-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
.deposit-inline-input {
|
||||
width: 150px;
|
||||
max-width: 100%;
|
||||
}
|
||||
.deposit-inline-input-disabled {
|
||||
background-color: #f4f5f7 !important;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.deposit-balance-wrap {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
.deposit-history-icon {
|
||||
color: #0ea5b7;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
}
|
||||
#moreInfoModal .modal-dialog {
|
||||
max-width: 520px;
|
||||
}
|
||||
#moreInfoModal .modal-content {
|
||||
background: #ffffff;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 14px;
|
||||
color: #111827;
|
||||
box-shadow: 0 18px 40px rgba(15, 23, 42, 0.12);
|
||||
}
|
||||
#moreInfoModal .modal-header,
|
||||
#moreInfoModal .modal-footer {
|
||||
border-color: #e5e7eb;
|
||||
padding: 14px 18px;
|
||||
background: #ffffff;
|
||||
}
|
||||
#moreInfoModal .modal-body {
|
||||
padding: 0 18px 16px;
|
||||
background: #ffffff;
|
||||
}
|
||||
#moreInfoModal .deposit-history-title-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
#moreInfoModal .deposit-history-title-icon {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 8px;
|
||||
background: #0ea5b7;
|
||||
color: #fff;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 18px;
|
||||
}
|
||||
#moreInfoModal .modal-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #111827;
|
||||
margin: 0;
|
||||
}
|
||||
#moreInfoModal .deposit-history-close-btn {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border: 1px solid #d1d5db;
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
color: #6b7280;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
}
|
||||
#moreInfoModal .deposit-history-close-btn:hover {
|
||||
background: #f3f4f6;
|
||||
color: #111827;
|
||||
}
|
||||
#moreInfoModal .deposit-history-amount-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 12px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
#moreInfoModal .deposit-history-amount-card {
|
||||
background: #f9fafb;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 10px;
|
||||
padding: 12px 14px;
|
||||
}
|
||||
#moreInfoModal .deposit-history-amount-label {
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
color: #6b7280;
|
||||
margin-bottom: 6px;
|
||||
font-weight: 600;
|
||||
}
|
||||
#moreInfoModal .deposit-history-amount-value {
|
||||
font-size: 24px;
|
||||
line-height: 1.2;
|
||||
font-weight: 700;
|
||||
color: #111827;
|
||||
word-break: break-word;
|
||||
}
|
||||
#moreInfoModal .deposit-history-meta-box {
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
background: #f9fafb;
|
||||
}
|
||||
#moreInfoModal .deposit-history-meta-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 12px 14px;
|
||||
background: #ffffff;
|
||||
}
|
||||
#moreInfoModal .deposit-history-meta-row + .deposit-history-meta-row {
|
||||
border-top: 1px solid #e5e7eb;
|
||||
}
|
||||
#moreInfoModal .deposit-history-meta-label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: #6b7280;
|
||||
font-size: 14px;
|
||||
}
|
||||
#moreInfoModal .deposit-history-meta-value {
|
||||
color: #111827;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
text-align: right;
|
||||
}
|
||||
#moreInfoModal .deposit-history-footer-btn {
|
||||
border: 1px solid #d1d5db;
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
color: #374151;
|
||||
padding: 6px 16px;
|
||||
font-size: 14px;
|
||||
}
|
||||
#moreInfoModal .deposit-history-footer-btn:hover {
|
||||
background: #f3f4f6;
|
||||
color: #111827;
|
||||
}
|
||||
.deposit-inline-actions {
|
||||
display: inline-flex;
|
||||
gap: 4px;
|
||||
}
|
||||
.deposit-inline-actions .btn {
|
||||
padding: 2px 6px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
@media (max-width: 767.98px) {
|
||||
.deposit-title {
|
||||
font-size: 22px;
|
||||
@ -208,7 +367,26 @@ $vd_col_width_px = nhance_dt_column_widths_px($vd_header_labels, $vd_col_max_len
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach($depositdata as $row) { ?>
|
||||
<tr id="<?php echo $row->id;?>">
|
||||
<?php
|
||||
$isCreditRow = ($row->transaction_type == 'Credit');
|
||||
$isDebitRow = ($row->transaction_type == 'Debit');
|
||||
$creditAmount = $isCreditRow ? (float) $row->amount : 0;
|
||||
$debitAmount = $isDebitRow ? (float) $row->amount : 0;
|
||||
$hasAmtHistory = isset($row->new_amt) && trim((string) $row->new_amt) !== '';
|
||||
$historyOldAmt = trim((string) ($row->old_amt ?? '-')) !== '' ? (string) $row->old_amt : '-';
|
||||
$historyNewAmt = trim((string) ($row->new_amt ?? '-')) !== '' ? (string) $row->new_amt : '-';
|
||||
$historyUpdatedBy = trim((string) ($row->amt_updated_by_name ?? '')) !== ''
|
||||
? (string) $row->amt_updated_by_name
|
||||
: (!empty($row->amt_updated_by) ? ('User #' . $row->amt_updated_by) : '-');
|
||||
$historyUpdatedAt = !empty($row->amt_updated_at)
|
||||
? date('d M Y, h:i A', strtotime((string) $row->amt_updated_at))
|
||||
: '-';
|
||||
?>
|
||||
<tr id="<?php echo $row->id;?>"
|
||||
data-row-id="<?= (int) $row->id ?>"
|
||||
data-transaction-type="<?= esc((string) $row->transaction_type, 'attr') ?>"
|
||||
data-credit="<?= esc((string) $creditAmount, 'attr') ?>"
|
||||
data-debit="<?= esc((string) $debitAmount, 'attr') ?>">
|
||||
<td><?php echo date('d-M-Y h:i A', strtotime($row->created_at)); ?></td>
|
||||
<td><?php echo isset($row->record_date)? date('d-M-Y', strtotime($row->record_date)):'-' ?></td>
|
||||
<td><?php echo $row->unit ?? ' - '; ?></td>
|
||||
@ -228,10 +406,39 @@ $vd_col_width_px = nhance_dt_column_widths_px($vd_header_labels, $vd_col_max_len
|
||||
<td><?php echo $row->endorsement_no ?? '-'; ?></td>
|
||||
<td><?php echo isset($subTypeOptions[$row->sub_type]) ? $subTypeOptions[$row->sub_type] : ''; ?>
|
||||
</td>
|
||||
<td><?php echo ($row->transaction_type == 'Credit') ? $row->amount : '-'; ?></td>
|
||||
<td><?php echo ($row->transaction_type == 'Debit') ? $row->amount : '-'; ?></td>
|
||||
<td><?php echo $row->balance; ?></td>
|
||||
<td><?php echo $row->description; ?></td>
|
||||
<td class="deposit-inline-edit-cell" data-type="credit">
|
||||
<div class="deposit-inline-cell">
|
||||
<input type="text" class="form-control form-control-sm deposit-inline-input <?= $isCreditRow ? '' : 'deposit-inline-input-disabled' ?>" placeholder="-" value="<?= $isCreditRow ? esc((string) $row->amount, 'attr') : '' ?>" <?= $isCreditRow ? '' : 'disabled' ?>>
|
||||
</div>
|
||||
</td>
|
||||
<td class="deposit-inline-edit-cell" data-type="debit">
|
||||
<div class="deposit-inline-cell">
|
||||
<input type="text" class="form-control form-control-sm deposit-inline-input <?= $isDebitRow ? '' : 'deposit-inline-input-disabled' ?>" placeholder="-" value="<?= $isDebitRow ? esc((string) $row->amount, 'attr') : '' ?>" <?= $isDebitRow ? '' : 'disabled' ?>>
|
||||
</div>
|
||||
</td>
|
||||
<td class="deposit-balance-cell">
|
||||
<span class="deposit-balance-wrap">
|
||||
<span><?php echo $row->balance; ?></span>
|
||||
<?php if ($hasAmtHistory) : ?>
|
||||
<i class="mdi mdi-information-outline deposit-history-icon js-deposit-history-trigger"
|
||||
data-old-amt="<?= esc($historyOldAmt, 'attr') ?>"
|
||||
data-new-amt="<?= esc($historyNewAmt, 'attr') ?>"
|
||||
data-updated-by="<?= esc($historyUpdatedBy, 'attr') ?>"
|
||||
data-updated-at="<?= esc($historyUpdatedAt, 'attr') ?>"
|
||||
onclick="openDepositHistoryModal(this); return false;"
|
||||
aria-label="Amount change history"
|
||||
tabindex="0"></i>
|
||||
<?php endif; ?>
|
||||
</span>
|
||||
</td>
|
||||
<td class="deposit-description-cell"
|
||||
data-toggle="tooltip"
|
||||
data-bs-toggle="tooltip"
|
||||
data-placement="top"
|
||||
data-bs-placement="top"
|
||||
title="<?= esc((string) ($row->description ?? ''), 'attr') ?>">
|
||||
<?php echo $row->description; ?>
|
||||
</td>
|
||||
<td><?php echo $row->username; ?></td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
@ -245,6 +452,95 @@ $vd_col_width_px = nhance_dt_column_widths_px($vd_header_labels, $vd_col_max_len
|
||||
<!-- end col -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" id="moreInfoModal" tabindex="-1" aria-labelledby="depositHistoryModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<div class="deposit-history-title-wrap">
|
||||
<span class="deposit-history-title-icon" aria-hidden="true">
|
||||
<i class="mdi mdi-history"></i>
|
||||
</span>
|
||||
<h5 class="modal-title" id="depositHistoryModalLabel">Amount change history</h5>
|
||||
</div>
|
||||
<button type="button" class="deposit-history-close-btn" data-dismiss="modal" data-bs-dismiss="modal" aria-label="Close">
|
||||
<i class="mdi mdi-close"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="deposit-history-amount-grid">
|
||||
<div class="deposit-history-amount-card">
|
||||
<div class="deposit-history-amount-label">Old amount</div>
|
||||
<div class="deposit-history-amount-value" id="history_old_amt">-</div>
|
||||
</div>
|
||||
<div class="deposit-history-amount-card">
|
||||
<div class="deposit-history-amount-label">New amount</div>
|
||||
<div class="deposit-history-amount-value" id="history_new_amt">-</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="deposit-history-meta-box">
|
||||
<div class="deposit-history-meta-row">
|
||||
<div class="deposit-history-meta-label">
|
||||
<i class="mdi mdi-account-outline"></i>
|
||||
<span>Changed by</span>
|
||||
</div>
|
||||
<div class="deposit-history-meta-value" id="history_updated_by">-</div>
|
||||
</div>
|
||||
<div class="deposit-history-meta-row">
|
||||
<div class="deposit-history-meta-label">
|
||||
<i class="mdi mdi-calendar-month-outline"></i>
|
||||
<span>Changed at</span>
|
||||
</div>
|
||||
<div class="deposit-history-meta-value" id="history_updated_at">-</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="deposit-history-footer-btn" data-dismiss="modal" data-bs-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function formatDepositHistoryAmount(value) {
|
||||
if (value === null || value === undefined) {
|
||||
return '-';
|
||||
}
|
||||
var raw = String(value).trim();
|
||||
if (raw === '' || raw === '-') {
|
||||
return '-';
|
||||
}
|
||||
var num = parseFloat(raw.replace(/,/g, ''));
|
||||
if (isNaN(num)) {
|
||||
return raw;
|
||||
}
|
||||
return '₹' + num.toLocaleString('en-IN', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
});
|
||||
}
|
||||
|
||||
function openDepositHistoryModal(iconEl) {
|
||||
var $icon = $(iconEl);
|
||||
$('#history_old_amt').text(formatDepositHistoryAmount($icon.data('old-amt')));
|
||||
$('#history_new_amt').text(formatDepositHistoryAmount($icon.data('new-amt')));
|
||||
$('#history_updated_by').text($icon.data('updated-by') || '-');
|
||||
$('#history_updated_at').text($icon.data('updated-at') || '-');
|
||||
|
||||
var modalElement = document.getElementById('moreInfoModal');
|
||||
if (typeof bootstrap !== 'undefined' && typeof bootstrap.Modal !== 'undefined' && modalElement) {
|
||||
var myModal = new bootstrap.Modal(document.getElementById('moreInfoModal'));
|
||||
myModal.show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof $('#moreInfoModal').modal === 'function') {
|
||||
$('#moreInfoModal').modal('show');
|
||||
return;
|
||||
}
|
||||
|
||||
toastr.warning('History view is not available right now.');
|
||||
}
|
||||
</script>
|
||||
<!-- Add this modal markup at the end of your HTML body -->
|
||||
<!-- Button to trigger modal -->
|
||||
|
||||
@ -372,6 +668,40 @@ $vd_col_width_px = nhance_dt_column_widths_px($vd_header_labels, $vd_col_max_len
|
||||
|
||||
});
|
||||
nhanceListDataTableBeforeInit();
|
||||
var depositPageStorageKey = 'view_deposit_datatable_page_' + window.location.pathname + window.location.search;
|
||||
function getStoredDepositPage() {
|
||||
var page = parseInt(sessionStorage.getItem(depositPageStorageKey), 10);
|
||||
return isNaN(page) || page < 0 ? 0 : page;
|
||||
}
|
||||
function storeDepositPage(pageNumber) {
|
||||
sessionStorage.setItem(depositPageStorageKey, String(pageNumber));
|
||||
}
|
||||
window.storeCurrentDepositTablePage = function () {
|
||||
try {
|
||||
var table = $('#scroll-horizontal-datatable').DataTable();
|
||||
storeDepositPage(table.page());
|
||||
} catch (e) {
|
||||
// No-op when table is not available.
|
||||
}
|
||||
};
|
||||
function initDepositDescriptionTooltips() {
|
||||
var selector = '#scroll-horizontal-datatable [data-bs-toggle="tooltip"]';
|
||||
if (typeof bootstrap !== 'undefined' && typeof bootstrap.Tooltip !== 'undefined') {
|
||||
document.querySelectorAll(selector).forEach(function (el) {
|
||||
var existing = bootstrap.Tooltip.getInstance(el);
|
||||
if (existing) {
|
||||
existing.dispose();
|
||||
}
|
||||
new bootstrap.Tooltip(el, { container: 'body' });
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (typeof $.fn.tooltip === 'function') {
|
||||
var $targets = $('#scroll-horizontal-datatable [data-toggle="tooltip"]');
|
||||
$targets.tooltip('dispose');
|
||||
$targets.tooltip({ container: 'body' });
|
||||
}
|
||||
}
|
||||
var vdDepositTable = $('#scroll-horizontal-datatable').DataTable(nhanceMergeListDataTableOptions({
|
||||
// dom: "<'row'<'col-sm-2'f><'col-sm-10 text-right'B>>" +
|
||||
// "<'row'<'col-sm-12'tr>>" +
|
||||
@ -430,6 +760,17 @@ $vd_col_width_px = nhance_dt_column_widths_px($vd_header_labels, $vd_col_max_len
|
||||
myModal.show();
|
||||
}
|
||||
},
|
||||
{
|
||||
text: '<i class="mdi mdi-content-save"></i><span class=" btn-custom"> Save </span>',
|
||||
className: 'btn app-btn-primary mr-2 deposit-top-save-btn d-none',
|
||||
action: function () {
|
||||
if (typeof window.saveDepositInlineChanges === 'function') {
|
||||
window.saveDepositInlineChanges();
|
||||
} else {
|
||||
toastr.warning('Save handler is not ready yet.');
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
extend: 'collection',
|
||||
text: '<span class=" btn-custom"> Export </span><i class="mdi mdi-menu-down"></i>',
|
||||
@ -475,8 +816,27 @@ $vd_col_width_px = nhance_dt_column_widths_px($vd_header_labels, $vd_col_max_len
|
||||
<?php endfor; ?>
|
||||
],
|
||||
}));
|
||||
vdDepositTable.on('page.dt', function () {
|
||||
storeDepositPage(vdDepositTable.page());
|
||||
});
|
||||
initDepositDescriptionTooltips();
|
||||
vdDepositTable.on('draw', function () {
|
||||
initDepositDescriptionTooltips();
|
||||
});
|
||||
var selectedPage = getStoredDepositPage();
|
||||
if (selectedPage > 0 && selectedPage < vdDepositTable.page.info().pages) {
|
||||
vdDepositTable.page(selectedPage).draw('page');
|
||||
}
|
||||
nhanceListDataTableAfterInit();
|
||||
nhanceListDataTableBindAdjust(vdDepositTable);
|
||||
|
||||
$(document).on('click keydown', '.js-deposit-history-trigger', function (e) {
|
||||
if (e.type === 'keydown' && e.key !== 'Enter' && e.key !== ' ') {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
openDepositHistoryModal(this);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -584,6 +944,9 @@ $vd_col_width_px = nhance_dt_column_widths_px($vd_header_labels, $vd_col_max_len
|
||||
// Handle success response
|
||||
toastr.success("Transaction saved successfully", "Success");
|
||||
// Reload the page or perform other actions as needed
|
||||
if (typeof window.storeCurrentDepositTablePage === 'function') {
|
||||
window.storeCurrentDepositTablePage();
|
||||
}
|
||||
location.reload();
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
@ -615,6 +978,9 @@ $vd_col_width_px = nhance_dt_column_widths_px($vd_header_labels, $vd_col_max_len
|
||||
toastr.error('An unexpected error occurred. Please try again later.', 'Error');
|
||||
}
|
||||
|
||||
if (typeof window.storeCurrentDepositTablePage === 'function') {
|
||||
window.storeCurrentDepositTablePage();
|
||||
}
|
||||
location.reload(); // Uncomment if you need to reload the page on error
|
||||
}
|
||||
});
|
||||
@ -641,4 +1007,237 @@ $vd_col_width_px = nhance_dt_column_widths_px($vd_header_labels, $vd_col_max_len
|
||||
$(this).removeClass('is-invalid');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
var numericRegex = /^[0-9]+(\.[0-9]{1,2})?$/;
|
||||
|
||||
function formatAmount(value) {
|
||||
var num = parseFloat(value);
|
||||
if (isNaN(num)) {
|
||||
return '0.00';
|
||||
}
|
||||
return num.toFixed(2);
|
||||
}
|
||||
|
||||
function normalizeInput($input) {
|
||||
var value = ($input.val() || '').replace(/[^0-9.]/g, '');
|
||||
var parts = value.split('.');
|
||||
if (parts.length > 2) {
|
||||
value = parts[0] + '.' + parts.slice(1).join('');
|
||||
parts = value.split('.');
|
||||
}
|
||||
if (parts[1] && parts[1].length > 2) {
|
||||
value = parts[0] + '.' + parts[1].slice(0, 2);
|
||||
}
|
||||
$input.val(value);
|
||||
}
|
||||
|
||||
function setRowDisplay($row, creditAmount, debitAmount) {
|
||||
var $creditCell = $row.find('td[data-type="credit"]');
|
||||
var $debitCell = $row.find('td[data-type="debit"]');
|
||||
var creditNum = parseFloat(creditAmount) || 0;
|
||||
var debitNum = parseFloat(debitAmount) || 0;
|
||||
|
||||
$row.attr('data-credit', creditNum);
|
||||
$row.attr('data-debit', debitNum);
|
||||
$creditCell.find('.deposit-inline-input').val(creditNum > 0 ? formatAmount(creditNum) : '');
|
||||
$debitCell.find('.deposit-inline-input').val(debitNum > 0 ? formatAmount(debitNum) : '');
|
||||
}
|
||||
|
||||
function recalculateVisibleBalances() {
|
||||
var running = 0;
|
||||
var rows = $('#scroll-horizontal-datatable tbody tr').get().reverse();
|
||||
$(rows).each(function () {
|
||||
var $row = $(this);
|
||||
var credit = parseFloat($row.attr('data-credit')) || 0;
|
||||
var debit = parseFloat($row.attr('data-debit')) || 0;
|
||||
running += credit;
|
||||
running -= debit;
|
||||
$row.find('.deposit-balance-cell').text(formatAmount(running));
|
||||
});
|
||||
}
|
||||
|
||||
function getTopSaveButton() {
|
||||
return $('.deposit-top-save-btn');
|
||||
}
|
||||
|
||||
function hasPendingChanges() {
|
||||
var pending = false;
|
||||
$('#scroll-horizontal-datatable tbody tr').each(function () {
|
||||
var $row = $(this);
|
||||
var creditInputVal = ($row.find('td[data-type="credit"] .deposit-inline-input').val() || '').trim();
|
||||
var debitInputVal = ($row.find('td[data-type="debit"] .deposit-inline-input').val() || '').trim();
|
||||
var originalCredit = parseFloat($row.attr('data-credit')) || 0;
|
||||
var originalDebit = parseFloat($row.attr('data-debit')) || 0;
|
||||
var inputCredit = creditInputVal === '' ? 0 : parseFloat(creditInputVal);
|
||||
var inputDebit = debitInputVal === '' ? 0 : parseFloat(debitInputVal);
|
||||
|
||||
if (isNaN(inputCredit) || isNaN(inputDebit)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Math.abs(inputCredit - originalCredit) > 0.0001 || Math.abs(inputDebit - originalDebit) > 0.0001) {
|
||||
pending = true;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return pending;
|
||||
}
|
||||
|
||||
function updateTopSaveButtonVisibility() {
|
||||
var $saveBtn = getTopSaveButton();
|
||||
if (!$saveBtn.length) {
|
||||
return;
|
||||
}
|
||||
$saveBtn.toggleClass('d-none', !hasPendingChanges());
|
||||
}
|
||||
|
||||
$(document).on('input', '.deposit-inline-input', function () {
|
||||
var $input = $(this);
|
||||
normalizeInput($input);
|
||||
|
||||
updateTopSaveButtonVisibility();
|
||||
});
|
||||
|
||||
function collectChanges() {
|
||||
var changes = [];
|
||||
var hasValidationError = false;
|
||||
$('#scroll-horizontal-datatable tbody tr').each(function () {
|
||||
var $row = $(this);
|
||||
var creditInputVal = ($row.find('td[data-type="credit"] .deposit-inline-input').val() || '').trim();
|
||||
var debitInputVal = ($row.find('td[data-type="debit"] .deposit-inline-input').val() || '').trim();
|
||||
var originalCredit = parseFloat($row.attr('data-credit')) || 0;
|
||||
var originalDebit = parseFloat($row.attr('data-debit')) || 0;
|
||||
var hasCredit = creditInputVal !== '';
|
||||
var hasDebit = debitInputVal !== '';
|
||||
|
||||
if (!hasCredit && !hasDebit) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasCredit && hasDebit) {
|
||||
toastr.warning('Please fill only one of Credit or Debit per row.');
|
||||
hasValidationError = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
var type = hasCredit ? 'credit' : 'debit';
|
||||
var value = hasCredit ? creditInputVal : debitInputVal;
|
||||
var amount = parseFloat(value);
|
||||
var isChanged = (type === 'credit')
|
||||
? (Math.abs(amount - originalCredit) > 0.0001)
|
||||
: (Math.abs(amount - originalDebit) > 0.0001);
|
||||
|
||||
// Skip untouched rows (important for legacy 0/-ve values)
|
||||
if (!isChanged) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!numericRegex.test(value)) {
|
||||
toastr.warning('Enter a valid amount (up to 2 decimals).');
|
||||
hasValidationError = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (amount <= 0) {
|
||||
toastr.warning('Amount must be greater than 0.');
|
||||
hasValidationError = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
changes.push({
|
||||
rowId: $row.data('row-id'),
|
||||
type: type,
|
||||
amount: amount,
|
||||
$row: $row
|
||||
});
|
||||
});
|
||||
|
||||
if (hasValidationError) {
|
||||
return false;
|
||||
}
|
||||
return changes;
|
||||
}
|
||||
|
||||
window.saveDepositInlineChanges = function () {
|
||||
var changes = collectChanges();
|
||||
if (!Array.isArray(changes) || changes.length === 0) {
|
||||
if (changes !== false) {
|
||||
toastr.info('No changes to save.');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var clientId = '<?= esc((string) $clientData->id, 'js') ?>';
|
||||
var insurerId = '<?= esc((string) $insurerName->id, 'js') ?>';
|
||||
var cdAcPk = $('#cd_ac_pk').val();
|
||||
|
||||
var completed = 0;
|
||||
function saveNext(index) {
|
||||
if (index >= changes.length) {
|
||||
toastr.success('Saved ' + completed + ' change(s) successfully.');
|
||||
setTimeout(function () {
|
||||
if (typeof window.storeCurrentDepositTablePage === 'function') {
|
||||
window.storeCurrentDepositTablePage();
|
||||
}
|
||||
location.reload();
|
||||
}, 250);
|
||||
return;
|
||||
}
|
||||
|
||||
var change = changes[index];
|
||||
var transactionType = (change.type === 'credit') ? 'Credit' : 'Debit';
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '<?= base_url() . "client/update_deposit_amount" ?>',
|
||||
data: {
|
||||
deposit_id: change.rowId,
|
||||
amount: change.amount,
|
||||
transaction_type: transactionType,
|
||||
client_id: clientId,
|
||||
insurer_id: insurerId,
|
||||
cd_ac_pk: cdAcPk
|
||||
},
|
||||
success: function () {
|
||||
if (transactionType === 'Credit') {
|
||||
setRowDisplay(change.$row, change.amount, 0);
|
||||
} else {
|
||||
setRowDisplay(change.$row, 0, change.amount);
|
||||
}
|
||||
completed++;
|
||||
saveNext(index + 1);
|
||||
},
|
||||
error: function (xhr) {
|
||||
if (xhr.status === 400 && xhr.responseText) {
|
||||
try {
|
||||
var response = JSON.parse(xhr.responseText);
|
||||
toastr.error(response.message || 'Validation failed.');
|
||||
} catch (e) {
|
||||
toastr.error('Validation failed.');
|
||||
}
|
||||
} else if (xhr.status === 404) {
|
||||
toastr.error('Transaction not found for this account.');
|
||||
} else {
|
||||
toastr.error('Unable to update transaction right now.');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
saveNext(0);
|
||||
};
|
||||
|
||||
$(document).on('keydown', '.deposit-inline-input', function (e) {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
if (typeof window.saveDepositInlineChanges === 'function') {
|
||||
window.saveDepositInlineChanges();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
updateTopSaveButtonVisibility();
|
||||
});
|
||||
</script>
|
||||
Loading…
Reference in New Issue
Block a user