MERGE_LIVE_BUG FIXES

This commit is contained in:
Ubuntu 2026-07-06 11:24:06 +05:30
commit 85885cc7aa
10 changed files with 673 additions and 117 deletions

View File

@ -69,7 +69,7 @@ class Filters extends BaseConfig
'before' => [
'HttpRequestLog' => ['except' => 'cli/*'],
'Cors',
'AclFilter' => ['except' => ['login', 'logout', 'auth/*', 'oauth2callback','claim-form-download', 'claims-feedback-form', 'autobookstackLogin','employeeRest/*','processjob','getCommission','downloadEmployeeEcardZip', 'downloadClaimFile/*', 'api/v1/*']],
'AclFilter' => ['except' => ['login', 'logout', 'auth/*', 'oauth2callback','claim-form-download', 'claims-feedback-form', 'autobookstackLogin','employeeRest/*','processjob','getCommission','downloadEmployeeEcardZip', 'downloadClaimFile/*', 'api/v1/*', 'cronCdLowBalanceAlert', 'sendCroneRemainderMail']],
'SecurityInputFilter' => ['except' => ['/client/notification/create','/ticket/crud_mail_template/*','test_mail','leads/sendMail', 'ticket/reply'] ],
'GlobalPostFileUploadGuard'
// 'csrf',

View File

@ -56,6 +56,7 @@ $routes->get("testMailAttachments", "ClientController::testMailAttachments");
$routes->get("updatePolicyTermsKey", "ClientController::updatePolicyTermsKey");
$routes->get("updateRemainderDate", "ClientController::updateRemainderDate");
$routes->get("sendCroneRemainderMail", "DashboardController::sendCroneRemainderMail");
$routes->get("cronCdLowBalanceAlert", "MasterController::cronCdLowBalanceAlert");
$routes->get("sendextraparam", "ClientController::sendextraparam");
$routes->get("updateRenewalData", "ClientController::updateRenewalData");
$routes->get("updateRenewalDataNotExistingClient", "ClientController::updateRenewalDataNotExistingClient");
@ -554,6 +555,7 @@ $routes->group("leads", ["filter" => "authMVC"], function ($routes) {
$routes->get("exportQCRandRFQ/(:any)", "LeadsController::exportQCRandRFQ/$1");
$routes->get("featchLeadDataAndInsertClient/(:any)", "LeadsController::featchLeadDataAndInsertClient/$1");
$routes->get("featchClientPolicyFromLead/(:any)", "LeadsController::featchClientPolicyFromLead/$1");
$routes->get("getLeadPolicyTerms/(:num)", "LeadsController::getLeadPolicyTerms/$1");
});
$routes->group("rfq", ["filter" => "authMVC"], function ($routes) {
@ -595,6 +597,7 @@ $routes->cli('cli/check_env', 'MasterController::checkEnv');
$routes->cli('cli/reset-token-timeout', 'RestAuthenticationController::resetTokenTimeOut');
$routes->cli('cli/enrollOpendAndClose', 'DashboardController::updatePolicyEnrollmentStatus');
$routes->cli("cli/sendCroneRemainderMail", "DashboardController::sendCroneRemainderMail");
$routes->cli("cli/cronCdLowBalanceAlert", "MasterController::cronCdLowBalanceAlert");
$routes->cli('cli/update-emp-policy-status', 'ClientController::updateEmpAndPolicyStatus');
$routes->cli('cli/insurerRFQRemainder', 'LeadsController::remainderForQcr');
$routes->cli('cli/sendMailWithAutoQuery','TicketController::sendMailWithAutoQuery');

View File

@ -3061,12 +3061,23 @@ class EmployeeRestController extends AdminController
->where('client_branch_id', $this->request->getGet('client_branch_id'))
->where('is_active', 1)->findAll();
$employeeSelfData = $this->employeeModel->where('emp_code', $this->request->getGet('emp_code'))
->where('client_id', $this->request->getGet('client_id'))
->where('client_branch_id', $this->request->getGet('client_branch_id'))
->where('family_floater_key', 'self')->where('is_active', 1)
->orderBy('id', 'DESC')
->get()->getRow();
// get self data corporate email ore mobile no should not be null
$employeeSelfData = $this->employeeModel
->where('emp_code', $this->request->getGet('emp_code'))
->where('client_id', $this->request->getGet('client_id'))
->where('client_branch_id', $this->request->getGet('client_branch_id'))
->where('family_floater_key', 'self')
->where('is_active', 1)
->groupStart()
->where('email_corporate IS NOT NULL', null, false)
->orWhere('mobile IS NOT NULL', null, false)
->groupEnd()
->orderBy('id', 'DESC')
->get()
->getRow();
// dd(db_connect()->getLastQuery()->getQuery());
// dd($employeeSelfData->id); //employee id
$employeeName = $employeeSelfData->name ?? "";

View File

@ -4129,16 +4129,17 @@ class LeadsController extends BaseController
return $this->respond(['status' => false, 'message' => 'Failed to create Client', 'data' => null], 200);
}
$result = $this->createClientWithLeadData($data);
$result = $this->createClientWithMatchingLeadsPolicies($data);
if ($result) {
$policy_data = $this->clientPolicyModel->where('client_id', $result)->where('is_active', 1)->first();
return $this->respond([
'status' => true,
'message' => 'New Client created successfully',
'client_id' => $result,
'data' => $data,
'client_policy_id' => $policy_data['id'] ?? null,
'status' => true,
'message' => 'New Client created successfully',
'client_id' => $result['client_id'],
'data' => $data,
'client_policy_id' => $result['primary_policy_id'],
'client_policy_ids' => $result['policy_ids'],
'lead_ids' => $result['lead_ids'],
], 200);
}
@ -4150,6 +4151,110 @@ class LeadsController extends BaseController
}
}
/**
* Create one client + branch + contact from the given lead, then create a policy
* for every active lead that shares the same client_name, client_short_name,
* branch_name and branch_code and does not yet have a policy.
*
* @return array{client_id: int, branch_id: int, primary_policy_id: int|null, policy_ids: int[], lead_ids: int[]}|false
*/
public function createClientWithMatchingLeadsPolicies(array $data)
{
try {
$matchingLeads = $this->getMatchingLeadsForClientInsert($data);
if (empty($matchingLeads)) {
return false;
}
$client_id = $this->clientModel->insert($this->prepareClientData($data));
if (! $client_id) {
return false;
}
$branch_id = $this->clientBranchModel->insert($this->prepareClientBranchData($data, $client_id));
if (! $branch_id) {
return false;
}
$this->levelContactModel->insert($this->prepareContactData($data, $branch_id));
$policy_ids = [];
$lead_ids = [];
$primary_policy_id = null;
$primary_lead_id = (int) $data['id'];
foreach ($matchingLeads as $lead) {
$lead_ids[] = (int) $lead['id'];
$policy_id = $this->createClientPolicyWithLeadData($lead, $client_id, $branch_id);
// Ensure client link is set even if policy insert fails for a lead.
$this->leadsModel->where('id', $lead['id'])->set('is_client_created', $client_id)->update();
if ($policy_id) {
$policy_ids[] = (int) $policy_id;
if ((int) $lead['id'] === $primary_lead_id) {
$primary_policy_id = (int) $policy_id;
}
}
}
if (empty($policy_ids)) {
return false;
}
if ($primary_policy_id === null) {
$primary_policy_id = $policy_ids[0];
}
return [
'client_id' => $client_id,
'branch_id' => $branch_id,
'primary_policy_id' => $primary_policy_id,
'policy_ids' => $policy_ids,
'lead_ids' => $lead_ids,
];
} catch (\Throwable $e) {
$this->myLogger->logme('error', 'createClientWithMatchingLeadsPolicies failed');
return false;
}
}
/**
* Active leads with the same client/branch identity that still need a policy.
*/
private function getMatchingLeadsForClientInsert(array $data): array
{
$leads = $this->leadsModel
->where('is_active', 1)
->where('client_name', $data['client_name'] ?? null)
->where('client_short_name', $data['client_short_name'] ?? null)
->where('branch_name', $data['branch_name'] ?? null)
->where('branch_code', $data['branch_code'] ?? null)
->groupStart()
->where('is_policy_created', null)
->orWhere('is_policy_created', '')
->groupEnd()
->orderBy('id', 'asc')
->findAll();
// Always include the primary lead if it was filtered out somehow.
$primaryId = (int) ($data['id'] ?? 0);
$found = false;
foreach ($leads as $lead) {
if ((int) $lead['id'] === $primaryId) {
$found = true;
break;
}
}
if (! $found && $primaryId > 0) {
array_unshift($leads, $data);
}
return $leads;
}
public function createClientWithLeadData($data)
{
try {
@ -4283,21 +4388,128 @@ class LeadsController extends BaseController
// }
}
if ($data['lead_form_type'] == 1) {
$client_policy_data['policy_terms'] = $this->preparePolicyTermsFromRFQ($data);
} else {
$placementJson = $this->getPlacementJson($data);
if ($placementJson) {
$client_policy_data['placement_json'] = $placementJson;
$client_policy_data['policy_terms'] = $this->convertNonEbQCRJsonToPolicyTerms(json_decode($placementJson, true));
}
$termsData = $this->buildPolicyTermsFromLead($data);
if ($termsData['policy_terms'] !== null) {
$client_policy_data['policy_terms'] = $termsData['policy_terms'];
}
if ($termsData['placement_json'] !== null) {
$client_policy_data['placement_json'] = $termsData['placement_json'];
}
// print_r($client_policy_data); die;
return $client_policy_data;
}
/**
* Build policy_terms (and placement_json for non-EB) from lead RFQ/QCR data.
*
* @return array{policy_terms: ?string, placement_json: ?string}
*/
private function buildPolicyTermsFromLead(array $data): array
{
$policy_terms = null;
$placement_json = null;
if ((int) ($data['lead_form_type'] ?? 0) === 1) {
$policy_terms = $this->preparePolicyTermsFromRFQ($data);
} else {
$placement_json = $this->getPlacementJson($data);
if ($placement_json) {
$policy_terms = $this->convertNonEbQCRJsonToPolicyTerms(json_decode($placement_json, true));
}
}
return [
'policy_terms' => $policy_terms,
'placement_json' => $placement_json,
];
}
public function getLeadPolicyTerms($lead_id)
{
try {
$data = $this->leadsModel
->where('leads.id', $lead_id)
->where('leads.is_active', 1)
->first();
if (! $data) {
return $this->respond([
'status' => false,
'message' => 'Lead not found or inactive',
'data' => null,
], 200);
}
$termsData = $this->buildPolicyTermsFromLead($data);
$policy_terms = $termsData['policy_terms'];
if ($policy_terms === null) {
return $this->respond([
'status' => false,
'message' => 'Policy terms could not be generated. Check RFQ/QCR and proposal data.',
'data' => null,
], 200);
}
$client_policy_id = (int) ($this->request->getGet('client_policy_id') ?? 0);
$updated = false;
if ($client_policy_id > 0) {
$client_policy = $this->clientPolicyModel
->where('id', $client_policy_id)
->where('is_active', 1)
->first();
if (! $client_policy) {
return $this->respond([
'status' => false,
'message' => 'Client policy not found or inactive',
'data' => null,
], 200);
}
$updateData = [
'policy_terms' => $policy_terms,
];
if ($termsData['placement_json'] !== null) {
$updateData['placement_json'] = $termsData['placement_json'];
}
$updated = (bool) $this->clientPolicyModel->update($client_policy_id, $updateData);
if (! $updated) {
return $this->respond([
'status' => false,
'message' => 'Failed to update policy terms',
'data' => null,
], 200);
}
}
return $this->respond([
'status' => true,
'message' => $updated
? 'Policy terms updated successfully'
: 'Policy terms fetched successfully',
'lead_id' => (int) $lead_id,
'client_policy_id' => $client_policy_id > 0 ? $client_policy_id : null,
'updated' => $updated,
'lead_form_type' => (int) ($data['lead_form_type'] ?? 0),
'policy_type_id' => (int) ($data['policy_type_id'] ?? 0),
'policy_terms' => json_decode($policy_terms, true),
], 200);
} catch (\Throwable $e) {
$this->myLogger->logme('error', 'getLeadPolicyTerms failed');
return $this->respond([
'status' => false,
'message' => 'Internal server error',
'error' => $e->getMessage(),
], 500);
}
}
private function preparePolicyTermsFromRFQ($data)
{
try {

View File

@ -2284,6 +2284,49 @@ class MasterController extends AdminController
'greater_than_equal_to' => 'Opening amount cannot be negative'
]
],
'cd_balance_low_alert_amount' => [
'rules' => 'permit_empty|numeric|greater_than_equal_to[0]',
'errors' => [
'numeric' => 'CD balance low alert amount must be numeric',
'greater_than_equal_to' => 'CD balance low alert amount cannot be negative'
]
],
];
if (!$this->validate($rules)) {
return $this->response->setStatusCode(400)->setJSON([
'status' => false,
'message' => 'Input validation failed',
'code' => 400,
'errors' => $this->validator->getErrors()
]);
}
} else {
$existingRecord = $this->CDMasterModel->where('id', $id)->where('is_active', 1)->first();
if ($existingRecord) {
foreach (['client_id', 'insurer_id', 'insurer_branch_id', 'opening_date', 'cd_ac_no', 'opening_bal'] as $field) {
if (! isset($sanitized_post_data[$field]) || $sanitized_post_data[$field] === '') {
$sanitized_post_data[$field] = $existingRecord[$field] ?? null;
}
}
}
$rules = [
'opening_bal' => [
'rules' => 'required|numeric|greater_than_equal_to[0]',
'errors' => [
'required' => 'Opening amount is required',
'numeric' => 'Opening amount must be numeric',
'greater_than_equal_to' => 'Opening amount cannot be negative'
]
],
'cd_balance_low_alert_amount' => [
'rules' => 'permit_empty|numeric|greater_than_equal_to[0]',
'errors' => [
'numeric' => 'CD balance low alert amount must be numeric',
'greater_than_equal_to' => 'CD balance low alert amount cannot be negative'
]
],
];
if (!$this->validate($rules)) {
return $this->response->setStatusCode(400)->setJSON([
@ -2298,6 +2341,9 @@ class MasterController extends AdminController
$date = (string) ($sanitized_post_data['opening_date'] ?? '');
$sanitized_post_data['opening_date'] = date('Y-m-d', strtotime($date));
if (array_key_exists('cd_balance_low_alert_amount', $sanitized_post_data) && $sanitized_post_data['cd_balance_low_alert_amount'] === '') {
$sanitized_post_data['cd_balance_low_alert_amount'] = null;
}
$this->myLogger->logme("error", 'Formatted opening_date: ' . $data['opening_date']);
$loggedInUserID = get_session_userid();
@ -2463,6 +2509,14 @@ class MasterController extends AdminController
'greater_than_equal_to' => 'Opening amount cannot be negative'
]
],
'cd_balance_low_alert_amount' => [
'rules' => 'permit_empty|numeric|greater_than_equal_to[0]',
'errors' => [
'numeric' => 'CD balance low alert amount must be numeric',
'greater_than_equal_to' => 'CD balance low alert amount cannot be negative'
]
],
];
if (!$this->validate($rules)) {
return $this->response->setStatusCode(400)->setJSON([
@ -2477,6 +2531,9 @@ class MasterController extends AdminController
$id = $sanitized_post_data['PrimaryKey'] ?? null;
$date = ((string) $sanitized_post_data['opening_date']) ?? null;
$sanitized_post_data['opening_date'] = date('Y-m-d', strtotime($date));
if (array_key_exists('cd_balance_low_alert_amount', $sanitized_post_data) && $sanitized_post_data['cd_balance_low_alert_amount'] === '') {
$sanitized_post_data['cd_balance_low_alert_amount'] = null;
}
if ($data) {
@ -3879,5 +3936,211 @@ class MasterController extends AdminController
return $newName;
}
/**
* Cron: Check CD balances (cash_deposit via cd_ac_pk) against cd_balance_low_alert_amount
* and notify account managers when balance is low.
*/
public function cronCdLowBalanceAlert()
{
log_message('error', 'cronCdLowBalanceAlert: started');
try {
$cdMasters = $this->CDMasterModel->getCdMastersWithLowAlertThreshold();
log_message('error', 'cronCdLowBalanceAlert: CD masters with alert threshold count = ' . count($cdMasters));
$lowBalanceAccounts = [];
foreach ($cdMasters as $cdMaster) {
$cdAcPk = (int) ($cdMaster['id'] ?? 0);
$threshold = (float) ($cdMaster['cd_balance_low_alert_amount'] ?? 0);
if ($cdAcPk <= 0 || $threshold <= 0) {
continue;
}
$lastDeposit = $this->clientDepositModel
->select('balance')
->where('cd_ac_pk', $cdAcPk)
->where('is_active', 1)
->orderBy('id', 'DESC')
->first();
if (! empty($lastDeposit)) {
$currentBalance = (float) ($lastDeposit['balance'] ?? 0);
} else {
$currentBalance = (float) ($cdMaster['opening_bal'] ?? 0);
}
if ($currentBalance >= $threshold) {
continue;
}
$lowBalanceAccounts[] = array_merge($cdMaster, [
'current_balance' => $currentBalance,
'alert_amount' => $threshold,
]);
}
if (empty($lowBalanceAccounts)) {
$response = [
'status' => true,
'message' => 'No CD accounts below the configured low-balance alert amount.',
'low_balance_count' => 0,
'account_manager_mails_sent' => 0,
'data' => [],
'errors' => [],
];
log_message('error', 'cronCdLowBalanceAlert: ' . json_encode($response));
return $this->respond($response, 200);
}
$accountManagerMailsSent = 0;
$mailErrors = [];
$data = [];
foreach ($lowBalanceAccounts as $account) {
$result = $this->sendCdLowBalanceAccountManagerAlert($account);
$accountManagerMailsSent += (int) ($result['sent'] ?? 0);
if (! empty($result['errors'])) {
$mailErrors = array_merge($mailErrors, $result['errors']);
}
if (! empty($result['data'])) {
$data = array_merge($data, $result['data']);
}
}
$response = [
'status' => true,
'message' => 'CD low balance alert cron completed.',
'low_balance_count' => count($lowBalanceAccounts),
'account_manager_mails_sent' => $accountManagerMailsSent,
'data' => $data,
'errors' => $mailErrors,
];
log_message('error', 'cronCdLowBalanceAlert: ' . json_encode($response));
return $this->respond($response, 200);
} catch (\Throwable $e) {
$response = [
'status' => false,
'message' => 'CD low balance alert cron failed.',
'low_balance_count' => 0,
'account_manager_mails_sent' => 0,
'data' => [],
'errors' => [$e->getMessage()],
];
log_message('error', 'cronCdLowBalanceAlert Exception: ' . $e->getMessage());
log_message('error', 'cronCdLowBalanceAlert: ' . json_encode($response));
return $this->respond($response, 500);
}
}
private function sendCdLowBalanceAccountManagerAlert(array $account): array
{
$clientId = (int) ($account['client_id'] ?? 0);
$cdAcPk = (int) ($account['id'] ?? 0);
$cdAcNo = (string) ($account['cd_ac_no'] ?? '');
if ($clientId <= 0) {
log_message('error', 'cronCdLowBalanceAlert ACM mail: skipped, invalid client_id for cd_ac_pk=' . $cdAcPk);
return ['sent' => 0, 'errors' => [], 'data' => []];
}
$recipients = $this->getAccountManagerRecipients($clientId);
if (empty($recipients)) {
$error = "Client {$clientId}: No account manager found.";
log_message('error', 'cronCdLowBalanceAlert ACM mail: ' . $error);
return ['sent' => 0, 'errors' => [$error], 'data' => []];
}
$sent = 0;
$errors = [];
$data = [];
$subject = 'CD Low Balance Alert - ' . ($account['client_name'] ?? 'Client');
foreach ($recipients as $recipient) {
$acmEmail = (string) ($recipient['email'] ?? '');
$acmName = (string) ($recipient['name'] ?? 'Account Manager');
$message = $this->buildCdLowBalanceAccountManagerMailContent($account, $acmName);
$mailResponse = MailHelper::send_email([
'mail' => $acmEmail,
'subject' => $subject,
'message' => $message,
'common' => [
'mail_type' => 'account_manager_cd_low_balance_alert',
'client_id' => $clientId,
],
]);
$decoded = is_string($mailResponse) ? json_decode($mailResponse, true) : $mailResponse;
$mailStatus = is_array($decoded) && ($decoded['status'] ?? '') === 'success' ? 'success' : 'failed';
$data[] = [
'client_id' => $clientId,
'cd_ac_pk' => $cdAcPk,
'cd_ac_no' => $cdAcNo,
'acm_name' => $acmName,
'acm_email' => $acmEmail,
'mail_status' => $mailStatus,
];
if ($mailStatus === 'success') {
$sent++;
} else {
$errors[] = "Client {$clientId}: Failed to send alert to {$acmEmail}.";
}
}
return ['sent' => $sent, 'errors' => $errors, 'data' => $data];
}
/**
* @return array<int, array{name: string, email: string}>
*/
private function getAccountManagerRecipients(int $clientId): array
{
$rows = $this->insurerRMModel
->select('user_profiles.first_name, user_profiles.email')
->join('user_profiles', 'user_profiles.id = client_rm.user_id')
->where('client_rm.client_id', $clientId)
->where('client_rm.level', 3)
->where('client_rm.is_active', 1)
->where('user_profiles.is_active', 1)
->findAll();
$recipients = [];
$seenEmails = [];
foreach ($rows as $row) {
$email = strtolower(trim((string) ($row['email'] ?? '')));
if ($email === '' || isset($seenEmails[$email])) {
continue;
}
$seenEmails[$email] = true;
$recipients[] = [
'name' => trim((string) ($row['first_name'] ?? 'Account Manager')) ?: 'Account Manager',
'email' => (string) $row['email'],
];
}
return $recipients;
}
private function buildCdLowBalanceAccountManagerMailContent(array $account, string $recipientName): string
{
return '<p>Dear ' . esc($recipientName) . ',</p>'
. '<p>The CD account balance for <strong>' . esc((string) ($account['client_name'] ?? '')) . '</strong> has fallen below the configured alert threshold.</p>'
. '<table border="1" cellpadding="8" cellspacing="0" style="border-collapse:collapse;">'
. '<tr><td><strong>Insurer</strong></td><td>' . esc((string) ($account['insurer_name'] ?? '')) . '</td></tr>'
. '<tr><td><strong>Insurer Branch</strong></td><td>' . esc((string) ($account['insurer_branch_name'] ?? '')) . '</td></tr>'
. '<tr><td><strong>CD Account No</strong></td><td>' . esc((string) ($account['cd_ac_no'] ?? '')) . '</td></tr>'
. '<tr><td><strong>Current Balance</strong></td><td>' . esc(number_format((float) ($account['current_balance'] ?? 0), 2, '.', '')) . '</td></tr>'
. '<tr><td><strong>Alert Amount</strong></td><td>' . esc(number_format((float) ($account['alert_amount'] ?? 0), 2, '.', '')) . '</td></tr>'
. '</table>'
. '<p>Please review the CD account and take the required action.</p>';
}
}

View File

@ -2134,7 +2134,7 @@ class TicketController extends BaseController
//send mail to the head for rejected ticket approvel
if($ticket_data['claim_status_id'] == 8 && $ticket_data['is_head_approved'] == 0){
$this->sendMailToTheHead($ticket_data);
$this->sendMailToTheHead($ticket_id);
}else{
$this->myLogger->logme('error', "Head Mail can't be sent");
$this->myLogger->logme('error', "CLAIM STATUS ID : {data}", ['data' => $ticket_data['claim_status_id']]);
@ -3096,9 +3096,11 @@ class TicketController extends BaseController
// -------------------------------------------------------------------------------------------------------------------------
// function for Reject ticket Head Approvel mail sent function
public function sendMailToTheHead($ticket_data)
public function sendMailToTheHead($ticket_id)
{
$this->myLogger->logme('error', "sendMailToTheHead Function Called");
$ticket_data = $this->ticketMasterModel->getTicketDataByTicketID($ticket_id);
if(!empty($ticket_data)){
// print_r($ticket_data); die;

View File

@ -20,6 +20,7 @@ class CDMasterModel extends Model
'insurer_branch_id',
'cd_ac_no',
'opening_bal',
'cd_balance_low_alert_amount',
'opening_date',
'created_at',
'updated_at',
@ -110,4 +111,26 @@ class CDMasterModel extends Model
$result = $query->getResultArray();
return $result;
}
/**
* Active CD master rows that have a low-balance alert threshold configured.
*
* @return array<int, array<string, mixed>>
*/
public function getCdMastersWithLowAlertThreshold(): array
{
return $this->db->table('cd_master')
->select('cd_master.*, clients.client_name, clients.short_name, insurers.name AS insurer_name, insurer_branch.branch_name AS insurer_branch_name')
->join('clients', 'clients.id = cd_master.client_id')
->join('insurers', 'insurers.id = cd_master.insurer_id')
->join('insurer_branch', 'insurer_branch.id = cd_master.insurer_branch_id', 'left')
->where('cd_master.is_active', 1)
->where('clients.is_active', 1)
->where('insurers.is_active', 1)
->where('cd_master.cd_balance_low_alert_amount IS NOT NULL', null, false)
->where('cd_master.cd_balance_low_alert_amount >', 0)
->orderBy('cd_master.id', 'desc')
->get()
->getResultArray();
}
}

View File

@ -86,6 +86,21 @@
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<label for="cd_balance_low_alert_amount">CD Balance Low Alert Amount</label>
<input type="text" class="form-control" id="cd_balance_low_alert_amount" name="cd_balance_low_alert_amount"
inputmode="decimal"
autocomplete="off"
onkeypress="return cdAmountNumberOnly(event)"
data-parsley-pattern="^(\d+(\.\d{1,2})?)?$"
data-parsley-pattern-message="Only numbers are allowed."
data-parsley-min="0"
data-parsley-min-message="CD balance low alert amount cannot be negative."
data-parsley-trigger="keyup">
</div>
</div>
</div>
<div id="cdButtonWrapper" class="form-group text-right m-b-0">
@ -103,6 +118,33 @@
<script>
function cdAmountNumberOnly(event) {
var charcode = event.which || event.keyCode;
if (charcode === 8 || charcode === 9 || charcode === 37 || charcode === 39) {
return true;
}
if (charcode >= 48 && charcode <= 57) {
return true;
}
if (charcode === 46 && event.target.value.indexOf('.') === -1) {
return true;
}
return false;
}
function sanitizeCdAmountInput(input) {
var value = input.value.replace(/[^\d.]/g, '');
var parts = value.split('.');
if (parts.length > 2) {
value = parts[0] + '.' + parts.slice(1).join('');
parts = value.split('.');
}
if (parts.length > 1) {
value = parts[0] + '.' + parts[1].slice(0, 2);
}
input.value = value;
}
//checking and setting this is CD master page or not
var isCdMasterPage = <?php echo isset($CD_Master_Data) ? 'true' : 'false'; ?>;
console.log("isCdMasterPage", isCdMasterPage);
@ -156,6 +198,13 @@
$('#insurer_id_for_cd').select2();
$('#insurer_branch_id').select2();
}
$('#cd_balance_low_alert_amount').on('input paste', function() {
var input = this;
setTimeout(function() {
sanitizeCdAmountInput(input);
}, 0);
});
})
$('#cd_ac_no_for_cd_master').keyup(function(){
@ -209,6 +258,12 @@
}
let formData = new FormData($('#CDMasterForm')[0]);
var $disabledInputs = $('#CDMasterForm').find('input:disabled, select:disabled, textarea:disabled');
$disabledInputs.each(function() {
if (this.name) {
formData.set(this.name, $(this).val());
}
});
var openingDate = formData.get('opening_date');
if (openingDate) {
formData.set('opening_date', cdOpeningDateForSubmit(openingDate));
@ -281,7 +336,6 @@
} else {
toastr.error('An unexpected error occurred. Please try again later.', 'Error');
}
toastr.error('An error occurred while adding the CD account number.', 'Error');
}
});
});
@ -294,6 +348,7 @@
$('#opening_date').val('');
$('#cd_ac_no_for_cd_master').val('');
$('#opening_bal').val('');
$('#cd_balance_low_alert_amount').val('');
$('#CDMasterForm').parsley().reset();
$('#cd_ac_no_for_cd_master').prop("disabled", false);
$('#opening_bal').prop("disabled", false);

View File

@ -1,7 +1,7 @@
<?php
helper('datatable_view');
$cdm_header_labels = [
'S.No', 'Client Name', 'Insurer Name', 'Insurer Branch Name', 'Opening Date', 'CD Account No', 'Opening Amount', 'Date/User', 'Action',
'S.No', 'Client Name', 'Insurer Name', 'Insurer Branch Name', 'Opening Date', 'CD Account No', 'Opening Amount', 'CD Balance Low Alert Amount', 'Date/User', 'Action',
];
$cdm_col_count = count($cdm_header_labels);
$cdm_col_max_len = array_fill(0, $cdm_col_count, 0);
@ -19,29 +19,23 @@ foreach ($CD_Master_Data as $index => $row) {
$cdm_col_max_len[4] = max($cdm_col_max_len[4], mb_strlen($od));
$cdm_col_max_len[5] = max($cdm_col_max_len[5], mb_strlen((string) ($row['cd_ac_no'] ?? '')));
$cdm_col_max_len[6] = max($cdm_col_max_len[6], mb_strlen((string) ($row['opening_bal'] ?? '')));
$cdm_col_max_len[7] = max($cdm_col_max_len[7], mb_strlen((string) ($row['cd_balance_low_alert_amount'] ?? '')));
$du = ! empty($row['created_at'])
? (date('d/m/Y h:i A', strtotime((string) $row['created_at'])) . ' by ' . ($row['user_name'] ?? ''))
: '';
$cdm_col_max_len[7] = max($cdm_col_max_len[7], mb_strlen($du));
$cdm_col_max_len[8] = max($cdm_col_max_len[8], 4);
$cdm_col_max_len[8] = max($cdm_col_max_len[8], mb_strlen($du));
$cdm_col_max_len[9] = max($cdm_col_max_len[9], 4);
}
$cdm_col_min_px = [48, 140, 120, 140, 100, 120, 100, 200, 72];
$cdm_col_max_px = [64, 400, 320, 360, 120, 200, 160, 480, 88];
$cdm_col_min_px = [72, 140, 120, 140, 100, 120, 100, 160, 200, 72];
$cdm_col_max_px = [88, 400, 320, 360, 120, 200, 160, 220, 480, 88];
$cdm_col_width_px = nhance_dt_column_widths_px($cdm_header_labels, $cdm_col_max_len, $cdm_col_min_px, $cdm_col_max_px);
?>
<style>
.col-12 {
max-width: 98% !important;
}
.column-header {margin-right: 10px;}
.dataTables_length label {height: 21px !important;}
</style>
<style>
@ -75,10 +69,25 @@ $cdm_col_width_px = nhance_dt_column_widths_px($cdm_header_labels, $cdm_col_max_
font-size: 13px;
line-height: 1.25;
}
#cd-master-list-table_wrapper .dataTables_scrollHead table thead th,
#cd-master-list-table thead th {
padding: 5px 2.35rem 5px 11px !important;
padding: 6px 1.5rem 6px 11px !important;
font-size: 13px;
line-height: 1.25;
height: auto !important;
white-space: nowrap !important;
vertical-align: middle !important;
}
#cd-master-list-table_wrapper .dataTables_scrollHead table thead th:first-child,
#cd-master-list-table thead th:first-child {
padding-right: 1.1rem !important;
}
#cd-master-list-table_wrapper .dataTables_scrollHead table thead th:last-child,
#cd-master-list-table thead th:last-child {
padding-right: 1.35rem !important;
}
#cd-master-list-table_wrapper {
overflow-x: auto;
}
</style>
@ -86,17 +95,6 @@ $cdm_col_width_px = nhance_dt_column_widths_px($cdm_header_labels, $cdm_col_max_
<div class="col-12">
<div class="card">
<div class="card-body">
<!-- <div class="row" style="margin-bottom:1rem;">
<div class="col-6" style="align-self: center;">
<h4 style="position: relative;">CD Master</h4>
</div>
<div class="col-6" style="text-align: right; position: relative;top: 56px;">
<!- <button type="button" id="btnAdd" class="btn btn-primary waves-effect waves-light"
data-toggle="modal" data-target="#con-close-modal" data-placement="top" title="Add"
data-trigger="hover">ADD</button> ->
</div>
</div> -->
<table data-custom-table-css="table" data-nhance-list-dt="1" id="cd-master-list-table" class="table mb-0 nowrap" cellspacing="0">
<thead class="bg-light">
<tr>
@ -107,12 +105,13 @@ $cdm_col_width_px = nhance_dt_column_widths_px($cdm_header_labels, $cdm_col_max_
<th class="font-weight-medium">Opening Date&nbsp;</th>
<th class="font-weight-medium">CD Account No&nbsp;</th>
<th class="font-weight-medium">Opening Amount&nbsp;</th>
<th class="font-weight-medium">CD Balance Low Alert Amount&nbsp;</th>
<th class="font-weight-medium">Date/User&nbsp;</th>
<th class="font-weight-medium text-center">Action&nbsp;</th>
</tr>
</thead>
<tbody>
<?php foreach($CD_Master_Data as $index => $row){ ?>
<?php foreach ($CD_Master_Data as $index => $row) { ?>
<tr>
<td><?= $index + 1; ?></td>
<td><?php echo $row['client_name']; ?>( <?= $row['short_name'] ?> )</td>
@ -121,20 +120,21 @@ $cdm_col_width_px = nhance_dt_column_widths_px($cdm_header_labels, $cdm_col_max_
<td><?php echo date('d/m/Y', strtotime($row['opening_date'])); ?></td>
<td><?php echo $row['cd_ac_no']; ?></td>
<td><?php echo $row['opening_bal']; ?></td>
<td><?php echo date('d/m/Y h:i A', strtotime($row['created_at'])) ?> <br>by <?php echo $row['user_name']; ?></td>
<td><?php echo ($row['cd_balance_low_alert_amount'] ?? '') !== '' && ($row['cd_balance_low_alert_amount'] ?? null) !== null ? $row['cd_balance_low_alert_amount'] : '&nbsp;'; ?></td>
<td><?php echo date('d/m/Y h:i A', strtotime($row['created_at'])); ?> by <?php echo $row['user_name']; ?></td>
<td class="text-center table-action-cell">
<div class="btn-group dropdown">
<a href="javascript: void(0);"class="dropdown-toggle arrow-none btn btn-light btn-sm" data-toggle="dropdown" aria-expanded="false"><i class="mdi mdi-dots-horizontal"></i></a>
<a href="javascript: void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm" data-toggle="dropdown" aria-expanded="false"><i class="mdi mdi-dots-horizontal"></i></a>
<div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item btnEdit" data-id="<?= $row['id'];?>" data-toggle="modal" data-target="#con-close-modal"> <i class="mdi mdi-pencil mr-2 text-muted font-18 vertical-middle"></i>Edit</a>
<?php if($row['cd_ac_no_count'] == 0) { ?>
<a class="dropdown-item" data-id="<?= $row['id'];?>" onclick="removeCDMaster(this)"><i class="mdi mdi-delete mr-2 text-muted font-18 vertical-middle"></i>Delete</a>
<a class="dropdown-item btnEdit" data-id="<?= $row['id']; ?>" data-toggle="modal" data-target="#con-close-modal"> <i class="mdi mdi-pencil mr-2 text-muted font-18 vertical-middle"></i>Edit</a>
<?php if ($row['cd_ac_no_count'] == 0) { ?>
<a class="dropdown-item" data-id="<?= $row['id']; ?>" onclick="removeCDMaster(this)"><i class="mdi mdi-delete mr-2 text-muted font-18 vertical-middle"></i>Delete</a>
<?php } ?>
</div>
</div>
</td>
</tr>
<?php } ?>
<?php } ?>
</tbody>
</table>
</div>
@ -151,10 +151,7 @@ $cdm_col_width_px = nhance_dt_column_widths_px($cdm_header_labels, $cdm_col_max_
nhanceListDataTableBeforeInit();
var table = $('#cd-master-list-table').DataTable(nhanceMergeListDataTableOptions({
// dom: "<'row'<'col-sm-6'f><'col-sm-6 text-right'B>>" +
// "<'row'<'col-sm-12'tr>>" +
// "<'row'<'col-sm-5'i><'col-sm-7'p>>",
dom: "<'row'<'col-sm-6'f><'col-sm-6 text-right'B>>" +
dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
"<'row'<'col-sm-12'tr>>" +
"<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>",
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
@ -185,7 +182,8 @@ $cdm_col_width_px = nhance_dt_column_widths_px($cdm_header_labels, $cdm_col_max_
text: '<i class="mdi mdi-file-excel " ></i><span class=" btn-custom"> EXCEL </span>',
sheetName: 'CD-Master-List',
exportOptions: {
orthogonal: 'sort'
orthogonal: 'sort',
columns: ':not(:last-child)'
},
className: 'app-btn-primary ',
}
@ -216,34 +214,7 @@ $cdm_col_width_px = nhance_dt_column_widths_px($cdm_header_labels, $cdm_col_max_
{ targets: <?= $i ?>, width: '<?= (int) $cdm_col_width_px[$i] ?>px' },
<?php endfor; ?>
],
// scrollX: true,
// dom: "<'row'<'col-sm-6'f><'col-sm-6 text-right'B>>" +
// "<'row'<'col-sm-12'tr>>" +
// "<'row'<'col-sm-5'i><'col-sm-7'p>>",
// buttons: [
// {
// extend: 'csv',
// text: 'CSV',
// title: 'CD-Master-List',
// className: 'my_class',
// exportOptions: {
// columns: ':not(:last-child)'
// },
// },
// {
// text: 'Add',
// className: 'buttons-html5 ',
// action: function (e, dt, node, config) {
// showCdMasterAddModal();
// }
// }
// ],
// language: {
// search: "_INPUT_",
// searchPlaceholder: "Search..."
// },
// paging: true,
}));
}));
nhanceListDataTableAfterInit();
nhanceListDataTableBindAdjust(table);
@ -263,7 +234,7 @@ $cdm_col_width_px = nhance_dt_column_widths_px($cdm_header_labels, $cdm_col_max_
}
applyBottomRowDropup();
$('#cd-master-list-table').on('draw.dt', applyBottomRowDropup);
})
});
$(document).ready(function(){
@ -274,7 +245,7 @@ $cdm_col_width_px = nhance_dt_column_widths_px($cdm_header_labels, $cdm_col_max_
<?php if (session()->has('success')) : ?>
toastr.success('<?= session()->getFlashdata('success') ?>', 'Success');
<?php endif; ?>
})
});
$('body').on('click', '.btnEdit', function () {
@ -300,8 +271,8 @@ $cdm_col_width_px = nhance_dt_column_widths_px($cdm_header_labels, $cdm_col_max_
$('#insurer_branch_id').prop("disabled", true);
$('#opening_date').val(res.data.opening_date);
$('#cd_ac_no_for_cd_master').val(res.data.cd_ac_no);
//$('#cd_ac_no_for_cd_master').prop("disabled", true);
$('#opening_bal').val(res.data.opening_bal);
$('#cd_balance_low_alert_amount').val(res.data.cd_balance_low_alert_amount ?? '');
if(res.cd_transaction_count <= 1){
$('#opening_bal').prop("disabled", false)
}else{
@ -338,7 +309,6 @@ $cdm_col_width_px = nhance_dt_column_widths_px($cdm_header_labels, $cdm_col_max_
processData: false,
contentType: false,
success: function(res) {
// console.log(res.status == true);
if(res){
if (res.status == true) {
toastr.success('CD removed successfully.', 'Success');

View File

@ -2023,24 +2023,38 @@ function processTotalSumInsured(data) {
}
// Function to move add row button to last row
// Function to move add row button to last visible row
// On QCR page, rows with QCR unchecked are hidden — place + on the last available (visible) row
function moveAddRowButton() {
// console.log('table', rfqTable)
const lastRow = rfqTable.rows[rfqTable.rows.length - 1];
const actionCell = lastRow.querySelector('.action');
// Remove any existing add buttons so we can place on the last visible row
rfqTable.querySelectorAll('.action #addRow, .action button.btn-primary.btn-sm').forEach(function(btn) {
if (btn.id === 'addRow' || btn.textContent.trim() === '+') {
btn.remove();
}
});
if (actionCell && !actionCell.querySelector('#addRow')) {
// Prefer last non-hidden body row (hidden when QCR is unchecked on QCR page)
const bodyRows = rfqTable.tBodies[0] ? rfqTable.tBodies[0].rows : [];
let lastVisibleRow = null;
for (let i = bodyRows.length - 1; i >= 0; i--) {
if (!bodyRows[i].classList.contains('hidden')) {
lastVisibleRow = bodyRows[i];
break;
}
}
if (!lastVisibleRow) {
return;
}
const actionCell = lastVisibleRow.querySelector('.action');
if (actionCell) {
actionCell.innerHTML += ' <button id="addRow" class="btn btn-primary btn-sm">+</button>';
document.getElementById('addRow').addEventListener('click', addRow);
}
// const actionCell = lastRow.cells[lastRow.cells.length - 1];
// actionCell.innerHTML =
// '<input type="checkbox" name="qcr"> QCR <input type="checkbox" name="client"> Client <button class="btn btn-danger btn-sm removeRow"></button> <button id="addRow" class="btn btn-primary btn-sm">+</button>';
// document.getElementById('addRow').addEventListener('click', addRow);
// const lastRow = rows[rows.length - 1];
hideQCR(); // keep QCR checkbox hidden on QCR page after row changes
}
@ -2406,11 +2420,14 @@ function addRow(e) {
const actionCell = newRow.insertCell();
actionCell.classList.add('action');
actionCell.innerHTML =
'<input type="checkbox" name="qcr" onchange="setRowWiseQCRandSTCDatachange(this)" checked> <span class="qcr_check_box_title">QCR</span> <input type="checkbox" name="client" onchange="setRowWiseQCRandSTCDatachange(this)" checked> Client <button class="btn btn-danger btn-sm removeRow"></button> <button class="btn btn-primary btn-sm" onclick="addRow(event)">+</button>';
'<input type="checkbox" name="qcr" onchange="setRowWiseQCRandSTCDatachange(this)" checked> <span class="qcr_check_box_title">QCR</span> <input type="checkbox" name="client" onchange="setRowWiseQCRandSTCDatachange(this)" checked> Client <button class="btn btn-danger btn-sm removeRow"></button>';
e.target.remove();
if (e && e.target) {
e.target.remove();
}
updateRowNumbers();
realignSpecialConditions();
moveAddRowButton(); // place + on last visible row
isFormDataModified = true; // if any value changeing in the table to set true
}