diff --git a/app/Config/Filters.php b/app/Config/Filters.php index dc52bc8c..c6166946 100755 --- a/app/Config/Filters.php +++ b/app/Config/Filters.php @@ -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', diff --git a/app/Config/Routes.php b/app/Config/Routes.php index c16dd299..7158f0ca 100755 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -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"); @@ -595,6 +596,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'); diff --git a/app/Controllers/EmployeeRestController.php b/app/Controllers/EmployeeRestController.php index feeb8634..cc4767dc 100755 --- a/app/Controllers/EmployeeRestController.php +++ b/app/Controllers/EmployeeRestController.php @@ -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 ?? ""; diff --git a/app/Controllers/MasterController.php b/app/Controllers/MasterController.php index 863b1bb1..022c3a31 100755 --- a/app/Controllers/MasterController.php +++ b/app/Controllers/MasterController.php @@ -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 + */ + 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 '

Dear ' . esc($recipientName) . ',

' + . '

The CD account balance for ' . esc((string) ($account['client_name'] ?? '')) . ' has fallen below the configured alert threshold.

' + . '' + . '' + . '' + . '' + . '' + . '' + . '
Insurer' . esc((string) ($account['insurer_name'] ?? '')) . '
Insurer Branch' . esc((string) ($account['insurer_branch_name'] ?? '')) . '
CD Account No' . esc((string) ($account['cd_ac_no'] ?? '')) . '
Current Balance' . esc(number_format((float) ($account['current_balance'] ?? 0), 2, '.', '')) . '
Alert Amount' . esc(number_format((float) ($account['alert_amount'] ?? 0), 2, '.', '')) . '
' + . '

Please review the CD account and take the required action.

'; + } + } \ No newline at end of file diff --git a/app/Controllers/TicketController.php b/app/Controllers/TicketController.php index 0200523b..b4171cb9 100644 --- a/app/Controllers/TicketController.php +++ b/app/Controllers/TicketController.php @@ -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; diff --git a/app/Models/CDMasterModel.php b/app/Models/CDMasterModel.php index ee503a86..d5df7b00 100755 --- a/app/Models/CDMasterModel.php +++ b/app/Models/CDMasterModel.php @@ -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> + */ + 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(); + } } diff --git a/app/Views/cd_master_add_modal.php b/app/Views/cd_master_add_modal.php index 29597a75..2e197d6d 100644 --- a/app/Views/cd_master_add_modal.php +++ b/app/Views/cd_master_add_modal.php @@ -86,6 +86,21 @@ +
+
+ + +
+
+
@@ -103,6 +118,33 @@ \ No newline at end of file +