myLogger = \Config\Services::mylogger();
$this->clientModel = new ClientModel();
$this->userModel = new UserModel();
$this->clientBranchModel = new ClientBranchModel();
$this->clientPolicyModel = new ClientPolicyModel();
$this->levelContactModel = new LevelContactModel();
$this->leadsModel = new LeadsModel();
$this->policyTypeModel = new PolicyTypeModel();
$this->kycEntityTypeModel = new KYCEntityTypeModel();
$this->policyTransactionStatusModel = new PolicyTransactionStatusModel();
$this->insurerBranchModel = new InsurerBranchModel();
$this->tpaBranchModel = new TPABranchModel();
$this->RFQModel = new RFQModel();
$this->insurerModel = new InsurerModel();
$this->occupancyModel = new OccupancyMasterModel();
$this->leadFilesModel = new LeadFilesModel();
$this->leadInstallmentPaymentDetails = new LeadInstallmentPaymentDetails();
$this->gmailSentHistoryModel = new GmailSentHistoryModel();
$this->leadModel = new SalesActualLeadModel();
$this->contactModel = new SalesContactPersonModel();
$this->issuer = [1 => 'JIBS', 2 => 'Nhance'];
$this->clientType = [1 => 'Group', 2 => 'Individual'];
$this->leadType = [1 => 'Fresh', 2 => 'Renewal', 3 => 'Roll Over'];
$data = $this->kycEntityTypeModel->where('is_active', 1)->findAll();
$this->buisnessType = array_column($data, 'name', 'id');
// $this->buisnessType = [1 => 'Public Sector', 2 => 'Private Sector',3=> 'Trust',4 => 'Proprietorship',5 => 'Partnership',6 => 'Private',7 => 'Individual'];
$this->leadsStatus = [
'queued' => 'In-Queued',
'rfq_created' => 'RFQ Created',
'rfq_sent' => 'RFQ Sent',
'qcr_created' => 'QCR Created',
'qcr_sent' => 'QCR Sent',
'lost' => 'Lost',
'co_insurer_pending' => 'Co-Insurer Pending',
'won' => 'Won',
'completed_with_corrections' => 'Completed with Corrections',
'completed_without_corrections' => 'Completed without Corrections',
];
$this->claim_type_for_gpa = [
'nil' => 'Nil',
'accident_death' => 'Accident Death',
'permanent_total_disablement' => 'Permanent Total Disablement',
'permanent_partial_disablement' => 'Permanent Partial Disablement',
'temporary_total_disablement_benefit' => 'Temporary Total Disablement benefit',
];
$this->cause_of_death = [
'natural_death' => 'Natural Death',
'suicide' => 'Suicide',
'accident' => 'Accident',
'cardiac_arrest' => 'Cardiac Arrest',
'septic_shock' => 'Septic shock',
'heart_attack' => 'Heart Attack',
];
$this->member_data_excel_columns = [
'sno' => [
'col_idx' => 0,
'col_cell_name' => 'A',
'col_name' => 'Sl no',
'is_mandatory' => false,
'data_type' => 'str',
'format' => null,
'allowed_values' => null,
],
'emp_code' => [
'col_idx' => 1,
'col_cell_name' => 'B',
'col_name' => 'Emp Code',
'is_mandatory' => true,
'data_type' => 'str',
'format' => null,
'allowed_values' => null,
],
'name' => [
'col_idx' => 2,
'col_cell_name' => 'C',
'col_name' => 'Name',
'is_mandatory' => true,
'data_type' => 'str',
'format' => null,
'allowed_values' => null,
],
'relationship' => [
'col_idx' => 3,
'col_cell_name' => 'D',
'col_name' => 'Relationship',
'is_mandatory' => true,
'data_type' => 'str',
'format' => null,
'allowed_values' => [
'Self', 'Spouse', 'Son', 'Daughter', 'Father', 'Mother',
'Father-in-law', 'Mother-in-law',
'self', 'spouse', 'son', 'daughter', 'father', 'mother',
'father-in-law', 'mother-in-law',
],
'custom' => 'check_relationship_for_member_data',
'params' => ['row', 'relationship', 'columns_to_check'],
],
'gender' => [
'col_idx' => 4,
'col_cell_name' => 'E',
'col_name' => 'Gender',
'is_mandatory' => true,
'data_type' => 'str',
'format' => null,
'allowed_values' => ['M', 'F'],
],
'dob' => [
'col_idx' => 5,
'col_cell_name' => 'F',
'col_name' => 'DOB',
'is_mandatory' => true,
'data_type' => 'str',
'format' => 'd-M-Y',
'allowed_values' => null,
'age_validation' => true,
// 'custom' => 'check_dob_diff',
// 'params' => ['row', 'relationship', 'default_age_ratio', 'policy_details']
],
'age' => [
'col_idx' => 6,
'col_cell_name' => 'G',
'col_name' => 'Age',
'is_mandatory' => true,
'data_type' => 'int',
'format' => null,
'allowed_values' => null,
],
'email' => [
'col_idx' => 7,
'col_cell_name' => 'H',
'col_name' => 'Email',
'is_mandatory' => false,
'data_type' => 'str',
'format' => null,
'allowed_values' => null,
],
'mobile' => [
'col_idx' => 8,
'col_cell_name' => 'I',
'col_name' => 'Mobile',
'is_mandatory' => false,
'data_type' => 'str',
'format' => null,
'allowed_values' => null,
],
'si' => [
'col_idx' => 9,
'col_cell_name' => 'J',
'col_name' => 'SI',
'is_mandatory' => false,
'data_type' => 'int',
'format' => null,
'allowed_values' => null,
],
];
$this->general_relationships = [
'self' => [
'name' => 'Self',
'gender' => 'M',
'age_min' => 18,
'age_max' => null,
],
'spouse' => [
'name' => 'Spouse',
'gender' => 'F',
'age_min' => 18,
'age_max' => null,
],
'son' => [
'name' => 'Son',
'gender' => 'M',
'age_min' => null,
'age_max' => 25,
],
'daughter' => [
'name' => 'Daughter',
'gender' => 'F',
'age_min' => null,
'age_max' => 25,
],
'father' => [
'name' => 'Father',
'gender' => 'M',
'age_min' => 18,
'age_max' => null,
],
'mother' => [
'name' => 'Mother',
'gender' => 'F',
'age_min' => 18,
'age_max' => null,
],
'father-in-law' => [
'name' => 'Father in Law',
'gender' => 'M',
'age_min' => 18,
'age_max' => null,
],
'mother-in-law' => [
'name' => 'Mother in Law',
'gender' => 'F',
'age_min' => 18,
'age_max' => null,
],
];
}
public function viewLeadsList()
{
$data['tab_name'] = 'Opportunities';
$data['page_name'] = 'Opportunities';
// Set basic data
$data['issuer'] = $this->issuer;
$data['client_type'] = $this->clientType;
$data['lead_type'] = $this->leadType;
$data['lead_status'] = $this->leadsStatus;
// Fetch policy types and entity data
$data['policy_type'] = $this->policyTypeModel->where('is_active', 1)->findAll();
$data['entity'] = $this->kycEntityTypeModel->where('is_active', 1)->findAll();
// Data needed for mail modals on Opportunities list (reuse RFQ behaviour)
$data['userList'] = $this->userModel->getUserListForRFQ();
$data['exclusiveUserList'] = $this->userModel->getexclusiveUserListForRFQ();
// Attachments for mails are lead-specific and will be handled at RFQ level;
// keep placeholder here so modal templates render without notices.
$data['attachment_html'] = '';
// dd($lastFiveYears);
if ($this->request->is('get')) {
// Fetch leads data
$data['lead_data_list'] = $this->leadsModel->getLeadDataForLising();
// Load layout and pass data
$this->loadLayout('lead_filter', $data);
} else {
$isFromDashboard = $this->request->getPost("is_dashboard");
if (isset($isFromDashboard) && ! empty($isFromDashboard) && $isFromDashboard == 1) {
$ids = $this->request->getPost('ids');
$ids = array_filter(explode(',', $ids));
if (! empty($ids)) {
$idsStr = implode(',', array_map('intval', $ids)); // sanitize IDs to be integers
$where = "leads.id IN ($idsStr)";
} else {
$where = '1 = 0'; // No valid IDs, return empty result
}
// dd($where );
$data['lead_data_list'] = $this->leadsModel->getLeadDataForLising($where);
$this->loadLayout('lead_filter', $data);
} else {
$search_data = $this->request->getPost();
$where = [];
foreach ($search_data as $search_objects => $key) {
if ($key != null && $key != '' && $key != 0 && $search_objects != 'is_dashboard') {
if ($search_objects == 'status') {
$where[('leads.' . $search_objects)] = $key;
} else {
$where[$search_objects] = $key;
}
}
}
// !dd($where);
$data['lead_data_list'] = $this->leadsModel->getLeadDataForLising($where);
$html = view('leads_list', $data);
return $this->respond(['status' => true, 'html' => $html], 200);
}
}
}
public function createLead()
{
$id = $this->request->getPost('id');
$actual_lead_id = $this->request->getPost('actual_lead_id');
$actual_lead_id = ! empty($actual_lead_id) ? $actual_lead_id : null;
$this->normalizeClaimHistoryPostData();
$postData = $this->request->getPost();
$data = $this->prepareLeadData();
$rules = [
'lead_type' => [
'rules' => 'integer',
'errors' => ['required' => 'Opportunity Type is required'],
],
'issuer' => [
'rules' => 'required',
'errors' => ['required' => 'Issuer is required'],
],
'gst' => [
'rules' => 'required|regex_match[/^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}Z[0-9A-Z]{1}$/]',
'errors' => [
'required' => 'GST Number is required.',
'regex_match' => 'Invalid GST format. Example: 22AAAAA0000A1Z5',
],
],
'branch_name' => [
'rules' => 'required|regex_match[/^[a-zA-Z0-9\s_-]+$/]|min_length[3]',
'errors' => [
'required' => 'Branch Name is required.',
'regex_match' => 'Branch Name can only contain letters, numbers, spaces, hyphens and underscores..',
'min_length' => 'Branch Name must be at least 3 characters long.',
],
],
'branch_code' => [
'rules' => 'required|regex_match[/^[a-zA-Z0-9_-]+$/]',
'errors' => [
'required' => 'Branch Code is required.',
'regex_match' => 'Branch Code can only contain letters, numbers, hyphens and underscores..',
],
],
'contact_person_name' => [
'rules' => 'required|regex_match[/^[a-zA-Z0-9_ -]+$/]',
'errors' => [
'required' => 'Contact Person Name is required',
'regex_match' => 'Contact person name only letters, numbers and spaces, hyphens and underscores..',
],
],
'contact_person_mobile' => [
'rules' => 'required',
'rules' => 'required|exact_length[10]|regex_match[/^[0-9]+$/]',
'errors' => [
'required' => 'Contact Person Mobile is required',
'regex_match' => 'Contact Person Mobile Only numbers',
'exact_length' => 'Contact Person Mobile must be exactly 10 digits long.',
],
],
'contact_person_email' => [
'rules' => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]',
'errors' => [
'required' => 'Contact Person Email address is required.',
'regex_match' => 'Please enter a valid email format (e.g., name@domain.com).',
],
// 'rules' => 'required|valid_email|regex_match[/^[a-zA-Z0-9_-]+$/]',
// 'regex_match' => 'Contact Person Email Only letters, numbers and characters _ - @ . are allowed'
],
'salse_person_id' => [
'rules' => 'required',
'errors' => ['required' => 'Sales Person is required'],
],
'status' => [
'rules' => 'required',
'errors' => ['required' => 'Status is required'],
],
'next_reminder_date' => [
'label' => 'Next Reminder Date',
'rules' => 'permit_empty|valid_date[d/m/Y]',
'errors' => [
'valid_date' => 'The {field} must be in the format DD/MM/YYYY.',
],
],
];
if (isset($postData['lead_type']) && in_array((int) $postData['lead_type'], [1, 3], true)) {
$rules['entity_type_id'] = [
'rules' => 'required',
'errors' => ['required' => 'Entity Type is required'],
];
$clientNameRules = 'required|regex_match[/^[a-zA-Z0-9_ -]+$/]';
$clientShortNameRules = 'required|regex_match[/^[a-zA-Z0-9_-]+$/]';
// Only validate for uniqueness if it's a new client being specified.
// If an existing client is selected from the dropdown, `client_id` will be present.
if (empty($postData['client_id'])) {
$ignoreClientId = null;
$lead = null;
if ($id) { // This is an update of a lead
$lead = $this->leadsModel->find($id);
if ($lead && ! empty($lead['is_client_created'])) {
$ignoreClientId = (int) $lead['is_client_created'];
}
}
$submittedName = trim((string) ($postData['client_name'] ?? ''));
$submittedShort = trim((string) ($postData['client_short_name'] ?? ''));
$nameUnchanged = $lead && strcasecmp(trim((string) ($lead['client_name'] ?? '')), $submittedName) === 0;
$shortUnchanged = $lead && strcasecmp(trim((string) ($lead['client_short_name'] ?? '')), $submittedShort) === 0;
if ($ignoreClientId) {
$clientNameRules .= '|is_unique[clients.client_name,id,' . $ignoreClientId . ']';
$clientShortNameRules .= '|is_unique[clients.short_name,id,' . $ignoreClientId . ']';
} else {
// On edit, unchanged names belong to this opportunity — do not treat as duplicates.
// (Client row may already exist even when is_client_created is unset.)
if (! $nameUnchanged) {
$clientNameRules .= '|is_unique[clients.client_name]';
}
if (! $shortUnchanged) {
$clientShortNameRules .= '|is_unique[clients.short_name]';
}
}
}
$rules['client_name'] = [
'rules' => $clientNameRules,
'errors' => [
'required' => 'Client Name is required',
'regex_match' => 'Client Name only letters, numbers, space, hyphens and underscores are allowed',
'is_unique' => 'This Client Name already exists.',
],
];
$rules['client_short_name'] = [
'rules' => $clientShortNameRules,
'errors' => [
'required' => 'Client Short Name is required',
'regex_match' => 'Client Short Name only letters, numbers, hyphens and underscores are allowed',
'is_unique' => 'This Client Short Name already exists.',
],
];
}
foreach ($postData as $key => $value) {
// Check if the key starts with 'docs_name_'
if (strpos($key, 'docs_name_') === 0) {
$rules[$key . '.*'] = [
'label' => 'Document Name',
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9_\- ]+$/]',
'errors' => [
'regex_match' => 'Document Name in {field} can only contain letters, numbers, hyphens, and underscores.',
],
];
}
}
// print_r($rules); die;
if ((int) $this->request->getPost('lead_form_type') === 2) {
$rules['policy_type_id'] = [
'rules' => 'required',
'errors' => ['required' => 'Policy Type is required'],
];
$rules['policy_start_date'] = [
'rules' => 'required',
'errors' => ['required' => 'Date of Commencement is required'],
];
$rules['policy_end_date'] = [
'rules' => 'required',
'errors' => ['required' => 'Date of Expiry is required'],
];
if ((int) $this->request->getPost('claim_history') === 1) {
$nilClaimsRows = $this->resolveNilClaimRowFlags($postData);
$claimRowCount = $this->getClaimHistoryRowCount($postData, $nilClaimsRows);
for ($claimIndex = 0; $claimIndex < $claimRowCount; $claimIndex++) {
$rules["first_year.$claimIndex"] = [
'rules' => 'required|regex_match[/^\d{4}-\d{4}$/]',
'errors' => [
'required' => 'Claim Year is required for all entries.',
'regex_match' => 'Year must be in format YYYY-YYYY.',
],
];
if (! empty($nilClaimsRows[$claimIndex])) {
continue;
}
$rules["first_policy_type_.$claimIndex"] = [
'rules' => 'required|regex_match[/^[a-zA-Z0-9 _-]+$/]',
'errors' => [
'required' => 'Policy Type is required in Claim History.',
'regex_match' => 'Policy Type only letters, numbers, space, hyphens and underscores are allowed',
],
];
$rules["first_date_of_loss_.$claimIndex"] = [
'rules' => 'required|regex_match[/^[0-9]{2}-[0-9]{2}-[0-9]{4}$/]',
'errors' => [
'required' => 'Date of Loss is required.',
'regex_match' => 'Date of Loss must be inValid format.',
],
];
$rules["first_cause_of_loss.$claimIndex"] = [
'rules' => 'required|regex_match[/^[a-zA-Z0-9 _-]+$/]',
'errors' => [
'required' => 'Cause of Loss is required.',
'regex_match' => 'Cause of Loss only letters, numbers, space, hyphens and underscores are allowed',
],
];
$rules["first_claim_amount.$claimIndex"] = [
'rules' => 'required|numeric',
'errors' => [
'required' => 'Claim Amount is required.',
'numeric' => 'Claim Amount must be a number.',
],
];
$rules["first_settled_amount.$claimIndex"] = [
'rules' => 'required|numeric',
'errors' => [
'required' => 'Settled Amount is required.',
'numeric' => 'Settled Amount must be a number.',
],
];
$rules["first_claim_status.$claimIndex"] = [
'rules' => 'required|alpha_space',
'errors' => [
'required' => 'Claim Status is required.',
'alpha_space' => 'Claim Status should only contain letters and spaces.',
],
];
}
}
}
$leadFormType = (int) ($postData['lead_form_type'] ?? 1);
$postedPolicyTypeIds = array_map('intval', (array) ($postData['policy_type_id'] ?? []));
$claimHistoryPolicyTypes = [1, 6, 7];
$hasClaimHistoryPolicy = count(array_intersect($postedPolicyTypeIds, $claimHistoryPolicyTypes)) > 0;
$isEbClaimHistory = $leadFormType === 1
&& in_array((int) ($postData['lead_type'] ?? 0), [1, 2, 3], true)
&& $hasClaimHistoryPolicy
&& (int) ($postData['claim_history'] ?? 0) === 1;
if ($isEbClaimHistory) {
$nilClaimsRows = $this->resolveNilClaimRowFlags($postData);
$claimRowCount = $this->getClaimHistoryRowCount($postData, $nilClaimsRows);
for ($claimIndex = 0; $claimIndex < $claimRowCount; $claimIndex++) {
$rules["first_year.$claimIndex"] = [
'rules' => 'required|regex_match[/^\d{4}-\d{4}$/]',
'errors' => [
'required' => 'Claim Year is required for all entries.',
'regex_match' => 'Year must be in format YYYY-YYYY.',
],
];
if (! empty($nilClaimsRows[$claimIndex])) {
continue;
}
$rules["emp_id.$claimIndex"] = [
'rules' => 'required',
'errors' => ['required' => 'Employee ID is required in Claim History.'],
];
$rules["emp_name.$claimIndex"] = [
'rules' => 'required',
'errors' => ['required' => 'Employee Name is required in Claim History.'],
];
$rules["gender.$claimIndex"] = [
'rules' => 'required|in_list[Female,Male]',
'errors' => [
'required' => 'Gender is required in Claim History.',
'in_list' => 'Gender must be Female or Male.',
],
];
$rules["designation.$claimIndex"] = [
'rules' => 'required',
'errors' => ['required' => 'Designation is required in Claim History.'],
];
$rules["sum_insured.$claimIndex"] = [
'rules' => 'required|numeric',
'errors' => [
'required' => 'Sum Insured is required in Claim History.',
'numeric' => 'Sum Insured must be a number.',
],
];
$rules["first_death_date.$claimIndex"] = [
'rules' => 'required|regex_match[/^[0-9]{2}-([0-9]{2}|[A-Za-z]{3})-[0-9]{4}$/]',
'errors' => [
'required' => 'Date of Death is required.',
'regex_match' => 'Date of Death must be in DD-MM-YYYY or DD-MMM-YYYY format.',
],
];
$rules["first_cause_of_death.$claimIndex"] = [
'rules' => 'required|in_list[natural_death,suicide,accident,cardiac_arrest,septic_shock,heart_attack]',
'errors' => [
'required' => 'Nature/Cause Of Death is required.',
'in_list' => 'Nature/Cause Of Death is invalid.',
],
];
$rules["first_claim_amount.$claimIndex"] = [
'rules' => 'required|numeric',
'errors' => [
'required' => 'Claim/Settled Amount is required.',
'numeric' => 'Claim/Settled Amount must be a number.',
],
];
}
}
// 1. MANUALLY VALIDATE FILES BEFORE PROCESSING
// $allFiles = $this->request->getFiles();
// foreach ($allFiles as $inputName => $files) {
// // If it's a single file, wrap it in an array to use the same logic
// $fileArray = is_array($files) ? $files : [$files];
// foreach ($fileArray as $index => $file) {
// if ($file->isValid() && !$file->hasMoved()) {
// $extension = strtolower($file->getExtension());
// // Logic: index 0 is Excel only, others are mixed
// if ($index === 0) {
// $allowed = ['xls', 'xlsx'];
// } else {
// $allowed = ['xls', 'xlsx', 'pdf', 'jpg', 'jpeg', 'png'];
// }
// if (!in_array($extension, $allowed)) {
// // Return 400 Error immediately
// return $this->response->setStatusCode(400)->setJSON([
// 'status' => false,
// 'message' => 'Input validation failed',
// 'code' => 400,
// 'errors' => [
// $inputName => "File " . ($index + 1) . " has an invalid extension ($extension). Allowed: " . implode(',', $allowed)
// ]
// ]);
// }
// }
// }
// }
$isValid = $this->validate($rules);
$start_dates = $this->request->getPost('policy_start_date');
$end_dates = $this->request->getPost('policy_end_date');
if ($start_dates && $end_dates) {
$starts = is_array($start_dates) ? $start_dates : [$start_dates];
$ends = is_array($end_dates) ? $end_dates : [$end_dates];
foreach ($starts as $index => $s_date) {
$e_date = $ends[$index] ?? null;
if ($s_date && $e_date) {
$f_start = change_date_format($s_date, 'd/m/Y', 'Y-m-d');
$f_end = change_date_format($e_date, 'd/m/Y', 'Y-m-d');
if ($f_start && $f_end && ($f_end < $f_start)) {
$isValid = false;
// Determine the error key name
// If it's an array, we use index (e.g., policy_end_date.0)
$errorKey = is_array($start_dates) ? "policy_end_date.$index" : "policy_end_date";
$this->validator->setError($errorKey, 'Date of Expiry cannot be before Date of Commencement.');
}
}
}
}
if (! $isValid) {
return $this->response->setStatusCode(400)->setJSON([
'status' => false,
'message' => 'Input validation failed',
'code' => 400,
'errors' => $this->validator->getErrors(),
]);
}
// print_r($data); die;
if (! $id) {
return $this->insertNewLead($data);
} else {
return $this->updateOldLead($id, $data);
}
}
private function prepareLeadData()
{
$request_data = $this->request->getPost();
$data = sanitizeInputArrayAdvanced($request_data);
$data['client_type'] = 1;
if (! in_array((int) $data['lead_type'], [1, 3], true)) {
$client_data = $this->clientModel->where('id', $data['client_id'])->where('is_active', 1)->first();
$data['client_name'] = $client_data['client_name'];
$data['client_short_name'] = $client_data['short_name'];
$data['entity_type_id'] = $client_data['entity_type_id'];
$data['client_type'] = $client_data['client_type'];
} else {
if (! empty($data['client_id'])) {
$data['is_client_created'] = $data['client_id'];
} else {
$data['client_id'] = 0;
$data['client_branch_id'] = 0;
$data['source_policy_id'] = 0;
// Keep existing client link on update when "Existing Client" is off.
// Otherwise is_client_created is wiped to null and later edits fail uniqueness checks.
if (! empty($data['id'])) {
$existingLead = $this->leadsModel->find($data['id']);
if ($existingLead && ! empty($existingLead['is_client_created'])) {
$data['is_client_created'] = $existingLead['is_client_created'];
}
}
}
}
$data['client_code'] = generate_client_code();
if ($data['client_code'] == 2) {
$data['client_code'] = generate_client_code('IC');
}
if ((int) ($data['lead_form_type'] ?? 1) === 2) {
$data = $this->prepareSingleLeadData($data);
} else {
$data = $this->prepareMultipleLeadData($data);
}
// print_r($data); die;
return $data;
}
private function prepareSingleLeadData($data)
{
// print_r($data); die;
$index_plus_one = 1;
$form_file_name = "file_name_" . $index_plus_one;
$form_docs_name = "docs_name_" . $index_plus_one;
$leads_file_primary_key = $data['leads_file_id'] ?? [];
$multi_file_data = [];
if (isset($data[$form_docs_name])) {
$files = $this->request->getFileMultiple($form_file_name);
$multi_file_data = $this->uploadMultiFiles($files, $data[$form_docs_name], $leads_file_primary_key);
}
// Separate the insurer and insurer branch, handle missing or invalid data
if (isset($data['insurer']) && strpos($data['insurer'], '-') !== false) {
list($insurer_branch_id, $insurer_id) = explode('-', $data['insurer']);
} else {
$insurer_branch_id = 0;
$insurer_id = 0;
}
$data['insurer_id'] = $insurer_id;
$data['insurer_branch_id'] = $insurer_branch_id;
// Separate the insurer and insurer branch, handle missing or invalid data
if (isset($data['tpa']) && strpos($data['tpa'], '-') !== false) {
list($tpa_branch_id, $tpa_id) = explode('-', $data['tpa']);
} else {
$tpa_branch_id = 0;
$tpa_id = 0;
}
$data['tpa_id'] = $tpa_id;
$data['tpa_branch_id'] = $tpa_branch_id;
if (! empty($data['policy_start_date'])) {
$data['policy_start_date'] = change_date_format($data['policy_start_date']);
} else {
$data['policy_start_date'] = null;
}
if (! empty($data['policy_end_date'])) {
$data['policy_end_date'] = change_date_format($data['policy_end_date']);
} else {
$data['policy_end_date'] = null;
}
// $data['file_name'] = file_Upload($files, $uploadFilePath);
$data['multi_file_data'] = $multi_file_data ?? null;
$processcedData[] = $data;
// print_r($data);die();
return $processcedData;
}
private function prepareMultipleLeadData($data)
{
// print_r($data); die;
$processedData = [];
foreach ($data['policy_type_id'] as $index => $value) {
$index_plus_one = $index + 1;
$form_file_name = "file_name_" . $index_plus_one;
$form_docs_name = "docs_name_" . $index_plus_one;
$leads_file_primary_key = $data['leads_file_id'] ?? [];
$files = $this->request->getFileMultiple($form_file_name);
$docs_names_from_post = $data[$form_docs_name] ?? [];
$multi_file_data = $this->uploadMultiFiles($files, $docs_names_from_post, $leads_file_primary_key);
// Separate the insurer and insurer branch, handle missing or invalid data
if (isset($data['insurer'][$index]) && strpos($data['insurer'][$index], '-') !== false) {
list($insurer_branch_id, $insurer_id) = explode('-', $data['insurer'][$index]);
} else {
$insurer_branch_id = 0;
$insurer_id = 0;
}
// Separate the insurer and insurer branch, handle missing or invalid data
if (isset($data['tpa'][$index]) && strpos($data['tpa'][$index], '-') !== false) {
list($tpa_branch_id, $tpa_id) = explode('-', $data['tpa'][$index]);
} else {
$tpa_branch_id = 0;
$tpa_id = 0;
}
// Separate the insurer and insurer branch, handle missing or invalid data
if (isset($data['proposed_insurer'][$index]) && strpos($data['proposed_insurer'][$index], '-') !== false) {
list($proposed_insurer_branch_id, $proposed_insurer_id) = explode('-', $data['proposed_insurer'][$index]);
} else {
$proposed_insurer_branch_id = 0;
$proposed_insurer_id = 0;
}
// Separate the TPA and TPA branch, handle missing or invalid data
if (isset($data['proposed_tpa'][$index]) && strpos($data['proposed_tpa'][$index], '-') !== false) {
list($proposed_tpa_branch_id, $proposed_tpa_id) = explode('-', $data['proposed_tpa'][$index]);
} else {
$proposed_tpa_branch_id = 0;
$proposed_tpa_id = 0;
}
if (! empty($data['policy_start_date'][$index])) {
$policy_start_date = change_date_format($data['policy_start_date'][$index], 'd/m/Y', 'Y-m-d');
} else {
$policy_start_date = null;
}
if (! empty($data['policy_end_date'][$index])) {
$policy_end_date = change_date_format($data['policy_end_date'][$index], 'd/m/Y', 'Y-m-d');
} else {
$policy_end_date = null;
}
if (! empty($data['incurred_claim_date'][$index])) {
$incurred_claims_date = change_date_format($data['incurred_claim_date'][$index], 'd/m/Y', 'Y-m-d');
} else {
$incurred_claims_date = null;
}
// if (!empty($data['premium_date'][$index])) {
// $premium_date = change_date_format($data['premium_date'][$index], 'd/m/Y', 'Y-m-d');
// } else {
// $premium_date = null;
// }
if (! empty($data['source_policy_start_date']) && $data['lead_type'] == 2) {
$data['source_policy_start_date'] = change_date_format($data['source_policy_start_date'], 'd/m/Y', 'Y-m-d');
} else {
$data['source_policy_start_date'] = null;
}
if (! empty($data['source_policy_end_date']) && $data['lead_type'] == 2) {
$data['source_policy_end_date'] = change_date_format($data['source_policy_end_date'], 'd/m/Y', 'Y-m-d');
} else {
$data['source_policy_end_date'] = null;
}
// convert the date
// Clean the input and handle empty strings
$reminderDate = trim($data['next_reminder_date'] ?? '');
if ($reminderDate !== '' && $reminderDate != null && $reminderDate != 0) {
$convertedDate = change_date_format($reminderDate);
$data['next_reminder_date'] = $convertedDate ?: null;
} else {
$data['next_reminder_date'] = null;
}
//convert 0 and 1
if (isset($data['is_insurer_auto_mail'])) {
$data['is_insurer_auto_mail'] = 1;
} else {
$data['is_insurer_auto_mail'] = 0;
}
// $file_name = file_Upload($files[$index], $uploadFilePath);
$last_3_years_claims = $data['finyear'];
$processedData[] = [
'lost_reason' => $data['lost_reason'] ?? null,
'actual_lead_id' => $data['actual_lead_id'] ?? null,
'lead_type' => $data['lead_type'],
'issuer' => $data['issuer'],
'client_type' => $data['client_type'],
'client_name' => $data['client_name'],
'client_short_name' => $data['client_short_name'],
'entity_type_id' => $data['entity_type_id'],
'client_code' => $data['client_code'],
'pan' => $data['pan'] ?? null,
'gst' => $data['gst'] ?? null,
'branch_name' => $data['branch_name'],
'branch_code' => $data['branch_code'],
'contact_person_name' => $data['contact_person_name'],
'contact_person_mobile' => $data['contact_person_mobile'],
'contact_person_email' => $data['contact_person_email'],
'client_id' => $data['client_id'] ?? 0,
'client_branch_id' => $data['client_branch_id'] ?? 0,
'is_client_created' => $data['is_client_created'] ?? null,
'source_policy_id' => $data['source_policy_id'] ?? 0,
'policy_type_id' => $value,
'salse_person_id' => $data['salse_person_id'] ?? 0,
'rfq_qcr_viewers' => $data['rfq_qcr_viewers'] ?? null,
'insurer_id' => $insurer_id ?? 0,
'insurer_branch_id' => $insurer_branch_id ?? 0,
'tpa_id' => $tpa_id ?? 0,
'tpa_branch_id' => $tpa_branch_id ?? 0,
'policy_start_date' => $policy_start_date,
'policy_end_date' => $policy_end_date,
'no_of_lives' => $data['no_of_lives'][$index] ?? null,
'incurred_claims' => $data['incurred_claims'][$index] ?? 0,
'location' => $data['location'][$index] ?? null,
'proposed_insurer_id' => $proposed_insurer_id ?? 0,
'proposed_insurer_branch_id' => $proposed_insurer_branch_id ?? 0,
'proposed_tpa_id' => $proposed_tpa_id ?? 0,
'proposed_tpa_branch_id' => $proposed_tpa_branch_id ?? 0,
'renewal_emp_count' => $data['renewal_emp_count'][$index] ?? 0,
'renewal_dept_count' => $data['renewal_dept_count'][$index] ?? 0,
'renewal_no_of_lives' => $data['renewal_no_of_lives'][$index] ?? 0,
'incept_emp_count' => $data['incept_emp_count'][$index] ?? 0,
'incept_dept_count' => $data['incept_dept_count'][$index] ?? 0,
'incept_no_of_lives' => $data['incept_no_of_lives'][$index] ?? 0,
'exp_emp_count' => $data['exp_emp_count'][$index] ?? 0,
'exp_dept_count' => $data['exp_dept_count'][$index] ?? 0,
'exp_no_of_lives' => $data['exp_no_of_lives'][$index] ?? 0,
'incurred_claims_date' => $incurred_claims_date,
'paid_claims' => $data['paid_claims'][$index] ?? 0,
'outstanding_claims' => $data['outstanding_claims'][$index] ?? 0,
'policy_run_days' => $data['policy_run_days'][$index] ?? 0,
'premium_at_inception' => $data['premium_at_inception'][$index] ?? 0,
'premium_date' => $data['premium_date'][$index] ?? 0,
'earned_premium' => $data['earned_premium'][$index] ?? 0,
'annualised_claims' => $data['annualised_claims'][$index] ?? 0,
'incurred_claims_ratio' => $data['incurred_claims_ratio'][$index] ?? 0,
'earned_claims_ratio' => $data['earned_claims_ratio'][$index] ?? 0,
'total_si_at_incept' => $data['total_si_at_incept'][$index] ?? 0,
'total_si_at_renewal' => $data['total_si_at_renewal'][$index] ?? 0,
'total_lives_at_incept' => $data['total_lives_at_incept'][$index] ?? 0,
'premium_at_incept' => $data['premium_at_incept'][$index] ?? 0,
'fin_years_claims' => $last_3_years_claims,
// 'file_name' => $file_name,
'multi_file_data' => $multi_file_data ?? null,
'status' => $data['status'] ?? null,
'notes' => $data['notes'] ?? null,
'lead_form_type' => $data['lead_form_type'] ?? 1,
'custom_fields' => $data['custom_fields'] ?? null,
'source_policy_start_date' => $data['source_policy_start_date'] ?? null,
'source_policy_end_date' => $data['source_policy_end_date'] ?? null,
'claim_history' => $data['claim_history'] ?? 0,
'next_reminder_date' => $data['next_reminder_date'] ?? null,
'is_insurer_auto_mail' => $data['is_insurer_auto_mail'] ?? 0,
];
}
// print_r($processedData);die();
return $processedData;
}
private function insertNewLead($data)
{
$insertCount = [];
foreach ($data as $value) {
$insert = $this->leadsModel->insert($value);
if ($insert) {
$this->insertMultiFilesData($value['multi_file_data'], $insert);
}
$insertCount[] = $insert;
$this->insertLeadStatus($insert, $value['status'], 3);
// if (in_array((int) $value['lead_type'], [1, 3], true) && $value['status'] == 'won') {
// $leadDataForClient = $this->leadsModel->find($insert);
// $this->createClientWithLeadData($leadDataForClient);
// }
if ($value['lead_form_type'] == 1) {
//for this push the job to the calculateMembersDemography() function
$job_details = new Jobs();
// $r = Jobs::addJob(['job_name' => 'memberDataListExcelFileFormatValidation', 'payload' => [
// 'lead_id' => $insert,
// ]]);
$r = Jobs::addJob(['job_name' => 'calculateMembersDemography', 'payload' => [
'lead_id' => $insert,
]]);
}
}
if (count($insertCount) > 0) {
return $this->respond(['status' => true, 'lead_id' => $insert, 'message' => 'New Opportunity created successfully', 'data' => $data], 200);
}
return $this->respond(['status' => false, 'lead_id' => $insert, 'message' => "Failed to create Opportunity", 'data' => $data], 200);
}
private function updateOldLead($id, $data)
{
if ($this->leadsModel->where('id', $id)->set($data[0])->update()) {
$this->insertMultiFilesData($data[0]['multi_file_data'], $id, $data[0]['lead_form_type']);
$this->insertLeadStatus($id, $data[0]['status'], 3);
// if (in_array((int) $data[0]['lead_type'], [1, 3], true) && $data[0]['status'] == 'won') {
// $leadDataForClient = $this->leadsModel->find($id);
// if (empty($leadDataForClient['is_client_created'])) {
// $this->createClientWithLeadData($leadDataForClient);
// }
// }
return $this->respond(['status' => true, 'lead_id' => $id, 'message' => "Opportunity updated successfully", 'data' => $data], 200);
}
return $this->respond(['status' => false, 'lead_id' => $id, 'message' => "Failed to update Opportunity", 'data' => $data], 200);
}
// Get the Single Lead data for edit uisng ajax (do not delete)
public function getLeadDataForEdit($id)
{
$data = $this->leadsModel
->where('leads.id', $id)
->where('leads.is_active', 1)
->first();
$data['lead_edit_data'] = $this->leadsModel
->where('leads.id', $id)
->where('leads.is_active', 1)
->first();
if (! empty($data['policy_start_date'])) {
$data['policy_start_date'] = change_date_format($data['policy_start_date'], 'Y-m-d', 'd/m/Y');
} else {
$data['policy_start_date'] = null;
}
if (! empty($data['policy_end_date'])) {
$data['policy_end_date'] = change_date_format($data['policy_end_date'], 'Y-m-d', 'd/m/Y');
} else {
$data['policy_end_date'] = null;
}
if (! empty($data['incurred_claims_date'])) {
$data['incurred_claims_date'] = change_date_format($data['incurred_claims_date'], 'Y-m-d', 'd/m/Y');
} else {
$data['incurred_claims_date'] = null;
}
// if (!empty($data['premium_date'])) {
// $data['premium_date'] = change_date_format($data['premium_date'], 'Y-m-d', 'd/m/Y');
// } else {
// $data['premium_date'] = null;
// }
$data['multi_file_data'] = $this->leadFilesModel
->where('lead_id', $id)
->where('type !=', 2)
->where('is_active', 1)
->first() ?? null;
$data['lastFiveYears'] = $this->getLastFiveFinancialYears();
$data['gpaClaimType'] = $this->claim_type_for_gpa;
$data['causeOfDeath'] = $this->cause_of_death;
if (! empty($data['fin_years_claims'])) {
$decoded = json_decode($data['fin_years_claims'], true);
$data['lead_edit_data']['fin_years_claims_array'] = isset($decoded['finyear']) ? $decoded['finyear'] : [];
} else {
$data['lead_edit_data']['fin_years_claims_array'] = [];
}
$data['html'] = $this->generateViewPageHtml($data['policy_type_id'], $data) ?? "";
// print_r($data); die;
if ($data) {
return $this->respond(['status' => true, 'data' => $data], 200);
} else {
return $this->respond(['status' => false], 200);
}
}
private function insertLeadStatus($primaryKey, $status, $statusType)
{
$statusData = [
'policy_tran_id' => $primaryKey,
'status' => $status,
'status_type' => $statusType,
'created_by' => get_session_userid(),
];
$this->policyTransactionStatusModel->insert($statusData);
}
public function uploadMultiFiles($files, $docs_names, $primaryKey)
{
$uploadFilePath = WRITEPATH . 'uploads/lead_files/';
$multi_file_data = [];
foreach ($files as $index => $value) {
$file_name = storage_file_Upload($value, $uploadFilePath, UPLOAD_EXT_LEAD_FILES);
$multi_file_data[] = [
'file_name' => $file_name,
'docs_name' => $docs_names[$index] ?? '',
'id' => $primaryKey[$index] ?? "",
];
}
return $multi_file_data;
}
public function insertMultiFilesData($data, $lead_id, $lead_form_type = null)
{
log_message('info', 'insertMultiFilesData() called with lead_id: ' . $lead_id);
if (! empty($data)) {
log_message('info', 'Data is not empty. Total items: ' . count($data));
foreach ($data as $key => $value) {
log_message('info', "Processing item at index {$key}: " . json_encode($value));
if (! empty($value['id'])) {
log_message('info', "Record with ID {$value['id']} exists. Preparing to update.");
$lead_file_data = [
'lead_id' => $lead_id,
'docs_name' => $value['docs_name'],
];
if (! empty($value['file_name'])) {
$lead_file_data['file_name'] = $value['file_name'];
log_message('info', "file_name found: {$value['file_name']}");
if (! empty($lead_form_type) && $lead_form_type == 1 && $key == 0) {
//for this push the job to the calculateMembersDemography() function
$job_details = new Jobs();
// $r = Jobs::addJob(['job_name' => 'memberDataListExcelFileFormatValidation', 'payload' => [
// 'lead_id' => $lead_id,
// ]]);
$r = Jobs::addJob(['job_name' => 'calculateMembersDemography', 'payload' => [
'lead_id' => $lead_id,
]]);
log_message('info', "calculateMembersDemography JOB PUSHED");
}
}
log_message('info', "Updating leadFilesModel where id = {$value['id']} with data: " . json_encode($lead_file_data));
$this->leadFilesModel->where('id', $value['id'])->set($lead_file_data)->update();
} else {
log_message('info', "No ID found. Preparing to insert new record.");
if (! empty($value['file_name'])) {
$lead_file_data = [
'lead_id' => $lead_id,
'docs_name' => $value['docs_name'],
'file_name' => $value['file_name'],
];
log_message('info', "Inserting into leadFilesModel: " . json_encode($lead_file_data));
$this->leadFilesModel->insert($lead_file_data);
} else {
log_message('warning', "Skipped insert: file_name missing in data at index {$key}");
}
}
if ($key == 0 && ! empty($value['file_name'])) {
log_message('info', "Setting first file_name '{$value['file_name']}' to leadsModel for lead_id: {$lead_id}");
$this->leadsModel->where('id', $lead_id)->set('file_name', $value['file_name'])->update();
}
}
} else {
log_message('warning', 'insertMultiFilesData() received empty data array.');
}
log_message('info', 'insertMultiFilesData() completed successfully.');
return true;
}
public function removeMultiFile()
{
$id = $this->request->getGet('lead_file_id');
if (! empty($id)) {
$this->leadFilesModel->where('id', $id)->set(['is_active' => 0])->update();
return $this->respond(['status' => true, 'message' => 'File removed successfully'], 200);
} else {
return $this->respond(['status' => false, 'message' => 'File could not be removed.'], 200);
}
}
public function uploadLeadAttachment()
{
try {
$leadId = (int) $this->request->getPost('lead_id');
$docsName = trim($this->request->getPost('docs_name') ?? '');
if ($leadId <= 0) {
return $this->respond(['status' => 'error', 'code' => 400, 'message' => 'Invalid lead id'], 400);
}
if (empty($docsName)) {
return $this->respond(['status' => 'error', 'code' => 400, 'message' => 'Document label is required'], 400);
}
$file = $this->request->getFile('attachment_file');
if (! $file || ! $file->isValid() || $file->hasMoved()) {
return $this->respond(['status' => 'error', 'code' => 400, 'message' => 'Invalid or missing file'], 400);
}
$uploadFilePath = WRITEPATH . 'uploads/lead_files/';
$fileName = storage_file_Upload($file, $uploadFilePath, UPLOAD_EXT_LEAD_FILES);
if (empty($fileName)) {
return $this->respond(['status' => 'error', 'code' => 400, 'message' => 'File upload failed. Check file type or size.'], 400);
}
$this->leadFilesModel->insert([
'lead_id' => $leadId,
'docs_name' => $docsName,
'file_name' => $fileName,
]);
// Return refreshed attachment HTML
$attachments = $this->leadFilesModel
->where('lead_id', $leadId)
->where('type !=', 2)
->where('is_active', 1)
->findAll();
$attachmentHtml = view('rfq/attachment_files', ['multi_file_data' => $attachments]);
return $this->respond([
'status' => 'success',
'code' => 200,
'message' => 'File uploaded successfully',
'attachment_html' => $attachmentHtml,
], 200);
} catch (\Throwable $e) {
$this->myLogger->logme('error', 'uploadLeadAttachment: ' . $e->getMessage());
return $this->respond(['status' => 'error', 'code' => 500, 'message' => 'Upload failed'], 500);
}
}
//--------RFQ-----------------------------------------------------------------------------------------------
// public function viewRFQ($id, $type = 1)
// {
// $data['rfq_data'] = $this->RFQModel
// ->where('lead_id', $id)
// // ->where('type', $type)
// ->where('is_active', 1)
// ->orderBy('id', 'desc')
// ->first();
// // dd($data);
// $data['rfq_count'] = $this->RFQModel
// ->where('lead_id', $id)
// // ->where('type', 1)
// ->where('is_active', 1)
// ->countAllResults();
// $data['qcr_count'] = $this->RFQModel
// ->where('lead_id', $id)
// ->where('type', 2)
// ->where('is_active', 1)
// ->countAllResults();
// // dd(count($data['rfq_data']));
// $data['lead_id'] = $id;
// $lead_data = $this->leadsModel
// ->select('
// leads.*,
// policy_type.question_json,
// policy_type.policy_type,
// policy_type.long_name,
// user_profiles.email as created_person_email
// ')
// ->join('policy_type', 'leads.policy_type_id = policy_type.id')
// ->join('user_profiles', 'leads.created_by = user_profiles.id', 'left')
// ->where('leads.id', $id)
// ->where('leads.is_active', 1)
// ->first();
// // dd($lead_data);
// if ($lead_data['lead_type'] != 1) {
// $client_policy_data = $this->clientPolicyModel->where('is_active', 1)->where('id', $lead_data['source_policy_id'])->first();
// if (isset($client_policy_data)) {
// $data['policy_terms'] = $client_policy_data['policy_terms'];
// }
// }
// $data['question_json'] = $lead_data['question_json'];
// $data['page_name'] = $type == 2 ? 'QCR' : 'RFQ';
// $data['insurer'] = $this->insurerBranchModel->getInsurerBranchesWithInsurerNames();
// $data['userList'] = $this->userModel->getUserListForRFQ();
// $data['exclusiveUserList'] = $this->userModel->getexclusiveUserListForRFQ();
// $data['lead_data'] = $lead_data;
// $data['tpa_list'] = $this->tpaBranchModel->getTpaBranchesWithTpaNames();
// $data['account_manager'] = $this->userModel->where('is_active', 1)->where('role', 3)->findAll();
// $mail_content = '
//
Dear Sir,
// Greetings From Nhance India!
// Please find attached the {{RFQ_OR_QCR}} for {{POLICY_TYPE}} policy pertaining to {{CLIENT_NAME}}.
// Kindly request you to share the competitive quotes at the earliest.
// In case of any query, please feel free to contact us.
// Thank You!
// Best regards,
//
//
{{LOGGED_USER_NAME}}
//
Mobile: {{LOGGED_USER_MOBILE}}
//
//
// ';
// if ($data['lead_data']['policy_end_date'] != null) {
// $subject = "{{CLIENT_NAME}} _ {{POLICY_TYPE}} _ {{RFQ_OR_QCR}} _ {{POLICY_YEAR}} {{POLICY_END_DATE}}";
// } else {
// $subject = "{{CLIENT_NAME}} _ {{POLICY_TYPE}} _ {{RFQ_OR_QCR}} _ {{POLICY_YEAR}}";
// }
// if ($lead_data['policy_type_id'] == 1) {
// $data['totalSumAssured'] = $this->handleMemberDataGPATotalSumInsurerFromExcel(['lead_id' => $id]);
// } else {
// $data['totalSumAssured'] = [];
// }
// $data['mail_content'] = $this->transformMailContent($lead_data, $mail_content, $data['page_name']);
// $data['subject'] = $this->transformMailContent($lead_data, $subject, $data['page_name']);
// $data['placement_mail_content'] = $this->transformMailContent($lead_data, $mail_content, 'Placement');
// $data['placement_subject'] = $this->transformMailContent($lead_data, $subject, 'Placement');
// $data['multi_file_data'] = $this->leadFilesModel->where('lead_id', $id)->where('is_active', 1)->findAll() ?? null;
// $installment_data['installments'] = $this->leadInstallmentPaymentDetails->where('lead_id', $id)->where('is_active', 1)->findAll() ?? null;
// $data['lead_data']['installment_data'] = view('rfq/installment_fields', $installment_data) ?? null;
// $data['attachment_html'] = view('rfq/attachment_files', $data) ?? "";
// $data['user_team'] = $this->userModel->select("ut.team_id")->join("user_teams ut", "ut.user_id = user_profiles.id and ut.is_active = 1")->where("user_profiles.is_active", 1)->first()['team_id'];
// $data['multi_file_data_html'] = $this->renderFileFields($data['multi_file_data']);
// // dd($data);
// if ($data['lead_data']['lead_form_type'] == 1) {
// $data['demogrphy_html_data'] = $this->generateDemographyDataTable(['lead_id' => $id]);
// $this->loadLayout('view_rfq.php', $data);
// } else if ($data['lead_data']['lead_form_type'] == 2) {
// $data['occupancy'] = $this->occupancyModel->findAll();
// // dd($data['occupancy']);
// if ($lead_data['lead_type'] != 1 && $data['rfq_count'] == 0) {
// $client_policy_data = $this->clientPolicyModel->where('is_active', 1)->where('id', $lead_data['source_policy_id'])->first();
// if (isset($client_policy_data)) {
// $data['rfq_data']['json'] = $client_policy_data['placement_json'];
// }
// }
// if (!empty($data['question_json'])) {
// $data['policies'] = json_decode($data['question_json'], true)['policies'];
// $data["child_table_data"] = json_decode($data['question_json'], true)['child_table_data'];
// }
// $data['product'] = $this->leadsModel->select("policy_type.policy_type")->join('policy_type', 'leads.policy_type_id = policy_type.id')->where('leads.id', $id)->first()['policy_type'];
// // $data['lead_register_data'] = json_decode($data['lead_data']['custom_fields']);
// // dd($data['lead_register_data']);
// // $data['entity_type'] = $this->kycEntityTypeModel->where('is_active', 1)->findAll();
// $data['buisness_type'] = $this->buisnessType;
// // dd($data);
// $data['client_type'] = $this->clientType;
// $this->loadLayout('view_rfq_non_eb', $data);
// }
// }
public function viewRFQ($id, $type = 1)
{
// 1. Combine RFQ queries into a single query
$rfqStats = $this->RFQModel
->select('
COUNT(*) as total_count,
SUM(CASE WHEN type != 2 THEN 1 ELSE 0 END) as rfq_count,
SUM(CASE WHEN type = 2 THEN 1 ELSE 0 END) as qcr_count
')
->where('lead_id', $id)
->where('is_active', 1)
->first();
$data['rfq_count'] = $rfqStats['rfq_count'] ?? 0;
$data['qcr_count'] = $rfqStats['qcr_count'] ?? 0;
// 2. Get RFQ data separately (only if needed)
$data['rfq_data'] = $this->RFQModel
->where('lead_id', $id)
->where('is_active', 1)
->orderBy('id', 'desc')
->first();
$data['lead_id'] = $id;
// 3. Optimize lead data query with specific field selection
$lead_data = $this->leadsModel
->select('
leads.*,
lead_files.status as demography_file_status,
policy_type.question_json,
policy_type.policy_type,
policy_type.long_name,
user_profiles.email as created_person_email
')
->join('policy_type', 'leads.policy_type_id = policy_type.id')
->join('user_profiles', 'leads.created_by = user_profiles.id', 'left')
->join('lead_files', 'leads.id = lead_files.lead_id AND lead_files.type = 2 AND lead_files.is_active = 1', 'left')
->where('leads.id', $id)
->where('leads.is_active', 1)
->first();
if (! $lead_data) {
// Handle case where lead doesn't exist
throw new \Exception('Opportunity not found');
}
// 4. Conditional query - only fetch if needed
if (! in_array((int) $lead_data['lead_type'], [1, 3], true)) {
$client_policy_data = $this->clientPolicyModel
->select('policy_terms, placement_json')
->where('is_active', 1)
->where('id', $lead_data['source_policy_id'])
->first();
if ($client_policy_data) {
$data['policy_terms'] = $client_policy_data['policy_terms'];
// Store for later use in lead_form_type == 2 condition
if ($lead_data['lead_form_type'] == 2 && $data['rfq_count'] == 0) {
$data['rfq_data']['json'] = $client_policy_data['placement_json'];
}
}
}
// 5. Cache decoded JSON to avoid multiple decodes
$question_json_decoded = ! empty($lead_data['question_json'])
? json_decode($lead_data['question_json'], true)
: null;
$data['question_json'] = $lead_data['question_json'];
$data['tab_name'] = $type == 2 ? 'QCR' : 'RFQ';
$data['page_name'] = $type == 2 ? 'QCR' : 'RFQ';
$data['lead_data'] = $lead_data;
// 6. Parallel/batch data fetching for independent queries
$data['insurer'] = $this->insurerBranchModel->getInsurerBranchesWithInsurerNames();
$data['userList'] = $this->userModel->getUserListForRFQ();
$data['exclusiveUserList'] = $this->userModel->getexclusiveUserListForRFQ();
$data['tpa_list'] = $this->tpaBranchModel->getTpaBranchesWithTpaNames();
$data['account_manager'] = $this->userModel
->where('is_active', 1)
->where('role', 3)
->findAll();
// 7. Define mail templates as constants or config
$mail_content = $this->getMailTemplate();
$placement_mail_content = $this->getMailTemplate('placement');
$subject = $this->getSubjectTemplate($lead_data);
// 8. Pre-calculate sum assured only for GPA
$data['totalSumAssured'] = ($lead_data['policy_type_id'] == 1) ? $this->handleMemberDataGPATotalSumInsurerFromExcel(['lead_id' => $id]) : [];
// 9. Transform mail content once
$data['mail_content'] = $this->transformMailContent($lead_data, $mail_content, $data['page_name']);
$data['subject'] = $this->transformMailContent($lead_data, $subject, $data['page_name']);
$data['placement_mail_content'] = $this->transformMailContent($lead_data, $placement_mail_content, 'Placement');
$data['placement_subject'] = $this->transformMailContent($lead_data, $subject, 'Placement');
// 10. Combine related queries
$data['multi_file_data'] = $this->leadFilesModel
->where('lead_id', $id)
->where('type !=', 2)
->where('is_active', 1)
->findAll();
$installment_data['installments'] = $this->leadInstallmentPaymentDetails
->where('lead_id', $id)
->where('is_active', 1)
->findAll();
// 11. Generate views efficiently
$data['lead_data']['installment_data'] = ! empty($installment_data['installments'])
? view('rfq/installment_fields', $installment_data)
: null;
$data['attachment_html'] = view('rfq/attachment_files', $data);
$user_team = $this->userModel
->select("ut.team_id")
->join("user_teams ut", "ut.user_id = user_profiles.id and ut.is_active = 1")
->where("user_profiles.is_active", 1)
->first();
$data['user_team'] = $user_team['team_id'] ?? null;
$data['multi_file_data_html'] = $this->renderFileFields($data['multi_file_data']);
// 12. Conditional rendering based on lead_form_type
if ($lead_data['lead_form_type'] == 1) {
// $data['demogrphy_html_data'] = $this->generateDemographyDataTable(['lead_id' => $id]);
$this->loadLayout('view_rfq.php', $data);
} else if ($lead_data['lead_form_type'] == 2) {
$data['occupancy'] = $this->occupancyModel->findAll();
// Use cached decoded JSON
if ($question_json_decoded) {
$data['policies'] = $question_json_decoded['policies'] ?? [];
$data["child_table_data"] = $question_json_decoded['child_table_data'] ?? [];
}
$data['product'] = $lead_data['policy_type'];
$data['buisness_type'] = $this->buisnessType;
$data['client_type'] = $this->clientType;
$this->loadLayout('view_rfq_non_eb', $data);
}
}
// 13. Extract mail template to separate method for reusability
private function getMailTemplate($template_type = "")
{
if ($template_type == 'placement') {
return '
Dear Sir,
Greetings From Nhance India!
Please find attached the {{RFQ_OR_QCR}} for {{POLICY_TYPE}} policy pertaining to {{CLIENT_NAME}}.
Kindly request you to issue the policy at the earliest.
In case of any query, please feel free to contact us.
Thank You!
Best regards,
{{LOGGED_USER_NAME}}
Mobile: {{LOGGED_USER_MOBILE}}
';
} else {
return '
Dear Sir,
Greetings From Nhance India!
Please find attached the {{RFQ_OR_QCR}} for {{POLICY_TYPE}} policy pertaining to {{CLIENT_NAME}}.
Kindly request you to share the competitive quotes at the earliest.
In case of any query, please feel free to contact us.
Thank You!
Best regards,
{{LOGGED_USER_NAME}}
Mobile: {{LOGGED_USER_MOBILE}}
';
}
}
// 14. Extract subject template to separate method
private function getSubjectTemplate($lead_data)
{
return $lead_data['policy_end_date'] != null
? "{{CLIENT_NAME}} _ {{POLICY_TYPE}} _ {{RFQ_OR_QCR}} _ {{POLICY_YEAR}} {{POLICY_END_DATE}}"
: "{{CLIENT_NAME}} _ {{POLICY_TYPE}} _ {{RFQ_OR_QCR}} _ {{POLICY_YEAR}}";
}
public function savePolicyInfo()
{
$data = $this->request->getPost();
$lead_id = $data['lead_id'];
$json_data = $data['registration_json'];
$existingJson = $this->RFQModel->where('is_active', 1)->where("lead_id", $lead_id)->first()['registration_json'] ?? null;
if (empty($existingJson)) {
$this->RFQModel
->where('lead_id', $lead_id)
->where('type', 1)
->where('is_active', 1)
->set('is_active', 0)
->update();
$insertData['lead_id'] = $lead_id;
$insertData['registration_json'] = json_encode($json_data);
$this->RFQModel->insert($insertData);
return $this->respond(['status' => true, "message" => "Policy Inforamtion saved"]);
} else {
return $this->respond(['status' => true, "message" => "Policy Inforamtion Already saved"]);
}
}
public function createRFQ()
{
$data = $this->request->getPost();
$lead_id = $data['lead_id'] ?? null;
if (! $lead_id) {
return $this->respond(['status' => false, 'message' => 'Opportunity ID is required'], 400);
}
$rfq_created = $this->RFQModel->where('lead_id', $lead_id)->where('is_active', 1)->countAllResults();
$qcr_created = false;
$inputJson = json_decode($data['json'], true);
if (isset($inputJson['premium_data']) && ! empty($inputJson['premium_data'])) {
$sortedJson = $this->reorderProposalsByInsurerTotal($inputJson);
$data['json'] = json_encode($sortedJson);
$qcr_created = true;
}
$quote_received_insurer = [];
if (($data['submit_type'] ?? "") === 'QCR') {
$inputJson = json_decode($data['json'], true);
$proposal_data = $inputJson['proposal_data'] ?? array_pop($inputJson);
if (isset($proposal_data['over_all_column_data'])) {
$proposel_count = $proposal_data['over_all_column_data'];
$this->myLogger->logme("error", "insurer id : " . json_encode($proposel_count));
foreach ($proposel_count as $key => $value) {
if (! empty($value['insurers'])) {
$qcr_created = true;
foreach ($value['insurers'] as $insurer => $id) {
if (! empty($id['id'])) {
$quote_received_insurer[] = $id['id'];
}
}
}
}
}
}
$this->myLogger->logme("error", "quote_received_insurer : " . json_encode($quote_received_insurer));
$data['type'] = 1;
//update the quote received insurer data to the lead
if (count($quote_received_insurer) != 0) {
$this->leadsModel->update($lead_id, ['quote_received_insurer' => json_encode($quote_received_insurer ?? [])]);
$this->myLogger->logme("error", "quote_received_insurer data updated in the lead: " . json_encode($quote_received_insurer));
}
// Deactivate existing RFQs for this lead and type
if (! isset($data['rfq_primaryKey']) && empty($data['rfq_primaryKey'])) {
$this->RFQModel
->where('lead_id', $lead_id)
->where('type', 1)
->where('is_active', 1)
->set('is_active', 0)
->update();
}
// Handle registration_json
if (empty($data['registration_json'])) {
// Fetch the latest registration_json if not provided
$latest = $this->RFQModel
->select('registration_json')
->where('lead_id', $lead_id)
->orderBy('id', 'DESC')
->first();
if (! empty($latest['registration_json'])) {
$data['registration_json'] = $latest['registration_json'];
}
}
// print_r($data); die;
// $data['json'] = "";
if (isset($data['rfq_primaryKey']) && ! empty($data['rfq_primaryKey'])) {
$this->RFQModel->update($data['rfq_primaryKey'], $data);
$insertId = $data['rfq_primaryKey'];
$affectedRows = db_connect()->affectedRows();
} else {
// Insert new RFQ
$insertId = $this->RFQModel->insert($data);
}
if ($insertId) {
if (empty($rfq_created)) {
$this->leadsModel->update($lead_id, ['status' => "rfq_created"]);
}
if ($qcr_created == true) {
$this->leadsModel
->where('id', $lead_id)
->whereNotIn('status', ['qcr_sent', 'lost', 'co_insurer_pending', 'won', 'completed_with_corrections', 'completed_without_corrections'])
->set(['status' => "qcr_created"])
->update();
}
$message = ($data['submit_type'] ?? '') == 'QCR' ? 'QCR saved successfully' : 'RFQ saved successfully';
return $this->respond([
'status' => true,
'id' => $insertId,
'message' => $message,
'data' => $data,
'affectedRows' => $affectedRows ?? null,
], 200);
}
$message = ($data['submit_type'] ?? '') == 'QCR' ? 'Failed to save QCR' : 'Failed to save RFQ';
return $this->respond([
'status' => false,
'id' => null,
'message' => $message,
'data' => $data,
], 200);
}
public function createQCR()
{
$data = $this->request->getPost();
$lead_id = $data['lead_id'];
$data['type'] = 2;
$this->RFQModel
->where('lead_id', $lead_id)
->where('type', 2)
->where('is_active', 1)
->set('is_active', 0)
->update();
$result = $this->RFQModel->insert($data);
if ($result) {
return $this->respond(['status' => true, 'id' => $result, 'message' => 'QCR created successfully', 'data' => $data], 200);
}
return $this->respond(['status' => false, 'id' => $result, 'message' => "Failed to create QCR", 'data' => $data], 200);
}
public function reorderProposalsByInsurerTotal(array $data): array
{
if (! isset($data['premium_data']['data'])) {
return $data;
}
$original = $data['premium_data']['data'];
$proposals = [];
$others = [];
$emptyKeyData = [];
foreach ($original as $key => $value) {
// Match only keys that look like 'Proposal X'
if (preg_match('/^(Proposal\s+\d+|Existing Renewal|Existing Rollover)$/', $key)) {
// Get the insurer entry (not 'Quote Asked')
foreach ($value as $subKey => $subVal) {
if ($subKey !== 'Quote Asked' && isset($subVal['Total'])) {
$proposals[$key] = $value;
break;
} else {
$proposals[$key] = $value;
}
}
} else {
if ($key === '' && isset($value['']) && is_array($value[''])) {
// Capture empty key to push it later
$emptyKeyData[$key] = $value;
} else {
$others[$key] = $value;
}
}
}
foreach ($proposals as $proposalKey => &$proposal) {
$quoteAsked = $proposal['Quote Asked']; // Keep Quote Asked separately
unset($proposal['Quote Asked']);
uasort($proposal, function ($a, $b) {
return $a['Total'] <=> $b['Total']; // Ascending by Total
});
// Add Quote Asked back at the top
$proposal = array_merge(['Quote Asked' => $quoteAsked], $proposal);
}
// Sort proposals by their insurer's total
uasort($proposals, function ($a, $b) {
$totalA = 0;
$totalB = 0;
foreach ($a as $key => $val) {
if ($key !== 'Quote Asked' && isset($val['Total'])) {
$totalA = floatval($val['Total']);
break;
}
}
foreach ($b as $key => $val) {
if ($key !== 'Quote Asked' && isset($val['Total'])) {
$totalB = floatval($val['Total']);
break;
}
}
return $totalA <=> $totalB;
});
// Merge back the sorted proposals into the full structure
$data['premium_data']['data'] = array_merge($others, $proposals, $emptyKeyData);
$data = $this->reorderProposalDataByPremiumOrder($data);
return $data;
}
private function reorderProposalDataByPremiumOrder(array $data): array
{
if (! isset($data['premium_data']['data'], $data['proposal_data']['over_all_column_data'])) {
return $data;
}
$porposalData = $data['premium_data']['data'];
$premiumProposals = array_keys($data['premium_data']['data']);
$filteredProposals = [];
//Reorder the Insurer based on the Premium data insurer
foreach ($porposalData as $key => $value) {
if (in_array($key, ['Particulars', ""])) {
continue;
}
// Ensure the premium data is an array and not empty
if (! is_array($value) || empty($value)) {
continue;
}
// Step 1: Get the premium display_name order (excluding "Quote Asked")
$premiumOrder = array_keys(array_diff_key($value, ["Quote Asked" => ""]));
// Skip if no valid order found
if (empty($premiumOrder)) {
continue;
}
// usort($data['proposal_data']['over_all_column_data'][$key]['insurers'], function($a, $b) use ($premiumOrder) {
// return array_search($a['display_name'], $premiumOrder) - array_search($b['display_name'], $premiumOrder);
// });
// Step 2: Reorder proposal insurers based on premium order
usort($data['proposal_data']['over_all_column_data'][$key]['insurers'], function ($a, $b) use ($premiumOrder) {
$posA = array_search($a['display_name'] ?? '', $premiumOrder);
$posB = array_search($b['display_name'] ?? '', $premiumOrder);
// If not found, push to the end
$posA = $posA === false ? PHP_INT_MAX : $posA;
$posB = $posB === false ? PHP_INT_MAX : $posB;
return $posA - $posB;
});
}
// Collect proposal keys that match the pattern "Proposal X"
foreach ($premiumProposals as $key) {
if (preg_match('/^(Proposal\s+\d+|Existing Renewal|Existing Rollover)$/', $key) && isset($data['proposal_data']['over_all_column_data'][$key])) {
$filteredProposals[$key] = $data['proposal_data']['over_all_column_data'][$key];
}
}
// dd($premiumProposals, $filteredProposals);
$data['proposal_data']['over_all_column_data'] = $filteredProposals;
$data = $this->reorderProposalInHeaderAndData($data);
return $data;
}
private function reorderProposalInHeaderAndData(array $data): array
{
$tableData = $data['table_data'];
$sortedProposalOrder = $data['proposal_data']['over_all_column_data'];
$headers = $tableData['headers'] ?? [];
$dataRows = $tableData['data'] ?? [];
// Step 1: Separate static and proposal headers
$staticHeaders = [];
$proposalHeaders = [];
$actionHeader = [];
foreach ($headers as $header) {
if (in_array($header['parentHeader'], array_keys($sortedProposalOrder))) {
$proposalHeaders[$header['parentHeader']] = $header;
} else {
if ($header['parentHeader'] == "Action") {
$actionHeader[] = $header;
} else {
$staticHeaders[] = $header;
}
}
}
//Reorder the SubHeader of the Proposel
foreach ($sortedProposalOrder as $proposalKey => $proposalData) {
// Skip if insurers or subHeaders are missing
if (
! isset($proposalData['insurers']) ||
! is_array($proposalData['insurers']) ||
! isset($proposalHeaders[$proposalKey]['subHeaders']) ||
! is_array($proposalHeaders[$proposalKey]['subHeaders'])
) {
continue;
}
// Keep "Quote Asked" fixed at index 0
$newSubHeaders = ["Quote Asked"];
// Add insurers in the sorted order
foreach ($proposalData['insurers'] as $insurer) {
if (in_array($insurer['display_name'], $proposalHeaders[$proposalKey]['subHeaders'])) {
$newSubHeaders[] = $insurer['display_name'];
}
}
// Replace the subHeaders in the header array
$proposalHeaders[$proposalKey]['subHeaders'] = $newSubHeaders;
}
// dd($staticHeaders, $proposalHeaders, $sortedProposalOrder);
// Step 2: Reorder headers
$reorderedHeaders = [];
foreach ($sortedProposalOrder as $proposalKey => $proposalValue) {
if (isset($proposalHeaders[$proposalKey])) {
$reorderedHeaders[] = $proposalHeaders[$proposalKey];
}
}
// print_rr($reorderedHeaders); die;
$proposel_count = 1;
foreach ($reorderedHeaders as $key => &$value) {
if (! in_array($value['parentHeader'], ["Existing Renewal", "Existing Rollover"])) {
$value['parentHeader'] = 'Proposal ' . $proposel_count;
$proposel_count++;
}
}
unset($value);
//merge the all headers
$reorderedHeaders = array_merge($staticHeaders, $reorderedHeaders, $actionHeader);
// Step 3: Reorder each row's `data` by matching parentth
foreach ($dataRows as $dataRowIndex => &$row) {
$staticData = [];
$proposalData = [];
$actionData = [];
foreach ($row['data'] as $entry) {
if (in_array($entry['parentth'], array_keys($sortedProposalOrder))) {
$proposalData[$entry['parentth']][] = $entry;
} else {
if ($entry['parentth'] == "Action") {
$actionData[] = $entry;
} else {
$staticData[] = $entry;
}
}
}
$reorderedProposalData = [];
foreach ($sortedProposalOrder as $proposalKey => $proposalValue) {
if (isset($proposalData[$proposalKey])) {
foreach ($proposalData[$proposalKey] as $entry) {
$reorderedProposalData[] = $entry;
}
}
}
$dubParTh = "";
$increament = 0;
foreach ($reorderedProposalData as $key => &$value) {
if ($dubParTh == $value['parentth']) {
if (! in_array($value['parentth'], ["Existing Renewal", "Existing Rollover"])) {
$value['parentth'] = 'Proposal ' . ($increament);
}
} else {
$dubParTh = $value['parentth'];
if (! in_array($value['parentth'], ["Existing Renewal", "Existing Rollover"])) {
$increament = $increament + 1;
$value['parentth'] = 'Proposal ' . ($increament);
}
}
}
unset($value);
//Reorder the Row Data based on the Insurer
$reorderedProposalData = $this->reorderProposalRowData($reorderedProposalData, $proposalHeaders);
// print_rr($reorderedProposalData); die;
$row['data'] = array_merge($staticData, $reorderedProposalData, $actionData);
}
$data['table_data']['headers'] = $reorderedHeaders;
$data['table_data']['data'] = $dataRows;
$renumberedArray = $this->renumberProposalKeys($data['proposal_data']['over_all_column_data']);
$updatedDataSet = $this->renumberProposalKeys($data['premium_data']['data']);
$data['proposal_data']['over_all_column_data'] = ! empty($renumberedArray) ? $renumberedArray : $data['proposal_data']['over_all_column_data'];
$data['premium_data']['data'] = ! empty($updatedDataSet) ? $updatedDataSet : $data['premium_data']['data'];
return $data;
}
private function reorderProposalRowData(array $proposalRowData, array $proposalHeaderData): array
{
$newRowData = [];
// Group rows by parentth
$groupedRows = [];
foreach ($proposalRowData as $row) {
if (! isset($row['parentth'])) {
continue; // skip invalid rows
}
$groupedRows[$row['parentth']][] = $row;
}
// Reorder each group's rows based on header subHeaders
foreach ($proposalHeaderData as $proposalKey => $headerInfo) {
if (
! isset($groupedRows[$proposalKey]) ||
! isset($headerInfo['subHeaders']) ||
! is_array($headerInfo['subHeaders'])
) {
continue;
}
$order = $headerInfo['subHeaders'];
$rows = $groupedRows[$proposalKey];
// Sort whole rows according to subth position in $order
usort($rows, function ($a, $b) use ($order) {
return array_search($a['subth'], $order) - array_search($b['subth'], $order);
});
// Append sorted rows to final array
$newRowData = array_merge($newRowData, $rows);
}
return $newRowData;
}
private function renumberProposalKeys(array $input): array
{
$result = [];
$counter = 1;
foreach ($input as $key => $value) {
if (strpos($key, 'Proposal') === 0) {
$newKey = 'Proposal ' . $counter++;
$result[$newKey] = $value;
} else {
$result[$key] = $value;
}
}
return $result;
}
private function saveLeadInstallmentDetails(int $lead_id, $installments): array
{
if (is_string($installments)) {
$installments = json_decode($installments, true) ?? [];
}
if (! is_array($installments) || empty($installments)) {
return [];
}
$existingInstallments = $this->leadInstallmentPaymentDetails
->where('lead_id', $lead_id)
->where('is_active', 1)
->orderBy('id', 'ASC')
->findAll();
$savedInstallments = [];
foreach ($installments as $index => $installment) {
if (! is_array($installment)) {
continue;
}
$utrNo = isset($installment['utr_no']) ? trim((string) $installment['utr_no']) : '';
$data = [
'lead_id' => $lead_id,
'installment_amount' => $installment['installment_amount'] ?? null,
'payment_date' => ! empty($installment['payment_date'])
? change_date_format($installment['payment_date'])
: null,
'utr_no' => $utrNo !== '' ? $utrNo : null,
];
$installmentId = ! empty($installment['id']) ? (int) $installment['id'] : null;
if (! $installmentId && isset($existingInstallments[$index]['id'])) {
$installmentId = (int) $existingInstallments[$index]['id'];
}
if ($installmentId) {
$this->leadInstallmentPaymentDetails->update($installmentId, $data);
$data['id'] = $installmentId;
} else {
$data['is_active'] = 1;
$newId = $this->leadInstallmentPaymentDetails->insert($data);
$data['id'] = $newId;
}
$savedInstallments[] = $data;
}
return $savedInstallments;
}
public function removeInstallments()
{
$id = $this->request->getGet('id');
// print_r($id); die;
if (! empty($id)) {
$this->leadInstallmentPaymentDetails->whereIn('id', $id)->set(['is_active' => 0])->update();
} else {
return $this->respond(['status' => false, 'message' => 'Could not be removed.'], 200);
}
if ($this->request->getGet('lead_id')) {
$lead_id = $this->request->getGet('lead_id');
$data = [
'no_of_installment' => 0,
'is_installment' => 0,
];
$this->leadsModel->where('id', $lead_id)->set($data)->update();
}
return $this->respond(['status' => true, 'message' => 'Removed successfully'], 200);
}
//-----RFQ and QCR EXPORT------------------------------------------------------------------------------------------------
//export main route function
public function exportQCRandRFQ($lead_id, $type, $lead_form_type)
{
$this->exportExcelForQCRandRFQ($lead_id, $type, $lead_form_type);
}
//FOR EXCEL
public function exportExcelForQCRandRFQ($lead_id, $type, $lead_form_type = 1)
{
if ($lead_form_type == 2) {
$filepath = $this->constructNonEbExcelToSaveTemp($lead_id, $type);
} else {
$filepath = $this->constructExcelToSaveTemp($lead_id, $type);
}
$lead_data = $this->RFQModel->getRFQTableDataWithLeadIDAndType($lead_id, $type);
// dd($filepath);
//Excel merging part
if ($lead_data['file_name'] != null && $lead_data['file_name'] != '') {
$temp_file_path = $filepath['filePath'];
$temp_file_name = $filepath['fileName'];
$lead_file_path = storage_ensure_local_file(WRITEPATH . 'uploads/lead_files', $lead_data['file_name']);
// dd($lead_data, $temp_file_path, $temp_file_name, $lead_file_path);
if (! empty($lead_file_path) && is_file($lead_file_path)) {
$filePaths = [
['file_path' => $temp_file_path, 'sheets' => []],
['file_path' => $lead_file_path, 'sheets' => []],
];
// $outputPath = dirname($temp_file_path) . '/' . 'merged_' . $temp_file_name;
$result = ExcelMergeHelper::mergeExcelFiles($filePaths, $temp_file_path);
// print_rr($result);
}
}
$filepath = $filepath['filePath'];
if (file_exists($filepath)) {
// Set headers to force download
header('Content-Description: File Transfer');
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment; filename="' . basename($filepath) . '"');
header('Content-Length: ' . filesize($filepath));
header('Pragma: public');
// Output the file content
readfile($filepath);
// Delete the file after download
unlink($filepath);
exit;
} else {
echo "File does not exist.";
}
}
//Construct excel file and save the file to the folder and return file path
public function constructExcelToSaveTemp($lead_id, $type, $propsal_and_insurer = null)
{
helper('excel_util_helper');
$rfq_data = $this->RFQModel->getRFQTableDataWithLeadIDAndType($lead_id, $type);
$claim_history = $rfq_data['claim_history'] ?? 0;
$is_placement = false;
$length = 0;
// dd($rfq_data, $lead_id, $type, $propsal_and_insurer);
// print_r($propsal_and_insurer); die;
// print_r($is_placement); die;
$data = json_decode($rfq_data['json'], true);
$sheetName = 'Worksheet';
if ($type == 2) {
$sheetName = 'QCR';
// $propsal_and_insurer = "Proposal 1-ICICIPRU-ICICI001";
$data = $this->convertJsonForQCR($data, $type);
if ($propsal_and_insurer !== null) {
list($proposal_key, $insurer_key) = explode('-', $propsal_and_insurer, 2);
$data = $this->transformProposelData($data, $proposal_key, $insurer_key);
$is_placement = true;
$sheetName = 'Placement';
}
} else if ($type == 1) {
$sheetName = 'RFQ';
$data = $this->convertJsonForQCR($data, $type);
} // dd($data);
$lead_data = [];
if (in_array((int) $rfq_data['lead_type'], [1], true)) {
if (in_array($rfq_data['policy_type_id'], [2, 3, 4, 5])) {
$lead_data = [
'Insured' => $rfq_data['client_name'],
'Policy Type' => $this->leadType[$rfq_data['lead_type']] ?? "",
'No of Employees' => $rfq_data['incept_emp_count'],
'No of Dependents' => $rfq_data['incept_dept_count'],
'Total Lives' => $rfq_data['incept_no_of_lives'],
// 'Period of Insurance ' => ! empty($rfq_data['policy_start_date']) && ! empty($rfq_data['policy_end_date']) ? date('d-m-Y', strtotime($rfq_data['policy_start_date'])) . ' to ' . date('d-m-Y', strtotime($rfq_data['policy_end_date'])) : "To be decided",
// 'Insurer' => $rfq_data['insurer_name'] ?? " - ",
// 'TPA' => $rfq_data['tpa_name'] ?? " - ",
// 'Policy Run Days' => $rfq_data['policy_run_days'],
];
} else if (in_array($rfq_data['policy_type_id'], [1, 6, 7])) {
$lead_data = [
'Insured' => $rfq_data['client_name'],
'No of Employees at Inception' => $rfq_data['incept_emp_count'],
// 'Total Sum Insured at Inception ' => $rfq_data['total_si_at_incept'],
// 'Policy Period' => ! empty($rfq_data['policy_start_date']) && ! empty($rfq_data['policy_end_date']) ? date('d-m-Y', strtotime($rfq_data['policy_start_date'])) . ' to ' . date('d-m-Y', strtotime($rfq_data['policy_end_date'])) : "To be decided",
// 'Insurer' => $rfq_data['insurer_name'] ?? " - ",
// 'TPA' => $rfq_data['tpa_name'] ?? " - ",
'Policy Type' => $this->leadType[$rfq_data['lead_type']] ?? "",
'Mortality Experience for last 3 years' => $claim_history == "1" ? "Mentioned in Claims sheet" : "Nil",
];
}else {
$lead_data = [
'Insured' => $rfq_data['client_name'],
'Policy Type' => $this->leadType[$rfq_data['lead_type']] ?? ""
];
}
} else {
if (in_array($rfq_data['policy_type_id'], [2, 3, 4, 5])) {
if ($is_placement == true) {
$lead_data = [
'Insured' => $rfq_data['client_name'],
'Policy Type' => $this->leadType[$rfq_data['lead_type']] ?? "",
// 'No of Employees at Inception' => $rfq_data['incept_emp_count'],
// 'No of Dependents at Inception' => $rfq_data['incept_dept_count'],
// 'Total Lives at Inception ' => $rfq_data['incept_no_of_lives'],
// 'No of Employees at Expiry' => $rfq_data['exp_emp_count'],
// 'No of Dependents at Expiry' => $rfq_data['exp_dept_count'],
// 'Total Lives at Expiry ' => $rfq_data['exp_no_of_lives'],
'No of Employees at Renewal' => $rfq_data['renewal_emp_count'],
'No of Dependents at Renewal' => $rfq_data['renewal_dept_count'],
'Total Lives at Renewal ' => $rfq_data['renewal_no_of_lives'],
'Period of Insurance ' => ! empty($rfq_data['policy_start_date']) && ! empty($rfq_data['policy_end_date']) ? date('d-m-Y', strtotime($rfq_data['policy_start_date'])) . ' to ' . date('d-m-Y', strtotime($rfq_data['policy_end_date'])) : "To be decided",
'Insurer' => $rfq_data['insurer_name'] ?? " - ",
'TPA' => $rfq_data['tpa_name'] ?? " - ",
// 'Policy Run Days' => $rfq_data['policy_run_days'],
// 'Inception Premium' => formatIndianCurrency($rfq_data['premium_at_inception']),
// 'Premium as on (' . date('d-m-Y', strtotime($rfq_data['incurred_claims_date'])) . ')' => formatIndianCurrency($rfq_data['premium_date']),
// 'Earned Premium' => formatIndianCurrency(intval($rfq_data['earned_premium'])),
// 'Incurred Claims as on (' . date('d-m-Y', strtotime($rfq_data['incurred_claims_date'])) . ')' => formatIndianCurrency($rfq_data['incurred_claims']),
// 'Annualised Claims' => formatIndianCurrency(intval($rfq_data['annualised_claims'])),
// 'Incurred Claims Ratio' => $rfq_data['incurred_claims_ratio'] . " %",
// 'Earned Claims Ratio' => $rfq_data['earned_claims_ratio'] . " %",
];
} else {
$lead_data = [
'Insured' => $rfq_data['client_name'],
'Policy Type' => $this->leadType[$rfq_data['lead_type']] ?? "",
'No of Employees at Inception' => $is_placement ? '-' : $rfq_data['incept_emp_count'],
'No of Dependents at Inception' => $is_placement ? '-' : $rfq_data['incept_dept_count'],
'Total Lives at Inception ' => $is_placement ? '-' : $rfq_data['incept_no_of_lives'],
'No of Employees at Expiry' => $is_placement ? '-' : $rfq_data['exp_emp_count'],
'No of Dependents at Expiry' => $is_placement ? '-' : $rfq_data['exp_dept_count'],
'Total Lives at Expiry ' => $is_placement ? '-' : $rfq_data['exp_no_of_lives'],
'No of Employees at Renewal' => $rfq_data['renewal_emp_count'],
'No of Dependents at Renewal' => $rfq_data['renewal_dept_count'],
'Total Lives at Renewal ' => $rfq_data['renewal_no_of_lives'],
'Period of Insurance ' => ! empty($rfq_data['policy_start_date']) && ! empty($rfq_data['policy_end_date'])
? date('d-m-Y', strtotime($rfq_data['policy_start_date'])) . ' to ' . date('d-m-Y', strtotime($rfq_data['policy_end_date']))
: "To be decided",
'Insurer' => $rfq_data['insurer_name'] ?? " - ",
'TPA' => $rfq_data['tpa_name'] ?? " - ",
'Policy Run Days' => $is_placement ? '-' : $rfq_data['policy_run_days'],
'Inception Premium' => $is_placement ? '-' : formatIndianCurrency($rfq_data['premium_at_inception']),
'Premium as on (' . date('d-m-Y', strtotime($rfq_data['incurred_claims_date'])) . ')' => $is_placement ? '-' : formatIndianCurrency($rfq_data['premium_date']),
'Earned Premium' => $is_placement ? '-' : formatIndianCurrency(intval($rfq_data['earned_premium'])),
'Incurred Claims as on (' . date('d-m-Y', strtotime($rfq_data['incurred_claims_date'])) . ')' => $is_placement ? '-' : formatIndianCurrency($rfq_data['incurred_claims']),
'Annualised Claims' => $is_placement ? '-' : formatIndianCurrency(intval($rfq_data['annualised_claims'])),
'Incurred Claims Ratio' => $is_placement ? '-' : $rfq_data['incurred_claims_ratio'] . " %",
'Earned Claims Ratio' => $is_placement ? '-' : $rfq_data['earned_claims_ratio'] . " %",
];
}
} else if (in_array($rfq_data['policy_type_id'], [1, 6, 7])) {
$lead_data = [
'Insured' => $rfq_data['client_name'],
'No of Employees at Inception' => $rfq_data['incept_emp_count'],
'Total Sum Insured at Inception' => $rfq_data['total_si_at_incept'],
'Premium at inception' => $rfq_data['premium_at_incept'],
'Policy Period' => ! empty($rfq_data['policy_start_date']) && ! empty($rfq_data['policy_end_date']) ? date('d-m-Y', strtotime($rfq_data['policy_start_date'])) . ' to ' . date('d-m-Y', strtotime($rfq_data['policy_end_date'])) : "To be decided",
'Policy Type' => $this->leadType[$rfq_data['lead_type']] ?? "",
'Existing Insurer' => $rfq_data['insurer_name'],
'No of Employees at Renewal' => $rfq_data['renewal_emp_count'],
'Total Sum Insured at Renewal' => $rfq_data['total_si_at_renewal'],
'Claims Experience for last 3 years' => $claim_history == "1" ? "Mentioned in Claims sheet" : "Nil",
// 'Insurer' => $rfq_data['insurer_name'] ?? " - ",
// 'TPA' => $rfq_data['tpa_name'] ?? " - ",
// 'Total Lives at Inception' => $rfq_data['total_lives_at_incept'],
];
} else {
$lead_data = [
'Insured' => $rfq_data['client_name'],
'Policy Type' => $this->leadType[$rfq_data['lead_type']] ?? ""
];
}
}
// print_r($lead_data); die;
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->setTitle($sheetName);
// Start with lead_data at the top
$rowNumber = 1;
$mergeRange1 = "A{$rowNumber}:B{$rowNumber}";
// $sheet->mergeCells($mergeRange1);
$sheet->setCellValue("A{$rowNumber}", "Nhance India Insurance Broking Pvt Ltd");
$sheet->getStyle("A{$rowNumber}")->applyFromArray([
'font' => [
'bold' => true,
'size' => 23,
],
'alignment' => [
'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
],
]);
// Apply center alignment to the cell
// $sheet->getStyle("C{$rowNumber}")->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
// $sheet->getStyle("C{$rowNumber}")->getAlignment()->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER);
$rowNumber = $rowNumber + 1;
// Initialize max width tracking variables
$maxWidthA = 0;
$maxWidthB = 0;
// dd($lead_data);
if(! empty($lead_data)) {
foreach ($lead_data as $key => $value) {
// Merge A:B for key and C:D for value
$mergeRangeKey = "A{$rowNumber}:B{$rowNumber}";
// $mergeRangeValue = "C{$rowNumber}";
$sheet->mergeCells($mergeRangeKey);
// $sheet->mergeCells($mergeRangeValue);
// Set values in merged cells
$sheet->setCellValue("A{$rowNumber}", $key);
$sheet->setCellValue("C{$rowNumber}", $value);
// // Apply styles for alignment and bold text in A:B
// $sheet->getStyle($mergeRangeKey)->applyFromArray([
// 'font' => ['bold' => true],
// 'alignment' => [
// 'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
// 'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
// ],
// ]);
// Apply bold style to B column if the key is "Insured"
if ($key == "Insured") {
$sheet->getStyle("C{$rowNumber}")->applyFromArray([
'font' => ['bold' => true],
]);
}
// Track max width needed for columns
$maxWidthA = max($maxWidthA, mb_strlen($key));
$maxWidthB = max($maxWidthB, mb_strlen($value));
$rowNumber++;
}
}
// Set column width based on max content length (adjusted for padding)
$sheet->getColumnDimension('A')->setWidth($maxWidthA * 1.2);
// $sheet->getColumnDimension('B')->setWidth($maxWidthA * 1.5);
$sheet->getColumnDimension('C')->setWidth(35);
// $rowNumber += 2;
// Add headers and subheaders
$headers = $data['table_data']['headers'];
$sheet->setCellValue("A{$rowNumber}", "Details of Coverage");
$sheet->getStyle("A{$rowNumber}")->applyFromArray([
'font' => [
'bold' => true,
],
'fill' => [
'fillType' => Fill::FILL_SOLID,
'startColor' => ['rgb' => 'ADD8E6'],
],
'borders' => [
'allBorders' => [
'borderStyle' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN,
'color' => ['argb' => 'FF000000'], // Black color
],
],
]);
if ($type == 2) {
$subheader_count = array_sum(
array_map(
fn($header) => count(array_filter(
$header['subHeaders'],
fn($sub) => $sub !== "Quote Asked"
)),
$data['table_data']['headers']
)
);
} else {
$subheader_count = array_sum(array_map(fn($header) => count($header['subHeaders']), $data['table_data']['headers']));
}
if ($is_placement == false) {
$subheader_count = $subheader_count - 2;
}
$headerCount = count($data['table_data']['headers']);
$lastColumn = Coordinate::stringFromColumnIndex($subheader_count);
// dd( $headerCount, $lastColumn);
// Merge cells from A to the last column
$mergeRange = "A{$rowNumber}:{$lastColumn}{$rowNumber}";
$sheet->getStyle($mergeRange)->applyFromArray([
'borders' => [
'allBorders' => [
'borderStyle' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN,
'color' => ['argb' => 'FF000000'], // Black color
],
],
]);
$sheet->mergeCells($mergeRange);
$rowNumber_for_remove_quote_asked = $rowNumber;
$rowNumber = $rowNumber + 1;
if ($type == 2) {
$subHeaderRow = $rowNumber + 1;
} else {
// $subHeaderRow = $rowNumber_for_remove_quote_asked;
$subHeaderRow = $rowNumber + 1;
}
$columnLetter = 'A';
$header_actual_count = 0;
$headerIndex = 0;
foreach ($headers as $header) {
// Kint::dump($header);
if ($is_placement == true) {
//for placement proposal only
$nxt_ro = $rowNumber + 1;
$sheet->mergeCells("C{$rowNumber}:C{$nxt_ro}");
}
if (in_array($header['parentHeader'], ['Item Key', 'Action'])) {
continue;
}
if (! in_array($header['parentHeader'], ['Item Key', 'Action', 'Sno', 'Particulars'])) {
$header_actual_count++;
//Quote asked not showed in the QCR excel so some proposel has only one Quote Asked, So that case avoid the proposel name
$subHeaderCount = count($header['subHeaders'] ?? []) ?? 0;
if ($subHeaderCount <= 1 && $type == 2 && $is_placement == false) {
// print_rr($header);
continue;
}
}
if ($header['parentHeader'] === 'Sno') {
$header['parentHeader'] = 'S.No.';
$sheet->getColumnDimension('A')->setWidth(10);
//merge the 2 row cells of the S.No.
$nxt_ro = $rowNumber + 1;
$sheet->mergeCells("{$columnLetter}{$rowNumber}:{$columnLetter}{$nxt_ro}");
}
if ($header['parentHeader'] === 'Particulars') {
$sheet->getColumnDimension('B')->setWidth(35);
//merge the 2 row cells of the particulars
$nxt_ro = $rowNumber + 1;
$sheet->mergeCells("{$columnLetter}{$rowNumber}:{$columnLetter}{$nxt_ro}");
}
$startColumn = $columnLetter; // Start of the current header range
if ($type == 2) {
$subHeaderCount = count(array_filter($header['subHeaders'], function ($subHeader) {
return $subHeader !== "Quote Asked";
}));
} else {
$subHeaderCount = count($header['subHeaders']); // Number of subheaders for this parent header
}
// Set parent header value
if ($is_placement == true && ! in_array($header['parentHeader'], ['Particulars', 'S.No.'])) {
$sheet->setCellValue("{$startColumn}{$rowNumber}", "Terms");
} else {
if (in_array($header['parentHeader'], ["Existing Renewal", "Existing Rollover"])) {
$sheet->setCellValue("{$startColumn}{$rowNumber}", "Existing Terms");
} else if (in_array($header['parentHeader'], ["Particulars", "S.No."])) {
$sheet->setCellValue("{$startColumn}{$rowNumber}", $header['parentHeader']);
} else {
$headerIndex = $headerIndex + 1;
$sheet->setCellValue("{$startColumn}{$rowNumber}", "Proposal Terms " . $headerIndex);
}
}
$sheet->getStyle("{$startColumn}{$rowNumber}")->applyFromArray([
'font' => ['bold' => true],
'alignment' => [
'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
],
]);
// Merge header cells if it spans multiple subheaders
if ($subHeaderCount > 1) {
$endColumn = Coordinate::stringFromColumnIndex(Coordinate::columnIndexFromString($startColumn) + $subHeaderCount - 1);
$sheet->mergeCells("{$startColumn}{$rowNumber}:{$endColumn}{$rowNumber}");
} else {
$endColumn = $startColumn; // No merge needed if only one subheader
}
// Add subheaders
foreach ($header['subHeaders'] as $subHeader) {
if ($type == 2 && in_array($subHeader, ['Quote Asked'])) {
continue;
}
if ($is_placement == false) {
$sheet->setCellValue("{$columnLetter}{$subHeaderRow}", $subHeader);
}
if ($columnLetter != "A" && $columnLetter != "B" && $columnLetter != "C") {
$sheet->getColumnDimension($columnLetter)->setWidth(35);
} else {
$sheet->getColumnDimension('A')->setWidth(10);
}
if ($is_placement == false) {
$sheet->getStyle("{$columnLetter}{$subHeaderRow}")->applyFromArray([
'font' => ['bold' => true],
'alignment' => [
'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
],
]);
}
$columnLetter++; // Move to the next column for subheaders
// Kint::dump($subHeader);
$length++;
}
}
if ($header_actual_count == 1 && $type == 1) {
$sheet->getColumnDimension('B')->setWidth(80);
} else {
$sheet->getColumnDimension('B')->setWidth(35);
}
// Apply border to the header range
$prevColumn1 = $this->getPreviousColumn($columnLetter);
$headerRange = "A{$rowNumber}:" . "{$prevColumn1}" . "{$subHeaderRow}";
$sheet->getStyle($headerRange)->applyFromArray([
'borders' => [
'allBorders' => [
'borderStyle' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN,
'color' => ['argb' => 'FF000000'], // Black color
],
],
]);
// Increase row height for headers and subheaders
$sheet->getRowDimension($rowNumber)->setRowHeight(25); // Header row height
$sheet->getRowDimension($subHeaderRow)->setRowHeight(20); // Subheader row height
if ($is_placement == true) {
$rowNumber = $subHeaderRow + 1;
} else {
$rowNumber = $subHeaderRow + 1;
}
// dd($rowNumber);
$column_data = $data['table_data']['data'];
$serial_no = 1;
$maxColumnWidths = [];
// Add table data rows
foreach ($column_data as $dataRow) {
$columnLetter = 'A';
foreach ($dataRow['data'] as $cellData) {
if (in_array($cellData['parentth'], ['Item Key', 'Action'])) {
continue;
}
if ($type == 2 && in_array($cellData['subth'], ['Quote Asked'])) {
continue;
}
if ($cellData['parentth'] == 'Sno') {
$sheet->setCellValue("{$columnLetter}{$rowNumber}", $serial_no);
} else {
$sheet->setCellValue("{$columnLetter}{$rowNumber}", $cellData['value']);
}
$columnLetter++;
}
$rowNumber++;
$serial_no++;
}
$prevColumn2 = $this->getPreviousColumn($columnLetter);
$dataRange = "A" . ($subHeaderRow + 1) . ":" . "{$prevColumn2}" . ($rowNumber - 1);
$sheet->getStyle($dataRange)->applyFromArray([
'borders' => [
'allBorders' => [
'borderStyle' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN,
'color' => ['argb' => 'FF000000'], // Black color
],
],
]);
//premium data
if ($type == 2) {
if ($is_placement == true) {$columnLetterForPremium = "B";} else { $columnLetterForPremium = "B";}
$rowNumber += 2;
// Add premium data
// dd($data);
// $labelArray = ["Premium", "GST (%)", "GST Amount (₹)", "Total"];
$labelArray = ["Premium", "GST Amount (₹)", "Total"];
$premiumData = $data['premium_data']['data'];
// dd($premiumData);
// $premium = [$labelArray[0]];
// $gst = [$labelArray[1]];
// $gstAmt = [$labelArray[1]];
// $total = [$labelArray[2]];
// if($is_placement == true){
$premium[] = $labelArray[0];
$gstAmt[] = $labelArray[1];
$total[] = $labelArray[2];
// }
foreach ($premiumData as $proposal => $insurers) {
if ($proposal != 'Particulars') {
foreach ($insurers as $insurer => $values) {
if ($insurer == 'Quote Asked') {
// $premium[] = count($insurers ?? []) > 1 ? $labelArray[0] : "";
// $gst[] = "";
// $gstAmt[] = count($insurers ?? []) > 1 ? $labelArray[1] : "";
// $total[] = count($insurers ?? []) > 1 ? $labelArray[2] : "";
} else {
$premium[] = formatIndianCurrency($values[$labelArray[0]]);
// $gst[] = $values[$labelArray[1]];
$gstAmt[] = formatIndianCurrency($values[$labelArray[1]]);
$total[] = formatIndianCurrency($values[$labelArray[2]]);
}
}
}
}
// dd([$premium, $gstAmt, $total]); die;
foreach ([$premium, $gstAmt, $total] as $index => $rowData) {
$columnLetter = $columnLetterForPremium;
foreach ($rowData as $key => $value) {
$sheet->setCellValue("{$columnLetter}{$rowNumber}", $value);
if (in_array($value, $labelArray)) {
$sheet->getStyle("{$columnLetter}{$rowNumber}")->applyFromArray(['font' => ['bold' => true]]);
}
$columnLetter++;
}
$rowNumber++;
}
if ($is_placement == true) {
$prevColumn3 = $this->getPreviousColumn($columnLetter);
} else {
$prevColumn3 = $this->getPreviousColumn($columnLetter, 2);
}
$premiumRange = $columnLetterForPremium . ($rowNumber - 3) . ":" . "{$prevColumn3}" . ($rowNumber - 1);
// dd($prevColumn3, $premiumRange, $this->getPreviousColumn($columnLetter, 2), $columnLetter, $rowNumber);
$sheet->getStyle($premiumRange)->applyFromArray([
'borders' => [
'allBorders' => [
'borderStyle' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN,
'color' => ['argb' => 'FF000000'], // Black color
],
],
]);
}
//set imgage and align the row and column
if ($type == 2) {
if ($is_placement == true) {
$columnLetter_img = $this->getPreviousColumn($columnLetter);
} else {
$columnLetter_img = $this->getPreviousColumn($columnLetter, 2);
}
} else {
$columnLetter_img = $this->getPreviousColumn($columnLetter);
}
$company_name = $this->getPreviousColumn($columnLetter_img);
$mergeRange1 = "A1:{$company_name}1";
$sheet->mergeCells($mergeRange1);
$rowCount = count($lead_data);
for ($i = 2; $i <= $rowCount + 1; $i++) {
$mergeRange1 = "C{$i}:{$columnLetter_img}{$i}";
$sheet->mergeCells($mergeRange1);
$sheet->getStyle($mergeRange1)->applyFromArray([
'borders' => [
'allBorders' => [
'borderStyle' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN,
'color' => ['argb' => 'FF000000'], // Black color
],
],
]);
}
$sheet->getColumnDimension($columnLetter_img)->setWidth(35);
$sheet->getRowDimension(1)->setRowHeight(50); // Adjust as needed
$drawing = new Drawing();
$path = ROOTPATH . "public/assets/images/Nhance-Logo-Final.png"; // Use FCPATH for server path
$drawing->setPath($path);
$drawing->setCoordinates("{$columnLetter_img}1"); // Set position in column B
$drawing->setHeight(35); // Adjust image
$offsetX = 35;
$drawing->setOffsetX($offsetX); // Adjust horizontal offset
$drawing->setOffsetY(10); // Adjust vertical offset
$drawing->setWorksheet($sheet);
//end
// Auto-size columns
// foreach ($sheet->getColumnIterator() as $column) {
// $sheet->getColumnDimension($column->getColumnIndex())->setAutoSize(true);
// }
$dataRange = $sheet->calculateWorksheetDimension();
$sheet->getStyle($dataRange)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER)->setVertical(Alignment::VERTICAL_CENTER)->setWrapText(true);
$sheet->getStyle('A1')->applyFromArray([
'alignment' => [
'wrapText' => false, // Disables text wrapping for A1
],
]);
// Apply border to the entire sheet
preg_match('/([A-Z]+)(\d+):([A-Z]+)(\d+)/', $dataRange, $matches);
if ($matches) {
$startColumn = $matches[1]; // A
$startRow = $matches[2]; // 1
$endColumn = $matches[3]; // G
$endRow = $matches[4]; // 75
// Convert column letter to index, reduce by 1, and convert back
$endColumnIndex = Coordinate::columnIndexFromString($endColumn) - 1;
$newEndColumn = Coordinate::stringFromColumnIndex($endColumnIndex);
// Generate the new range (e.g., "A1:F75" instead of "A1:G75")
$newDataRange = "{$startColumn}{$startRow}:{$newEndColumn}{$endRow}";
// $sheet->getStyle($newDataRange)->getBorders()->getAllBorders()->setBorderStyle(Border::BORDER_THIN);
}
$lastRow = count($lead_data) + 1;
$leadRange = "A1:{$columnLetter_img}{$lastRow}";
$sheet->getStyle($leadRange)->applyFromArray([
'borders' => [
'allBorders' => [
'borderStyle' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN,
'color' => ['argb' => 'FF000000'], // Black color
],
],
]);
// Set filename
$string = ($type == 2) ? ($is_placement == true ? 'Placement' : 'QCR') : 'RFQ';
$current_year = date('Y');
$next_year = $current_year + 1;
$policy_year = "$current_year-$next_year";
if (! empty($rfq_data['policy_end_date'])) {
$policy_expiry = strtotime($rfq_data['policy_end_date']);
$formatted_policy = date("d-m-Y", $policy_expiry);
$filename = "{$rfq_data['client_name']}_{$rfq_data['policy_type']}_{$string}_" . $policy_year . '(Due On ' . $formatted_policy . ')' . '.xlsx';
} else {
$filename = "{$rfq_data['client_name']}_{$rfq_data['policy_type']}_{$string}_" . $policy_year . '_' . '.xlsx';
}
$filenameBase = pathinfo($filename, PATHINFO_FILENAME);
$filenameBase = preg_replace('/[^a-zA-Z0-9\-_ ]/u', '', $filenameBase);
$filenameBase = preg_replace('/\s+/', ' ', trim($filenameBase));
$filename = ($filenameBase !== '' ? $filenameBase : 'export') . '.xlsx';
//claim history new sheet;
if ($claim_history == 1 && !empty($rfq_data['fin_years_claims'])) {
$claim_details = $this->sanitizeClaimDetailsForExport(
json_decode($rfq_data['fin_years_claims'], true) ?? []
);
if (! empty($claim_details['finyear'])) {
// Get headers dynamically
$headers = array_map(function ($key) {
return ucwords(str_replace('_', ' ', $key));
}, array_keys($claim_details['finyear'][0]));
$newSheet = new Worksheet($spreadsheet, 'Claims Experience');
$spreadsheet->addSheet($newSheet);
$spreadsheet->setActiveSheetIndexByName('Claims Experience');
$sheet = $spreadsheet->getActiveSheet();
// Set headers
$sheet->fromArray($headers, null, 'A1');
// Apply background color and bold style to headers
$headerCellRange = 'A1:' . chr(64 + count($headers)) . '1'; // e.g., A1:G1
$sheet->getStyle($headerCellRange)->getFont()->setBold(true);
$sheet->getStyle($headerCellRange)->getFill()->setFillType(Fill::FILL_SOLID)->getStartColor()->setRGB('ADD8E6');
// Add border to header
$sheet->getStyle($headerCellRange)->getBorders()->getAllBorders()->setBorderStyle(Border::BORDER_THIN);
// Fill data and apply borders
$row = 2;
foreach ($claim_details['finyear'] as $record) {
$col = 'A';
foreach ($record as $array_key => $value) {
$label = ucwords(str_replace('_', ' ', ($value ?? "")));
if (in_array($array_key, ['sum_insured', 'claim_amount', 'settled'])) {
$label = formatIndianCurrency(intval($label));
}
$sheet->setCellValue($col . $row, $label);
$col++;
}
// Apply border to each data row
$sheet->getStyle('A' . $row . ':' . chr(64 + count($headers)) . $row)
->getBorders()->getAllBorders()->setBorderStyle(Border::BORDER_THIN);
$row++;
}
// Auto-size all columns based on header count
for ($i = 0; $i < count($headers); $i++) {
$colLetter = chr(65 + $i); // 'A', 'B', etc.
$sheet->getColumnDimension($colLetter)->setAutoSize(true);
}
// Enable wrap text for all cells
// $maxColLetter = chr(64 + count($headers)); // Last column letter
// $sheet->getStyle("A2:{$maxColLetter}{$row}")->getAlignment()->setWrapText(true);
// // Optional: center vertically for neatness
// $sheet->getStyle("A2:{$maxColLetter}{$row}")->getAlignment()->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER);
$maxColLetter = chr(64 + count($headers));
$dataRange = "A1:{$maxColLetter}" . ($row - 1);
$sheet->getStyle($dataRange)->getAlignment()
->setWrapText(true)
->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER)
->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER);
// Optional: Make row height auto (helps when wrap text is on)
for ($i = 2; $i < $row; $i++) {
$sheet->getRowDimension($i)->setRowHeight(-1);
}
}
}
// Save to temporary location
$uploadFilePath = WRITEPATH . 'tmp/' . $filename;
$writer = new Xlsx($spreadsheet);
$writer->save($uploadFilePath);
return [
'filePath' => $uploadFilePath,
'fileName' => $filename,
];
}
//Merge RFQ/QCR file with lead members file
public function mergeQuoteExcelFileWithMembersListExcelFile($lead_id, $type, $source_file_path)
{
$rfq_data = $this->RFQModel->getRFQTableDataWithLeadIDAndType($lead_id, $type);
if ($source_file_path && $rfq_data['file_name']) {
}
return true;
}
public function calculateMembersDemography($params, $returnType = null)
{
$lead_id = $params['lead_id'];
$lead_data = $this->leadsModel->find((int) $lead_id);
if (! $lead_data) {
return ['status' => 'failed', 'message' => 'Opportunity data not found'];
}
$file_name_with_path = ! empty($lead_data['file_name'])
? storage_ensure_local_file(WRITEPATH . 'uploads/lead_files', $lead_data['file_name'])
: null;
try {
if ($lead_data['file_name']) {
//check physical file
if (empty($file_name_with_path) || ! file_exists($file_name_with_path)) {
//file not found update status and reason
$message = "Lead Physcial file not found";
// echo $message;
$this->myLogger->logme('error', ($message . ' for file ' . ($lead_data['file_name'] ?? '')));
return ['status' => 'failed', 'message' => 'no physical file'];
}
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($file_name_with_path);
//get members data
$members_sheet = $spreadsheet->getSheet(0);
$highestRowAndColumn = $members_sheet->getHighestRowAndColumn();
// dd($highestRowAndColumn);
$uncleaned_members = $members_sheet->rangeToArray('A1:' . $highestRowAndColumn['column'] . $highestRowAndColumn['row']);
$members = ExcelSanitizeHelper::sanitizeArrayData($uncleaned_members);
//get age band data
$age_band_sheet = $spreadsheet->getSheet(1);
$highestRowAndColumn = $age_band_sheet->getHighestRowAndColumn();
$age_band_data = $age_band_sheet->rangeToArray('A1:' . $highestRowAndColumn['column'] . $highestRowAndColumn['row']);
//check age or dob column
$members_heading = $members[0];
$available_col = null;
$col_index = null;
if (in_array('age', array_map('strtolower', $members_heading))) {
$available_col = 'age';
$col_index = array_search('age', array_map('strtolower', $members_heading));
} elseif (in_array('dob', array_map('strtolower', $members_heading))) {
$available_col = 'dob';
$col_index = array_search('dob', array_map('strtolower', $members_heading));
}
if ($available_col == null) {
$message = 'No DOB or Age column ';
$this->myLogger->logme('error', ($message . $file_name_with_path));
return ['status' => 'failed', 'message' => $message];
}
try {
$classifiers = $this->getDemographyData($members, $age_band_data, $members_heading, $available_col, $col_index);
} catch (Exception $e) {
return ['status' => 'fail', 'message' => $e->getMessage()];
}
//for this to view the demography in the RFQ and QCR page to using internal
if ($returnType == "internal") {
return ['data' => $classifiers];
// print_rr($classifiers); die;
}
try {
$result = $this->generateClassifierSpreadsheet($classifiers, WRITEPATH . 'uploads/lead_files/');
if ($result['success']) {
$this->myLogger->logme('error', "Spreadsheet generated successfully!");
echo "Location: " . $result['fullpath'] . "\n";
echo "Filename: " . $result['filename'] . "\n";
$filePaths = [
['file_path' => $file_name_with_path, 'sheets' => [0, 1]],
['file_path' => $result['fullpath'], 'sheets' => []],
];
$outputPath = $file_name_with_path;
$result_merge = ExcelMergeHelper::mergeExcelFiles($filePaths, $outputPath);
if ($result_merge) {
storage_mirror_generated_file($outputPath, WRITEPATH . 'uploads/lead_files', $lead_data['file_name']);
// Call the delete function after the file is successfully created
$deleteResponse = $this->deleteGeneratedFile($result['fullpath']);
// Add delete message to response
$response['deleteMessage'] = $deleteResponse['message'];
return ['status' => 'success', 'message' => 'Member_Data Merged Suceesfully'];
}
} else {
echo "Error generating spreadsheet: " . $result['error'];
}
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
return ['status' => 'fail', 'message' => $e->getMessage()];
}
} else {
$this->myLogger->logme('error', (' no file found ' . $file_name_with_path));
return ['status' => 'failed', 'message' => 'no file found'];
}
} catch (Exception $e) {
$this->myLogger->logme("error", "Exception: " . $e->getMessage() . " --- Line: " . $e->getLine() . " --- Trace: " . $e->getTraceAsString());
return ['status' => 'fail', 'error' => "File not found / Wrong file"];
}
}
public function getDemographyData($members, $age_band_data, $members_heading, $available_col, $col_index)
{
$first_loop = 0;
// $col_index_si = array_search('si enhancement', array_map('strtolower', $members_heading));
$col_index_si = array_search('si', array_map('strtolower', $members_heading));
$col_index_relationship = array_search('relationship', array_map('strtolower', $members_heading));
$relations = [];
$si_amt = ['general']; // Initialize with 'general' as per first loop condition
// First pass - collect unique relations and SI amounts
foreach ($members as $member) {
if ($first_loop == 0) {
$first_loop++;
continue;
}
if ($member == null) {
continue;
}
if (! in_array($member[$col_index_relationship], $relations) && $member[$col_index_relationship] != null) {
$relations[] = $member[$col_index_relationship];
}
if (! in_array($member[$col_index_si], $si_amt) && $member[$col_index_si] != null) {
$si_amt[] = $member[$col_index_si];
}
}
// Initialize classifier array
$classifiers = [];
foreach ($si_amt as $si) {
$classifiers[$si] = [];
// Initialize relation counts including Grand Total row
foreach ($relations as $relation) {
$classifiers[$si][$relation] = [];
foreach ($age_band_data as $age_bands) {
foreach ($age_bands as $age_interval) {
$classifiers[$si][$relation][$age_interval] = 0;
}
}
// Add Grand Total column for each relation echo('test passed 3.5');
$classifiers[$si][$relation]['Grand Total'] = 0;
}
// Initialize Grand Total row
$classifiers[$si]['Grand Total'] = [];
foreach ($age_band_data as $age_bands) {
foreach ($age_bands as $age_interval) {
$classifiers[$si]['Grand Total'][$age_interval] = 0;
}
}
// Add grand total of grand totals
$classifiers[$si]['Grand Total']['Grand Total'] = 0;
}
// Process members and count them
foreach ($members as $member) {
if ($first_loop == 0) {
$first_loop++;
continue;
}
// Get age
$age = $this->getAge($available_col, $member, $col_index);
$relation = $member[$col_index_relationship];
$member_si = $member[$col_index_si];
// Find the correct age band (only once per member)
$found_band = false;
foreach ($age_band_data as $age_bands) {
if ($found_band) {
break;
}
foreach ($age_bands as $age_band) {
//get Min and Max Age
list($min_age, $max_age) = $this->getAgeRange($age_band);
// If age fits in this band
if ($age >= $min_age && $age <= $max_age) {
// Update counts for general category
if (isset($classifiers['general'][$relation][$age_band])) {
// Increment count for specific relation and age band
$classifiers['general'][$relation][$age_band]++;
// Update row total (Grand Total column)
$classifiers['general'][$relation]['Grand Total']++;
// Update column total (Grand Total row)
$classifiers['general']['Grand Total'][$age_band]++;
// Update grand total of grand totals
$classifiers['general']['Grand Total']['Grand Total']++;
}
// Update counts for specific SI category
if (isset($classifiers[$member_si][$relation][$age_band])) {
// Increment count for specific relation and age band
$classifiers[$member_si][$relation][$age_band]++;
// Update row total (Grand Total column)
$classifiers[$member_si][$relation]['Grand Total']++;
// Update column total (Grand Total row)
$classifiers[$member_si]['Grand Total'][$age_band]++;
// Update grand total of grand totals
$classifiers[$member_si]['Grand Total']['Grand Total']++;
}
$found_band = true;
break;
}
}
}
}
return $classifiers;
}
public function generateClassifierSpreadsheet($classifiers, $outputDir = 'exports')
{
if (! file_exists($outputDir)) {
if (! mkdir($outputDir, 0755, true)) {
throw new Exception("Failed to create directory: $outputDir");
}
}
// Check if directory is writable
if (! is_writable($outputDir)) {
throw new Exception("Directory is not writable: $outputDir");
}
// Generate unique filename
$timestamp = date('Y-m-d_His');
$filename = "member_classification_{$timestamp}.xlsx";
$filepath = $outputDir . DIRECTORY_SEPARATOR . $filename;
// Check if file already exists (shouldn't happen with timestamp, but just in case)
if (file_exists($filepath)) {
$counter = 1;
while (file_exists($outputDir . DIRECTORY_SEPARATOR . "member_classification_{$timestamp}_{$counter}.xlsx")) {
$counter++;
}
$filename = "member_classification_{$timestamp}_{$counter}.xlsx";
$filepath = $outputDir . DIRECTORY_SEPARATOR . $filename;
}
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->setTitle('Demography_Data');
// Get all age bands
$age_bands = array_keys(reset($classifiers['general']));
array_pop($age_bands); // Remove 'Grand Total'
$age_bands[] = 'Grand Total'; // Add it back at the end
$currentRow = 5; // Start from row 5 to match the example
// Function to write section data
$writeSectionData = function ($data, $sheet, &$currentRow, $si_type) use ($age_bands) {
// Add section header for the SI type
$sheet->setCellValue('B' . $currentRow, strtoupper($si_type));
// Style section header
$sheet->getStyle('B' . $currentRow)->applyFromArray([
'font' => ['bold' => true, 'size' => 14],
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER],
]);
$currentRow++; // Move to the next row after the section header
// Set headers for the data table
$sheet->setCellValue('B' . $currentRow, 'Relationship');
$col = 'C';
foreach ($age_bands as $band) {
$sheet->setCellValue($col . $currentRow, $band);
$col++;
}
// Style headers
$lastCol = chr(ord('B') + count($age_bands));
$headerRange = 'B' . $currentRow . ':' . $lastCol . $currentRow;
$sheet->getStyle($headerRange)->applyFromArray([
'font' => ['bold' => true],
'borders' => [
'allBorders' => [
'borderStyle' => Border::BORDER_THIN,
'color' => ['rgb' => '000000'],
],
],
'alignment' => [
'horizontal' => Alignment::HORIZONTAL_CENTER,
],
]);
$currentRow++;
// Write data rows
foreach ($data as $relation => $values) {
if ($relation !== 'Grand Total') {
$sheet->setCellValue('B' . $currentRow, $relation);
$col = 'C';
foreach ($age_bands as $band) {
$value = $values[$band] ?: ''; // Convert 0 to empty string
$sheet->setCellValue($col . $currentRow, $value);
$col++;
}
// Style data row
$dataRange = 'B' . $currentRow . ':' . $lastCol . $currentRow;
$sheet->getStyle($dataRange)->applyFromArray([
'borders' => [
'allBorders' => [
'borderStyle' => Border::BORDER_THIN,
'color' => ['rgb' => '000000'],
],
],
'alignment' => [
'horizontal' => Alignment::HORIZONTAL_CENTER,
],
]);
$currentRow++;
}
}
// Add Grand Total row
$sheet->setCellValue('B' . $currentRow, 'Grand Total');
$col = 'C';
foreach ($age_bands as $band) {
$sheet->setCellValue($col . $currentRow, $data['Grand Total'][$band]);
$col++;
}
// Style Grand Total row
$totalRange = 'B' . $currentRow . ':' . $lastCol . $currentRow;
$sheet->getStyle($totalRange)->applyFromArray([
'font' => ['bold' => true],
'borders' => [
'allBorders' => [
'borderStyle' => Border::BORDER_THIN,
'color' => ['rgb' => '000000'],
],
],
'fill' => [
'fillType' => Fill::FILL_SOLID,
'startColor' => ['rgb' => 'F2F2F2'],
],
'alignment' => [
'horizontal' => Alignment::HORIZONTAL_CENTER,
],
]);
$currentRow += 3; // Add gap after each section
};
// Write each SI section with gaps
foreach ($classifiers as $si_type => $data) {
$writeSectionData($data, $sheet, $currentRow, $si_type);
}
// Auto-size columns
foreach (range('B', chr(ord('B') + count($age_bands))) as $col) {
$sheet->getColumnDimension($col)->setAutoSize(true);
}
// Create Excel file
try {
// Create Excel file
$writer = new Xlsx($spreadsheet);
$writer->save($filepath);
// Verify file was created successfully
if (! file_exists($filepath)) {
throw new Exception("Failed to create file: $filepath");
}
// Return the file info after creation
$response = [
'success' => true,
'filepath' => $filepath,
'filename' => $filename,
'fullpath' => realpath($filepath),
];
return $response;
} catch (Exception $e) {
return [
'success' => false,
'error' => $e->getMessage(),
];
}
}
// Function to delete generated file
public function deleteGeneratedFile($filePath)
{
try {
if (file_exists($filePath)) {
unlink($filePath); // Delete the file
return [
'success' => true,
'message' => "File deleted successfully",
];
} else {
return [
'success' => false,
'message' => "File does not exist",
];
}
} catch (Exception $e) {
return [
'success' => false,
'error' => $e->getMessage(),
];
}
}
public function getAge($available_col, $member, $col_index)
{
if ($available_col == 'age') {
$age = $member[$col_index];
} else {
$dob = $member[$col_index];
if ($dob == 'DOB') {
return;
}
$dobDate = \DateTime::createFromFormat('d-M-Y', $dob);
$currentDate = new \DateTime();
if ($dobDate == false) {
$this->myLogger->logme('error', $dob . 'is not valid');
return;
}
$age = $currentDate->diff($dobDate)->y;
}
return $age;
}
public function getAgeRange($age_band)
{
$age_band = trim($age_band);
// Parse age range
if (strpos($age_band, '-') === false) {
$min_age = (int) filter_var($age_band, FILTER_SANITIZE_NUMBER_INT);
$max_age = PHP_INT_MAX;
} else {
$parts = explode("-", $age_band);
$min_age = (int) $parts[0];
$max_age = (int) $parts[1];
}
return [$min_age, $max_age];
}
//this funciton for send mail to insurer and client with either RFQ/QCR
public function sendMailWithAttachement()
{
helper('excel_util_helper');
helper('MailHelper');
helper('ExcelMergeHelper');
$params = $this->request->getPost();
// print_r($params); die;
$lead_id = (int) $params['lead_id'];
$file_type = $params['file_type']; //rfq or qcr
$recipient_type = $params['recipient_type']; //insurer or client or internal or placement
$recipient_mail = $params['recipient_mail']; // - only primary key of contacts
$recipient_mail = json_decode($params['recipient_mail'], true); // - only primary key of contacts
$propsal_and_insurer = isset($params['proposal_insurer']) ? $params['proposal_insurer'] : null;
$mail_content = $params['mail_content'];
$mail_subject = $params['subject'];
$attachment_file_ids = $params['selected_attachment_files'];
$from_mail = getenv('email.fromEmail');
if (in_array($recipient_type, ['placement', 'insurer', 'internal'])) {
$from_mail = getenv('LEAD_INSURER_FROM_MAIL_ID');
} else if (in_array($recipient_type, ['client'])) {
$from_mail = getenv('LEAD_CLIENT_FROM_MAIL_ID');
}
$type = "";
$insurer_ids = null;
if (in_array($recipient_type, ['placement'])) {
$type = "placement";
} else if (in_array($recipient_type, ['insurer'])) {
$type = "insurer";
$insurer_ids = json_decode($params['insurer_ids'] ?? "", true) ?? [];
} else if (in_array($recipient_type, ['internal'])) {
$type = "internal";
} else if (in_array($recipient_type, ['client'])) {
$type = "client";
}
// print_r($insurer_ids); die;
if ($recipient_type === 'placement') {
$lead_data = $this->leadsModel->find((int) $lead_id);
$data = [];
if (! empty($params['policy_start_date'])) {
$converted_start = change_date_format($params['policy_start_date']); // converts to Y-m-d
if ($converted_start !== $lead_data['policy_start_date']) {
$data['policy_start_date'] = $converted_start;
}
}
if (! empty($params['policy_end_date'])) {
$converted_end = change_date_format($params['policy_end_date']); // converts to Y-m-d
if ($converted_end !== $lead_data['policy_end_date']) {
$data['policy_end_date'] = $converted_end;
}
}
if (! empty($data)) {
$this->leadsModel->update($lead_id, $data);
}
}
$result_data = [];
// dd($recipient_mail);
if ($recipient_type == 'insurer' && (! is_array($recipient_mail) || count($recipient_mail) == 0)) {
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => '', 'messgae' => 'Recipient mail not found ! '], 200);
}
// dd();
//gather lead info
$lead_data = $this->leadsModel
->select('leads.*,policy_type.long_name,policy_type.policy_type,user_profiles.email as created_person_email')
->join('policy_type', 'leads.policy_type_id = policy_type.id', 'left')
->join('user_profiles', 'leads.created_by = user_profiles.id', 'left')
->where('leads.id', $lead_id)
->first();
// log_message('error', 'Lead Data' . json_encode($lead_data));
// print_r($lead_data ); die;
$cc_mails = [];
$bcc_mails = [];
//get CC Mails
if ($recipient_type == 'internal' || $recipient_type == 'insurer' || $recipient_type == 'client') {
$cc_data = isset($params['cc']) ? $params['cc'] : "";
$param_cc_mail = json_decode($cc_data, true);
if (isset($param_cc_mail) && is_array($param_cc_mail) && count($param_cc_mail) > 0) {
// Fetch user data where ID is in the param_cc_mail array
$userData = $this->userModel
->where('is_active', 1)
->whereIn('id', $param_cc_mail)
->findAll();
// print_r($userData); die;
// Extract emails from the fetched user data
$cc_mails = array_column($userData, 'email');
// print_r(json_encode($cc_mails)); die;
// If no emails were found, return an error response
// if (empty($cc_mails)) {
// return $this->respond(['status' => 'failed','code' => 400,'data' => '','message' => 'No valid CC mail addresses found!'], 200);
// }
} else {
// Handle case where param_cc_mail is not valid
// return $this->respond(['status' => 'failed','code' => 400,'data' => '','message' => 'CC mail not found!'], 200);
}
// Append external CC emails for client mail (sent as email strings, not user IDs)
if ($recipient_type === 'client' && ! empty($params['external_cc'])) {
$externalCCs = json_decode($params['external_cc'], true);
if (is_array($externalCCs)) {
$cc_mails = array_merge($cc_mails, array_values(array_filter(array_map('trim', $externalCCs))));
}
}
} else if ($recipient_type == 'placement') {
if(isset($params['cc']) && !empty($params['cc'])) {
if(is_array($params['cc'])) {
$cc_mails = $params['cc'];
} else {
$cc_mails = json_decode($params['cc'], true);
}
}
}
//get BCC Mails
if ($recipient_type == 'insurer' || $recipient_type == 'client') {
$bcc_data = isset($params['bcc']) ? $params['bcc'] : "";
$param_bcc_mail = json_decode($bcc_data, true);
if (isset($param_bcc_mail) && is_array($param_bcc_mail) && count($param_bcc_mail) > 0) {
// Fetch user data where ID is in the param_cc_mail array
$userData = $this->userModel
->where('is_active', 1)
->whereIn('id', $param_bcc_mail)
->findAll();
// print_r($userData); die;
// Extract emails from the fetched user data
$bcc_mails = array_column($userData, 'email');
// print_r(json_encode($cc_mails)); die;
// If no emails were found, return an error response
// if (empty($cc_mails)) {
// return $this->respond(['status' => 'failed','code' => 400,'data' => '','message' => 'No valid CC mail addresses found!'], 200);
// }
} else {
// Handle case where param_cc_mail is not valid
// return $this->respond(['status' => 'failed','code' => 400,'data' => '','message' => 'CC mail not found!'], 200);
}
}
if ($recipient_type == 'client' && ($lead_data['contact_person_email'] == '' || $lead_data['contact_person_email'] == null)) {
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => '', 'messgae' => 'Recipient mail not found ! '], 200);
}
// dd($lead_data);
$reply_to = $lead_data['created_person_email'] ?? "";
//get file path to attach
if ($lead_data["lead_form_type"] == 2 && $recipient_type === 'placement') {
// For Non-EB placement: copy QCR sheet as placement file, then download it
$file_info = $this->createAndDownloadPlacementSheet($lead_data);
} else if ($lead_data["lead_form_type"] == 2) {
// For Non-EB, download RFQ/QCR file from Google Sheet based on recipient type
$file_info = $this->downloadFileFromGoogleSheet($lead_data, $recipient_type);
} else {
$file_info = $this->constructExcelToSaveTemp($lead_id, ($file_type == 'rfq' ? 1 : 2), $propsal_and_insurer);
}
// $file_info = $this->constructExcelToSaveTemp($lead_id, ($file_type == 'rfq' ? 1 : 2), $propsal_and_insurer);
log_message('error', 'File Info' . json_encode($file_info));
if ($lead_data['file_name'] != null && $lead_data['file_name'] != '') {
$temp_file_path = $file_info['filePath'];
$temp_file_name = $file_info['fileName'];
$lead_file_path = storage_ensure_local_file(WRITEPATH . 'uploads/lead_files', $lead_data['file_name']);
if (! empty($lead_file_path) && is_file($lead_file_path)) {
$filePaths = [
['file_path' => $temp_file_path, 'sheets' => []],
['file_path' => $lead_file_path, 'sheets' => []],
];
$outputPath = dirname($temp_file_path) . '/' . $temp_file_name;
$result = ExcelMergeHelper::mergeExcelFiles($filePaths, $outputPath);
// print_rr($result);
}
} else {
$result = $file_info['filePath'];
// return $this->respond(['status' => 'fail', 'code' => 200, 'messgae' => 'File Not Found'], 200);
}
// print_rr($result); die;
// print_rr($file_info);
// $file_path = WRITEPATH."uploads/excel/sample/correction.xls";
// $file_name = $file_type.'.xlsx';
// !dd($file_info);
$file_path = $result;
$file_name = basename($result);
// Attacments part
$attachments = [['fileName' => $file_name, 'filePath' => $file_path]];
$other_attachments = $this->handleMultiFileAttachments($attachment_file_ids, $lead_id);
$attachments = array_merge($attachments, $other_attachments);
// print_r($attachments); die;
//get recipient address
if ($recipient_type == 'placement') {
// Placement TO is client contact email sent directly
if(!is_array($recipient_mail)) {
$recipient_mail = [$recipient_mail];
}
$recipient_data = [];
foreach ($recipient_mail as $mail) {
$recipient_data[] = ['name' => $lead_data['contact_person_name'] ?? '', 'email' => trim($mail)];
}
} else if ($recipient_type == 'insurer') {
if(!is_array($recipient_mail)) {
$recipient_mail = [$recipient_mail];
}
$recipient_data = $this->levelContactModel
->where(['contact_type' => 'insurer', 'is_active' => 1])
->whereIn('id', $recipient_mail)
->findAll();
} else if ($recipient_type == 'client') {
$mailIDS = explode(',', $params['contact_mail']);
$recipient_data = [];
foreach ($mailIDS as $mail) {
$recipient_data[] = ['name' => $lead_data['contact_person_name'], 'email' => $mail];
}
} else {
$recipient_data = [['name' => "Team", 'email' => $params['to']]];
}
// print_r($recipient_data); die;
$subject = $file_type == 'rfq' ? 'Request for Quotation from ' . $lead_data['client_name'] . ' for ' . $lead_data['policy_type'] : 'Quotation Comparison Report for ' . $lead_data['policy_type'];
$original_message = 'Request for Quotation (RFQ)
Dear {{RECIPIENT_NAME}},
We are reaching out to request a quotation for the following insurance coverage. Please review the details below and provide your quote at your earliest convenience.
RFQ Details
| Client name | {{CLIENT_NAME}} |
|---|
| Coverage Type | {{POLICY_LONG_NAME}} |
|---|
| Policy Start Date | {{POLICY_START_DATE}} |
|---|
| Policy Duration | {{DURATION}} |
|---|
Please note: Additional terms and details are included in the attachment for your reference.
Please feel free to reach out if you require any further information to prepare the quote. We look forward to receiving your proposal.
Best regards,
Nhance India Pvt Ltd
© Nhance India Pvt Ltd. All rights reserved.
';
//for mail content
if (! empty($mail_content)) {
$original_message = $mail_content;
}
//for mail subject
if (! empty($mail_subject)) {
$subject = $mail_subject;
}
// print_r($subject); die;
$common = ['module' => "leads", 'pk' => $lead_id, 'mail_type' => $type, 'common' => null];
if ($recipient_data) {
foreach ($recipient_data as $position => $recipient) {
if ($recipient_type == 'insurer') {
$common['common'] = $insurer_ids[$position] ?? null;
}
$message = $original_message;
$message = str_replace("{{RECIPIENT_NAME}}", $recipient['name'] != "" ? $recipient['name'] : " ", $message);
$message = str_replace("{{CLIENT_NAME}}", $lead_data['client_name'] != "" ? $lead_data['client_name'] : "", $message);
$message = str_replace("{{POLICY_LONG_NAME}}", $lead_data['long_name'] != "" ? $lead_data['long_name'] : "", $message);
$message = str_replace("{{POLICY_START_DATE}}", change_date_format($lead_data['policy_start_date'], 'Y-m-d', 'd-m-Y'), $message);
$message = str_replace("{{DURATION}}", calculate_days_bw_dates($lead_data['policy_end_date'] ?? "", $lead_data['policy_start_date'] ?? "")->days, $message) ?? '--';
// print_rr($message);calculate_days_bw_dates
$string = implode(", ", $cc_mails);
$bcc_string = implode(", ", $bcc_mails);
$res = MailHelper::send_email(['from_mail' => $from_mail, 'mail' => $recipient['email'], 'cc' => $string, 'subject' => $subject, 'message' => $message, 'attachments' => $attachments, 'reply_to' => $reply_to, 'bcc' => $bcc_string, 'common' => $common]);
// !dd($res);
$result_data[] = ['mail' => $recipient['email'], 'status' => $res];
}
}
if ($recipient_type == 'placement' && isset($params['acm_email']) && ! empty($params['acm_email'])) {
$common['mail_type'] = "internal acm";
$acm_mail_content = '
Dear Sir,
The placement for the {{POLICY_TYPE}} policy pertaining to {{CLIENT_NAME}} has been successfully won.
Please proceed with the next steps accordingly.
Thank You!
Best regards,
';
$acm_mail_content = $this->transformMailContent($lead_data, $acm_mail_content, $data['page_name'] = 'QCR');
$res = MailHelper::send_email(['from_mail' => $from_mail, 'mail' => $params['acm_email'], 'subject' => $subject, 'message' => $acm_mail_content, 'attachments' => $attachments, 'common' => $common]);
}
$is_placement = false;
if ($recipient_type == 'placement') {
$is_placement = true;
if(isset($propsal_and_insurer)) {
if (! empty($propsal_and_insurer[0])) {
$parts = explode('-', $propsal_and_insurer, 2);
$proposal_key = $parts[0] ?? null;
$insurer_key = $parts[1] ?? null;
} else {
$proposal_key = null;
$insurer_key = null;
}
}
$lead_update_data = [
'proposel_name' => $proposal_key ?? null ,
'insurer_name' => $insurer_key ?? null,
'insurer' => $params['insurer_and_branch'] ?? null,
];
$data = [
'proposel_data' => json_encode($lead_update_data),
'status' => 'won',
'placement_date' => ! empty($params['placement_date']) ? change_date_format($params['placement_date']) : null,
'payment_date' => ! empty($params['payment_date']) ? change_date_format($params['payment_date']) : null,
'utr_no' => $params['utr_no'] ?? null,
'is_cd' => $params['is_cd'] ?? null,
'premium_amount' => $params['premium_amount'] ?? null,
'total_amount' => $params['total_amount'] ?? null,
'cd_amount' => $params['cd_amount'] ?? null,
'no_of_installment' => $params['no_of_installment'] ?? null,
'is_installment' => $params['is_installment'] ?? null,
'acm_id' => $params['acm_pk'] ?? null,
'agreed_percentage' => $params['agreed_percentage'] ?? null,
];
if (isset($params['tpa_id']) && ! empty($params['tpa_id'])) {
list($tpaBranchId, $tpaId) = explode('-', $params['tpa_id']);
$data['tpa_branch_id'] = $tpaBranchId;
$data['tpa_id'] = $tpaId;
}
$this->leadsModel->where('id', $lead_id)->set($data)->update();
if (isset($params['installments']) && ! empty($params['installments'])) {
$this->saveLeadInstallmentDetails((int) $lead_id, $params['installments']);
}
}
if ($recipient_type == 'client') {
$this->leadsModel
->where('id', $lead_id)
->whereNotIn('status', ['lost', 'co_insurer_pending', 'won', 'completed_with_corrections', 'completed_without_corrections'])
->set(['status' => "qcr_sent"])
->update();
}
if ($recipient_type == 'insurer') {
$this->leadsModel
->where('id', $lead_id)
->whereNotIn('status', ['qcr_created', 'qcr_sent', 'lost', 'co_insurer_pending', 'won', 'completed_with_corrections', 'completed_without_corrections'])
->set(['status' => "rfq_sent"])
->update();
}
//delete attachment file
unlink($file_path);
return $this->respond(['status' => 'success', 'code' => 200, 'data' => $result_data, 'is_placement' => $is_placement], 200);
}
public function getLevelContects()
{
$data = $this->levelContactModel->getContactForRFQ();
if ($data) {
return $this->respond(['status' => true, 'data' => $data], 200);
} else {
return $this->respond(['status' => false], 200);
}
}
public function getInsurerBranchContacts($insurer_and_branch_id)
{
if (strpos($insurer_and_branch_id, '-') === false) {
return $this->respond(['status' => false, 'message' => 'Invalid ID format'], 400);
}
// Split the insurer_and_branch_id
list($insurerBranchId, $insurerId) = explode('-', $insurer_and_branch_id);
$data = $this->levelContactModel->getContactForRFQ($insurerId, $insurerBranchId);
if (! empty($data)) {
return $this->respond(['status' => true, 'data' => $data], 200);
} else {
return $this->respond(['status' => false, 'data' => $data, 'message' => 'No contacts were found for the selected insurer.'], 200);
}
}
public function transformProposelData($data, $proposel, $insurer)
{
// print_r($data['premium_data']['data']); die;
$headerData = [];
// Default headers: S. No and Particulars
$defaultHeaders = [
[
'parentHeader' => 'Sno',
'subHeaders' => ['-'],
],
[
'parentHeader' => 'Particulars',
'subHeaders' => ['-'],
],
];
// Add default headers to the result
$headerData = array_merge($headerData, $defaultHeaders);
foreach ($data['table_data']['headers'] as $header) {
// Check if the parentHeader matches the target proposal
if ($header['parentHeader'] === $proposel) {
// Check if subHeaders contain the target insurer key
foreach ($header['subHeaders'] as $subHeader) {
if ($subHeader === $insurer) {
$headerData[] = [
'parentHeader' => $header['parentHeader'],
'subHeaders' => [
// 'Quote Asked', // Default value
$subHeader, // Matched insurer key
],
];
break;
}
}
}
}
$columnData = [];
foreach ($data['table_data']['data'] as $entry) {
$sno = $entry['SNO'];
$items = $entry['items'];
$dataEntry = $entry['data'];
$result = [
"SNO" => $sno,
"items" => $items,
"data" => [],
];
foreach ($dataEntry as $item) {
// Include Sno and Particulars by default
if (in_array($item['parentth'], ['Sno', 'Particulars'])) {
$result['data'][] = [
"parentth" => $item['parentth'],
"subth" => $item['subth'],
"value" => $item['value'],
"input_value" => $item['input_value'],
];
}
// Include Proposal with Quote Asked by default
/* For proposel do not remove it */
// if ($item['parentth'] === $proposel && $item['subth'] === "Quote Asked") {
// $result['data'][] = [
// "parentth" => $item['parentth'],
// "subth" => $item['subth'],
// "value" => $item['value'],
// "input_value" => $item['input_value']
// ];
// }
// Example of including matching specific proposals and insurers
if ($item['parentth'] === $proposel && $item['subth'] === $insurer) {
$result['data'][] = [
"parentth" => $item['parentth'],
"subth" => $item['subth'],
"value" => $item['value'],
"input_value" => $item['input_value'],
];
}
}
// Add to the final result
$columnData[] = $result;
}
$premiumData = [];
foreach ($data['premium_data']['data'] as $key => $value) {
if ($key === $proposel) {
// $premiumData[$key]['Quote Asked'] = $value['Quote Asked'];
$premiumData[$key][$insurer] = $value[$insurer];
}
}
$data['table_data']['headers'] = $headerData;
$data['table_data']['data'] = $columnData;
$data['premium_data']['data'] = $premiumData;
return $data;
}
public function convertJsonForQCR($json, $type)
{
if ($json) {
// Deep copy of JSON
$first_json = json_decode(json_encode($json), true);
// dd($first_json);
if ($type == 2) {
// Column-wise Check: Remove headers and relevant data if qcr == 0
foreach ($json['proposal_data']['over_all_column_data'] as $proposalKey => $proposalData) {
if (($proposalData['qcr'] == 0 || $proposalData['qcr'] === false) || ($proposalData['stc'] == 0 || $proposalData['stc'] === false)) {
// Remove matching parentHeader in headers
foreach ($first_json['table_data']['headers'] as $index => $header) {
if ($header['parentHeader'] === $proposalKey) {
unset($first_json['table_data']['headers'][$index]);
}
}
// Remove data entries with matching parentth
foreach ($first_json['table_data']['data'] as &$item) {
$item['data'] = array_values(array_filter($item['data'], function ($entry) use ($proposalKey) {
return $entry['parentth'] !== $proposalKey;
}));
}
// Remove proposalKey from over_all_column_data
unset($first_json['proposal_data']['over_all_column_data'][$proposalKey]);
if ($type == 2) {
// Remove proposalKey from premium_data
unset($first_json['premium_data']['data'][$proposalKey]);
}
}
// Insurer Check: Remove subHeaders and relevant data for insurers with qcr == 0
foreach ($proposalData['insurers'] as $insurerIndex => $insurer) {
if (($insurer['qcr'] === 0 || $insurer['qcr'] === false) || ($insurer['stc'] === 0 || $insurer['stc'] === false)) {
foreach ($first_json['table_data']['headers'] as &$header) {
if (isset($header['subHeaders'])) {
$header['subHeaders'] = array_values(array_filter($header['subHeaders'], function ($sub) use ($insurer) {
return $sub !== $insurer['display_name'];
}));
}
}
foreach ($first_json['table_data']['data'] as &$item) {
$item['data'] = array_values(array_filter($item['data'], function ($entry) use ($insurer) {
return $entry['subth'] !== $insurer['display_name'];
}));
}
// Remove insurer from proposal's insurers array
unset($first_json['proposal_data']['over_all_column_data'][$proposalKey]['insurers'][$insurerIndex]);
if ($type == 2) {
unset($first_json['premium_data']['data'][$proposalKey][$insurer['display_name']]);
}
}
}
}
// Row-wise Check: Remove rows if qcr == 0 for actions
foreach ($first_json['table_data']['data'] as $rowKey => $rowData) {
foreach ($rowData['data'] as $data) {
if ($data['parentth'] === "Action" && (isset($data['input_value']['qcr']) && $data['input_value']['qcr'] == 0) || (isset($data['input_value']['stc']) && $data['input_value']['stc'] == 0)) {
unset($first_json['table_data']['data'][$rowKey]);
break;
}
}
}
// Reindex arrays to maintain proper structure
$first_json['table_data']['headers'] = array_values($first_json['table_data']['headers']);
$first_json['table_data']['data'] = array_values($first_json['table_data']['data']);
$first_json['proposal_data']['over_all_column_data'] = array_map(function ($proposal) {
$proposal['insurers'] = array_values($proposal['insurers']);
return $proposal;
}, $first_json['proposal_data']['over_all_column_data']);
} else {
//remove insurer as Subheaders for RFQ
foreach ($first_json['table_data']['headers'] as &$header) {
$header['subHeaders'] = array_filter($header['subHeaders'], function ($subHeader) {
return in_array($subHeader, ['Quote Asked', '-']);
});
}
//Remove insurer Row wise data for RFQ
foreach ($first_json['table_data']['data'] as &$row) {
// Filter the inner data array
$row['data'] = array_filter(
$row['data'],
function ($item) {
return in_array($item['subth'], ['Quote Asked', '-']);
}
);
$row['data'] = array_values($row['data']);
}
// Ensure to unset the reference after the loop
unset($row);
// Remove insurers from Proposal Data key for RFQ
foreach ($first_json['proposal_data']['over_all_column_data'] as $key => &$proposal) {
if (isset($proposal['insurers'])) {
// Set the insurers array to empty
$proposal['insurers'] = [];
}
}
// Ensure to reset the reference
unset($proposal);
}
return $first_json;
}
return null;
}
//----- Featch Lead data and insert Client -------------------------------------------------------------------------------------------
public function featchLeadDataAndInsertClient($lead_id)
{
try {
$data = $this->leadsModel->where('leads.id', $lead_id)->where('leads.is_active', 1)->first();
if (! $data) {
$this->myLogger->logme('error', 'featchLeadDataAndInsertClient: Lead not found or inactive', ['lead_id' => $lead_id]);
return $this->respond(['status' => false, 'message' => 'Failed to create Client', 'data' => null], 200);
}
$result = $this->createClientWithMatchingLeadsPolicies($data);
if ($result) {
return $this->respond([
'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);
}
$this->myLogger->logme('error', 'featchLeadDataAndInsertClient: Client creation failed after processing', ['lead_id' => $lead_id]);
return $this->respond(['status' => false, 'message' => 'Failed to create client', 'data' => $data], 200);
} catch (\Throwable $e) {
$this->myLogger->logme('error', 'featchLeadDataAndInsertClient failed: ' . $e->getMessage());
return $this->respond(['status' => false, 'message' => 'Internal server error', 'error' => $e->getMessage()], 500);
}
}
/**
* 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;
}
// Reuse client/branch from a previous partial attempt (client created, policy failed).
$existingClientId = (int) ($data['is_client_created'] ?? 0);
if ($existingClientId > 0) {
$client_id = $existingClientId;
$existingBranch = $this->clientBranchModel
->where('client_id', $client_id)
->where('branch_name', $data['branch_name'] ?? null)
->where('branch_code', $data['branch_code'] ?? null)
->where('is_active', 1)
->first();
if ($existingBranch) {
$branch_id = (int) $existingBranch['id'];
} else {
$branch_id = $this->clientBranchModel->insert($this->prepareClientBranchData($data, $client_id));
if (! $branch_id) {
return false;
}
$this->levelContactModel->insert($this->prepareContactData($data, $branch_id));
}
} else {
$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: ' . $e->getMessage());
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 {
$client_data = $this->prepareClientData($data);
$client_id = $this->clientModel->insert($client_data);
if ($client_id) {
$this->createClientBranchAndContactWithLeadData($data, $client_id);
$this->leadsModel->where('id', $data['id'])->set('is_client_created', $client_id)->update();
}
return $client_id;
} catch (\Throwable $e) {
$this->myLogger->logme('error', 'createClientWithLeadData failed');
return false;
}
}
public function createClientBranchAndContactWithLeadData($data, $client_id)
{
try {
$branch_data = $this->prepareClientBranchData($data, $client_id);
$branch_id = $this->clientBranchModel->insert($branch_data);
if ($branch_id) {
$this->leadsModel->where('id', $data['id'])->set('is_client_created', $client_id)->update();
$contact_data = $this->prepareContactData($data, $branch_id);
$this->levelContactModel->insert($contact_data);
$this->createClientPolicyWithLeadData($data, $client_id, $branch_id);
}
return $branch_id;
} catch (\Throwable $e) {
$this->myLogger->logme('error', 'createClientBranchAndContactWithLeadData failed');
return false;
}
}
public function createClientPolicyWithLeadData($data, $client_id, $branch_id)
{
try {
$client_policy_data = $this->prepareClientPolicyData($data, $client_id, $branch_id);
$client_policy_id = $this->clientPolicyModel->insert($client_policy_data);
if ($client_policy_id) {
$this->leadsModel->where('id', $data['id'])->set('is_client_created', $client_id)->update();
$this->leadsModel->where('id', $data['id'])->set('is_policy_created', $client_policy_id)->update();
}
return $client_policy_id;
} catch (\Throwable $e) {
$this->myLogger->logme('error', 'createClientPolicyWithLeadData failed: ' . $e->getMessage() . ' at ' . $e->getFile() . ':' . $e->getLine());
return false;
}
}
private function prepareClientData($data)
{
return [
'client_type' => 1,
'entity_type_id' => $data['entity_type_id'] ?? null,
'client_code' => $data['client_code'] ?? null,
'client_name' => $data['client_name'] ?? null,
'short_name' => $data['client_short_name'] ?? null,
'pan' => $data['pan'] ?? null,
];
}
private function prepareClientBranchData($data, $client_id)
{
$default_unit = trim(($data['client_short_name'] ?? '') . '-' . ($data['branch_code'] ?? ''), '-');
return [
'client_id' => $client_id,
'branch_name' => $data['branch_name'],
'branch_code' => $data['branch_code'],
'gst' => $data['gst'],
'units' => json_encode([$default_unit]),
];
}
private function prepareContactData($data, $branch_id)
{
return [
'name' => $data['contact_person_name'],
'mobile' => $data['contact_person_mobile'],
'email' => $data['contact_person_email'],
'contact_type' => 'client',
'ref_id' => $branch_id,
];
}
private function prepareClientPolicyData($data, $client_id, $branch_id)
{
$proposel_data = json_decode($data['proposel_data'] ?? '', true) ?? [];
if ($data['lead_form_type'] == 1) {
// EB: insurer stored as "{insurer_branch_id}-{insurer_id}" inside proposel_data
list($insurer_branch_id, $insurer_id) = explode('-', $proposel_data['insurer'] ?? '-', 2);
} else {
// Non-EB: insurer IDs are direct columns on the lead row
$insurer_id = $data['insurer_id'] ?? null;
$insurer_branch_id = $data['insurer_branch_id'] ?? null;
}
$client_policy_data = [
'client_id' => $client_id,
'client_branch_id' => $branch_id,
'policy_type_id' => $data['policy_type_id'],
'insurer_id' => $insurer_id,
'insurer_branch_id' => $insurer_branch_id,
'tpa_id' => $data['tpa_id'],
'tpa_branch_id' => $data['tpa_branch_id'],
'policy_start_date' => $data['policy_start_date'],
'policy_end_date' => $data['policy_end_date'],
'policy_status' => 1,
'policy_entry_from' => 3,
'is_from_lead' => $data['id'],
];
$policy_type_id = $data['policy_type_id'];
if (in_array($policy_type_id, [1, 2, 6, 7])) {
$client_policy_data['is_addon'] = 1; // Base Policy
} elseif (in_array($policy_type_id, [4, 5])) {
$client_policy_data['is_addon'] = 2; // SI TOPUP
} elseif ($policy_type_id == 3) {
// if ($base_policy) {
// $data['is_addon'] = 3; // Dependent Addon
// } else {
$data['is_addon'] = 1;
// }
}
$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 {
$proposel_data = json_decode($data['proposel_data'], true);
$QCRData = $this->RFQModel->where('is_active', 1)->where('lead_id', $data['id'])->first();
if (! $QCRData) {
throw new \Exception('RFQ Data not found');
}
$JSON = json_decode($QCRData['json'], true);
$converted_json = $this->transformProposelData($JSON, $proposel_data['proposel_name'], $proposel_data['insurer_name']);
// log_message('error', 'Converted JSON: ' . json_encode($converted_json));
return $this->convertQCRJsonToPolicyTerms($converted_json, $data['policy_type_id'], $proposel_data['proposel_name'], $proposel_data['insurer_name']);
} catch (\Throwable $e) {
$this->myLogger->logme('error', 'preparePolicyTermsFromRFQ');
return null;
}
}
public function featchClientPolicyFromLead($client_id, $branch_id, $lead_id)
{
$data = $this->leadsModel->where('leads.id', $lead_id)->where('leads.is_active', 1)->first();
if (! $data) {
return $this->respond(['status' => false, 'message' => 'Failed to create policy', 'data' => null], 200);
}
if (empty($client_id) && ! empty($data['client_id'])) {
$client_id = $data['client_id'];
}
if (empty($branch_id) && ! empty($data['client_branch_id'])) {
$branch_id = $data['client_branch_id'];
}
$result = $this->createClientPolicyWithLeadData($data, $client_id, $branch_id);
// print_r($result); die;
if ($result) {
$policy_data = $this->clientPolicyModel->where('id', $result)->where('is_active', 1)->first();
return $this->respond(['status' => true, 'message' => 'New Policy created successfully', 'client_policy_id' => $result, 'data' => $data, 'client_id' => $policy_data['client_id']], 200);
}
return $this->respond(['status' => false, 'message' => 'Failed to create policy', 'data' => $data], 200);
}
// ---------------------------------------------------------------------------------------------------------------
//Function for convert the RFQ and QCR Json to Policy Terms Json
public function convertQCRJsonToPolicyTerms($data, $policy_type, $proposel_name, $insurer_name)
{
// Extract special conditions from original QCR data first.
// transformProposelData drops the Quote Asked column, which is the label source.
$specialConditionTerms = $this->extractSpecialConditionsFromQCR(
$data,
$policy_type,
$proposel_name,
$insurer_name
);
//transform the data into the currernt proposel and insurere ( get single proposel )
$data = $this->transformProposelData($data, $proposel_name, $insurer_name);
$terms_array = $specialConditionTerms;
// Initialize age_ratio based on policy type
$age_ratio = $policy_type == 2 ? [
'self' => ['min' => '18', 'max' => '60'],
'spouse' => ['min' => 0, 'max' => 0],
'child' => ['min' => 0, 'max' => '25'],
'elders' => ['min' => 0, 'max' => 0],
] : [
'self' => ['min' => '18', 'max' => '60'],
];
foreach ($data['table_data']['data'] as $dataRow) {
$item = $dataRow['items'] ?? '';
foreach ($dataRow['data'] as $cellData) {
$parentth = $cellData['parentth'] ?? '';
$subth = $cellData['subth'] ?? '';
$input_value = $cellData['input_value'] != "" ? $cellData['input_value'] : ($cellData['value'] ?? '');
$value = $cellData['value'] ?? '';
if ($item == "family_composition" && $subth == $insurer_name && $policy_type == 2) {
$age_ratio_array = json_decode($input_value, true)['age_ratio'] ?? null;
if (! empty($age_ratio_array)) {
$age_ratio = $age_ratio_array;
}
}
// Skip unwanted keys
if (in_array($parentth, ['Sno', 'Item Key', 'Particulars', 'Action']) || in_array($subth, ['Quote Asked'])) {
continue;
}
// Special conditions already extracted above.
if (str_starts_with((string) $item, 'special_condition')) {
continue;
}
switch (true) {
// CASE 1: Handle sum insured
case in_array($item, ['sum_insured', 'sumInsured2']):
$si_amt = explode(',', $value);
$terms_array[$item] = $si_amt[0] ?? '';
$terms_array['multiple_sum_insured'] = array_slice($si_amt, 1);
// Add age_ratio after Sum insured
if ($policy_type == 1) {
$terms_array['age_ratio'] = $age_ratio;
}
break;
// CASE 2: Handle family floaters
case $item === 'family_composition':
$terms_array['family_floaters'] = isJsonString($input_value) ? json_decode($input_value, true) : $input_value;
// Add age_ratio after family floaters
if ($policy_type == 2) {
$terms_array['age_ratio'] = $age_ratio;
}
break;
// DEFAULT CASE :
default:
// $terms_array[$item] = isJsonString($input_value) ? (json_decode($input_value, true)['key'] ?? '') : $value;
$terms_array[$item] = $value;
break;
}
}
}
return json_encode($terms_array);
}
/**
* Build special_condition_label/input arrays from QCR rows.
*
* Mapping:
* - Canonical RFQ hidden format "label--value" (or display "label - value") on insurer cell
* - Otherwise Quote Asked = label, insurer cell = value
*
* @return array>
*/
private function extractSpecialConditionsFromQCR($data, $policy_type, $proposel_name, $insurer_name): array
{
$labelKey = $policy_type == 1 ? 'gpa_special_condition_label' : 'special_condition_label';
$inputKey = $policy_type == 1 ? 'gpa_special_condition_input' : 'special_condition_input';
$result = [
$labelKey => [],
$inputKey => [],
];
if (empty($data['table_data']['data']) || ! is_array($data['table_data']['data'])) {
return [];
}
foreach ($data['table_data']['data'] as $dataRow) {
$item = $dataRow['items'] ?? '';
if (! str_starts_with((string) $item, 'special_condition')) {
continue;
}
$quoteAsked = '';
$insurerDisplay = '';
$insurerInput = '';
foreach ($dataRow['data'] ?? [] as $cellData) {
$parentth = $cellData['parentth'] ?? '';
$subth = $cellData['subth'] ?? '';
if ($parentth !== $proposel_name) {
continue;
}
$cellInput = isset($cellData['input_value']) && $cellData['input_value'] !== ''
? $cellData['input_value']
: ($cellData['value'] ?? '');
$cellValue = $cellData['value'] ?? '';
if ($subth === 'Quote Asked') {
$quoteAsked = $this->normalizeSpecialConditionCellText($cellValue, $cellInput);
}
if ($subth === $insurer_name) {
$insurerDisplay = is_string($cellValue) ? trim($cellValue) : '';
$insurerInput = is_string($cellInput) ? trim((string) $cellInput) : '';
}
}
[$label, $input] = $this->parseSpecialConditionParts($insurerInput, $insurerDisplay, $quoteAsked);
if ($label === '' && $input === '') {
continue;
}
$result[$labelKey][] = $label;
$result[$inputKey][] = $input;
}
if ($result[$labelKey] === [] && $result[$inputKey] === []) {
return [];
}
return $result;
}
/**
* Prefer visible cell text; fall back to hidden input_value.
*/
private function normalizeSpecialConditionCellText($display_value, $input_value): string
{
$display = is_string($display_value) ? trim($display_value) : '';
if ($display !== '') {
return $display;
}
return is_string($input_value) ? trim($input_value) : '';
}
/**
* Parse RFQ/QCR special-condition cells into [label, value].
*
* @return array{0: string, 1: string}
*/
private function parseSpecialConditionParts($input_value, $display_value = '', $quote_asked = ''): array
{
// Prefer canonical hidden "label--value"; otherwise prefer visible insurer text
// (QCR edits often update the cell text but leave the hidden input stale/empty).
$raw = '';
if (is_string($input_value) && str_contains($input_value, '--')) {
$raw = trim($input_value);
} elseif (is_string($display_value) && trim($display_value) !== '') {
$raw = trim($display_value);
} elseif (is_string($input_value)) {
$raw = trim($input_value);
}
$quoteAsked = is_string($quote_asked) ? trim($quote_asked) : '';
if ($raw === '' && $quoteAsked === '') {
return ['', ''];
}
if ($raw !== '' && str_contains($raw, '--')) {
$parts = explode('--', $raw, 2);
return [trim($parts[0] ?? ''), trim($parts[1] ?? '')];
}
if ($raw !== '' && str_contains($raw, ' - ')) {
$parts = explode(' - ', $raw, 2);
return [trim($parts[0] ?? ''), trim($parts[1] ?? '')];
}
// QCR columns: Quote Asked = label, insurer response = value
if ($quoteAsked !== '' && $raw !== '') {
return [$quoteAsked, $raw];
}
if ($quoteAsked !== '') {
return [$quoteAsked, $quoteAsked];
}
// Single insurer cell with no Quote Asked / separator: use as label and value
return [$raw, $raw];
}
public function convertNonEbQCRJsonToPolicyTerms($allTableData)
{
if (! empty($allTableData)) {
array_pop($allTableData); // Remove the last item from the array
$terms_array = [];
foreach ($allTableData as $TableIndex => $tableGroup) {
if ($TableIndex == 0) {
continue;
}
foreach ($tableGroup['table_data']['data'] as $dataRow) {
$item = $dataRow['items'] ?? '';
foreach ($dataRow['data'] as $cellData) {
$parentth = $cellData['parentth'] ?? '';
$subth = $cellData['subth'] ?? '';
$value = $cellData['display_content'] ?? '';
// Skip unwanted keys
if (in_array($parentth, ['SNO', 'Particulars', 'Action']) || $subth === 'Sum Insured' || $subth === 'Fidelity Limit') {
continue;
}
// Skip if item matches parentth
if ($item === $parentth) {
continue;
}
// Set the value directly, overwriting with latest encountered
if (! empty($item) && ! empty($value)) {
$finalItem = $item;
if (strpos($tableGroup['tableId'], 'summary') !== false) {
$policy_type = explode('_', $tableGroup['tableId'])[0] ?? '';
$finalItem = $policy_type . ' - ' . $item;
}
$terms_array[$finalItem] = $value;
}
}
}
}
return json_encode($terms_array);
} else {
return null;
}
}
public function getPlacementJson(array $data): ?string
{
$proposel_data = json_decode($data['proposel_data'] ?? '', true) ?? [];
$proposelName = $proposel_data['proposel_name'] ?? '';
$insurerName = $proposel_data['insurer_name'] ?? '';
// Non-EB placement JSON is filtered by the won proposal/insurer selection.
// Without that selection, skip placement terms rather than throwing.
if ($proposelName === '' || $insurerName === '') {
return null;
}
$QCRData = $this->RFQModel
->where('is_active', 1)
->where('lead_id', $data['id'])
->first();
if (! $QCRData || empty($QCRData['json'])) {
return null;
}
$jsonArray = json_decode($QCRData['json'], true);
$proposalData = array_pop($jsonArray); // Get last item and remove it from the array
if (empty($jsonArray)) {
return null;
}
$placement_json_data = array_map(function ($jsonData) use ($proposelName, $insurerName) {
return $this->transformNonEbProposelData(
$jsonData,
$proposelName,
$insurerName,
null,
1
);
}, $jsonArray);
if (! empty($proposalData['proposal_data']['over_all_column_data']) && is_array($proposalData['proposal_data']['over_all_column_data'])) {
foreach ($proposalData['proposal_data']['over_all_column_data'] as $key => &$proposal) {
// Keep only the required proposal
if ($key !== $proposelName) {
unset($proposalData['proposal_data']['over_all_column_data'][$key]);
} else {
// Within the matched proposal, filter insurers
if (! empty($proposal['insurers'])) {
$proposal['insurers'] = array_values(array_filter($proposal['insurers'], function ($insurer) use ($insurerName) {
return ($insurer['display_name'] ?? null) === $insurerName;
}));
}
}
}
unset($proposal); // Good practice after foreach by reference
}
$placement_json_data[] = $proposalData;
return json_encode($placement_json_data);
}
//------------------------------------------------------------------------------------------------
public function transformMailContent($lead_data, $mail_content, $page_name)
{
$current_year = date('Y');
$next_year = $current_year + 1;
$policy_year = "$current_year-$next_year";
$page_name = $page_name == "QCR" ? "Quote Comparison Report" : $page_name;
$log_path = base_url() . '/public/assets/images/Nhance-Logo-Final.png';
// $log_path ='https://venbait.in/nhance/dev/public/assets/images/Nhance-Logo-Final.png';
// dd($log_path);
$policy_expiry = strtotime($lead_data['policy_end_date']);
$formatted_policy = date("d-m-Y", $policy_expiry);
$logged_user_id = get_session_userid();
$logged_user_data = $this->userModel->where("id", $logged_user_id)->where("is_active", 1)->first();
if ($lead_data) {
// Replacing placeholders with actual values
$message = $mail_content;
$message = str_replace("{{CLIENT_NAME}}", $lead_data['client_name'] ?? "Valued Client", $message);
$message = str_replace("{{POLICY_LONG_NAME}}", $lead_data['long_name'] ?? $lead_data['policy_type'] ?? "Insurance Policy", $message);
$message = str_replace("{{POLICY_TYPE}}", $lead_data['policy_type'] ?? "Insurance Policy", $message);
$message = str_replace("{{RFQ_OR_QCR}}", $page_name, $message);
$message = str_replace("{{POLICY_YEAR}}", $policy_year, $message);
$message = str_replace("{{POLICY_END_DATE}}", " (Due On " . $formatted_policy . ")", $message);
$message = str_replace("{{LOGGED_USER_NAME}}", ucfirst($logged_user_data['first_name'] ?? "") . " " . ucfirst($logged_user_data['last_name'] ?? ""), $message);
$message = str_replace("{{LOGGED_USER_EMAIL}}", $logged_user_data['email'] ?? "", $message);
$message = str_replace("{{LOGGED_USER_MOBILE}}", $logged_user_data['mobile'] ?? "", $message);
$message = str_replace("{{LOGO_PATH}}", $log_path ?? "", $message);
return $message;
} else {
return ''; // Return empty if no lead data found
}
}
/**
* Return subject and mail content for a lead and a given template type.
*
* GET params:
* - lead_id (int, required)
* - template_type (string, one of: rfq, qcr, placement; default: rfq)
*
* Response:
* {
* status: 'success'|'error',
* code: 200|4xx|5xx,
* subject: string,
* mail_content: string,
* message: string
* }
*/
public function getLeadMailTemplate()
{
try {
$leadId = (int) $this->request->getGet('lead_id');
$templateType = strtolower((string) $this->request->getGet('template_type'));
if ($leadId <= 0) {
return $this->respond(
['status' => 'error', 'code' => 400, 'message' => 'Invalid lead id'],
400
);
}
if (! in_array($templateType, ['rfq', 'qcr', 'placement'], true)) {
// Default to RFQ if not provided or invalid
$templateType = 'rfq';
}
// Fetch master lead data similar to sendMailWithAttachement / viewRFQ
$lead_data = $this->leadsModel
->select('
leads.*,
policy_type.long_name,
policy_type.policy_type,
user_profiles.email as created_person_email
')
->join('policy_type', 'leads.policy_type_id = policy_type.id', 'left')
->join('user_profiles', 'leads.created_by = user_profiles.id', 'left')
->where('leads.id', $leadId)
->where('leads.is_active', 1)
->first();
if (! $lead_data) {
$this->myLogger->logme('error', "MAIL TEMPLATE: lead not found | lead_id={$leadId}");
return $this->respond(
['status' => 'error', 'code' => 404, 'message' => 'Lead not found'],
404
);
}
// Determine page name and mail template type
if ($templateType === 'qcr') {
$pageName = 'QCR';
$mailTemplateId = '';
} elseif ($templateType === 'placement') {
$pageName = 'Placement';
$mailTemplateId = 'placement';
} else {
// rfq
$pageName = 'RFQ';
$mailTemplateId = '';
}
$rawMailTemplate = $this->getMailTemplate($mailTemplateId);
$rawSubjectTemplate = $this->getSubjectTemplate($lead_data);
$mail_content = $this->transformMailContent($lead_data, $rawMailTemplate, $pageName);
$subject = $this->transformMailContent($lead_data, $rawSubjectTemplate, $pageName);
//attachement files
$attachments = $this->leadFilesModel
->where('lead_id', $leadId)
->where('type !=', 2)
->where('is_active', 1)
->findAll();
$attachment_html = view('rfq/attachment_files', ['multi_file_data' => $attachments]);
return $this->respond(
[
'status' => 'success',
'code' => 200,
'subject' => $subject,
'mail_content' => $mail_content,
'message' => 'Mail template generated successfully',
'attachment_html' => $attachment_html,
],
200
);
} catch (\Throwable $e) {
$errorDetails = [
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
];
$this->myLogger->logme(
'error',
'MAIL TEMPLATE: exception occurred | ' . json_encode($errorDetails)
);
return $this->respond(
[
'status' => 'error',
'code' => 500,
'message' => 'Failed to generate mail template',
],
500
);
}
}
private function normalizeClaimHistoryPostData(): void
{
$post = $this->request->getPost();
if ((int) ($post['claim_history'] ?? 0) !== 1) {
return;
}
$rows = $this->getFinyearRowsFromPost($post);
if ($rows === []) {
return;
}
$leadFormType = (int) ($post['lead_form_type'] ?? 1);
$claimArrays = $leadFormType === 2
? $this->buildNonEbClaimHistoryPostArrays($rows)
: $this->buildEbClaimHistoryPostArrays($rows);
$this->request->setGlobal('post', array_merge($post, $claimArrays));
}
/**
* Remove internal nil_claims flag from stored/exported claim rows.
*
* @param array $claimDetails
*
* @return array
*/
private function sanitizeClaimDetailsForExport(array $claimDetails): array
{
if (empty($claimDetails['finyear']) || ! is_array($claimDetails['finyear'])) {
return $claimDetails;
}
$claimDetails['finyear'] = array_map(static function ($record) {
if (! is_array($record)) {
return $record;
}
unset($record['nil_claims']);
return $record;
}, $claimDetails['finyear']);
return $claimDetails;
}
/**
* @return list>
*/
private function getFinyearRowsFromPost(array $postData): array
{
$jsonPayload = $postData['finyear'] ?? $postData['fin_years_claims'] ?? null;
if (! is_string($jsonPayload) || $jsonPayload === '') {
return [];
}
$decoded = json_decode($jsonPayload, true);
if (! is_array($decoded) || empty($decoded['finyear']) || ! is_array($decoded['finyear'])) {
return [];
}
return $decoded['finyear'];
}
/**
* @param list> $rows
*
* @return array>
*/
private function buildEbClaimHistoryPostArrays(array $rows): array
{
$arrays = [
'first_year' => [],
'nil_claims' => [],
'emp_id' => [],
'emp_name' => [],
'gender' => [],
'designation' => [],
'sum_insured' => [],
'first_death_date' => [],
'first_cause_of_death' => [],
'first_claim_amount' => [],
];
foreach ($rows as $row) {
$isNil = ! empty($row['nil_claims']);
$arrays['first_year'][] = (string) ($row['year'] ?? '');
$arrays['nil_claims'][] = $isNil ? '1' : '0';
if ($isNil) {
$arrays['emp_id'][] = 'Nil';
$arrays['emp_name'][] = 'Nil';
$arrays['gender'][] = 'Nil';
$arrays['designation'][] = 'Nil';
$arrays['sum_insured'][] = 'Nil';
$arrays['first_death_date'][] = 'Nil';
$arrays['first_cause_of_death'][] = 'Nil';
$arrays['first_claim_amount'][] = 'Nil';
} else {
$arrays['emp_id'][] = (string) ($row['emp_id'] ?? '');
$arrays['emp_name'][] = (string) ($row['emp_name'] ?? '');
$arrays['gender'][] = (string) ($row['gender'] ?? '');
$arrays['designation'][] = (string) ($row['designation'] ?? '');
$arrays['sum_insured'][] = (string) ($row['sum_insured'] ?? '');
$arrays['first_death_date'][] = (string) ($row['death_date'] ?? '');
$arrays['first_cause_of_death'][] = (string) ($row['cause_of_death'] ?? '');
$arrays['first_claim_amount'][] = (string) ($row['settled'] ?? ($row['claim_amount'] ?? ''));
}
}
return $arrays;
}
/**
* @param list> $rows
*
* @return array>
*/
private function buildNonEbClaimHistoryPostArrays(array $rows): array
{
$arrays = [
'first_year' => [],
'nil_claims' => [],
'first_policy_type_' => [],
'first_date_of_loss_' => [],
'first_cause_of_loss' => [],
'first_claim_amount' => [],
'first_settled_amount' => [],
'first_claim_status' => [],
];
foreach ($rows as $row) {
$isNil = ! empty($row['nil_claims']);
$arrays['first_year'][] = (string) ($row['year'] ?? '');
$arrays['nil_claims'][] = $isNil ? '1' : '0';
if ($isNil) {
$arrays['first_policy_type_'][] = 'Nil';
$arrays['first_date_of_loss_'][] = 'Nil';
$arrays['first_cause_of_loss'][] = 'Nil';
$arrays['first_claim_amount'][] = 'Nil';
$arrays['first_settled_amount'][] = 'Nil';
$arrays['first_claim_status'][] = 'Nil';
} else {
$arrays['first_policy_type_'][] = (string) ($row['policy_type'] ?? '');
$arrays['first_date_of_loss_'][] = (string) ($row['date_of_loss'] ?? '');
$arrays['first_cause_of_loss'][] = (string) ($row['cause_of_loss'] ?? '');
$arrays['first_claim_amount'][] = (string) ($row['claim_amount'] ?? '');
$arrays['first_settled_amount'][] = (string) ($row['settled_amount'] ?? '');
$arrays['first_claim_status'][] = (string) ($row['status'] ?? '');
}
}
return $arrays;
}
/**
* @return array Row index => 1 when nil claims, else 0
*/
private function resolveNilClaimRowFlags(array $postData): array
{
$flags = [];
foreach ((array) ($postData['nil_claims'] ?? []) as $idx => $val) {
$flags[(int) $idx] = (int) $val;
}
foreach ($this->getFinyearRowsFromPost($postData) as $idx => $row) {
if (! is_array($row)) {
continue;
}
if (! empty($row['nil_claims'])) {
$flags[(int) $idx] = 1;
} elseif (! isset($flags[(int) $idx])) {
$flags[(int) $idx] = 0;
}
}
return $flags;
}
/**
* @param array $nilClaimFlags
*/
private function getClaimHistoryRowCount(array $postData, array $nilClaimFlags): int
{
$counts = [
count((array) ($postData['first_year'] ?? [])),
count($nilClaimFlags),
];
$counts[] = count($this->getFinyearRowsFromPost($postData));
return max(0, ...$counts);
}
public function getLastFiveFinancialYears(): array
{
$year = (int) date('Y');
$month = (int) date('m');
// $month = (int) 5;
// Financial year starts in April
$currentFYStart = ($month >= 4) ? $year : $year - 1;
$financialYears = [];
for ($i = 0; $i < 6; $i++) {
$startYear = $currentFYStart - $i;
$endYear = $startYear + 1;
$financialYears[] = "{$startYear}-{$endYear}";
}
return $financialYears;
}
/**
* Fetch basic placement-related lead data for Non-EB placement modal (AJAX).
*
* Route: GET /rfq/placementData/{lead_id}
*/
public function getPlacementData($id)
{
try {
$leadId = (int) $id;
if ($leadId <= 0) {
return $this->respond(
['status' => 'error', 'code' => 400, 'message' => 'Invalid lead id'],
400
);
}
$lead = $this->leadsModel
->where('id', $leadId)
->where('is_active', 1)
->first();
if (! $lead) {
$this->myLogger->logme('error', "PLACEMENT DATA: lead not found | lead_id={$leadId}");
return $this->respond(
['status' => 'error', 'code' => 404, 'message' => 'Lead not found'],
404
);
}
$data = [
'id' => (int) $lead['id'],
'policy_start_date' => ! empty($lead['policy_start_date']) ? date('d-m-Y', strtotime($lead['policy_start_date'])) : null,
'policy_end_date' => ! empty($lead['policy_end_date']) ? date('d-m-Y', strtotime($lead['policy_end_date'])) : null,
'placement_date' => ! empty($lead['placement_date']) ? date('d-m-Y', strtotime($lead['placement_date'])) : null,
'payment_date' => ! empty($lead['payment_date']) ? date('d-m-Y', strtotime($lead['payment_date'])) : null,
'utr_no' => $lead['utr_no'] ?? null,
'premium_amount' => $lead['premium_amount'] ?? null,
'cd_amount' => $lead['cd_amount'] ?? null,
'total_amount' => $lead['total_amount'] ?? null,
'is_cd' => isset($lead['is_cd']) ? (int) $lead['is_cd'] : 1,
'is_installment' => isset($lead['is_installment']) ? (int) $lead['is_installment'] : 0,
'no_of_installment' => $lead['no_of_installment'] ?? 1,
'status' => $lead['status'] ?? null,
];
// Fetch insurer level contacts based on lead's insurer/branch
$insurerId = $lead['insurer_id'] ?? null;
$insurerBranchId = $lead['insurer_branch_id'] ?? null;
// echo $insurerId;die();
$insurerContacts = [];
if (! empty($insurerId)) {
// getContactForRFQ($insurerId, $branchId) returns insurer-level contacts
$contacts = $this->levelContactModel->getContactForRFQ($insurerId, $insurerBranchId);
// print_rr($contacts);die();
if (! empty($contacts) && is_array($contacts)) {
foreach ($contacts as $contact) {
$insurerContacts[] = [
'id' => (int) ($contact['id'] ?? 0),
'display' => trim(
($contact['insurer_name'] ?? '') . ' - ' .
($contact['branch_code'] ?? '') . ' - ' .
($contact['contact_person_name'] ?? '') . ' - ' .
($contact['contact_person_email'] ?? '')
),
'email' => $contact['contact_person_email'] ?? '',
];
}
}
}
// Client contact from lead data for placement TO
$clientContact = null;
if (! empty($lead['contact_person_email'])) {
$clientContact = [
'name' => $lead['contact_person_name'] ?? '',
'email' => $lead['contact_person_email'],
];
}
$data['insurer_contacts'] = $insurerContacts;
$data['client_contact'] = $clientContact;
return $this->respond(
[
'status' => 'success',
'code' => 200,
'data' => $data,
],
200
);
} catch (\Throwable $e) {
$errorDetails = [
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
];
$this->myLogger->logme(
'error',
'PLACEMENT DATA: exception occurred | ' . json_encode($errorDetails)
);
return $this->respond(
[
'status' => 'error',
'code' => 500,
'message' => 'Failed to fetch placement data',
],
500
);
}
}
// ------------ RFQ NON EB FUNCTIONS-----------------------------------------------------------------------------------------
public function rfqNonEB()
{
$this->loadLayout('view_rfq_non_eb');
}
/**
* Dedicated endpoint to view RFQ for Non-EB opportunities from the Opportunities List.
* Internally reuses the existing viewRFQ flow without modifying its logic.
*/
public function viewNonEbRFQFromList($id)
{
echo 'NONEB_RFQ';
}
/**
* Dedicated endpoint to view QCR for Non-EB opportunities from the Opportunities List.
* Internally reuses the existing viewRFQ flow without modifying its logic.
*/
public function viewNonEbQCRFromList($id)
{
echo 'NONEB_QCR';
}
// get lead data for edit both EB and NON-EB
public function getLeadNonEB($type, $actual_lead_id = null, $id = null)
{
// Convert '0' to null so it doesn't break your existing DB checks
$actual_lead_id = ($actual_lead_id == 0 || $actual_lead_id == '0') ? null : $actual_lead_id;
// Set basic data
$data = [
'issuer' => $this->issuer,
'client_type' => $this->clientType,
'lead_type' => $this->leadType,
'lead_status' => $this->leadsStatus,
'policy_type' => $this->policyTypeModel->where('is_active', 1)->findAll(),
'entity' => $this->kycEntityTypeModel->where('is_active', 1)->findAll(),
'insurer' => $this->insurerBranchModel->getInsurerBranchesWithInsurerNames(),
'tpa' => $this->tpaBranchModel->getTpaBranchesWithTpaNames(),
'lastFiveYears' => $this->getLastFiveFinancialYears(),
'gpaClaimType' => $this->claim_type_for_gpa,
'causeOfDeath' => $this->cause_of_death,
'selected_lead_type' => $type,
'actual_lead_id' => $actual_lead_id,
];
$data['tab_name'] = 'Opportunities';
$data['page_name'] = 'Opportunities';
if ($actual_lead_id > 0) {
// 🔹 Client Details (Single Row)
$data['actual_lead_client_details'] = $this->leadModel
->select("
sales_actual_leads.company_name,
clients.short_name,
clients.client_type,
sales_actual_leads.email,
sales_actual_leads.phone,
sales_actual_leads.address,
sales_actual_leads.website,
COALESCE(NULLIF(sales_actual_leads.gst_number, ''), clients.gst) AS gst_number,
sales_actual_leads.status,
sales_actual_leads.assigned_to
", false)
->join('clients', 'clients.id = sales_actual_leads.client_id', 'left')
->where('sales_actual_leads.lead_id', $actual_lead_id)
->first(); // first row only
// 🔹 Contact Person Details (First Row Only)
$data['actual_lead_contact_person_details'] = $this->contactModel
->select('contact_id, name, mobile, designation, email, is_primary')
->where('lead_id', $actual_lead_id)
->where('is_primary',1)
->orderBy('is_primary', 'DESC') // optional (primary first)
->first(); // only first row
}
else {
$data['actual_lead_client_details'] = null;
$data['actual_lead_contact_person_details'] = null;
}
// Fetch sales team members who are active in team 5
$data['salse_team'] = $this->userModel
->select('user_profiles.*')
->join('user_teams', 'user_profiles.id = user_teams.user_id')
->where('user_teams.team_id', 5)
->where('user_teams.is_active', 1)
->where('user_profiles.is_active', 1)
->findAll();
if (! empty($id)) {
$data['lead_edit_data'] = $this->leadsModel->where('id', $id)->first() ?? [];
$data['lead_edit_data']['multi_file_data'] = $this->leadFilesModel
->where('lead_id', $id)
->where('type !=', 2)
->where('is_active', 1)
->findAll() ?? null;
$data['lead_edit_data']['lead_file_count'] = count($data['lead_edit_data']['multi_file_data']);
// Decode and merge custom fields if present
$custom_fields_data = ! empty($data['lead_edit_data']['custom_fields'])
? json_decode($data['lead_edit_data']['custom_fields'], true)
: [];
if (! empty($custom_fields_data) && is_array($custom_fields_data)) {
$data['lead_edit_data'] = array_merge($data['lead_edit_data'], $custom_fields_data);
}
if (! empty($data['lead_edit_data'])) {
foreach (['policy_start_date', 'policy_end_date', 'source_policy_start_date', 'source_policy_end_date', 'incurred_claims_date', 'next_reminder_date'] as $dateField) {
$data['lead_edit_data'][$dateField] = ! empty($data['lead_edit_data'][$dateField])
? change_date_format($data['lead_edit_data'][$dateField], 'Y-m-d', 'd/m/Y')
: null;
}
$data['lead_edit_data']['fin_years_claims_array'] = ! empty($data['lead_edit_data']['fin_years_claims'])
? json_decode($data['lead_edit_data']['fin_years_claims'], true)['finyear'] ?? []
: [];
$data['lead_edit_data']['html'] = $this->generateViewPageHtml(
$data['lead_edit_data']['policy_type_id'] ?? null,
$data
) ?? "";
$html = view('rfq/multi_files', $data);
$data['lead_edit_data']['multi_file_html'] = trim($html) !== '' ? $html : null;
}
if (in_array((int) $data['lead_edit_data']['lead_type'], [1, 2, 3], true) && $data['lead_edit_data']['lead_form_type'] == 2) {
$data['lead_edit_data']['claims_details_html'] = view('rfq/claims_details_non_eb', [
'lead_edit_data' => $data['lead_edit_data'],
'lastFiveYears' => $data['lastFiveYears'] ?? $this->getLastFiveFinancialYears(),
]);
} else {
$data['lead_edit_data']['claims_details_html'] = "";
}
}
// dd($data);
return $this->loadLayout('leads_form_handler', $data);
}
public function getLeadEmailHistory($id)
{
if (! empty($id)) {
$result = $this->gmailSentHistoryModel
->where('pk', $id)
->where('module', "leads")
->orderBy('created_at', 'desc')
->findAll();
if (! empty($result)) {
$result = ! empty($result) ? $result : [];
return $this->response->setJSON((['status' => 'success', 'data' => $result]))->setStatusCode(200);
} else {
return $this->response->setJSON((['status' => 'error', 'message' => 'No History found']))->setStatusCode(400);
}
} else {
return $this->response->setJSON((['status' => 'error', 'message' => 'ID not found']))->setStatusCode(404);
}
// Select * from gmail_sent_history where pk != "" and module = "leads";
// echo $id;
}
public function getPolicyTypeFields()
{
$policy_type_id = $this->request->getGET('policy_type_id');
$html = $this->generateViewPageHtml($policy_type_id) ?? "";
if (! empty($html)) {
return $this->respond(['status' => true, 'code' => 200, 'message' => 'Policy Type FIELDS are found', 'data' => $html], 200);
} else {
return $this->respond(['status' => false, 'code' => 400, 'message' => 'Fields not found for this policy type'], 200);
}
}
/**
* AJAX endpoint to create or fetch RFQ Google Sheet for a lead.
*
* Flow:
* - Get lead_id from query param.
* - Fetch lead joined with policy_type (to read both misc columns).
* - If leads.misc has non-empty rfq_sheet_id, return that.
* - Else, read policy_type.misc.rfq_template_sheet_id and copy that template.
* - Apply protections (using RfqConfig if available).
* - Store new rfq_sheet_id back into leads.misc.
*/
public function createRfqSheet()
{
try {
$leadId = (int) $this->request->getGet('lead_id');
if ($leadId <= 0) {
return $this->respond(
['status' => 'error', 'code' => 400, 'message' => 'Invalid lead id'],
400
);
}
$this->myLogger->logme('error', "RFQ Sheet: starting for lead_id={$leadId}");
// Fetch lead + policy type + client address (LEFT JOIN for communication address on EB leads)
$lead = $this->leadsModel
->select('
leads.*,
policy_type.policy_type,
policy_type.long_name,
policy_type.misc as policy_misc,
clients.address1 as client_address1,
clients.address2 as client_address2,
clients.city as client_city,
clients.state as client_state,
clients.pincode as client_pincode
')
->join('policy_type', 'leads.policy_type_id = policy_type.id', 'left')
->join('clients', 'leads.client_id = clients.id', 'left')
->where('leads.id', $leadId)
->where('leads.is_active', 1)
->first();
if (! $lead) {
$this->myLogger->logme('error', "RFQ Sheet: lead not found | lead_id={$leadId}");
return $this->respond(
['status' => 'error', 'code' => 404, 'message' => 'Lead not found'],
404
);
}
// Decode leads.misc
$leadMisc = [];
if (! empty($lead['misc'])) {
$decoded = json_decode($lead['misc'], true);
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
$leadMisc = $decoded;
} else {
$this->myLogger->logme('error', "RFQ Sheet: invalid JSON in leads.misc | lead_id={$leadId}, misc={$lead['misc']}");
}
}
// If RFQ sheet already exists, just return it
if (! empty($leadMisc['rfq_sheet_id'])) {
$sheetId = (string) $leadMisc['rfq_sheet_id'];
$sheetLib = new GoogleSheetLib();
$sheetUrl = $sheetLib->sheetUrl($sheetId);
$this->myLogger->logme('error', "RFQ Sheet: existing sheet found | lead_id={$leadId}, sheet_id={$sheetId}");
return $this->respond(
[
'status' => 'success',
'code' => 200,
'sheet_id' => $sheetId,
'sheet_url' => $sheetUrl,
'message' => 'RFQ sheet already exists',
],
200
);
}
// Decode policy_type.misc and get template id
$policyMisc = [];
if (! empty($lead['policy_misc'])) {
$decodedPolicy = json_decode($lead['policy_misc'], true);
if (json_last_error() === JSON_ERROR_NONE && is_array($decodedPolicy)) {
$policyMisc = $decodedPolicy;
} else {
$this->myLogger->logme('error', "RFQ Sheet: invalid JSON in policy_type.misc | lead_id={$leadId}, policy_misc={$lead['policy_misc']}");
}
}
$templateId = $policyMisc['rfq_template_sheet_id'] ?? null;
if (empty($templateId)) {
$this->myLogger->logme(
'error',
"RFQ Sheet: rfq_template_sheet_id missing | lead_id={$leadId}, policy_misc=" . ($lead['policy_misc'] ?? 'NULL')
);
return $this->respond(
[
'status' => 'error',
'code' => 400,
'message' => 'RFQ template not found for this policy type',
],
400
);
}
// Prepare Google Sheet lib and config
$sheetLib = new GoogleSheetLib();
// Try to use RfqConfig if available for folder/protections
$parentFolderId = null;
$protections = [];
try {
/** @var \Config\RfqConfig $rfqConfig */
$rfqConfig = config('RfqConfig');
if ($rfqConfig) {
$parentFolderId = $rfqConfig->rfqParentFolderId ?? null;
$protections = $rfqConfig->protections ?? [];
}
} catch (\Throwable $e) {
// Config is optional; just log and continue with defaults
$this->myLogger->logme('error', 'RFQ Sheet: error loading RfqConfig | ' . $e->getMessage());
}
// Fallback to same parent folder as GoogleSheetController if config not set
if (! $parentFolderId) {
return $this->respond(
[
'status' => 'error',
'code' => 400,
'message' => 'Google config not correct, contact administrator',
],
400);
}
// Filename logic same as exportQCRandRFQ (lines 2660-2673)
$string = 'RFQ';
$current_year = date('Y');
$next_year = $current_year + 1;
$policy_year = "{$current_year}-{$next_year}";
$clientName = trim($lead['client_name'] ?? $lead['client_short_name'] ?? '');
$policyType = trim($lead['policy_type'] ?? '');
if (! empty($lead['policy_end_date'])) {
$policy_expiry = strtotime($lead['policy_end_date']);
$formatted_policy = date('d-m-Y', $policy_expiry);
$sheetName = "{$clientName}_{$policyType}_{$string}_{$policy_year}(Due On {$formatted_policy}).xlsx";
} else {
$sheetName = "{$clientName}_{$policyType}_{$string}_{$policy_year}_" . '.xlsx';
}
// Copy template in Drive
$newSheetId = $sheetLib->copyTemplate($templateId, $sheetName, $parentFolderId);
$this->myLogger->logme('error', "RFQ Sheet: template copied | lead_id={$leadId}, template_id={$templateId}, new_sheet_id={$newSheetId}");
// Fill placeholders across all sheets (see Config\RfqConfig::$rfqPlaceholders)
try {
/** @var \Config\RfqConfig $rfqCfg */
$rfqCfg = config('RfqConfig');
$placeholderData = $this->buildRfqPlaceholderData($lead);
$claimsPh = $rfqCfg->rfqClaimsPlaceholder ?? '{{CLAIMS_DETAILS}}';
$map = $rfqCfg->rfqPlaceholders ?? [];
$textReplacements = [];
foreach ($map as $placeholder => $key) {
if ($placeholder === $claimsPh) {
continue;
}
$textReplacements[$placeholder] = (string) ($placeholderData['values'][$key] ?? '');
}
$sheetLib->findAndReplaceAllSheets($newSheetId, $textReplacements);
if ($placeholderData['claims_table'] !== null) {
$written = $sheetLib->writeTableAtPlaceholder(
$newSheetId,
$claimsPh,
$placeholderData['claims_table']
);
if (! $written) {
$sheetLib->findAndReplaceAllSheets($newSheetId, [
$claimsPh => $placeholderData['claims_placeholder_fallback'],
]);
}
} else {
$sheetLib->findAndReplaceAllSheets($newSheetId, [
$claimsPh => $placeholderData['claims_placeholder_fallback'],
]);
}
$this->myLogger->logme('error', "RFQ Sheet: placeholders applied | lead_id={$leadId}, sheet_id={$newSheetId}");
} catch (\Throwable $e) {
$this->myLogger->logme(
'error',
"RFQ Sheet: placeholder fill failed | lead_id={$leadId}, error=" . $e->getMessage()
);
}
// Apply permissions: get sales users from DB (team_id 5) and add as editors
// $salesTeam = $this->userModel
// ->select('user_profiles.email')
// ->join('user_teams', 'user_profiles.id = user_teams.user_id')
// ->where('user_teams.team_id', 5)
// ->where('user_teams.is_active', 1)
// ->where('user_profiles.is_active', 1)
// ->findAll();
// $editorEmails = array_values(array_filter(array_unique(array_column($salesTeam, 'email'))));
// Decode rfq_qcr_viewers from lead and add as viewers
$editorEmails = [];
if (! empty($lead['rfq_qcr_viewers'])) {
$decoded = json_decode($lead['rfq_qcr_viewers'], true);
if (is_array($decoded)) {
$editorEmails = array_values(array_filter(array_unique($decoded)));
}
}
if ( ! empty($editorEmails)) {
try {
$sheetLib->applyPermissions($newSheetId, [
'editors' => $editorEmails,
// 'viewers' => $viewerEmails,
]);
$this->myLogger->logme('error', "RFQ Sheet: permissions applied | sheet_id={$newSheetId}, editors=" . count($editorEmails) );
} catch (\Throwable $e) {
$this->myLogger->logme(
'error',
"RFQ Sheet: failed to apply permissions | sheet_id={$newSheetId}, error=" . $e->getMessage()
);
}
}
// Apply protections if configured
if (! empty($protections)) {
try {
$sheetLib->applyProtections($newSheetId, $protections);
$this->myLogger->logme('error', "RFQ Sheet: protections applied | sheet_id={$newSheetId}");
} catch (\Throwable $e) {
$this->myLogger->logme(
'error',
"RFQ Sheet: failed to apply protections | sheet_id={$newSheetId}, error=" . $e->getMessage()
);
}
}
// Update leads.misc with new rfq_sheet_id
$leadMisc['rfq_sheet_id'] = $newSheetId;
$this->leadsModel->update($leadId, [
'misc' => json_encode($leadMisc),
'status' => 'rfq_created',
]);
$sheetUrl = $sheetLib->sheetUrl($newSheetId);
$this->myLogger->logme(
'error',
"RFQ Sheet: created and stored | lead_id={$leadId}, sheet_id={$newSheetId}"
);
return $this->respond(
[
'status' => 'success',
'code' => 200,
'sheet_id' => $newSheetId,
'sheet_url' => $sheetUrl,
'message' => 'RFQ sheet created successfully',
],
200
);
} catch (\Throwable $e) {
$errorDetails = [
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
];
$this->myLogger->logme(
'error',
'RFQ Sheet: exception occurred | ' . json_encode($errorDetails)
);
return $this->respond(
[
'status' => 'error',
'code' => 500,
'message' => 'Failed to create RFQ sheet',
],
500
);
}
}
/**
* Build values for RFQ Google Sheet placeholders (see RfqConfig::$rfqPlaceholders).
*
* @return array{values: array, claims_table: ?list>, claims_placeholder_fallback: string}
*/
private function buildRfqPlaceholderData(array $lead): array
{
helper('excel_util_helper');
$custom = [];
if (! empty($lead['custom_fields'])) {
$decoded = json_decode($lead['custom_fields'], true);
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
$custom = $decoded;
}
}
$clientParts = array_filter([
trim((string) ($lead['client_address1'] ?? '')),
trim((string) ($lead['client_address2'] ?? '')),
trim((string) ($lead['client_city'] ?? '')),
trim((string) ($lead['client_state'] ?? '')),
trim((string) ($lead['client_pincode'] ?? '')),
], static function ($s) {
return $s !== '';
});
$clientAddr = implode(', ', $clientParts);
$cfParts = array_filter([
trim((string) ($custom['address'] ?? '')),
trim((string) ($custom['pincode'] ?? '')),
], static function ($s) {
return $s !== '';
});
$cfAddr = implode(', ', $cfParts);
$communicationAddress = $clientAddr !== '' ? $clientAddr : $cfAddr;
$policyPeriod = '';
if (! empty($lead['policy_start_date']) && ! empty($lead['policy_end_date'])) {
$policyPeriod = date('d-m-Y', strtotime((string) $lead['policy_start_date']))
. ' to '
. date('d-m-Y', strtotime((string) $lead['policy_end_date']));
}
$lt = (int) ($lead['lead_type'] ?? 0);
$opportunityType = $this->leadType[$lt] ?? '';
$riskRaw = (string) ($custom['risk_location'] ?? '');
$riskLocation = $riskRaw !== ''
? ucwords(str_replace('_', ' ', strtolower($riskRaw)))
: '';
$occupancyRaw = (string) ($custom['business_description'] ?? $custom['occupancy_risk'] ?? '');
$occupancy = $occupancyRaw !== ''
? ucwords(str_replace('_', ' ', strtolower($occupancyRaw)))
: '';
$claimsTable = null;
$claimsFallback = 'No claims data available';
if (! empty($lead['fin_years_claims'])) {
$claimJson = json_decode($lead['fin_years_claims'], true);
if (json_last_error() === JSON_ERROR_NONE && is_array($claimJson)) {
$finyear = $claimJson['finyear'] ?? null;
if (is_array($finyear) && $finyear !== []) {
$claimsTable = $this->buildRfqClaimsTableValues($finyear);
}
}
}
$values = [
'insured_name' => trim((string) ($lead['client_name'] ?? $lead['client_short_name'] ?? '')),
'communication_address' => $communicationAddress,
'gst' => trim((string) ($lead['gst'] ?? '')),
'pan' => trim((string) ($lead['pan'] ?? '')),
'policy_period' => $policyPeriod,
'opportunity_type' => $opportunityType,
'risk_location' => $riskLocation,
'occupancy' => $occupancy,
'claims_details' => '',
];
return [
'values' => $values,
'claims_table' => $claimsTable,
'claims_placeholder_fallback' => $claimsFallback,
];
}
/**
* @param list> $finyear
*
* @return list>
*/
private function buildRfqClaimsTableValues(array $finyear): array
{
helper('excel_util_helper');
$finyear = array_map(static function ($record) {
if (! is_array($record)) {
return $record;
}
unset($record['nil_claims']);
return $record;
}, $finyear);
$first = $finyear[0] ?? null;
if (! is_array($first) || $first === []) {
return [['No data available']];
}
$headers = [];
foreach (array_keys($first) as $key) {
$headers[] = ucwords(str_replace('_', ' ', (string) $key));
}
$rows = [];
$rows[] = $headers;
foreach ($finyear as $record) {
if (! is_array($record)) {
continue;
}
$line = [];
foreach ($record as $arrayKey => $value) {
if (in_array($arrayKey, ['sum_insured', 'claim_amount', 'settled'], true)) {
$line[] = function_exists('formatIndianCurrency')
? formatIndianCurrency((int) $value)
: (string) $value;
} else {
$line[] = $value === null || $value === '' ? '' : (string) $value;
}
}
$rows[] = $line;
}
return $rows;
}
public function createQcrSheet()
{
try {
$leadId = (int) $this->request->getGet('lead_id');
if ($leadId <= 0) {
return $this->respond(
['status' => 'error', 'code' => 400, 'message' => 'Invalid lead id'],
400
);
}
$this->myLogger->logme('error', "QCR Sheet: starting for lead_id={$leadId}");
$lead = $this->leadsModel
->select('
leads.*,
policy_type.policy_type,
policy_type.long_name,
policy_type.misc as policy_misc
')
->join('policy_type', 'leads.policy_type_id = policy_type.id', 'left')
->where('leads.id', $leadId)
->where('leads.is_active', 1)
->first();
if (! $lead) {
$this->myLogger->logme('error', "QCR Sheet: lead not found | lead_id={$leadId}");
return $this->respond(
['status' => 'error', 'code' => 404, 'message' => 'Lead not found'],
404
);
}
$leadMisc = [];
if (! empty($lead['misc'])) {
$decoded = json_decode($lead['misc'], true);
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
$leadMisc = $decoded;
}
}
// If QCR sheet already exists, just return it
if (! empty($leadMisc['qcr_sheet_id'])) {
$sheetId = (string) $leadMisc['qcr_sheet_id'];
$sheetLib = new GoogleSheetLib();
$sheetUrl = $sheetLib->sheetUrl($sheetId);
$this->myLogger->logme('error', "QCR Sheet: existing sheet found | lead_id={$leadId}, sheet_id={$sheetId}");
return $this->respond(
[
'status' => 'success',
'code' => 200,
'sheet_id' => $sheetId,
'sheet_url' => $sheetUrl,
'message' => 'QCR sheet already exists',
],
200
);
}
// Source is the existing RFQ sheet for this lead
$rfqSheetId = $leadMisc['rfq_sheet_id'] ?? null;
if (empty($rfqSheetId)) {
$this->myLogger->logme('error', "QCR Sheet: rfq_sheet_id missing in leads.misc | lead_id={$leadId}");
return $this->respond(
['status' => 'error', 'code' => 400, 'message' => 'RFQ sheet not found. Please create the RFQ sheet first.'],
400
);
}
// Load config for folder id and permissions
$parentFolderId = null;
$permissions = [];
try {
/** @var \Config\RfqConfig $rfqConfig */
$rfqConfig = config('RfqConfig');
if ($rfqConfig) {
$parentFolderId = $rfqConfig->rfqParentFolderId ?? null;
$permissions = $rfqConfig->permissions ?? [];
}
} catch (\Throwable $e) {
$this->myLogger->logme('error', 'QCR Sheet: error loading RfqConfig | ' . $e->getMessage());
}
if (! $parentFolderId) {
return $this->respond(
['status' => 'error', 'code' => 400, 'message' => 'Google config not correct, contact administrator'],
400
);
}
// Build filename: same as RFQ naming but with QCR
$string = 'QCR';
$current_year = date('Y');
$next_year = $current_year + 1;
$policy_year = "{$current_year}-{$next_year}";
$clientName = trim($lead['client_name'] ?? $lead['client_short_name'] ?? '');
$policyType = trim($lead['policy_type'] ?? '');
if (! empty($lead['policy_end_date'])) {
$policy_expiry = strtotime($lead['policy_end_date']);
$formatted_policy = date('d-m-Y', $policy_expiry);
$sheetName = "{$clientName}_{$policyType}_{$string}_{$policy_year}(Due On {$formatted_policy}).xlsx";
} else {
$sheetName = "{$clientName}_{$policyType}_{$string}_{$policy_year}_.xlsx";
}
$sheetLib = new GoogleSheetLib();
$newSheetId = $sheetLib->copyTemplate($rfqSheetId, $sheetName, $parentFolderId);
$this->myLogger->logme('error', "QCR Sheet: copied from RFQ sheet | lead_id={$leadId}, rfq_sheet_id={$rfqSheetId}, new_sheet_id={$newSheetId}");
// Apply permissions: sales team as editors
// $salesTeam = $this->userModel
// ->select('user_profiles.email')
// ->join('user_teams', 'user_profiles.id = user_teams.user_id')
// ->where('user_teams.team_id', 5)
// ->where('user_teams.is_active', 1)
// ->where('user_profiles.is_active', 1)
// ->findAll();
// $editorEmails = array_values(array_filter(array_unique(array_column($salesTeam, 'email'))));
// Decode rfq_qcr_viewers from lead and add as viewers
$editorEmails = [];
if (! empty($lead['rfq_qcr_viewers'])) {
$decoded = json_decode($lead['rfq_qcr_viewers'], true);
if (is_array($decoded)) {
$editorEmails = array_values(array_filter(array_unique($decoded)));
}
}
// !dd($editorEmails);
if (! empty($editorEmails)) {
try {
$sheetLib->applyPermissions($newSheetId, [
'editors' => $editorEmails,
// 'viewers' => $viewerEmails,
]);
$this->myLogger->logme('error', "QCR Sheet: permissions applied | sheet_id={$newSheetId}, editors=" . count($editorEmails));
} catch (\Throwable $e) {
$this->myLogger->logme('error', "QCR Sheet: failed to apply permissions | sheet_id={$newSheetId}, error=" . $e->getMessage());
}
}
// No protections applied for QCR as of now
// Save qcr_sheet_id to leads.misc and update status
$leadMisc['qcr_sheet_id'] = $newSheetId;
$this->leadsModel->update($leadId, [
'misc' => json_encode($leadMisc),
'status' => 'qcr_created',
]);
$sheetUrl = $sheetLib->sheetUrl($newSheetId);
$this->myLogger->logme('error', "QCR Sheet: created and stored | lead_id={$leadId}, sheet_id={$newSheetId}");
return $this->respond(
[
'status' => 'success',
'code' => 200,
'sheet_id' => $newSheetId,
'sheet_url' => $sheetUrl,
'message' => 'QCR sheet created successfully',
],
200
);
} catch (\Throwable $e) {
$errorDetails = [
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
];
$this->myLogger->logme('error', 'QCR Sheet: exception occurred | ' . json_encode($errorDetails));
return $this->respond(
['status' => 'error', 'code' => 500, 'message' => 'Failed to create QCR sheet'],
500
);
}
}
/**
* Download RFQ/QCR Excel file from Google Sheet for Non-EB leads.
*
* Sheet selection based on recipient_type:
* - 'insurer' → rfq_sheet_id
* - 'client' → qcr_sheet_id
* - 'internal' → based on lead status: qcr_created/qcr_sent → qcr_sheet_id, else rfq_sheet_id
*
* @param string $recipientType insurer|client|internal
*/
protected function downloadFileFromGoogleSheet(array $lead_data, string $recipientType = 'insurer'): ?array
{
try {
$leadId = (int) ($lead_data['id'] ?? 0);
// Decode misc JSON
$misc = [];
if (! empty($lead_data['misc'])) {
$decoded = json_decode($lead_data['misc'], true);
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
$misc = $decoded;
} else {
$this->myLogger->logme('error', "GSHEET DOWNLOAD: invalid JSON in leads.misc | lead_id={$leadId}, misc={$lead_data['misc']}");
}
}
// Pick sheet id based on recipient type
$sheetId = null;
$sheetType = '';
if ($recipientType === 'client') {
$sheetId = $misc['qcr_sheet_id'] ?? null;
$sheetType = 'qcr_sheet_id';
} elseif ($recipientType === 'internal') {
$leadStatus = $lead_data['status'] ?? '';
if (in_array($leadStatus, ['qcr_created', 'qcr_sent'])) {
$sheetId = $misc['qcr_sheet_id'] ?? null;
$sheetType = 'qcr_sheet_id';
} else {
$sheetId = $misc['rfq_sheet_id'] ?? null;
$sheetType = 'rfq_sheet_id';
}
} else {
// insurer or default
$sheetId = $misc['rfq_sheet_id'] ?? null;
$sheetType = 'rfq_sheet_id';
}
if (empty($sheetId)) {
$this->myLogger->logme('error', "GSHEET DOWNLOAD: {$sheetType} missing in leads.misc | lead_id={$leadId}, recipient_type={$recipientType}");
throw new \RuntimeException("{$sheetType} not found in leads. Please create the sheet first.");
}
$this->myLogger->logme('error', "GSHEET DOWNLOAD: starting download | lead_id={$leadId}, sheet_id={$sheetId}");
$sheetLib = new GoogleSheetLib();
$binary = $sheetLib->downloadExcel($sheetId);
if (! $binary) {
$this->myLogger->logme('error', "GSHEET DOWNLOAD: empty content from Google | lead_id={$leadId}, sheet_id={$sheetId}");
throw new \RuntimeException('Empty content from Google Sheet');
}
$filename = "lead_{$leadId}_" . date('Ymd_His') . '.xlsx';
$uploadDir = WRITEPATH . 'tmp/';
$uploadFilePath = $uploadDir . $filename;
if (! is_dir($uploadDir)) {
if (! mkdir($uploadDir, 0777, true) && ! is_dir($uploadDir)) {
$this->myLogger->logme('error', "GSHEET DOWNLOAD: failed to create tmp dir | path={$uploadDir}");
throw new \RuntimeException('Failed to create temporary directory for Excel download');
}
}
$bytes = file_put_contents($uploadFilePath, $binary);
if ($bytes === false) {
$this->myLogger->logme('error', "GSHEET DOWNLOAD: failed to write file | path={$uploadFilePath}");
throw new \RuntimeException('Failed to write downloaded Excel file');
}
$this->myLogger->logme('error', "GSHEET DOWNLOAD: saved file | lead_id={$leadId}, path={$uploadFilePath}");
return [
'filePath' => $uploadFilePath,
'fileName' => $filename,
];
} catch (\Throwable $e) {
$errorDetails = [
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
];
$this->myLogger->logme(
'error',
'GSHEET DOWNLOAD: exception occurred | ' . json_encode($errorDetails)
);
// Final fallback to existing Non-EB Excel generator to avoid breaking mail flow
// $leadId = (int) ($lead_data['id'] ?? 0);
// $file_info = $this->constructNonEbExcelToSaveTemp($leadId, 1, null);
// return [
// 'filePath' => $file_info['filePath'],
// 'fileName' => $file_info['fileName'],
// ];
}
}
/**
* Create a placement Google Sheet by copying the QCR sheet (Non-EB only).
*
* - Reads qcr_sheet_id from leads.misc
* - If placement_sheet_id already exists, reuses it
* - Copies the QCR sheet, replacing "QCR" with "Placement" in the filename
* - Applies same editor/viewer permissions as QCR
* - Saves placement_sheet_id to leads.misc
* - Downloads the placement sheet as Excel and returns file info
*/
protected function createAndDownloadPlacementSheet(array $lead_data): array
{
$leadId = (int) ($lead_data['id'] ?? 0);
$sheetLib = new GoogleSheetLib();
// Decode leads.misc
$leadMisc = [];
if (! empty($lead_data['misc'])) {
$decoded = json_decode($lead_data['misc'], true);
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
$leadMisc = $decoded;
}
}
// If placement sheet already exists, download and return it
if (! empty($leadMisc['placement_sheet_id'])) {
$placementSheetId = (string) $leadMisc['placement_sheet_id'];
$this->myLogger->logme('error', "Placement Sheet: existing sheet found | lead_id={$leadId}, sheet_id={$placementSheetId}");
return $this->downloadPlacementExcel($sheetLib, $placementSheetId, $leadId);
}
// QCR sheet must exist to create placement
$qcrSheetId = $leadMisc['qcr_sheet_id'] ?? null;
if (empty($qcrSheetId)) {
$this->myLogger->logme('error', "Placement Sheet: qcr_sheet_id missing in leads.misc | lead_id={$leadId}");
throw new \RuntimeException('QCR sheet not found. Please create the QCR sheet first.');
}
// Get QCR filename and replace "QCR" with "Placement"
$qcrFileName = $sheetLib->getFileName($qcrSheetId);
$placementFileName = str_ireplace('QCR', 'Placement', $qcrFileName);
$this->myLogger->logme('error', "Placement Sheet: renaming | qcr_name={$qcrFileName}, placement_name={$placementFileName}");
// Load folder ID from config
$parentFolderId = null;
try {
$rfqConfig = config('RfqConfig');
if ($rfqConfig) {
$parentFolderId = $rfqConfig->rfqParentFolderId ?? null;
}
} catch (\Throwable $e) {
$this->myLogger->logme('error', 'Placement Sheet: error loading RfqConfig | ' . $e->getMessage());
}
if (! $parentFolderId) {
throw new \RuntimeException('Google config not correct, contact administrator');
}
// Copy QCR sheet as placement
$placementSheetId = $sheetLib->copyTemplate($qcrSheetId, $placementFileName, $parentFolderId);
$this->myLogger->logme('error', "Placement Sheet: copied from QCR | lead_id={$leadId}, qcr_sheet_id={$qcrSheetId}, placement_sheet_id={$placementSheetId}");
// Apply same permissions as QCR (editors: sales team, viewers: rfq_qcr_viewers)
// $salesTeam = $this->userModel
// ->select('user_profiles.email')
// ->join('user_teams', 'user_profiles.id = user_teams.user_id')
// ->where('user_teams.team_id', 5)
// ->where('user_teams.is_active', 1)
// ->where('user_profiles.is_active', 1)
// ->findAll();
// $editorEmails = array_values(array_filter(array_unique(array_column($salesTeam, 'email'))));
$editorEmails = [];
if (! empty($lead_data['rfq_qcr_viewers'])) {
$decoded = json_decode($lead_data['rfq_qcr_viewers'], true);
if (is_array($decoded)) {
$editorEmails = array_values(array_filter(array_unique($decoded)));
}
}
if (! empty($editorEmails) ) {
try {
$sheetLib->applyPermissions($placementSheetId, [
'editors' => $editorEmails,
// 'viewers' => $viewerEmails,
]);
$this->myLogger->logme('error', "Placement Sheet: permissions applied | sheet_id={$placementSheetId}, editors=" . count($editorEmails));
} catch (\Throwable $e) {
$this->myLogger->logme('error', "Placement Sheet: failed to apply permissions | sheet_id={$placementSheetId}, error=" . $e->getMessage());
}
}
// Save placement_sheet_id to leads.misc
$leadMisc['placement_sheet_id'] = $placementSheetId;
$this->leadsModel->update($leadId, [
'misc' => json_encode($leadMisc),
]);
$this->myLogger->logme('error', "Placement Sheet: saved to leads.misc | lead_id={$leadId}, placement_sheet_id={$placementSheetId}");
return $this->downloadPlacementExcel($sheetLib, $placementSheetId, $leadId);
}
/**
* Download a placement Google Sheet as Excel to tmp directory.
*/
private function downloadPlacementExcel(GoogleSheetLib $sheetLib, string $sheetId, int $leadId): array
{
$binary = $sheetLib->downloadExcel($sheetId);
if (! $binary) {
$this->myLogger->logme('error', "Placement Sheet: empty content from Google | lead_id={$leadId}, sheet_id={$sheetId}");
throw new \RuntimeException('Empty content from Google Sheet for placement');
}
$filename = "lead_{$leadId}_placement_" . date('Ymd_His') . '.xlsx';
$uploadDir = WRITEPATH . 'tmp/';
$uploadFilePath = $uploadDir . $filename;
if (! is_dir($uploadDir)) {
if (! mkdir($uploadDir, 0777, true) && ! is_dir($uploadDir)) {
throw new \RuntimeException('Failed to create temporary directory');
}
}
$bytes = file_put_contents($uploadFilePath, $binary);
if ($bytes === false) {
throw new \RuntimeException('Failed to write placement Excel file');
}
$this->myLogger->logme('error', "Placement Sheet: downloaded | lead_id={$leadId}, path={$uploadFilePath}");
return [
'filePath' => $uploadFilePath,
'fileName' => $filename,
];
}
public function generateViewPageHtml($policy_type_id, $data = [])
{
$data['insurer'] = $this->insurerBranchModel->getInsurerBranchesWithInsurerNames();
$data['tpa'] = $this->tpaBranchModel->getTpaBranchesWithTpaNames();
$data['lastFiveYears'] = $data['lastFiveYears'] ?? $this->getLastFiveFinancialYears();
$data['gpaClaimType'] = $data['gpaClaimType'] ?? $this->claim_type_for_gpa;
$data['causeOfDeath'] = $data['causeOfDeath'] ?? $this->cause_of_death;
$viewMap = [
1 => 'rfq/gpa',
6 => 'rfq/gpa',
7 => 'rfq/gpa',
2 => 'rfq/gmc',
3 => 'rfq/gmc',
4 => 'rfq/gmc',
5 => 'rfq/gmc',
17 => 'rfq/burglary',
22 => 'rfq/car',
23 => 'rfq/cpm',
24 => 'rfq/cyber_crime',
25 => 'rfq/do',
27 => 'rfq/eo',
49 => 'rfq/money',
19 => 'rfq/cgl',
59 => 'rfq/sfsp',
63 => 'rfq/wc',
15 => 'rfq/blu',
16 => 'rfq/bsu',
44 => 'rfq/marine',
45 => 'rfq/marine',
46 => 'rfq/marine',
47 => 'rfq/marine',
50 => 'rfq/office',
];
return isset($viewMap[$policy_type_id]) ? view($viewMap[$policy_type_id], $data) : "";
}
// ------------------- RFQ NON EB EXCEL EXPORT FUNCTION ---------------------------------------------------------------------------------------
public function constructNonEbExcelToSaveTemp($lead_id, $type, $propsal_and_insurer = null)
{
$rfq_data = $this->RFQModel->getRFQTableDataWithLeadIDAndType($lead_id, $type);
$claim_history = $rfq_data['claim_history'];
// dd($claim_history);
$lead_data = json_decode($rfq_data['custom_fields'], true) ?? [];
$lead_data['claim_history'] = $claim_history;
// dd($lead_data);
if (! is_array($lead_data)) {
$lead_data = [];
}
if (isset($lead_data['pincode']) && isset($lead_data['address'])) {
// Reorder keys: address first, then pincode, then the rest
$reordered = ['address' => $lead_data['address'], 'pincode' => $lead_data['pincode']];
// Remove existing keys
unset($lead_data['address'], $lead_data['pincode']);
// Merge reordered with the rest
$lead_data = $reordered + $lead_data;
}
$lead_data = array_merge(['Insured' => $rfq_data['client_name']], $lead_data);
$policy_registration_data = [];
if (isset($rfq_data['registration_json']) && ! empty($rfq_data['registration_json'])) {
$policy_registration_data = json_decode($rfq_data['registration_json'], true);
}
$jsonData = json_decode($rfq_data['json'], true);
$proposalData = end($jsonData);
array_pop($jsonData);
$length = 0;
// dd($jsonData[0]['table_data']['headers']);
foreach ($jsonData as $key => $data) {
if ($type == 2) {
$data = $this->convertNonEbJsonForQCR($data, $type, $proposalData, $key);
// Kint::dump($data, 'Second');
if ($propsal_and_insurer !== null) {
list($proposal_key, $insurer_key) = explode('-', $propsal_and_insurer, 2);
$data = $this->transformNonEbProposelData($data, $proposal_key, $insurer_key, $key);
}
unset($data['proposalData']);
} else if ($type == 1) {
$data = $this->convertNonEbJsonForQCR($data, $type, $proposalData, $key);
unset($data['proposalData']);
}
}
// dd($data);
foreach ($data['table_data']['headers'] as $header) {
if ($header['parentHeader'] != "Action") {
foreach ($header['subHeaders'] as $subHeader) {
// Kint::dump($header['parentHeader']);
// Kint::dump($subHeader);
$length++;
}
}
}
// dd($length);
$columnLetterForTitle = Coordinate::stringFromColumnIndex($length - 1);
$columnLetterForImage = Coordinate::stringFromColumnIndex($length);
// dd($columnLetter);
$sheetName = 'Worksheet';
if ($type == 2) {
$sheetName = 'QCR';
if ($propsal_and_insurer !== null) {
$sheetName = 'Placement';
}
} else if ($type == 1) {
$sheetName = 'RFQ';
}
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->setTitle($sheetName);
// Start with lead_data at the top
$rowNumber = 1;
$title = "Nhance India Insurance Broking Pvt Ltd";
$mergeRange1 = "A{$rowNumber}:{$columnLetterForTitle}{$rowNumber}";
$sheet->mergeCells($mergeRange1);
$sheet->setCellValue("A{$rowNumber}", $title);
$sheet->getStyle("A{$rowNumber}")->applyFromArray([
'font' => [
'bold' => true,
'size' => 20,
],
'alignment' => [
'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
],
]);
// Calculate title width
$maxWidthA = mb_strlen($title);
// dd($columnLetterForImage);
// Set column width to fit the image properly
$sheet->getColumnDimension($columnLetterForImage)->setWidth(40); // Adjust as needed
$sheet->getRowDimension($rowNumber)->setRowHeight(40); // Adjust as needed
$drawing = new Drawing();
$path = ROOTPATH . "public/assets/images/Nhance-Logo-Final.png"; // Use FCPATH for server path
$drawing->setPath($path);
$drawing->setCoordinates("{$columnLetterForImage}{$rowNumber}"); // Set position in column C
$drawing->setHeight(35); // Adjust image height
// Apply center alignment to the cell
$sheet->getStyle("{$columnLetterForImage}{$rowNumber}")->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
$sheet->getStyle("{$columnLetterForImage}{$rowNumber}")->getAlignment()->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER);
$rowNumber++;
// Initialize max width tracking variables
$maxWidthB = 0;
//Lead Data
foreach ($lead_data as $key => $value) {
// if ($key == 'risk_location') {
// $key == 'risk_details';
// }
// Kint::dump($key);
// Kint::dump($value);
$formattedKey = ucwords(str_replace('_', ' ', $key));
$formattedKey = $formattedKey == "Risk Location" ? "Risk Details" : $formattedKey;
$formattedKey = $formattedKey == "Address" ? "Communication Address" : $formattedKey;
$formattedValue = ucwords(str_replace("_", " ", $value));
if ($key == "claim_history") {
$formattedValue = $value == "1" ? "Yes" : "No";
$formattedKey = "Claim Experience";
}
// Kint::dump($formattedValue);
// Merge A:B for key
$mergeRangeKey = "A{$rowNumber}:{$columnLetterForTitle}{$rowNumber}";
$sheet->mergeCells($mergeRangeKey);
// Set values in merged cells
$sheet->setCellValue("A{$rowNumber}", $formattedKey);
$sheet->setCellValue("{$columnLetterForImage}{$rowNumber}", $formattedValue);
// Apply styles for alignment and bold text in A:B
$sheet->getStyle($mergeRangeKey)->applyFromArray([
'font' => ['bold' => true],
'alignment' => [
'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
],
]);
// Apply bold style to C column if the key is "Insured"
// if ($key === "Insured") {
// $sheet->getStyle("C{$rowNumber}")->applyFromArray([
// 'font' => ['bold' => true],
// ]);
// }
// Track max width needed for columns
$maxWidthA = max($maxWidthA, mb_strlen($formattedKey)); // Consider title and keys
$maxWidthB = max($maxWidthB, mb_strlen((string) $value));
// if (!empty($policy_registration_data)) {
// dd($policy_registration_data);
// }
$rowNumber++;
}
// die();
// Set column width based on max content length (adjusted for padding)
$sheet->getColumnDimension('A')->setWidth($maxWidthA * 1.2);
// $sheet->getColumnDimension('B')->setWidth($maxWidthA * 1.2);
// $sheet->getColumnDimension('C')->setWidth($maxWidthB * 1.2);
// $rowNumber += 1;
//Policy Registration
if (! empty($policy_registration_data)) {
foreach ($policy_registration_data as $key => $value) {
// kint::dump($value);
$startRow = $rowNumber; // Track where the block starts
// Title Row
$titleMergeRangeKey = "A{$rowNumber}:{$columnLetterForImage}{$rowNumber}";
$sheet->mergeCells($titleMergeRangeKey);
$sheet->setCellValue("A{$rowNumber}", ucfirst($key) . " Policy Details");
// Title Style
$sheet->getStyle($titleMergeRangeKey)->applyFromArray([
'font' => ['bold' => true],
'alignment' => [
'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
],
'fill' => [
'fillType' => Fill::FILL_SOLID,
'startColor' => ['rgb' => 'ADD8E6'],
],
]);
$rowNumber++; // Move to next row
// Policy Type
if (isset($value['policyType'])) {
$sheet->mergeCells("A{$rowNumber}:{$columnLetterForTitle}{$rowNumber}");
$sheet->setCellValue("A{$rowNumber}", "Policy Type");
$sheet->setCellValue("{$columnLetterForImage}{$rowNumber}", $value['policyType']);
$sheet->getStyle("A{$rowNumber}:C{$rowNumber}")->applyFromArray([
'font' => ['bold' => true],
'alignment' => [
'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
],
]);
$rowNumber++;
} else {
$sheet->mergeCells("A{$rowNumber}:{$columnLetterForTitle}{$rowNumber}");
$sheet->setCellValue("A{$rowNumber}", "Policy Type");
$sheet->setCellValue("{$columnLetterForImage}{$rowNumber}", ucfirst($key) . " Policy");
$sheet->getStyle("A{$rowNumber}:C{$rowNumber}")->applyFromArray([
'font' => ['bold' => true],
'alignment' => [
'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
],
]);
$rowNumber++;
}
// Type of Business & Nature of Business
if (isset($value['productSelection']) && ! empty($value['productSelection'])) {
// Type of Business
$sheet->mergeCells("A{$rowNumber}:{$columnLetterForTitle}{$rowNumber}");
$sheet->setCellValue("A{$rowNumber}", "Type of Business");
$sheet->setCellValue("{$columnLetterForImage}{$rowNumber}", $this->buisnessType[$value['productSelection']['type_of_buisness']] ?? '');
$sheet->getStyle("A{$rowNumber}")->applyFromArray([
'font' => ['bold' => true],
'alignment' => [
'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
],
]);
$rowNumber++;
// Nature of Business
$sheet->mergeCells("A{$rowNumber}:{$columnLetterForTitle}{$rowNumber}");
$sheet->setCellValue("A{$rowNumber}", "Nature of Business");
$sheet->setCellValue("{$columnLetterForImage}{$rowNumber}", $value['productSelection']['buisness_nature'] ?? '');
$sheet->getStyle("A{$rowNumber}")->applyFromArray([
'font' => ['bold' => true],
'alignment' => [
'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
],
]);
$rowNumber++;
}
if (isset($value['policyDetails']) && ! empty($value['policyDetails'])) {
// Type of Business
$sheet->mergeCells("A{$rowNumber}:{$columnLetterForTitle}{$rowNumber}");
$sheet->setCellValue("A{$rowNumber}", "Terrorism");
$sheet->setCellValue("{$columnLetterForImage}{$rowNumber}", ucfirst($value['policyDetails']['terrorism'] ?? 'No'));
$sheet->getStyle("A{$rowNumber}")->applyFromArray([
'font' => ['bold' => true],
'alignment' => [
'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
],
]);
$rowNumber++;
}
if (! empty($value['locations'])) {
foreach ($value['locations'] as $index => $location) {
// Merge title cell
$sheet->mergeCells("A{$rowNumber}:{$columnLetterForTitle}{$rowNumber}");
$sheet->setCellValue("A{$rowNumber}", "Risk " . ($location['select_location'] ?? 'Unknown'));
// Clean and safe address string
$address1 = ucfirst($location['policyRisk']['address1'] ?? '');
$address2 = ucfirst($location['policyRisk']['address2'] ?? '');
$pinCode = $location['policyRisk']['pin_code'] ?? 'No';
$fullAddress = "{$address1}, {$address2}, {$pinCode}";
$sheet->setCellValue("{$columnLetterForImage}{$rowNumber}", $fullAddress);
// Style the title cell
$sheet->getStyle("A{$rowNumber}")->applyFromArray([
'font' => ['bold' => true],
'alignment' => [
'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
],
]);
$rowNumber++;
}
}
$endRow = $rowNumber; // Block ends at previous row
// Apply border to the whole block
$sheet->getStyle("A{$startRow}:{$columnLetterForImage}{$endRow}")->applyFromArray([
'borders' => [
'allBorders' => [
'borderStyle' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN,
'color' => ['argb' => '000000'],
],
],
]);
// $rowNumber += 1; // Add space before the next block
}
$rowNumber--;
// die();
}
//Table Data
foreach ($jsonData as $key => $data) {
// Kint::dump($data, 'First');
if ($type == 2) {
$data = $this->convertNonEbJsonForQCR($data, $type, $proposalData, $key);
// Kint::dump($data, 'Second');
if ($propsal_and_insurer !== null) {
list($proposal_key, $insurer_key) = explode('-', $propsal_and_insurer, 2);
$data = $this->transformNonEbProposelData($data, $proposal_key, $insurer_key, $key);
}
unset($data['proposalData']);
} else if ($type == 1) {
$data = $this->convertNonEbJsonForQCR($data, $type, $proposalData, $key);
unset($data['proposalData']);
}
// dd($data);
// Add headers and subheaders
$headers = $data['table_data']['headers'];
// dd($headers);
$rowNumber = $rowNumber + 1;
$subHeaderRow = $rowNumber + 1;
$columnLetter = 'A';
foreach ($headers as $header) {
if (in_array($header['parentHeader'], ['Action'])) {
continue;
}
$sheet->getColumnDimension($columnLetter)->setWidth(60);
$sheet->getColumnDimension('C')->setWidth(35);
if ($header['parentHeader'] == 'Policy') {
// $header['parentHeader'] = 'S.No.';
$sheet->getColumnDimension('A')->setWidth(10);
}
if ($header['parentHeader'] === 'Particulars') {
$sheet->getColumnDimension('B')->setWidth(60);
}
if (strpos($header['parentHeader'], 'Proposal') === 0) {
$sheet->getColumnDimension('C')->setWidth(60);
$sheet->getColumnDimension('D')->setWidth(35);
}
$startColumn = $columnLetter; // Start of the current header range
$subHeaderCount = count($header['subHeaders']); // Number of subheaders for this parent header
// Kint::dump($subHeaderCount);
// Set parent header value
$sheet->setCellValue("{$startColumn}{$rowNumber}", $header['parentHeader']);
$sheet->getStyle("{$startColumn}{$rowNumber}")->applyFromArray([
'font' => ['bold' => true],
'alignment' => [
'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
],
'fill' => [
'fillType' => Fill::FILL_SOLID,
'startColor' => ['rgb' => 'ADD8E6'],
],
]);
// Merge header cells if it spans multiple subheaders
if ($subHeaderCount > 1) {
$endColumn = Coordinate::stringFromColumnIndex(Coordinate::columnIndexFromString($startColumn) + $subHeaderCount - 1);
$sheet->mergeCells("{$startColumn}{$rowNumber}:{$endColumn}{$rowNumber}");
} else {
$endColumn = $startColumn; // No merge needed if only one subheader
}
// Kint::dump($header);
// Add subheaders
foreach ($header['subHeaders'] as $subHeader) {
$sheet->setCellValue("{$columnLetter}{$subHeaderRow}", $subHeader);
if ($columnLetter != "A" && $columnLetter != "B" && $columnLetter != "C" && $columnLetter != "D") {
$sheet->getColumnDimension($columnLetter)->setWidth(35);
} else {
$sheet->getColumnDimension('A')->setWidth(10);
}
$sheet->getStyle("{$columnLetter}{$subHeaderRow}")->applyFromArray([
'font' => ['bold' => true],
'alignment' => [
'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
],
]);
$columnLetter++; // Move to the next column for subheaders
}
}
// For the subheader row, merge B and C and set the value
$sheet->setCellValue("B{$subHeaderRow}", "Sum Insured");
$sheet->mergeCells("B{$subHeaderRow}:C{$subHeaderRow}");
$sheet->getStyle("B{$subHeaderRow}:C{$subHeaderRow}")->applyFromArray([
'font' => ['bold' => true],
'alignment' => [
'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
],
]);
// Apply border to the header range
$prevColumn4 = $this->getPreviousColumn($columnLetter);
$headerRange = "A{$rowNumber}:" . "{$prevColumn4}" . "{$subHeaderRow}";
$sheet->getStyle($headerRange)->applyFromArray([
'borders' => [
'allBorders' => [
'borderStyle' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN,
'color' => ['argb' => 'FF000000'], // Black color
],
],
]);
// Increase row height for headers and subheaders
$sheet->getRowDimension($rowNumber)->setRowHeight(25); // Header row height
$sheet->getRowDimension($subHeaderRow)->setRowHeight(20); // Subheader row height
// $rowNumber = $subHeaderRow + 2;
$rowNumber = $subHeaderRow + 1;
$column_data = $data['table_data']['data'];
// dd($column_data);
$serial_no = 1;
$maxColumnWidths = [];
$RowSpanEnable = false;
// Add table data rows
foreach ($column_data as $dataRow) {
$columnLetter = 'A';
$hasPolicy = in_array('Policy', array_column($dataRow['data'], 'parentth'));
// Kint::dump($hasPolicy);
foreach ($dataRow['data'] as $cellData) {
if (in_array($cellData['parentth'], ['Action'])) {
continue;
}
if ($cellData['parentth'] == 'SNO') {
$sheet->setCellValue("{$columnLetter}{$rowNumber}", $serial_no);
} else if ($cellData['parentth'] == 'Policy') {
$mergeStart = $rowNumber; // Start row for merging
$mergeEnd = $rowNumber + ($cellData['rowspan'] - 1); // End row for merging
$sheet->setCellValue("{$columnLetter}{$mergeStart}", $dataRow['SNO']);
$sheet->mergeCells("{$columnLetter}{$mergeStart}:{$columnLetter}{$mergeEnd}");
$sheet->getStyle("{$columnLetter}{$mergeStart}:{$columnLetter}{$mergeEnd}")
->getAlignment()->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER);
} else {
if (! $hasPolicy && $key == 0) {
$RowSpanEnable = true;
$columnLetter++;
$sheet->setCellValue("{$columnLetter}{$rowNumber}", $cellData['display_content']);
} else {
$sheet->setCellValue("{$columnLetter}{$rowNumber}", $cellData['display_content']);
}
}
if (! ($key === 0 && ! $hasPolicy)) {
$columnLetter++;
}
}
$rowNumber++;
$serial_no++;
}
if ($key == 0 && $RowSpanEnable == true) {
$columnLetter++;
}
// dd($columnLetter);
$prevColumn5 = $this->getPreviousColumn($columnLetter);
$dataRange = "A" . ($subHeaderRow + 1) . ":" . "{$prevColumn5}" . ($rowNumber - 1);
$sheet->getStyle($dataRange)->applyFromArray([
'borders' => [
'allBorders' => [
'borderStyle' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN,
'color' => ['argb' => 'FF000000'], // Black color
],
],
]);
// Auto-size columns
// foreach ($sheet->getColumnIterator() as $column) {
// $sheet->getColumnDimension($column->getColumnIndex())->setAutoSize(true);
// }
$dataRange = $sheet->calculateWorksheetDimension();
$sheet->getStyle($dataRange)->getAlignment()
->setHorizontal(Alignment::HORIZONTAL_CENTER)
->setVertical(Alignment::VERTICAL_CENTER)
->setWrapText(true);
$sheet->getStyle('A1')->applyFromArray([
'alignment' => [
'wrapText' => false, // Disables text wrapping for A1
],
]);
// Apply border to the entire sheet
preg_match('/([A-Z]+)(\d+):([A-Z]+)(\d+)/', $dataRange, $matches);
if ($matches) {
$startColumn = $matches[1]; // A
$startRow = $matches[2]; // 1
$endColumn = $matches[3]; // G
$endRow = $matches[4]; // 75
// Convert column letter to index, reduce by 1, and convert back
$endColumnIndex = Coordinate::columnIndexFromString($endColumn) - 1;
$newEndColumn = Coordinate::stringFromColumnIndex($endColumnIndex);
// Generate the new range (e.g., "A1:F75" instead of "A1:G75")
$newDataRange = "{$startColumn}{$startRow}:{$newEndColumn}{$endRow}";
// $sheet->getStyle($newDataRange)->getBorders()->getAllBorders()->setBorderStyle(Border::BORDER_THIN);
}
$lastRow = count($lead_data) + 1;
$leadRange = "A1:{$columnLetterForImage}{$lastRow}";
$sheet->getStyle($leadRange)->applyFromArray([
'borders' => [
'allBorders' => [
'borderStyle' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN,
'color' => ['argb' => 'FF000000'], // Black color
],
],
]);
}
$columnWidth = $sheet->getColumnDimension("C")->getWidth(); // e.g., 20
$cellPixelWidth = $columnWidth * 7; // Approximate conversion (1 unit ≈ 7 pixels)
$imagePixelWidth = 250; // Approximate width of your image (in pixels)
$offsetX = max(0, ($cellPixelWidth - $imagePixelWidth) / 2);
$offsetX = $offsetX + 97;
if ($length >= 4) {
$offsetX = 35;
} else if ($length == 3) {
$offsetX = 140;
}
// dd($offsetX, $cellPixelWidth, $imagePixelWidth);
$drawing->setOffsetX($offsetX); // Dynamically center
$drawing->setOffsetY(10); // Dynamically center
$drawing->setWorksheet($sheet);
$sheet->getStyle('C1')->getAlignment()
->setHorizontal(Alignment::HORIZONTAL_CENTER)
->setVertical(Alignment::VERTICAL_CENTER);
if (! empty($rfq_data['fin_years_claims']) && $claim_history == 1) {
$claim_details = $this->sanitizeClaimDetailsForExport(
json_decode($rfq_data['fin_years_claims'], true) ?? []
);
if (! empty($claim_details['finyear'])) {
// Get headers dynamically
$headers = array_map(function ($key) {
return ucwords(str_replace('_', ' ', $key));
}, array_keys($claim_details['finyear'][0]));
$newSheet = new Worksheet($spreadsheet, 'Claims Experience');
$spreadsheet->addSheet($newSheet);
$spreadsheet->setActiveSheetIndexByName('Claims Experience');
$sheet = $spreadsheet->getActiveSheet();
// Set headers
$sheet->fromArray($headers, null, 'A1');
// Apply background color and bold style to headers
$headerCellRange = 'A1:' . chr(64 + count($headers)) . '1'; // e.g., A1:G1
$sheet->getStyle($headerCellRange)->getFont()->setBold(true);
$sheet->getStyle($headerCellRange)->getFill()->setFillType(Fill::FILL_SOLID)->getStartColor()->setRGB('ADD8E6');
// Add border to header
$sheet->getStyle($headerCellRange)->getBorders()->getAllBorders()->setBorderStyle(Border::BORDER_THIN);
// Fill data and apply borders
$row = 2;
foreach ($claim_details['finyear'] as $record) {
$col = 'A';
foreach ($record as $value) {
$sheet->setCellValue($col . $row, $value);
$col++;
}
// Apply border to each data row
$sheet->getStyle('A' . $row . ':' . chr(64 + count($headers)) . $row)
->getBorders()->getAllBorders()->setBorderStyle(Border::BORDER_THIN);
$row++;
}
// Auto-size all columns based on header count
for ($i = 0; $i < count($headers); $i++) {
$colLetter = chr(65 + $i); // 'A', 'B', etc.
$sheet->getColumnDimension($colLetter)->setAutoSize(true);
}
// Enable wrap text for all cells
$maxColLetter = chr(64 + count($headers)); // Last column letter
$sheet->getStyle("A2:{$maxColLetter}{$row}")->getAlignment()->setWrapText(true);
// Optional: center vertically for neatness
$sheet->getStyle("A2:{$maxColLetter}{$row}")->getAlignment()->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER);
// Optional: Make row height auto (helps when wrap text is on)
for ($i = 2; $i < $row; $i++) {
$sheet->getRowDimension($i)->setRowHeight(-1);
}
}
}
// dd($rowNumber);
// Set filename
$string = ($type == 2) ? 'QCR' : 'RFQ';
$filename = "{$string}_{$rfq_data['client_short_name']}_{$rfq_data['policy_type']}_" . date('YmdHis') . '.xlsx';
// Save to temporary location
$uploadFilePath = WRITEPATH . 'tmp/' . $filename;
$writer = new Xlsx($spreadsheet);
$writer->save($uploadFilePath);
return [
'filePath' => $uploadFilePath,
'fileName' => $filename,
];
}
public function convertNonEbJsonForQCR($jsonArr, $type, $proposeldata, $indexOfTheTable)
{
$first_json = $jsonArr;
$first_json = array_merge($first_json, $proposeldata);
if ($type == 2) {
// Column-wise Check: Remove headers and relevant data if qcr == 0
foreach ($first_json['proposal_data']['over_all_column_data'] as $proposalKey => $proposalData) {
if ((isset($proposalData['qcr']) && isset($proposalData['stc'])) && ($proposalData['qcr'] == 0 || $proposalData['qcr'] === false) || ($proposalData['stc'] == 0 || $proposalData['stc'] === false)) {
// Remove matching parentHeader in headers
foreach ($first_json['table_data']['headers'] as $index => $header) {
if ($header['parentHeader'] === $proposalKey) {
unset($first_json['table_data']['headers'][$index]);
}
}
// Remove data entries with matching parentth
foreach ($first_json['table_data']['data'] as &$item) {
$item['data'] = array_values(array_filter($item['data'], function ($entry) use ($proposalKey) {
return $entry['parentth'] !== $proposalKey;
}));
}
// Remove proposalKey from over_all_column_data
unset($first_json['proposal_data']['over_all_column_data'][$proposalKey]);
if ($type == 2) {
// Remove proposalKey from premium_data
unset($first_json['premium_data']['data'][$proposalKey]);
}
}
// Insurer Check: Remove subHeaders and relevant data for insurers with qcr == 0
foreach ($proposalData['insurers'] as $insurerIndex => $insurer) {
$qcr = $insurer['qcr'] ?? null;
$stc = $insurer['stc'] ?? null;
if (($qcr === 0 || $qcr === false) || ($stc === 0 || $stc === false)) {
foreach ($first_json['table_data']['headers'] as &$header) {
if (isset($header['subHeaders'])) {
$header['subHeaders'] = array_values(array_filter($header['subHeaders'], function ($sub) use ($insurer) {
return $sub !== $insurer['display_name'];
}));
}
}
foreach ($first_json['table_data']['data'] as &$item) {
$item['data'] = array_values(array_filter($item['data'], function ($entry) use ($insurer) {
return $entry['subth'] !== $insurer['display_name'];
}));
}
// Remove insurer from proposal's insurers array
unset($first_json['proposal_data']['over_all_column_data'][$proposalKey]['insurers'][$insurerIndex]);
if ($type == 2) {
unset($first_json['premium_data']['data'][$proposalKey][$insurer['display_name']]);
}
}
}
}
// Row-wise Check: Remove rows if qcr == 0 for actions
$groupedRows = [];
foreach ($first_json['table_data']['data'] as $rowKey => $rowData) {
$groupedRows[$rowData['row_id']][] = $rowData;
foreach ($rowData['data'] as $keyIndex => $data) {
if ($indexOfTheTable == 0) {
if ($data['parentth'] == "Policy" && $data['input_value'] != "Checked") {
unset($first_json['table_data']['data'][$rowKey]);
break;
}
} else {
if (
isset($data['parentth'], $data['input_value']['qcr'], $data['input_value']['stc']) &&
$data['parentth'] === "Action" &&
($data['input_value']['qcr'] == 0 || $data['input_value']['stc'] == 0)
) {
unset($first_json['table_data']['data'][$rowKey]);
break;
}
}
}
}
$finalRows = [];
if (! empty($groupedRows)) {
// Loop through each row_id group and check for Policy status
foreach ($groupedRows as $rowId => $rows) {
$hasPolicy = false;
$isChecked = false;
foreach ($rows as $row) {
foreach ($row['data'] as $cell) {
if ($cell['parentth'] === 'Policy') {
$hasPolicy = true;
if (trim(strtolower($cell['input_value'])) === 'checked') {
$isChecked = true;
}
break 2; // Found Policy, no need to continue further
}
}
}
// Step 3: If parent Policy is "Checked", retain the entire group
if (! $hasPolicy || $isChecked) {
foreach ($rows as $r) {
$finalRows[] = $r;
}
}
}
if (! empty($finalRows)) {
// Update the original data
$first_json['table_data']['data'] = array_values($finalRows);
}
}
// Reindex arrays to maintain proper structure
$first_json['table_data']['headers'] = array_values($first_json['table_data']['headers']);
$first_json['table_data']['data'] = array_values($first_json['table_data']['data']);
$first_json['proposal_data']['over_all_column_data'] = array_map(function ($proposal) {
$proposal['insurers'] = array_values($proposal['insurers']);
return $proposal;
}, $first_json['proposal_data']['over_all_column_data']);
} else {
//remove insurer as Subheaders for RFQ
foreach ($first_json['table_data']['headers'] as &$header) {
// Ensure 'subHeaders' exists and is an array before filtering
if (isset($header['subHeaders']) && is_array($header['subHeaders'])) {
$header['subHeaders'] = array_filter($header['subHeaders'], function ($subHeader) {
return in_array($subHeader, ['Sum Insured', '-'], true);
});
}
}
// Kint::dump($header);
//Remove insurer Row wise data for RFQ
$groupedRows = [];
foreach ($first_json['table_data']['data'] as &$row) {
// Filter the inner data array
$row['data'] = array_filter(
$row['data'],
function ($item) {
return in_array($item['subth'], ['Sum Insured', '-', "Liability Limit"]);
}
);
$row['data'] = array_values($row['data']);
$groupedRows[$row['row_id']][] = $row;
}
// Ensure to unset the reference after the loop
unset($row);
$finalRows = [];
if (! empty($groupedRows)) {
// Loop through each row_id group and check for Policy status
foreach ($groupedRows as $rowId => $rows) {
$hasPolicy = false;
$isChecked = false;
foreach ($rows as $row) {
foreach ($row['data'] as $cell) {
if ($cell['parentth'] === 'Policy') {
$hasPolicy = true;
if (trim(strtolower($cell['input_value'])) === 'checked') {
$isChecked = true;
}
break 2; // Found Policy, no need to continue further
}
}
}
// Step 3: If parent Policy is "Checked", retain the entire group
if (! $hasPolicy || $isChecked) {
foreach ($rows as $r) {
$finalRows[] = $r;
}
}
}
if (! empty($finalRows)) {
// Update the original data
$first_json['table_data']['data'] = array_values($finalRows);
}
}
// Remove insurers from Proposal Data key for RFQ
foreach ($proposeldata['proposal_data']['over_all_column_data'] as $key => &$proposal) {
if (isset($proposal['insurers'])) {
// Set the insurers array to empty
$proposal['insurers'] = [];
}
}
// Ensure to reset the reference
unset($proposal);
}
return $first_json;
return null;
}
public function transformNonEbProposelData(&$data, $proposal, $insurer, $tableCount = null, $actionColumn = null)
{
// Kint::dump($data, $proposal, $insurer, $tableCount);
// Initialize headers with defaults
$headerData = [];
// Add the first two default headers only once
$headerData[] = [
'parentHeader' => $data['table_data']['headers'][0]['parentHeader'],
'subHeaders' => ['-'],
];
$headerData[] = [
'parentHeader' => $data['table_data']['headers'][1]['parentHeader'],
'subHeaders' => ['-'],
];
$second_table_name = $data['table_data']['headers'][1]['parentHeader'] ?? "";
// Loop through the headers to find matching proposal and insurer
foreach ($data['table_data']['headers'] as $header) {
if ($header['parentHeader'] === $proposal) {
foreach ($header['subHeaders'] as $subHeader) {
if ($subHeader === $insurer) {
$headerData[] = [
'parentHeader' => $header['parentHeader'],
'subHeaders' => ['Sum Insured', $subHeader],
];
break 2; // Exit both loops after match is found
}
}
}
}
// Update the original data's headers
$data['table_data']['headers'] = $headerData;
if (! empty($actionColumn)) {
$data['table_data']['headers'][] = [
'parentHeader' => "Action",
'subHeaders' => ['-'],
];
}
foreach ($data['table_data']['data'] as &$entry) {
$sno = $entry['SNO'];
$items = $entry['items'];
$row_id = $entry['row_id'];
$filteredData = [];
foreach ($entry['data'] as $item) {
// Always include SNO and Particulars
if (in_array($item['parentth'], ['S.NO', "Policy", "", 'Particulars', $second_table_name])) {
$filteredData[] = $item;
}
// Include Proposal with Sum Insured
if ($item['parentth'] === $proposal && ($item['subth'] === "Sum Insured" || $item['subth'] === "Liability Limit")) {
$filteredData[] = $item;
}
// Include specific proposal and insurer
if ($item['parentth'] === $proposal && $item['subth'] === $insurer) {
$filteredData[] = $item;
}
if (! empty($actionColumn)) {
if ($item['parentth'] === "Action") {
$filteredData[] = $item;
}
}
}
// Update the entry with filtered data
$entry['SNO'] = $sno;
$entry['items'] = $items;
$entry['row_id'] = $row_id;
$entry['data'] = $filteredData;
}
unset($entry);
// kint::dump($data);
return $data;
}
public function getPreviousColumn($columnLetter, $decrement = 1)
{
$colIndex = Coordinate::columnIndexFromString($columnLetter);
$colIndex = max(1, $colIndex - $decrement); // ensure column index doesn't go below 1
return Coordinate::stringFromColumnIndex($colIndex);
}
public function handleMultiFileAttachments($json_string, $lead_id)
{
log_message('error', 'handleMultiFileAttachments called with lead_id: ' . $lead_id);
$attachments = [];
if (! empty($json_string)) {
log_message('error', 'json_string is not empty');
$fileIds = json_decode($json_string, true);
log_message('error', 'Decoded fileIds: ' . print_r($fileIds, true));
$lead_file_path = WRITEPATH . 'uploads/lead_files';
log_message('error', 'Lead file path: ' . $lead_file_path);
foreach ($fileIds as $id) {
log_message('error', 'Processing file ID: ' . $id);
$lead_file = $this->leadFilesModel
->where('lead_id', $lead_id)
->where('id', $id)
->where('type !=', 2)
->where('is_active', 1)
->first();
if ($lead_file) {
log_message('error', 'Found lead file: ' . print_r($lead_file, true));
if (empty($lead_file['file_name'])) {
log_message('error', 'File name is empty for ID: ' . $id);
continue;
}
$fullPath = storage_ensure_local_file($lead_file_path, $lead_file['file_name']);
log_message('error', 'Full file path: ' . ($fullPath ?? ''));
if (! empty($fullPath) && file_exists($fullPath)) {
log_message('error', 'File exists at path: ' . $fullPath);
log_message('error', 'File name is not empty: ' . $lead_file['file_name']);
$attachments[] = [
'fileName' => $lead_file['file_name'],
'filePath' => $fullPath,
];
} else {
log_message('error', 'File does not exist at path for: ' . $lead_file['file_name']);
}
} else {
log_message('warning', 'No active lead file found for ID: ' . $id . ' and lead_id: ' . $lead_id);
}
}
} else {
log_message('error', 'json_string is empty');
}
log_message('error', 'Attachments prepared: ' . print_r($attachments, true));
return $attachments;
}
public function handleMemberDataGPATotalSumInsurerFromExcel($params)
{
try {
$lead_id = $params['lead_id'];
$lead_data = $this->leadsModel->find((int) $lead_id);
if (! $lead_data) {
return ['status' => 'failed', 'message' => 'Opportunity data not found'];
}
$file_name_with_path = ! empty($lead_data['file_name'])
? storage_ensure_local_file(WRITEPATH . 'uploads/lead_files', $lead_data['file_name'])
: null;
if ($lead_data['file_name']) {
//check physical file
if (empty($file_name_with_path) || ! file_exists($file_name_with_path)) {
$message = "Lead Physcial file not found";
$this->myLogger->logme('error', ($message . ' for file ' . ($lead_data['file_name'] ?? '')));
return ['status' => 'failed', 'message' => 'no physical file'];
}
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($file_name_with_path);
//get members data
$members_sheet = $spreadsheet->getSheet(0);
$highestRowAndColumn = $members_sheet->getHighestRowAndColumn();
// Read raw values and skip formula evaluation to avoid Calculation exceptions from malformed formulas.
$uncleaned_members = $members_sheet->rangeToArray(
'A1:' . $highestRowAndColumn['column'] . $highestRowAndColumn['row'],
null,
false,
true,
false
);
$members = ExcelSanitizeHelper::sanitizeArrayData($uncleaned_members);
// Check column headings
$members_heading = $members[0];
$available_col = [];
$lower_headers = array_map('strtolower', $members_heading);
foreach ($lower_headers as $index => $header) {
if (preg_match('/^(sa\s*-\s*option|proposed\s+sum\s+insured)\s+\d+$/i', $header)) {
$sum = 0;
for ($i = 1; $i < count($members); $i++) {
$cell_raw = $members[$i][$index] ?? '';
$cell_clean = preg_replace('/[^0-9.\-]/', '', $cell_raw); // remove non-numeric chars
if ($cell_clean !== '' && is_numeric($cell_clean)) {
$sum += (float) $cell_clean;
}
}
$available_col[] = $sum;
}
}
return $available_col;
}
return [];
} catch (\Throwable $e) {
log_message('error', 'Exception in handleMemberDataGPATotalSumInsurerFromExcel: {message}', [
'message' => $e->getMessage(),
]);
log_message('error', 'Trace: {trace}', [
'trace' => $e->getTraceAsString(),
]);
return [];
}
}
public function generateDemographyDataTable($param = [])
{
if ($this->request !== null) {
$param['lead_id'] = $this->request->getGet('lead_id');
}
$returnData = $this->calculateMembersDemography($param, "internal");
$html = "";
if (isset($returnData['data'])) {
$data = $returnData['data'];
foreach ($data as $category => $records) {
// Extract all column headers (age groups) dynamically
$allColumns = [];
foreach ($records as $person => $ages) {
$allColumns = array_merge($allColumns, array_keys($ages));
}
$allColumns = array_unique($allColumns);
$allColumns = array_values($allColumns); // reset index
$category = ucfirst($category);
// Start table with clean styling
$html .= "{$category}
";
$html .= "";
// Table header with light background
$html .= "";
$html .= "| Relation | ";
foreach ($allColumns as $col) {
$html .= "{$col} | ";
}
$html .= "
";
// Table rows with clean styling
foreach ($records as $relation => $ages) {
$html .= "";
$html .= "| {$relation} | ";
foreach ($allColumns as $col) {
$value = isset($ages[$col]) ? $ages[$col] : "-";
$html .= "{$value} | ";
}
$html .= "
";
}
$html .= "
";
}
}
if ($this->request !== null) {
if (! empty($html)) {
return $this->respond(['status' => true, 'data' => $html], 200);
} else {
return $this->respond(['status' => false, 'data' => $html], 200);
}
}
return $html;
}
// function for insurer remainder mail cron job
public function remainderForQcr()
{
$lead_data = $this->leadsModel
->select(
'leads.*,
user_profiles.email as created_person_email,
policy_type.policy_type,
policy_type.long_name
')
->join('policy_type', 'leads.policy_type_id = policy_type.id')
->join('user_profiles', 'leads.created_by = user_profiles.id', 'left')
->where('leads.is_active', 1)
->where('user_profiles.is_active', 1)
->whereIn('leads.status', ['rfq_sent', 'qcr_created'])
->where('leads.next_reminder_date', date('Y-m-d'))
->findAll();
// dd($lead_data);
if (count($lead_data) == 0) {
$this->myLogger->logme("info", "No leads found for reminder mail at " . date('Y-m-d H:i:s'));
print_r(["status" => true, "message" => "There is no data to send reminder mail"]);
return ["status" => true, "message" => "There is no data to send reminder mail"];
}
$from_mail = getenv(LEAD_INSURER_FROM_MAIL_ID);
$subject = "Reminder for RFQ";
$content_string = '
Dear Sir,
Greetings from Nhance India!
This is a gentle reminder regarding our earlier mail about the RFQ for the {{POLICY_TYPE}} policy pertaining to {{CLIENT_NAME}}.
We kindly request you to share the competitive quotes at the earliest possible convenience.
In case of any clarification, please feel free to contact us.
Thank you for your prompt attention to this matter.
Best regards,
Nhance India
';
$reminder_sended_lead = [];
foreach ($lead_data as $lead) {
$leadId = $lead['id'];
$reply_to = $lead['created_person_email'] ?? "";
if (isset($lead['policy_end_date']) && $lead['policy_end_date'] != null) {
$subject = "{{CLIENT_NAME}} _ {{POLICY_TYPE}} _ {{RFQ_OR_QCR}} _ {{POLICY_YEAR}} {{POLICY_END_DATE}}";
} else {
$subject = "{{CLIENT_NAME}} _ {{POLICY_TYPE}} _ {{RFQ_OR_QCR}} _ {{POLICY_YEAR}}";
}
$mail_content = $this->transformMailContent($lead, $content_string, "RFQ");
$mail_subject = $this->transformMailContent($lead, $subject, "RFQ");
$this->myLogger->logme("info", "Processing lead_id={$leadId} for reminder mails");
// get sales person + created_by emails
$sales_person_emails = $this->userModel
->select('email')
->where('is_active', 1)
->whereIn('id', json_decode($lead['salse_person_id'] ?? "[]", true))
->findAll();
$email_list = array_column($sales_person_emails, 'email');
if (! empty($reply_to)) {
$email_list[] = $reply_to;
}
$email_list = array_filter(array_unique($email_list));
$email_string = implode(',', $email_list);
// Get latest sent mail timestamp
$createdAtRow = $this->gmailSentHistoryModel
->select("DATE_FORMAT(created_at, '%Y-%m-%d %H:%i') as created_at", false)
->where('isactive', 1)
->where('mail_type', 'insurer')
->where('pk', $leadId)
->orderBy('id', 'desc')
->first();
// dd(db_connect()->getLastQuery(), $createdAtRow);
if (! $createdAtRow) {
$this->myLogger->logme("warning", "No sent history found for lead_id={$leadId}, skipping...");
continue;
}
$createdAt = $createdAtRow['created_at'];
// Fetch mail data for that timestamp
$mail_datas = $this->gmailSentHistoryModel
->where('isactive', 1)
->where('mail_type', "insurer")
->where('pk', $leadId)
->where("DATE_FORMAT(created_at, '%Y-%m-%d %H:%i')", $createdAt)
->findAll();
if (empty($mail_datas)) {
$this->myLogger->logme("warning", "No mail data found for lead_id={$leadId} at created_at={$createdAt}");
continue;
}
// Filter only pending insurers
$quote_received_insurer = json_decode($lead['quote_received_insurer'] ?? "[]", true);
$reminder_mail_data = [];
if (empty($quote_received_insurer)) {
$reminder_mail_data = $mail_datas;
} else {
foreach ($mail_datas as $mail_value) {
$insurer_id = $mail_value['common'] ?? "";
if (! in_array($insurer_id, $quote_received_insurer)) {
$reminder_mail_data[] = $mail_value;
}
}
}
// dd($reminder_mail_data);
if (empty($reminder_mail_data)) {
$this->myLogger->logme("info", "All insurers already responded for lead_id={$leadId}, no reminder needed");
continue;
}
$this->myLogger->logme("info", "Sending " . count($reminder_mail_data) . " reminder mail(s) for lead_id={$leadId}");
$common = ['module' => "leads", 'pk' => $leadId, 'mail_type' => "remainder", 'common' => null];
foreach ($reminder_mail_data as $content) {
$insurerId = $content['common'] ?? null;
$common['common'] = $insurerId;
try {
if ($lead['is_insurer_auto_mail'] == 1) {
// send reminder to insurer
$res = MailHelper::send_email([
'from_mail' => $from_mail,
'mail' => $content['mail'] ?? "",
'cc' => json_decode($content['cc'] ?? "", true) ?? "",
'subject' => $mail_subject ?? "",
'message' => $mail_content ?? "",
'reply_to' => $reply_to,
'bcc' => json_decode($content["bcc"] ?? "", true) ?? "",
'common' => $common,
]);
$this->myLogger->logme("info", "Reminder mail sent to the insurer | lead_id={$leadId}, insurer_id={$insurerId}, response=" . json_encode($res));
$reminder_sended_lead[] = [
'lead_id' => $leadId,
'insurer_id' => $insurerId,
'response' => $res,
];
} else {
$this->myLogger->logme("info", "Reminder mail disable for this lead_id={$leadId}");
}
} catch (\Throwable $e) {
$errorDetails = [
'error_message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack_trace' => $e->getTraceAsString(),
];
$this->myLogger->logme("error", "Exception while sending reminder | lead_id={$leadId}, insurer_id={$insurerId}, error=" . json_encode($errorDetails));
$reminder_sended_lead[] = [
'lead_id' => $leadId,
'insurer_id' => $insurerId,
'response' => ['status' => false, 'error' => $errorDetails],
];
}
// send reminder to sales person / creator only
$internal_mail_response = MailHelper::send_email([
'mail' => $from_mail,
'cc' => $email_string,
'subject' => $mail_subject ?? "",
'message' => $mail_content ?? "",
'common' => $common,
]);
$this->myLogger->logme("info", "Reminder mail sent to sales persons | lead_id={$leadId}, recipients={$email_string}, response=" . json_encode($internal_mail_response));
}
}
return ['status' => true, "data" => $reminder_sended_lead];
}
public function uploadMultiFileFromRfq()
{
try {
$this->myLogger->logme('error', "uploadMultiFileFromRfq START");
$this->myLogger->logme('error', "Files received: " . json_encode($this->request->getPost() ?? []));
$lead_id = $this->request->getPost('lead_id');
$lead_form_type = $this->request->getPost('lead_form_type');
$leads_file_primary_key = $this->request->getPost('leads_file_id') ?? [];
$docs_name = $this->request->getPost('docs_name') ?? [];
$files = $this->request->getFileMultiple('file_name');
if (empty($files) || empty($docs_name)) {
$this->myLogger->logme('error', "No files or document names provided");
return $this->respond([
'status' => false,
'code' => 400,
'message' => 'No files or document names provided',
], 400);
}
// Upload process
$multi_file_data = $this->uploadMultiFiles($files, $docs_name, $leads_file_primary_key) ?? [];
$this->myLogger->logme('error', "uploadMultiFiles result: " . json_encode($multi_file_data ?? []));
if (! empty($multi_file_data)) {
$this->insertMultiFilesData($multi_file_data, $lead_id, $lead_form_type);
$this->myLogger->logme('error', "Files inserted successfully into DB");
// update lead file name if change
if (isset($multi_file_data[0]['file_name']) && ! empty($multi_file_data[0]['file_name'])) {
$this->leadsModel->where('id', $lead_id)->set('file_name', $multi_file_data[0]['file_name'])->update();
}
} else {
$this->myLogger->logme('error', "uploadMultiFiles returned empty");
}
// Get the updated lead file data
$lead_file_data = $this->leadFilesModel
->where('is_active', 1)
->where('type !=', 2)
->where('lead_id', $lead_id)
->findAll();
$this->myLogger->logme('error', "Fetched " . count($lead_file_data) . " lead file records");
$document_data = $this->renderFileFields($lead_file_data);
$attch_data['multi_file_data'] = $lead_file_data;
$attachment_html = view('rfq/attachment_files', $attch_data) ?? "";
$this->myLogger->logme('error', "uploadMultiFileFromRfq END");
return $this->respond([
'status' => true,
'code' => 200,
'message' => "File uploaded Successfully",
'document_data' => $document_data,
'attachment_html' => $attachment_html,
], 200);
} catch (\Throwable $e) {
$errorDetails = [
'error_message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack_trace' => $e->getTraceAsString(),
];
// Catch any error/exception and log with trace
$this->myLogger->logme('error', "Exception in uploadMultiFileFromRfq: " . json_encode($errorDetails));
return $this->respond([
'status' => false,
'code' => 500,
'message' => "File upload failed: " . $e->getMessage(),
'errorDetails' => $errorDetails,
], 500);
}
}
public function renderFileFields($multi_file_data = [])
{
$uploadFilePath = WRITEPATH . 'uploads/lead_files/';
$html = '';
// If data exists (edit mode)
if (! empty($multi_file_data)) {
foreach ($multi_file_data as $index => $value) {
$sample_dwn_link = ' [ Download ]';
$isFirstField = ($index === 0);
$placeholder = $isFirstField ? 'First file must be Demography.' : '';
$first_file_name = $isFirstField ? 'Member List' : '';
$member_data_link = $isFirstField ? $sample_dwn_link : '';
$read_only = $isFirstField ? 'readonly' : '';
$accept = $isFirstField ? '.xls,.xlsx' : '.xls,.xlsx,.pdf,.jpg,.jpeg,.png';
$displayIndex = $index + 1;
$html .= '
';
}
} else {
// Default (add mode)
$html .= '
';
}
return $html;
}
public function downloadMemberFile($fileName)
{
$fileName = basename((string) $fileName);
$uploadFilePath = WRITEPATH . 'uploads/lead_files';
$storage = \Config\Services::getFileStorageService();
$result = $storage->download($uploadFilePath, $fileName);
if (! ($result['success'] ?? false)) {
$data['message'] = 'The Physical File Not Found';
return view('errors/404', $data);
}
$downloadAs = storage_upload_display_name($fileName);
if (! empty($result['path']) && is_file($result['path'])) {
return $this->response->download($result['path'], null)->setFileName($downloadAs);
}
if (! empty($result['content'])) {
return $this->response->download($downloadAs, $result['content'])->setFileName($downloadAs);
}
$data['message'] = 'The Physical File Not Found';
return view('errors/404', $data);
}
// ----------- MEMBER DATA VALIDAATION ------------------------------------------------------------------------------------------------------
public function memberDataListValidation()
{
$lead_id = $this->request->getPost('lead_id');
$this->memberDataListExcelFileFormatValidation(['lead_id' => $lead_id]);
}
public function memberDataListExcelFileFormatValidation($params)
{
helper('excel_util_helper');
$this->myLogger->logme('error', 'Start memberDataListExcelFileFormatValidation');
$this->myLogger->logme('error', "Received params: " . json_encode($params));
$lead_id = $params['lead_id'];
$age_validation_check = $params['age_validation'] ?? null;
$lead_data = $this->leadsModel->where('id', $lead_id)->first();
// dd($lead_data);
$return = [];
if (! isset($lead_data)) {
$this->myLogger->logme('error', "Lead not found for lead_id: {$lead_id}");
$this->leadFilesModel->where('lead_id', $lead_id)->where('type', 2)->set(['status' => 'failed', 'error_data' => json_encode(['error_summary' => array_count_values([12]), 'error_data' => 'file not found in DB'])])->update();
return ['status' => false, 'msg' => 'file not found in DB'];
}
//get the lead files data
$lead_file_data = $this->leadFilesModel->where('is_active', 1)->where('type', 2)->first();
//check the lead file table has the error data entry if not than create new entry
if (empty($lead_file_data)) {
$data['type'] = 2;
$data['file_name'] = $lead_data['file_name'];
$data['docs_name'] = "Member List";
$data['lead_id'] = $lead_id;
$lead_file_last_insert_id = $this->leadFilesModel->insert($data);
$this->myLogger->logme('error', "New lead file entry created for store the error data, insert_id : '{$lead_file_last_insert_id}'");
}
$family_composition = [];
if ($age_validation_check && ! empty($lead_data['proposel_data'])) {
$family_composition = $this->getAgeRatioFromRfqJson($lead_id, $lead_data['proposel_data']);
$this->myLogger->logme('info', 'Family composition :', json_encode($family_composition));
}
// dd($family_composition);
// get the file path
$file_name_with_path = ! empty($lead_data['file_name'])
? storage_ensure_local_file(WRITEPATH . 'uploads/lead_files', $lead_data['file_name'])
: null;
$this->myLogger->logme('error', "File path: " . ($file_name_with_path ?? ''));
//check physical file
if (empty($file_name_with_path) || ! file_exists($file_name_with_path)) {
//file not found update status and reason
$message = "Physcial file not found";
$this->myLogger->logme('error', ($message . ' for lead id ' . $lead_id));
$this->leadFilesModel
->where('lead_id', $lead_id)
->where('type', 2)
->set(['status' => 'failed', 'error_data' => json_encode(['error_summary' => array_count_values([5]), 'error_data' => $message])])
->update();
return ['error_summary' => [5], 'error_data' => $message];
}
$this->myLogger->logme('info', 'File exists, starting validation process');
//start validation process
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($file_name_with_path);
$sheet = $spreadsheet->getActiveSheet();
$highestRowAndColumn = $sheet->getHighestRowAndColumn();
// dd($highestRowAndColumn);
$columns_to_check = $this->member_data_excel_columns;
$result = ['error_type' => 1, 'error_summary' => [], 'error_data' => []];
$keys = array_keys($columns_to_check);
$allowedHighestColumn = end($columns_to_check);
$excel_data = $sheet->rangeToArray('A1:' . $allowedHighestColumn['col_cell_name'] . $highestRowAndColumn['row']);
$excel_data = ExcelSanitizeHelper::sanitizeArrayData($excel_data);
$this->myLogger->logme('error', 'Excel data sanitized count : {row_count}', ['row_count' => count($excel_data)]);
// dd($excel_data);
//check number of columns in excel
$excel_columns = ($excel_data[0]);
//check columns order in excel
$column_count_res = check_columns_name_exist($columns_to_check, $excel_columns);
// dd($column_count_res);
if (isset($column_count_res) && count($column_count_res)) {
//columns count mismatch
$message = implode("\n", $column_count_res);
// echo $message;
$this->myLogger->logme('error', ($message . ' for lead id ' . $lead_id));
$this->leadFilesModel
->where('lead_id', $lead_id)
->where('type', 2)
->set(['status' => 'failed', 'error_data' => json_encode(['error_summary' => array_count_values([6]), 'error_data' => $message])])
->update();
return ['error_summary' => [6], 'error_data' => $message];
}
$relationship = $this->general_relationships;
$column_count_res = update_excel_column_indexes($columns_to_check, $excel_columns);
// dd($column_count_res);
//remove header
unset($excel_data[0]);
$member_family_data = [];
foreach ($excel_data as $row_key => $row) {
//1. avoid empty rows
if (check_row_is_empty_or_null($row)) {
$this->myLogger->logme('error', "Empty row found at index {$row_key}, stopping row iteration");
break;
}
//iterate each row for columns validations
foreach ($row as $col_key => $col) {
$is_mandatory = $columns_to_check[$keys[$col_key]]['is_mandatory'];
$format = $columns_to_check[$keys[$col_key]]['format'];
$allowed_values = $columns_to_check[$keys[$col_key]]['allowed_values'];
$custom_function = isset($columns_to_check[$keys[$col_key]]['custom']) ? $columns_to_check[$keys[$col_key]]['custom'] : null;
$binding_params = isset($columns_to_check[$keys[$col_key]]['params']) ? $columns_to_check[$keys[$col_key]]['params'] : null;
$column_dispaly_name = $columns_to_check[$keys[$col_key]]['col_name'];
$column_index = $columns_to_check[$keys[$col_key]]['col_idx'];
$column_cell = $columns_to_check[$keys[$col_key]]['col_cell_name'];
//mandatory check
if (is_bool($is_mandatory) && $is_mandatory === true) {
if ($col == "" || $col == null) {
array_push($result['error_summary'], 1); //push error code for summary
$result['error_data'][$row_key][$keys[$col_key]]['col_name'] = $column_dispaly_name; //push column name
$result['error_data'][$row_key][$keys[$col_key]]['col_idx'] = $column_index; //push column index
$result['error_data'][$row_key][$keys[$col_key]]['error'][] = 'Value is mandatory'; //push exact error desc
}
}
//if is_mandatory is array (action based mandatory check)
if (is_array($is_mandatory)) {
$allowed_actions = $columns_to_check[$keys[$col_key]]['is_mandatory'];
if ($col == "" || $col == null) {
array_push($result['error_summary'], 1);
$result['error_data'][$row_key][$keys[$col_key]]['col_name'] = $column_dispaly_name; //push column name
$result['error_data'][$row_key][$keys[$col_key]]['col_idx'] = $column_index; //push column index
$result['error_data'][$row_key][$keys[$col_key]]['error'][] = 'Value is mandatory for this action/event';
}
}
//format check
if (isset($format)) {
$format_error = check_excel_date_format($col, $format);
if (! $format_error['status']) {
array_push($result['error_summary'], 2);
$result['error_data'][$row_key][$keys[$col_key]]['col_name'] = $column_dispaly_name; //push column name
$result['error_data'][$row_key][$keys[$col_key]]['col_idx'] = $column_index; //push column index
$result['error_data'][$row_key][$keys[$col_key]]['error'][] = $format_error['error'];
}
}
//allowed values check
if (($is_mandatory === true && isset($allowed_values) && is_array($allowed_values)) || (is_array($is_mandatory) && (isset($allowed_values) && is_array($allowed_values)))) {
if (! in_array((trim($col)), $allowed_values)) {
array_push($result['error_summary'], 3);
$result['error_data'][$row_key][$keys[$col_key]]['col_name'] = $column_dispaly_name; //push column name
$result['error_data'][$row_key][$keys[$col_key]]['col_idx'] = $column_index; //push column index
$result['error_data'][$row_key][$keys[$col_key]]['error'][] = "Value not allowed: Expected " . implode(",", $allowed_values) . " and received $col";
}
}
//custom function check
if (isset($custom_function)) {
//convert string params into PHP variables
// Create an array of variables to pass custom helper funcitons
$param_values = [];
foreach ($binding_params as $bkey => $bparam) {
$param_values[] = ($$bparam);
}
// dd(($param_values));//die();
$res = call_user_func_array($custom_function, $param_values);
if ($res['status'] === false) {
array_push($result['error_summary'], 4);
$result['error_data'][$row_key][$keys[$col_key]]['col_name'] = $column_dispaly_name; //push column name
$result['error_data'][$row_key][$keys[$col_key]]['col_idx'] = $column_index; //push column index
$result['error_data'][$row_key][$keys[$col_key]]['error'][] = $res['error'];
}
}
}
//age validation
$age_validation = isset($columns_to_check['dob']['age_validation']) ?? null;
if (isset($age_validation) && $age_validation && $age_validation_check && ! empty($family_composition)) {
$format_error = check_age_validation($row, $family_composition);
if (! $format_error['status']) {
array_push($result['error_summary'], 2);
$result['error_data'][$row_key][$keys[$columns_to_check['dob']['col_idx']]]['col_name'] = $columns_to_check['dob']['col_name']; //push column name
$result['error_data'][$row_key][$keys[$columns_to_check['dob']['col_idx']]]['col_idx'] = $columns_to_check['dob']['col_idx']; //push column index
$result['error_data'][$row_key][$keys[$columns_to_check['dob']['col_idx']]]['error'][] = $format_error['error'];
}
}
$row['row_index'] = $row_key;
$relation_idx = $columns_to_check['relationship']['col_idx'];
$emp_code_idx = $columns_to_check['emp_code']['col_idx'];
if (isset($row[$relation_idx]) && strtolower($row[$relation_idx]) == 'self' && isset($member_family_data[$row[$emp_code_idx]])) {
array_unshift($member_family_data[$row[$emp_code_idx]], $row);
} else {
$member_family_data[$row[$emp_code_idx]][] = $row;
}
}
// dd($member_family_data);
$this->myLogger->logme('info', 'Row validation completed, starting duplicate check');
$check_row_dublicate = check_duplicate_rows_and_contacts($excel_data, $columns_to_check, $result);
// dd($check_row_dublicate);
if (count($check_row_dublicate)) {
$result = $check_row_dublicate;
}
$this->myLogger->logme('info', 'Starting family validation');
$result = validateFamily($member_family_data, $columns_to_check, $result);
// dd($family_validation);
if (isset($result['error_summary']) && count($result['error_summary'])) {
$result['error_summary'] = array_count_values($result['error_summary']);
$status = 'failed';
$failure_reason = ((json_encode($result)));
$this->leadFilesModel->where('lead_id', $lead_id)->where('type', 2)->set(['status' => $status, 'error_data' => $failure_reason])->update();
$this->myLogger->logme("error", '{lead_id} uploaded failed for this lead id', ['lead_id' => $lead_id]);
} else {
$this->leadFilesModel->where('lead_id', $lead_id)->where('type', 2)->set(['status' => 'success', 'error_data' => ''])->update();
$r = Jobs::addJob(['job_name' => 'calculateMembersDemography', 'payload' => ['lead_id' => $lead_id]]);
}
return $result;
}
public function getAgeRatioFromRfqJson($lead_id, $porposel_data)
{
$age_ratio = [];
$rfq_data = $this->RFQModel->where('is_active', 1)->where('lead_id', $lead_id)->orderBy('id', 'desc')->first();
if (empty($rfq_data) || empty($rfq_data['json'])) {
return $age_ratio;
}
$data = json_decode($rfq_data['json'], true);
$proposal_and_insurer = json_decode($porposel_data, true);
$insurer_key = $proposal_and_insurer['insurer_name'] ?? null;
if (isset($data['table_data']['data'])) {
foreach ($data['table_data']['data'] as $key => $value) {
if ($value['items'] == 'family_composition') {
foreach ($value['data'] as $family_composition) {
if ($family_composition['subth'] == $insurer_key) {
$age_ratio = json_decode($family_composition['input_value'] ?? "", true) ?? [];
}
}
}
}
}
return $age_ratio;
}
public function getMemberDataExcelFileErrors()
{
$lead_id = $this->request->getGet('lead_id');
// $lead_id = 329;
// Render views and capture output
$result = $this->getMemberDataListExcelErrorData($lead_id);
if ($result != 0) {
$result['lead_id'] = $lead_id;
echo view('excel_errors', $result);
} else if ($result == 0) {
$data['message'] = 'File Not Found Physically';
return view('errors/404', $data);
} else {
echo view('errors/html/production');
}
}
public function getMemberDataListExcelErrorData($lead_id)
{
try {
$file = $this->leadFilesModel->where('lead_id', $lead_id)->where('type', 2)->where('is_active', 1)->first();
$error_data = json_decode($file['error_data']);
// dd($error_data);
// return $error_data;
$file_name_with_path = storage_ensure_local_file(WRITEPATH . 'uploads/lead_files', $file['file_name']);
//check the file exist or not
if (empty($file_name_with_path) || ! file_exists($file_name_with_path)) {
$error_message = "File not found";
$this->myLogger->logme('error', ($error_message . ' for file id ' . $lead_id));
return 0;
}
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($file_name_with_path);
$sheet = $spreadsheet->getActiveSheet();
$highestRowAndColumn = $sheet->getHighestRowAndColumn();
$excel_data = $sheet->rangeToArray('A1:' . $highestRowAndColumn['column'] . $highestRowAndColumn['row']);
$excelErrorData['excel_header'] = $excel_data[0];
unset($excel_data[0]);
// Kint::dump($excel_data);
if ($error_data->error_type == 1) {
$finalArray = [];
foreach ($error_data->error_data as $key => $value) {
foreach ($value as $key2 => $value2) {
$error_data = $value2->error;
$data = ['value' => $excel_data[$key][$value2->col_idx], 'error' => $error_data];
$excel_data[$key][$value2->col_idx] = $data;
}
array_push($finalArray, $excel_data[$key]);
}
foreach ($finalArray as $fkey => $value) {
foreach ($value as $vkey => $arrayData) {
if (! is_array($arrayData)) {
$data = ['value' => $arrayData];
$finalArray[$fkey][$vkey] = $data;
}
}
}
$excelErrorData['excel_data'] = $finalArray;
return $excelErrorData;
} else if ($error_data->error_type == 2) {
$allErrors = [];
$typeTowArray = [];
foreach ($error_data->error_data as $index => $item) {
foreach ($item as $field) {
if (! isset($allErrors[$index])) {
$allErrors[$index] = [];
}
$allErrors[$index] = array_merge($allErrors[$index], $field->error);
}
}
// dd(array_keys($allErrors));
foreach ($allErrors as $key => $value) {
// echo $key;
// print_r($value);
foreach ($excel_data as $excel_data_index => $excel_data_value) {
if ($excel_data_value[0] == $key) {
$data = ['value' => $excel_data[$excel_data_index][1], 'error' => $value];
$excel_data[$excel_data_index][1] = $data;
array_push($typeTowArray, $excel_data[$excel_data_index]);
break;
}
}
}
// dd($data);
foreach ($typeTowArray as $fkey => $value) {
foreach ($value as $vkey => $arrayData) {
if (! is_array($arrayData)) {
$data = ['value' => $arrayData];
$typeTowArray[$fkey][$vkey] = $data;
}
}
}
$excelErrorData['excel_data'] = $typeTowArray;
return $excelErrorData;
}
} catch (\Exception $e) {
// Handle any exceptions
$errorMessage = $e->getMessage(); //die();
$this->myLogger->logme('error', $errorMessage);
return false; // You can return an error response here
}
}
public function downloadFullMemberDataExcelErrorFile($lead_id, $rowIndex = 1, $colIndex = 1)
{
// Get file data from the database
$file_data = $this->leadFilesModel->where('lead_id', $lead_id)->where('type', 2)->where('is_active', 1)->first();
$error = json_decode($file_data['error_data']);
// Check if the file exists
if (! $file_data) {
$error_message = "File not found";
$this->myLogger->logme('error', $error_message . ' for file id ' . $lead_id);
return $error_message;
}
$fileName = $file_data['file_name'];
$filePath = storage_ensure_local_file(WRITEPATH . 'uploads/lead_files', $fileName);
// Check if the file exists
if (empty($filePath) || ! file_exists($filePath)) {
$error_message = "File not found";
$this->myLogger->logme('error', $error_message . ' for file id ' . $lead_id);
$data['message'] = 'Physical File Not Found';
return view('errors/404', $data);
}
// Load the Excel file
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($filePath);
$sheet = $spreadsheet->getActiveSheet();
foreach ($error->error_data as $index => $error_data) {
$rowIndex = $index + 1;
if ($error->error_type == 1) {
foreach ($error_data as $key => $value) {
$colIndex = $value->col_idx + 1;
$originalValue = $sheet->getCell([$colIndex, $rowIndex])->getValue();
$newValue = implode(', ', $value->error);
$val = $originalValue . ' ( ' . $newValue . ' )';
$sheet->setCellValue([$colIndex, $rowIndex], $val);
$style = [
'fill' => [
'fillType' => \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID,
'startColor' => ['rgb' => 'ffad99'], // Red color
],
];
$sheet->getStyle([$colIndex, $rowIndex])->applyFromArray($style);
}
} else if ($error->error_type == 2) {
foreach ($error_data as $key => $value) {
$originalValue = $sheet->getCell([1, $rowIndex])->getValue();
$newValue = implode(', ', $value->error);
$val = $originalValue . ' ( ' . $newValue . ' )';
$sheet->setCellValue([$colIndex, $rowIndex], $val);
$style = [
'fill' => [
'fillType' => \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID,
'startColor' => ['rgb' => 'ffad99'], // Red color
],
];
$sheet->getStyle([$colIndex, $rowIndex])->applyFromArray($style);
}
}
}
// Create a temporary error Excel for download only (not persisted to S3)
$newFileName = 'error_with_highlight_' . $fileName;
$tmpDir = rtrim(WRITEPATH, '/\\') . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR . 'claim_files_runtime';
if (! is_dir($tmpDir)) {
@mkdir($tmpDir, 0755, true);
}
$newFilePath = $tmpDir . DIRECTORY_SEPARATOR . $newFileName;
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
$writer->save($newFilePath);
// Set headers to force download
$response = service('response');
$response->setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
$response->setHeader('Content-Disposition', 'attachment;filename="' . $newFileName . '"');
$response->setHeader('Cache-Control', 'max-age=0');
$response->setHeader('Content-Length', filesize($newFilePath));
$response->setBody(file_get_contents($newFilePath));
// Delete the temporary file
unlink($newFilePath);
// Return the response
return $response;
}
public function savePlacementDataAndValidateMemberDataFile()
{
try {
$this->myLogger->logme('error', '--- savePlacementDataAndValidateMemberDataFile START ---');
$params = $this->request->getPost();
$this->myLogger->logme('error', 'Received params: ' . json_encode($params));
if (empty($params['lead_id'])) {
$this->myLogger->logme('error', 'Lead ID missing');
return $this->respond([
'status' => false,
'code' => 400,
'message' => 'Opportunity ID is required',
], 400);
}
$lead_id = $params['lead_id'];
$proposal_insurer = $params['proposal_insurer'] ?? '';
// Handle proposal and insurer details
if (! empty($proposal_insurer) && strpos($proposal_insurer, '-') !== false) {
list($proposal_key, $insurer_key) = explode('-', $proposal_insurer, 2);
$lead_update_data = json_encode([
'proposel_name' => $proposal_key,
'insurer_name' => $insurer_key,
'insurer' => $params['insurer_and_branch'] ?? null,
]);
$this->myLogger->logme('error', "Proposal/Insurer parsed successfully: $proposal_key - $insurer_key");
} else {
$lead_update_data = null;
$this->myLogger->logme('error', 'Proposal/Insurer not provided or invalid format');
}
$data = [
'proposel_data' => $lead_update_data,
'placement_date' => ! empty($params['placement_date']) ? change_date_format($params['placement_date']) : null,
'payment_date' => ! empty($params['payment_date']) ? change_date_format($params['payment_date']) : null,
'utr_no' => $params['utr_no'] ?? null,
'is_cd' => $params['is_cd'] ?? null,
'premium_amount' => $params['premium_amount'] ?? null,
'total_amount' => $params['total_amount'] ?? null,
'cd_amount' => $params['cd_amount'] ?? null,
'no_of_installment' => $params['no_of_installment'] ?? null,
'is_installment' => $params['is_installment'] ?? null,
'acm_id' => $params['acm_pk'] ?? null,
];
// get lead data
$lead_data = $this->leadsModel->where('id', $lead_id)->first();
// Compare and update only if changed the start and end date
if (! empty($params['policy_start_date'])) {
$converted_start = change_date_format($params['policy_start_date']);
if ($converted_start !== $lead_data['policy_start_date']) {
$data['policy_start_date'] = $converted_start;
$this->myLogger->logme('error', "Policy start date updated: $converted_start");
}
}
if (! empty($params['policy_end_date'])) {
$converted_end = change_date_format($params['policy_end_date']);
if ($converted_end !== $lead_data['policy_end_date']) {
$data['policy_end_date'] = $converted_end;
$this->myLogger->logme('error', "Policy end date updated: $converted_end");
}
}
// Handle TPA details
if (! empty($params['tpa_id']) && strpos($params['tpa_id'], '-') !== false) {
list($tpaBranchId, $tpaId) = explode('-', $params['tpa_id']);
$data['tpa_branch_id'] = $tpaBranchId;
$data['tpa_id'] = $tpaId;
$this->myLogger->logme('error', "TPA details added: branch=$tpaBranchId, id=$tpaId");
}
$this->myLogger->logme('error', 'Prepared lead update data: ' . json_encode($data));
// Update main lead record
$this->leadsModel->update($lead_id, $data);
$this->myLogger->logme('error', "Lead updated successfully for ID: $lead_id");
// Save installment details
$savedInstallments = [];
if (! empty($params['installments'])) {
$savedInstallments = $this->saveLeadInstallmentDetails((int) $lead_id, $params['installments']);
$this->myLogger->logme('error', 'Installments saved: ' . json_encode($savedInstallments));
} else {
$this->myLogger->logme('error', 'No installments provided');
}
// Update lead file status to pending
$this->leadFilesModel->where('lead_id', $lead_id)->where('type', 2)->set(['status' => 'pending'])->update();
// Queue job after save
$r = Jobs::addJob(['job_name' => 'memberDataListExcelFileFormatValidation', 'payload' => ['lead_id' => $lead_id, 'age_validation' => true]]);
$this->myLogger->logme('error', "Job queued successfully: " . json_encode($r));
$this->myLogger->logme('error', "--- savePlacementDataAndValidateMemberDataFile END (SUCCESS) ---");
return $this->respond([
'status' => true,
'code' => 200,
'message' => 'Placement data saved successfully. File being validated',
'lead_id' => $lead_id,
'installments' => $savedInstallments,
'data' => $data,
'params' => $params,
], 200);
} catch (\Exception $e) {
$errorDetails = [
'error_message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack_trace' => $e->getTraceAsString(),
];
$this->myLogger->logme('error', "--- savePlacementDataAndValidateMemberDataFile ERROR --- " . json_encode($errorDetails));
return $this->respond([
'status' => false,
'code' => 500,
'message' => 'Error while validating the member data',
'error' => $errorDetails,
], 500);
}
}
public function savePlacementDataWithoutMail()
{
try {
$params = $this->request->getPost();
if (empty($params['lead_id'])) {
return $this->respond([
'status' => false,
'code' => 400,
'message' => 'Opportunity ID is required',
], 400);
}
$lead_id = $params['lead_id'];
$lead_data = $this->leadsModel->where('id', $lead_id)->first();
if (! $lead_data) {
return $this->respond([
'status' => false,
'code' => 404,
'message' => 'Lead not found',
], 404);
}
$data = [
'placement_date' => ! empty($params['placement_date']) ? change_date_format($params['placement_date']) : null,
'payment_date' => ! empty($params['payment_date']) ? change_date_format($params['payment_date']) : null,
'utr_no' => $params['utr_no'] ?? null,
'is_cd' => $params['is_cd'] ?? null,
'premium_amount' => $params['premium_amount'] ?? null,
'total_amount' => $params['total_amount'] ?? null,
'cd_amount' => $params['cd_amount'] ?? null,
'no_of_installment' => $params['no_of_installment'] ?? null,
'is_installment' => $params['is_installment'] ?? null,
'agreed_percentage' => $params['agreed_percentage'] ?? null,
];
if (! empty($params['acm_pk'])) {
$data['acm_id'] = $params['acm_pk'];
}
if (! empty($params['policy_start_date'])) {
$converted_start = change_date_format($params['policy_start_date']);
if ($converted_start !== $lead_data['policy_start_date']) {
$data['policy_start_date'] = $converted_start;
}
}
if (! empty($params['policy_end_date'])) {
$converted_end = change_date_format($params['policy_end_date']);
if ($converted_end !== $lead_data['policy_end_date']) {
$data['policy_end_date'] = $converted_end;
}
}
if (! empty($params['tpa_id']) && strpos($params['tpa_id'], '-') !== false) {
list($tpaBranchId, $tpaId) = explode('-', $params['tpa_id']);
$data['tpa_branch_id'] = $tpaBranchId;
$data['tpa_id'] = $tpaId;
}
$this->leadsModel->update($lead_id, $data);
$savedInstallments = [];
if (! empty($params['installments'])) {
$savedInstallments = $this->saveLeadInstallmentDetails((int) $lead_id, $params['installments']);
}
return $this->respond([
'status' => true,
'code' => 200,
'message' => 'Placement data saved successfully',
'lead_id' => $lead_id,
'installments' => $savedInstallments,
], 200);
} catch (\Exception $e) {
return $this->respond([
'status' => false,
'code' => 500,
'message' => 'Error while saving placement data',
'error' => $e->getMessage(),
], 500);
}
}
public function checkMemberDataFileValidationStatus()
{
$lead_id = $this->request->getVar('lead_id');
if (empty($lead_id)) {
return $this->respond([
'status' => false,
'code' => 400,
'message' => 'lead_id is required',
], 400);
}
// Fetch file validation status
$lead_file = $this->leadFilesModel
->select('id, lead_id, status')
->where('lead_id', $lead_id)
->where('type', 2)
->first();
if (! $lead_file) {
return $this->respond([
'status' => false,
'code' => 404,
'message' => 'No file found for this lead_id',
], 404);
}
// If validation is still running
if ($lead_file['status'] === 'pending' || $lead_file['status'] === null) {
return $this->respond([
'status' => true,
'code' => 202, // Accepted - still processing
'message' => 'Validation in progress',
'data' => ['status' => $lead_file['status']],
], 200);
}
// If validation finished (success or failed)
return $this->respond([
'status' => true,
'code' => 200,
'message' => 'Validation completed',
'data' => $lead_file,
], 200);
}
// ----------- END OF MEMBER DATA VALIDAATION ------------------------------------------------------------------------------------------------------
}