MERGE_TEST_MAIL_TEMPLATE

This commit is contained in:
Ubuntu 2026-06-03 09:43:59 +05:30
commit 8d16a02eb4
11 changed files with 1203 additions and 70 deletions

View File

@ -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]],

View File

@ -138,6 +138,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');
@ -241,7 +242,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");
});

View File

@ -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)
{

View File

@ -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',
],
]);
}
}

View File

@ -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 apacheSuperSetDemo()
{
@ -1218,7 +1228,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')
@ -1255,20 +1284,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',
],
]);
}

View File

@ -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')

View File

@ -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"

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>