From c471131171da8819be8d64b76efbda645efd404d Mon Sep 17 00:00:00 2001 From: Gowtham M Date: Fri, 3 Jul 2026 17:49:28 +0530 Subject: [PATCH 1/3] WELLNESS issue : GWM --- app/Controllers/EmployeeRestController.php | 23 ++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) 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 ?? ""; From 94eeff5a2182d55ea9fac3f5533669d5db90d848 Mon Sep 17 00:00:00 2001 From: Venkatesh Date: Sat, 4 Jul 2026 10:54:08 +0530 Subject: [PATCH 2/3] FEAT_ACM_CD_LOW_BAL_MAIL_AND_FIX_CLAIM_SAVE_ISSUE --- app/Config/Filters.php | 2 +- app/Config/Routes.php | 2 + app/Controllers/MasterController.php | 263 +++++++++++++++++++++++++++ app/Controllers/TicketController.php | 6 +- app/Models/CDMasterModel.php | 23 +++ app/Views/cd_master_add_modal.php | 57 +++++- app/Views/cd_master_list.php | 124 +++++-------- 7 files changed, 396 insertions(+), 81 deletions(-) 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/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 + From 396d9a2949dd1433ddf688af4ca108b5edf6617d Mon Sep 17 00:00:00 2001 From: Venkatesh Date: Sat, 4 Jul 2026 18:34:28 +0530 Subject: [PATCH 3/3] FIX_Add from Opportunities fetching only one policy for GMC/GPA/GTLI --- app/Config/Routes.php | 3 +- app/Controllers/LeadsController.php | 244 ++++++++++++++++++++++++++-- app/Views/view_rfq.php | 43 +++-- 3 files changed, 260 insertions(+), 30 deletions(-) diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 7158f0ca..234b82a9 100755 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -554,7 +554,8 @@ $routes->group("leads", ["filter" => "authMVC"], function ($routes) { $routes->post("sendMail", "LeadsController::sendMailWithAttachement"); $routes->get("exportQCRandRFQ/(:any)", "LeadsController::exportQCRandRFQ/$1"); $routes->get("featchLeadDataAndInsertClient/(:any)", "LeadsController::featchLeadDataAndInsertClient/$1"); - $routes->get("featchClientPolicyFromLead/(:any)", "LeadsController::featchClientPolicyFromLead/$1"); + $routes->get("featchClientPolicyFromLead/(:any)", "LeadsController::featchClientPolicyFromLead/$1"); + $routes->get("getLeadPolicyTerms/(:num)", "LeadsController::getLeadPolicyTerms/$1"); }); $routes->group("rfq", ["filter" => "authMVC"], function ($routes) { diff --git a/app/Controllers/LeadsController.php b/app/Controllers/LeadsController.php index 41f4252e..ecc9ed74 100644 --- a/app/Controllers/LeadsController.php +++ b/app/Controllers/LeadsController.php @@ -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 { diff --git a/app/Views/view_rfq.php b/app/Views/view_rfq.php index c1d4c104..32fe85e3 100644 --- a/app/Views/view_rfq.php +++ b/app/Views/view_rfq.php @@ -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 += ' '; document.getElementById('addRow').addEventListener('click', addRow); } - // const actionCell = lastRow.cells[lastRow.cells.length - 1]; - // actionCell.innerHTML = - // ' QCR Client '; - // 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 = - ' QCR Client '; + ' QCR Client '; - 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 }